├── .github └── workflows │ ├── change_version.patch │ ├── remove_dev_deps.patch │ └── tests.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── base ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── benches │ └── benchmarks.rs └── src │ ├── approx.rs │ ├── bit.rs │ ├── error.rs │ ├── lib.rs │ ├── math │ ├── inv.rs │ ├── log.rs │ ├── mod.rs │ └── root.rs │ ├── ring │ ├── div_rem.rs │ ├── gcd.rs │ ├── mod.rs │ └── root.rs │ └── sign.rs ├── benchmark ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── e.rs │ ├── fib.rs │ ├── io.rs │ ├── main.rs │ └── number.rs ├── float ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── benches │ └── primitive.rs ├── src │ ├── add.rs │ ├── cmp.rs │ ├── consts.rs │ ├── convert.rs │ ├── div.rs │ ├── error.rs │ ├── exp.rs │ ├── fbig.rs │ ├── fmt.rs │ ├── helper_macros.rs │ ├── iter.rs │ ├── lib.rs │ ├── log.rs │ ├── math │ │ └── mod.rs │ ├── mul.rs │ ├── ops.rs │ ├── parse.rs │ ├── repr.rs │ ├── root.rs │ ├── round.rs │ ├── round_ops.rs │ ├── shift.rs │ ├── sign.rs │ ├── third_party │ │ ├── mod.rs │ │ ├── num_order.rs │ │ ├── num_traits.rs │ │ ├── postgres │ │ │ ├── diesel_v1.rs │ │ │ ├── diesel_v2.rs │ │ │ ├── mod.rs │ │ │ └── postgres_types.rs │ │ ├── rand.rs │ │ ├── serde.rs │ │ └── zeroize.rs │ └── utils.rs └── tests │ ├── add.rs │ ├── cmp.rs │ ├── convert.rs │ ├── div.rs │ ├── exp.rs │ ├── helper_macros.rs │ ├── io.rs │ ├── iter.rs │ ├── log.rs │ ├── mul.rs │ ├── num_order.rs │ ├── postgres.rs │ ├── random.rs │ ├── root.rs │ ├── round.rs │ ├── serde.rs │ └── shift.rs ├── guide ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ ├── cheatsheet.md │ ├── construct.md │ ├── convert.md │ ├── faq.md │ ├── index.md │ ├── io │ ├── index.md │ ├── interop.md │ ├── parse.md │ ├── print.md │ └── serialize.md │ ├── ops │ ├── basic.md │ ├── bit.md │ ├── cmp.md │ ├── exp_log.md │ ├── index.md │ └── num_theory.md │ └── types.md ├── integer ├── CHANGELOG.md ├── Cargo.toml ├── NOTICE.md ├── README.md ├── benches │ └── primitive.rs ├── examples │ └── factorial.rs ├── src │ ├── add.rs │ ├── add_ops.rs │ ├── arch │ │ ├── generic │ │ │ ├── add.rs │ │ │ └── digits.rs │ │ ├── generic_16_bit │ │ │ ├── mod.rs │ │ │ ├── ntt.rs │ │ │ └── word.rs │ │ ├── generic_32_bit │ │ │ ├── mod.rs │ │ │ ├── ntt.rs │ │ │ └── word.rs │ │ ├── generic_64_bit │ │ │ ├── mod.rs │ │ │ ├── ntt.rs │ │ │ └── word.rs │ │ ├── mod.rs │ │ ├── x86 │ │ │ ├── add.rs │ │ │ └── mod.rs │ │ └── x86_64 │ │ │ ├── add.rs │ │ │ └── mod.rs │ ├── bits.rs │ ├── buffer.rs │ ├── cmp.rs │ ├── convert.rs │ ├── div │ │ ├── divide_conquer.rs │ │ ├── mod.rs │ │ └── simple.rs │ ├── div_const.rs │ ├── div_ops.rs │ ├── error.rs │ ├── fmt │ │ ├── digit_writer.rs │ │ ├── mod.rs │ │ ├── non_power_two.rs │ │ └── power_two.rs │ ├── gcd │ │ ├── lehmer.rs │ │ └── mod.rs │ ├── gcd_ops.rs │ ├── helper_macros.rs │ ├── ibig.rs │ ├── iter.rs │ ├── lib.rs │ ├── log.rs │ ├── math.rs │ ├── memory.rs │ ├── modular │ │ ├── add.rs │ │ ├── convert.rs │ │ ├── div.rs │ │ ├── fmt.rs │ │ ├── mod.rs │ │ ├── mul.rs │ │ ├── pow.rs │ │ ├── reducer.rs │ │ └── repr.rs │ ├── mul │ │ ├── helpers.rs │ │ ├── karatsuba.rs │ │ ├── mod.rs │ │ ├── ntt.rs │ │ ├── simple.rs │ │ └── toom_3.rs │ ├── mul_ops.rs │ ├── ops.rs │ ├── parse │ │ ├── mod.rs │ │ ├── non_power_two.rs │ │ └── power_two.rs │ ├── pow.rs │ ├── primitive.rs │ ├── radix.rs │ ├── remove.rs │ ├── repr.rs │ ├── root.rs │ ├── root_ops.rs │ ├── shift.rs │ ├── shift_ops.rs │ ├── sign.rs │ ├── sqr │ │ ├── mod.rs │ │ └── simple.rs │ ├── third_party │ │ ├── mod.rs │ │ ├── num_integer.rs │ │ ├── num_order.rs │ │ ├── num_traits.rs │ │ ├── rand.rs │ │ ├── serde.rs │ │ └── zeroize.rs │ └── ubig.rs └── tests │ ├── add.rs │ ├── bits.rs │ ├── cmp.rs │ ├── const_div.rs │ ├── convert.rs │ ├── div.rs │ ├── gcd.rs │ ├── hash.rs │ ├── helper_macros.rs │ ├── io.rs │ ├── iter.rs │ ├── log.rs │ ├── modular.rs │ ├── mul.rs │ ├── num_order.rs │ ├── pow.rs │ ├── random.rs │ ├── reducer.rs │ ├── remove.rs │ ├── root.rs │ ├── serde.rs │ ├── shift.rs │ └── sign.rs ├── macros ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── docs │ ├── dbig.md │ ├── fbig.md │ ├── ibig.md │ ├── rbig.md │ ├── static_dbig.md │ ├── static_fbig.md │ ├── static_ibig.md │ ├── static_rbig.md │ ├── static_ubig.md │ └── ubig.md ├── src │ ├── lib.rs │ └── parse │ │ ├── common.rs │ │ ├── float.rs │ │ ├── int.rs │ │ ├── mod.rs │ │ └── ratio.rs └── tests │ ├── float.rs │ ├── int.rs │ └── ratio.rs ├── python ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── dashu.pyi ├── pyproject.toml ├── src │ ├── convert.rs │ ├── float.rs │ ├── int.rs │ ├── lib.rs │ ├── ratio.rs │ ├── types.rs │ ├── utils.rs │ └── words.rs └── tests │ ├── test_convert.py │ ├── test_int.py │ └── test_words.py ├── rational ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── benches │ └── primitive.rs ├── src │ ├── add.rs │ ├── cmp.rs │ ├── convert.rs │ ├── div.rs │ ├── error.rs │ ├── fmt.rs │ ├── helper_macros.rs │ ├── iter.rs │ ├── lib.rs │ ├── mul.rs │ ├── ops.rs │ ├── parse.rs │ ├── rbig.rs │ ├── repr.rs │ ├── round.rs │ ├── sign.rs │ ├── simplify.rs │ └── third_party │ │ ├── dashu_float.rs │ │ ├── mod.rs │ │ ├── num_order.rs │ │ ├── num_traits.rs │ │ ├── rand.rs │ │ ├── serde.rs │ │ └── zeroize.rs └── tests │ ├── add.rs │ ├── cmp.rs │ ├── convert.rs │ ├── dashu_float.rs │ ├── div.rs │ ├── helper_macros.rs │ ├── io.rs │ ├── mul.rs │ ├── num_order.rs │ ├── random.rs │ ├── round.rs │ ├── serde.rs │ └── simplify.rs ├── rustfmt.toml ├── src ├── lib.rs └── macro-docs └── tests └── import.rs /.github/workflows/change_version.patch: -------------------------------------------------------------------------------- 1 | diff --git a/float/Cargo.toml b/float/Cargo.toml 2 | index 25f5bac..8b3ad61 100644 3 | --- a/float/Cargo.toml 4 | +++ b/float/Cargo.toml 5 | @@ -43,7 +43,7 @@ num-order = { optional = true, version = "1.2.0", default-features = false } 6 | serde = { optional = true, version = "1.0.130", default-features = false } 7 | zeroize = { optional = true, version = "1.5.7", default-features = false } 8 | diesel_v1 = { optional = true, version = "1.4.0", package = "diesel", default-features = false, features = ["postgres"]} 9 | -diesel_v2 = { optional = true, version = "2.0.0", package = "diesel", default-features = false, features = ["postgres_backend"]} 10 | +diesel_v2 = { optional = true, version = "=2.0.0", package = "diesel", default-features = false, features = ["postgres_backend"]} 11 | _bytes = { optional = true, version = "1.0", package = "bytes", default-features = false } 12 | 13 | # unstable dependencies 14 | -------------------------------------------------------------------------------- /.github/workflows/remove_dev_deps.patch: -------------------------------------------------------------------------------- 1 | diff --git a/base/Cargo.toml b/base/Cargo.toml 2 | index fc88cee..47d5459 100644 3 | --- a/base/Cargo.toml 4 | +++ b/base/Cargo.toml 5 | @@ -22,7 +22,6 @@ std = [] 6 | 7 | [dev-dependencies] 8 | rand = { version = "0.8.3" } 9 | -criterion = { version = "0.5.1", features = ["html_reports"] } 10 | 11 | [[bench]] 12 | name = "benchmarks" 13 | diff --git a/float/Cargo.toml b/float/Cargo.toml 14 | index afc2141..c633147 100644 15 | --- a/float/Cargo.toml 16 | +++ b/float/Cargo.toml 17 | @@ -59,9 +59,6 @@ rand_v08 = { version = "0.8.3", package = "rand" } 18 | postcard = { version = "1.0.2", features = ["alloc"] } 19 | serde_test = { version = "1.0.130" } 20 | serde_json = { version = "1.0" } 21 | -postgres = { version = "0.19.4" } 22 | - 23 | -criterion = { version = "0.5.1", features = ["html_reports"] } 24 | 25 | [[test]] 26 | name = "random" 27 | diff --git a/integer/Cargo.toml b/integer/Cargo.toml 28 | index a6a358f..128b7ca 100644 29 | --- a/integer/Cargo.toml 30 | +++ b/integer/Cargo.toml 31 | @@ -48,8 +48,6 @@ postcard = { version = "1.0.2", features = ["alloc"] } 32 | serde_test = { version = "1.0.130" } 33 | serde_json = { version = "1.0" } 34 | 35 | -criterion = { version = "0.5.1", features = ["html_reports"] } 36 | - 37 | [lib] 38 | bench = false 39 | 40 | diff --git a/rational/Cargo.toml b/rational/Cargo.toml 41 | index 93a7c96..772b479 100644 42 | --- a/rational/Cargo.toml 43 | +++ b/rational/Cargo.toml 44 | @@ -53,8 +53,6 @@ postcard = { version = "1.0.2", features = ["alloc"] } 45 | serde_test = { version = "1.0.130" } 46 | serde_json = { version = "1.0" } 47 | 48 | -criterion = { version = "0.5.1", features = ["html_reports"] } 49 | - 50 | [lib] 51 | bench = false 52 | 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /target 3 | /Cargo.lock 4 | /.vscode/settings.json 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu" 3 | version = "0.4.2" 4 | edition = "2021" 5 | authors = ["Jacob Zhong "] 6 | description = "A library set of big numbers" 7 | keywords = ["bignum", "numerics", "mathematics", "math"] 8 | categories = ["mathematics", "no-std", "parsing", "value-formatting"] 9 | repository = "https://github.com/cmpute/dashu" 10 | homepage = "https://github.com/cmpute/dashu" 11 | documentation = "https://docs.rs/dashu" 12 | readme = "README.md" 13 | license = "MIT OR Apache-2.0" 14 | include = ["/src"] 15 | rust-version = "1.61" 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | 20 | [workspace] 21 | members = [ 22 | "base", 23 | "benchmark", 24 | "integer", 25 | "float", 26 | "macros", 27 | "python", 28 | "rational", 29 | ] 30 | default-members = ["base", "integer", "float", "rational", "macros"] 31 | 32 | [features] 33 | default = ["std", "num-order"] 34 | std = ["dashu-base/std", "dashu-int/std", "dashu-float/std", "dashu-ratio/std"] 35 | 36 | # stable features 37 | serde = ["dashu-int/serde", "dashu-float/serde", "dashu-ratio/serde"] 38 | num-order = ["dashu-int/num-order", "dashu-float/num-order", "dashu-ratio/num-order"] 39 | zeroize = ["dashu-int/zeroize", "dashu-float/zeroize", "dashu-ratio/zeroize"] 40 | 41 | # unstable features 42 | rand = ["dashu-int/rand", "dashu-float/rand", "dashu-ratio/rand"] 43 | rand_v08 = ["dashu-int/rand_v08", "dashu-float/rand_v08", "dashu-ratio/rand_v08"] 44 | num-traits = ["dashu-int/num-traits", "dashu-float/num-traits", "dashu-ratio/num-traits"] 45 | num-traits_v02 = ["dashu-int/num-traits_v02", "dashu-float/num-traits_v02", "dashu-ratio/num-traits_v02"] 46 | 47 | # this feature enables all related features related to decimal crates. 48 | decimal-extras = ["dashu-float/postgres-types", "dashu-float/diesel"] 49 | 50 | [dependencies] 51 | rustversion = "1.0" 52 | 53 | # all crates under dashu will have the same major version, 54 | # but the minor and patch versions can be different. 55 | dashu-base = { version = "0.4.0", default-features = false, path = "./base" } 56 | dashu-int = { version = "0.4.1", default-features = false, path = "./integer" } 57 | dashu-float = { version = "0.4.2", default-features = false, path = "./float" } 58 | dashu-ratio = { version = "0.4.1", default-features = false, path = "./rational", features = ['dashu-float'] } 59 | dashu-macros = { version = "0.4.1", default-features = false, path = "./macros" } 60 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jacob Zhong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dashu 2 | 3 | [![Crate](https://img.shields.io/crates/v/dashu.svg)](https://crates.io/crates/dashu) 4 | [![Docs](https://docs.rs/dashu/badge.svg)](https://docs.rs/dashu) 5 | [![Tests](https://github.com/cmpute/dashu/actions/workflows/tests.yml/badge.svg)](https://github.com/cmpute/dashu/actions) 6 | [![MSRV 1.61](https://img.shields.io/badge/rustc-1.61%2B-informational.svg)](#dashu) 7 | [![License](https://img.shields.io/crates/l/dashu)](#license) 8 | 9 | 10 | A library set of arbitrary precision numbers (aka. big numbers) implemented in Rust. It's intended to be a Rust native alternative to GNU GMP + MPFR (+ MPC in future). It features: 11 | - Pure rust, full `no_std` support. 12 | - Focus on ergonomics & readability, and then efficiency. 13 | - Optimized speed and memory usage. 14 | - Current MSRV is 1.61. 15 | 16 | ## Sub-crates 17 | 18 | - [`dashu-base`](./base): Common trait definitions 19 | - [`dashu-int`](./integer): Arbitrary precision integers 20 | - [`dashu-float`](./float): Arbitrary precision floating point numbers 21 | - [`dashu-ratio`](./rational): Arbitrary precision rational numbers 22 | - [`dashu-macros`](./macros): Macros for creating big numbers 23 | 24 | `dashu` is a meta crate that re-exports all the types from these sub-crates. Please see the README.md in each subdirectory for crate-specific introduction. 25 | 26 | ## License 27 | 28 | Licensed under either of 29 | 30 | * Apache License, Version 2.0 31 | ([LICENSE-APACHE](../LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) 32 | * MIT license 33 | ([LICENSE-MIT](../LICENSE-MIT) or https://opensource.org/licenses/MIT) 34 | 35 | at your option. 36 | 37 | ## Contribution 38 | 39 | Unless you explicitly state otherwise, any contribution intentionally submitted 40 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 41 | dual licensed as above, without any additional terms or conditions. 42 | -------------------------------------------------------------------------------- /base/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.1 4 | 5 | - Mark `AbsEq` as deprecated. 6 | - Re-implement functions `next_up` and `next_down`, and expose them through the `utils` module. 7 | 8 | ## 0.4.0 9 | 10 | ### Add 11 | 12 | - Add `is_positive()` and `is_negative()` to the `Signed` trait. 13 | 14 | ### Change 15 | 16 | - `SquareRoot` and `CubicRoot` are moved to `dashu_base::math`. 17 | - `AbsCmp` is renamed to `AbsOrd`. 18 | - `FBig::square` and `Context::square` are renamed to `sqr`. 19 | 20 | ## 0.3.1 21 | 22 | - Add trait `Inverse`. 23 | - Implement `AbsCmp` and `AbsEq` for primitive types. 24 | 25 | ## 0.3.0 26 | 27 | ### Add 28 | 29 | - Add trait `AbsCmp` and `AbsEq` 30 | - Add trait `FloatEncoding` and implement it for `f32` and `f64` 31 | - Add trait `Signed` and implement it for all signed primitive types 32 | - Add conversion between `Sign` and `bool` 33 | - Implement `Abs` for `f32` and `f64` 34 | - Add types `error::{ConversionError, ParseError}` (originates from `dashu-int`) 35 | - Add trait `SquareRoot`, `SquareRootRem`, `CubicRoot`, `CubicRootRem` 36 | - Implement `EstimatedLog2` for `f32`, `f64` and signed integers 37 | 38 | ### Change 39 | 40 | - `trailing_zeros` has been removed from the `BitTest` trait 41 | - The definition of `BitTest::bit_len` has changed, and `BitTest` is now implemented for signed integers. 42 | 43 | ### Remove 44 | 45 | - `Root` and `RootRem` are removed (use `SquareRoot`, `SquareRootRem`, etc. instead) 46 | 47 | ## 0.2.1 48 | 49 | - Implement `RootRem` for `u8`, `u16`, `u32` 50 | - Add trait `Root` and implement it for `u8`, `u16`, `u32`, `u64`, `u128` 51 | 52 | ## 0.2.0 53 | 54 | - Add traits `Approximation`, `Sign` and `EstimatedLog2`. 55 | 56 | ## 0.1.1 57 | 58 | - Fix the bug of the GCD algorithm. 59 | 60 | ## 0.1.0 (Initial release) 61 | 62 | - including several common trait definitions. 63 | -------------------------------------------------------------------------------- /base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-base" 3 | version = "0.4.1" 4 | authors = ["Jacob Zhong "] 5 | edition = "2021" 6 | description = "Common trait definitions and tools for the `dashu` libraries" 7 | keywords = ["mathematics", "numerics"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-base" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | default = ["std"] 21 | std = [] 22 | 23 | [dev-dependencies] 24 | rand = { version = "0.8.3" } 25 | criterion = { version = "0.5.1", features = ["html_reports"] } 26 | 27 | [[bench]] 28 | name = "benchmarks" 29 | harness = false 30 | -------------------------------------------------------------------------------- /base/README.md: -------------------------------------------------------------------------------- 1 | # dashu-base 2 | 3 | Common trait definitions for `dashu` crates. See [Docs.rs](https://docs.rs/dashu-base/latest/dashu_base/) for the full documentation. 4 | 5 | ## License 6 | 7 | See the [top-level readme](../README.md). 8 | -------------------------------------------------------------------------------- /base/benches/benchmarks.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks. 2 | 3 | use criterion::{ 4 | black_box, criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, 5 | PlotConfiguration, 6 | }; 7 | 8 | use dashu_base::{CubicRoot, ExtendedGcd, Gcd, SquareRoot}; 9 | use rand::prelude::*; 10 | 11 | const SEED: u64 = 1; 12 | 13 | macro_rules! uop_case { 14 | ($t:ty, $bits:literal, $method:ident, $rng:ident, $group:ident) => { 15 | let bits = $bits; 16 | let a: $t = $rng.gen_range(0..1 << $bits); 17 | $group.bench_with_input(BenchmarkId::from_parameter(bits), &bits, |bencher, _| { 18 | bencher.iter(|| black_box(a).$method()) 19 | }); 20 | }; 21 | } 22 | 23 | macro_rules! binop_case { 24 | ($t:ty, $bits:literal, $method:ident, $rng:ident, $group:ident) => { 25 | let bits = $bits; 26 | let a: $t = $rng.gen_range(0..1 << $bits); 27 | let b: $t = $rng.gen_range(0..1 << $bits); 28 | $group.bench_with_input(BenchmarkId::from_parameter(bits), &bits, |bencher, _| { 29 | bencher.iter(|| black_box(a).$method(black_box(b))) 30 | }); 31 | }; 32 | } 33 | 34 | fn bench_gcd(criterion: &mut Criterion) { 35 | let mut rng = StdRng::seed_from_u64(SEED); 36 | let mut group = criterion.benchmark_group("gcd"); 37 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 38 | 39 | binop_case!(u16, 10, gcd, rng, group); 40 | binop_case!(u32, 20, gcd, rng, group); 41 | binop_case!(u64, 40, gcd, rng, group); 42 | binop_case!(u128, 80, gcd, rng, group); 43 | binop_case!(u128, 120, gcd, rng, group); 44 | 45 | group.finish(); 46 | 47 | let mut group = criterion.benchmark_group("gcd_ext"); 48 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 49 | 50 | binop_case!(u16, 10, gcd_ext, rng, group); 51 | binop_case!(u32, 20, gcd_ext, rng, group); 52 | binop_case!(u64, 40, gcd_ext, rng, group); 53 | binop_case!(u128, 80, gcd_ext, rng, group); 54 | binop_case!(u128, 120, gcd_ext, rng, group); 55 | 56 | group.finish(); 57 | } 58 | 59 | fn bench_roots(criterion: &mut Criterion) { 60 | let mut rng = StdRng::seed_from_u64(SEED); 61 | let mut group = criterion.benchmark_group("sqrt"); 62 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 63 | 64 | uop_case!(u16, 10, sqrt, rng, group); 65 | uop_case!(u32, 20, sqrt, rng, group); 66 | uop_case!(u64, 40, sqrt, rng, group); 67 | uop_case!(u128, 80, sqrt, rng, group); 68 | uop_case!(u128, 120, sqrt, rng, group); 69 | 70 | group.finish(); 71 | 72 | let mut group = criterion.benchmark_group("cbrt"); 73 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 74 | 75 | uop_case!(u16, 10, cbrt, rng, group); 76 | uop_case!(u32, 20, cbrt, rng, group); 77 | uop_case!(u64, 40, cbrt, rng, group); 78 | uop_case!(u128, 80, cbrt, rng, group); 79 | uop_case!(u128, 120, cbrt, rng, group); 80 | 81 | group.finish(); 82 | } 83 | 84 | criterion_group!(benches, bench_gcd, bench_roots,); 85 | 86 | criterion_main!(benches); 87 | -------------------------------------------------------------------------------- /base/src/approx.rs: -------------------------------------------------------------------------------- 1 | //! Trait definitions for approximated values 2 | 3 | /// Represent an calculation result with a possible error. 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub enum Approximation { 6 | /// The result is exact, contains the result value 7 | Exact(T), 8 | 9 | /// The result is inexact, contains the result value and error 10 | Inexact(T, E), 11 | } 12 | 13 | impl Approximation { 14 | /// Get the value of the calculation regardless of error 15 | #[inline] 16 | pub fn value(self) -> T { 17 | match self { 18 | Self::Exact(v) => v, 19 | Self::Inexact(v, _) => v, 20 | } 21 | } 22 | 23 | /// Get a reference to the calculation result 24 | #[inline] 25 | pub const fn value_ref(&self) -> &T { 26 | match self { 27 | Self::Exact(v) => v, 28 | Self::Inexact(v, _) => v, 29 | } 30 | } 31 | 32 | #[inline] 33 | pub fn unwrap(self) -> T { 34 | match self { 35 | Self::Exact(val) => val, 36 | Self::Inexact(_, _) => panic!("called `Approximation::unwrap()` on a `Inexact` value"), 37 | } 38 | } 39 | 40 | #[inline] 41 | pub fn error(self) -> Option { 42 | match self { 43 | Self::Exact(_) => None, 44 | Self::Inexact(_, e) => Some(e), 45 | } 46 | } 47 | 48 | #[inline] 49 | pub const fn error_ref(&self) -> Option<&E> { 50 | match self { 51 | Self::Exact(_) => None, 52 | Self::Inexact(_, e) => Some(e), 53 | } 54 | } 55 | 56 | #[inline] 57 | pub fn map(self, f: F) -> Approximation 58 | where 59 | F: FnOnce(T) -> U, 60 | { 61 | match self { 62 | Self::Exact(v) => Approximation::Exact(f(v)), 63 | Self::Inexact(v, e) => Approximation::Inexact(f(v), e), 64 | } 65 | } 66 | 67 | #[inline] 68 | pub fn and_then(self, f: F) -> Approximation 69 | where 70 | F: FnOnce(T) -> Approximation, 71 | { 72 | match self { 73 | Self::Exact(v) => match f(v) { 74 | Approximation::Exact(v2) => Approximation::Exact(v2), 75 | Approximation::Inexact(v2, e) => Approximation::Inexact(v2, e), 76 | }, 77 | Self::Inexact(v, e) => match f(v) { 78 | Approximation::Exact(v2) => Approximation::Inexact(v2, e), 79 | Approximation::Inexact(v2, e2) => Approximation::Inexact(v2, e2), 80 | }, 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /base/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error types. 2 | 3 | use core::fmt::{self, Display, Formatter}; 4 | 5 | /// Number out of bounds. 6 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 7 | pub enum ConversionError { 8 | /// The number is not in the representation range 9 | OutOfBounds, 10 | /// The conversion will cause a loss of precision 11 | LossOfPrecision, 12 | } 13 | 14 | impl Display for ConversionError { 15 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 16 | match self { 17 | ConversionError::OutOfBounds => f.write_str("number out of bounds"), 18 | ConversionError::LossOfPrecision => f.write_str("number can't be converted losslessly"), 19 | } 20 | } 21 | } 22 | 23 | #[cfg(feature = "std")] 24 | impl std::error::Error for ConversionError {} 25 | 26 | /// Error parsing a number. 27 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 28 | pub enum ParseError { 29 | /// No digits in the string. 30 | NoDigits, 31 | /// Invalid digit for a given radix. 32 | InvalidDigit, 33 | /// The radix is not supported. 34 | UnsupportedRadix, 35 | /// The radices of different components of the number are different 36 | InconsistentRadix, 37 | } 38 | 39 | impl Display for ParseError { 40 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 41 | match self { 42 | ParseError::NoDigits => f.write_str("no digits"), 43 | ParseError::InvalidDigit => f.write_str("invalid digit"), 44 | ParseError::UnsupportedRadix => f.write_str("unsupported radix"), 45 | ParseError::InconsistentRadix => f.write_str("inconsistent radix"), 46 | } 47 | } 48 | } 49 | 50 | #[cfg(feature = "std")] 51 | impl std::error::Error for ParseError {} 52 | -------------------------------------------------------------------------------- /base/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains general trait definitions and some commonly used structs and enums. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | pub mod approx; 6 | pub mod bit; 7 | pub mod error; 8 | pub mod math; 9 | pub mod ring; 10 | pub mod sign; 11 | 12 | /// Some useful utility functions that are also used internally in this crate. 13 | pub mod utils { 14 | pub use super::math::log::{next_down, next_up}; 15 | } 16 | 17 | pub use approx::*; 18 | pub use bit::*; 19 | pub use error::*; 20 | pub use math::*; 21 | pub use ring::*; 22 | pub use sign::*; 23 | -------------------------------------------------------------------------------- /base/src/math/inv.rs: -------------------------------------------------------------------------------- 1 | use super::Inverse; 2 | 3 | impl Inverse for f32 { 4 | type Output = f32; 5 | #[inline] 6 | fn inv(self) -> f32 { 7 | 1.0 / self 8 | } 9 | } 10 | 11 | impl Inverse for &f32 { 12 | type Output = f32; 13 | #[inline] 14 | fn inv(self) -> f32 { 15 | 1.0 / *self 16 | } 17 | } 18 | 19 | impl Inverse for f64 { 20 | type Output = f64; 21 | #[inline] 22 | fn inv(self) -> f64 { 23 | 1.0 / self 24 | } 25 | } 26 | 27 | impl Inverse for &f64 { 28 | type Output = f64; 29 | #[inline] 30 | fn inv(self) -> f64 { 31 | 1.0 / *self 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /base/src/math/mod.rs: -------------------------------------------------------------------------------- 1 | //! Trait definitions for math operations 2 | 3 | /// Fast estimation of the binary logarithm of a number 4 | /// 5 | /// # Panics 6 | /// 7 | /// Panics if the number is 0 8 | /// 9 | /// # Examples 10 | /// 11 | /// ``` 12 | /// use dashu_base::EstimatedLog2; 13 | /// 14 | /// let lb3 = 1.584962500721156f32; 15 | /// let (lb3_lb, lb3_ub) = 3u8.log2_bounds(); 16 | /// assert!(lb3_lb <= lb3 && lb3 <= lb3_ub); 17 | /// assert!((lb3 - lb3_lb) / lb3 < 1. / 256.); 18 | /// assert!((lb3_ub - lb3) / lb3 <= 1. / 256.); 19 | /// 20 | /// let lb3_est = 3u8.log2_est(); 21 | /// assert!((lb3 - lb3_est).abs() < 1e-3); 22 | /// ``` 23 | pub trait EstimatedLog2 { 24 | /// Estimate the bounds of the binary logarithm. 25 | /// 26 | /// The result is `(lower bound, upper bound)` such that `lower bound ≤ log2(self) ≤ upper bound`. 27 | /// The precision of the bounds must be at least 8 bits (relative error < 2^-8). 28 | /// 29 | /// With `std` disabled, the precision is about 13 bits. With `std` enabled, the precision 30 | /// can be full 24 bits. But the exact precision is not guaranteed and should not be not 31 | /// relied on. 32 | /// 33 | /// For negative values, the logarithm is calculated based on its absolute value. If the number 34 | /// is zero, then negative infinity will be returned. 35 | /// 36 | fn log2_bounds(&self) -> (f32, f32); 37 | 38 | /// Estimate the value of the binary logarithm. It's calculated as the 39 | /// average of [log2_bounds][EstimatedLog2::log2_bounds] by default. 40 | #[inline] 41 | fn log2_est(&self) -> f32 { 42 | let (lb, ub) = self.log2_bounds(); 43 | (lb + ub) / 2. 44 | } 45 | } 46 | 47 | /// Compute the multiplicative inverse (aka. reciprocal) of the number. 48 | /// 49 | /// # Examples 50 | /// 51 | /// ``` 52 | /// # use dashu_base::Inverse; 53 | /// assert_eq!(0.1234.inv(), 8.103727714748784); 54 | /// assert_eq!(f32::INFINITY.inv(), 0f32); 55 | /// ``` 56 | pub trait Inverse { 57 | type Output; 58 | fn inv(self) -> Self::Output; 59 | } 60 | 61 | /// Compute the square root of the number. 62 | /// 63 | /// The result should be rounded towards zero by default. 64 | /// 65 | /// # Examples 66 | /// 67 | /// ``` 68 | /// # use dashu_base::SquareRoot; 69 | /// assert_eq!(256u32.sqrt(), 16); 70 | /// assert_eq!(257u32.sqrt(), 16); 71 | /// ``` 72 | pub trait SquareRoot { 73 | type Output; 74 | 75 | fn sqrt(&self) -> Self::Output; 76 | } 77 | 78 | /// Compute the cubic root of the number. 79 | /// 80 | /// The result should be rounded towards zero by default. 81 | /// 82 | /// # Examples 83 | /// 84 | /// ``` 85 | /// # use dashu_base::CubicRoot; 86 | /// assert_eq!(216u32.cbrt(), 6); 87 | /// assert_eq!(217u32.cbrt(), 6); 88 | /// ``` 89 | pub trait CubicRoot { 90 | type Output; 91 | 92 | fn cbrt(&self) -> Self::Output; 93 | } 94 | 95 | mod inv; 96 | pub(crate) mod log; 97 | mod root; 98 | -------------------------------------------------------------------------------- /base/src/math/root.rs: -------------------------------------------------------------------------------- 1 | use super::{CubicRoot, SquareRoot}; 2 | use crate::{CubicRootRem, NormalizedRootRem, SquareRootRem}; 3 | 4 | // TODO(next): forward sqrt to f32/f64 if std is enabled and the input is small enough. 5 | // Implement after we have a benchmark. See https://github.com/Aatch/ramp/blob/master/src/int.rs#L579. 6 | 7 | impl SquareRoot for u8 { 8 | type Output = u8; 9 | 10 | #[inline] 11 | fn sqrt(&self) -> Self::Output { 12 | self.sqrt_rem().0 13 | } 14 | } 15 | 16 | impl CubicRoot for u8 { 17 | type Output = u8; 18 | 19 | #[inline] 20 | fn cbrt(&self) -> Self::Output { 21 | self.cbrt_rem().0 22 | } 23 | } 24 | 25 | macro_rules! impl_root_using_rootrem { 26 | ($t:ty, $half:ty) => { 27 | impl SquareRoot for $t { 28 | type Output = $half; 29 | 30 | #[inline] 31 | fn sqrt(&self) -> $half { 32 | if *self == 0 { 33 | return 0; 34 | } 35 | 36 | // normalize the input and call the normalized subroutine 37 | let shift = self.leading_zeros() & !1; // make sure shift is divisible by 2 38 | let (root, _) = (self << shift).normalized_sqrt_rem(); 39 | root >> (shift / 2) 40 | } 41 | } 42 | 43 | impl CubicRoot for $t { 44 | type Output = $half; 45 | 46 | #[inline] 47 | fn cbrt(&self) -> $half { 48 | if *self == 0 { 49 | return 0; 50 | } 51 | 52 | // normalize the input and call the normalized subroutine 53 | let mut shift = self.leading_zeros(); 54 | shift -= shift % 3; // make sure shift is divisible by 3 55 | let (root, _) = (self << shift).normalized_cbrt_rem(); 56 | root >> (shift / 3) 57 | } 58 | } 59 | }; 60 | } 61 | 62 | impl_root_using_rootrem!(u16, u8); 63 | impl_root_using_rootrem!(u32, u16); 64 | impl_root_using_rootrem!(u64, u32); 65 | impl_root_using_rootrem!(u128, u64); 66 | -------------------------------------------------------------------------------- /base/src/ring/div_rem.rs: -------------------------------------------------------------------------------- 1 | use super::{DivEuclid, DivRem, DivRemAssign, DivRemEuclid, RemEuclid}; 2 | 3 | macro_rules! impl_div_rem_ops_prim { 4 | ($($T:ty)*) => {$( 5 | impl DivRem for $T { 6 | type OutputDiv = $T; 7 | type OutputRem = $T; 8 | #[inline] 9 | fn div_rem(self, rhs: $T) -> ($T, $T) { 10 | (self / rhs, self % rhs) 11 | } 12 | } 13 | impl DivRemAssign for $T { 14 | type OutputRem = $T; 15 | #[inline] 16 | fn div_rem_assign(&mut self, rhs: $T) -> $T { 17 | let r = *self % rhs; 18 | *self /= rhs; 19 | r 20 | } 21 | } 22 | impl DivEuclid for $T { 23 | type Output = $T; 24 | #[inline] 25 | fn div_euclid(self, rhs: $T) -> $T { 26 | <$T>::div_euclid(self, rhs) 27 | } 28 | } 29 | impl RemEuclid for $T { 30 | type Output = $T; 31 | #[inline] 32 | fn rem_euclid(self, rhs: $T) -> $T { 33 | <$T>::rem_euclid(self, rhs) 34 | } 35 | } 36 | impl DivRemEuclid for $T { 37 | type OutputDiv = $T; 38 | type OutputRem = $T; 39 | #[inline] 40 | fn div_rem_euclid(self, rhs: $T) -> ($T, $T) { 41 | let (q, r) = (self / rhs, self % rhs); 42 | 43 | // depending on compiler to simplify the case for unsinged integers 44 | #[allow(unused_comparisons)] 45 | if r >= 0 { 46 | (q, r) 47 | } else if rhs >= 0{ 48 | (q - 1, r + rhs) 49 | } else { 50 | (q + 1, r - rhs) 51 | } 52 | } 53 | } 54 | )*} 55 | } 56 | impl_div_rem_ops_prim!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use super::*; 61 | 62 | #[test] 63 | fn test_simple() { 64 | assert_eq!(7u32.div_rem(4), (1, 3)); 65 | assert_eq!(7u32.div_rem_euclid(4), (1, 3)); 66 | assert_eq!(7i32.div_rem(-4), (-1, 3)); 67 | assert_eq!(7i32.div_rem_euclid(-4), (-1, 3)); 68 | assert_eq!((-7i32).div_rem(4), (-1, -3)); 69 | assert_eq!((-7i32).div_rem_euclid(4), (-2, 1)); 70 | assert_eq!((-7i32).div_rem(-4), (1, -3)); 71 | assert_eq!((-7i32).div_rem_euclid(-4), (2, 1)); 72 | 73 | let mut n = 7u32; 74 | let r = n.div_rem_assign(4); 75 | assert!(n == 1 && r == 3); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /benchmark/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benchmark" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | clap = { version = "4.4.6", features = ["derive"] } 9 | 10 | # meta crates 11 | dashu = { path = ".." } 12 | malachite = "0.4.2" 13 | num = "0.4.1" 14 | rug = { version = "1.22.0", optional = true } 15 | rust-gmp = { version = "0.5.0", optional = true } 16 | 17 | # integer crates 18 | ibig = "0.3.6" 19 | ramp = { version = "0.7.0", optional = true } 20 | 21 | # float crates 22 | bigdecimal = "0.4.2" 23 | 24 | [features] 25 | gmp = ["rug", "rust-gmp"] 26 | -------------------------------------------------------------------------------- /benchmark/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tomek Czajka 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 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | Benchmarks for arbitrary precision numbers with Rust implementations. The code is adopted from [bigint-benchmark-rs](https://github.com/tczajka/bigint-benchmark-rs), and see [LICENSE](./LICENSE) for the original license. 2 | 3 | ## Libraries 4 | 5 | | Library | Version | Notes | Supported Types | 6 | | -------------- | ------- | ------ | --------------- | 7 | | [dashu](https://crates.io/crates/dashu) | 0.4.0 | Pure Rust, no_std | integer, float, decimal, rational | 8 | | [rug](https://crates.io/crates/rug) | 1.22.0 | Links to libc and [GMP](https://gmplib.org/) | integer, float, rational, complex | 9 | | [rust-gmp](https://crates.io/crates/rust-gmp) | 0.5.0 | Links to libc and [GMP](https://gmplib.org/) | integer, float, rational | 10 | | [ibig](https://crates.io/crates/ibig) | 0.3.6 | Pure Rust, no_std | integer | 11 | | [malachite](https://crates.io/crates/malachite) | 0.4.2 | Pure Rust, LGPL, derived from GMP and FLINT | integer, rational | 12 | | [num](https://crates.io/crates/num) | 0.4.1 | Pure Rust, no_std | integer, rational, complex | 13 | | [ramp](https://crates.io/crates/ramp) | 0.7.0 | Requires nightly Rust, uses x86_64 assembly | integer | 14 | | [bigdecimal](https://crates.io/crates/bigdecimal) | 0.4.2 | Pure Rust | decimal | 15 | 16 | ## Tasks 17 | 18 | | Task | Description | Number Type | Difficulty | Algorithm | Operations | 19 | | ---- | --------- | ----------- | ---------- | --------- | ---------- | 20 | | `e` | n digits of e | Integer | Hard | Binary splitting | addition, multiplication, division, exponentiation, base conversion | 21 | | `e_decimal` | n digits of e | Decimal | - | Depends | - | 22 | | `fib` | n-th Fibonnaci number | Integer | Medium | Matrix exponentiation | addition, multiplication, base conversion | 23 | | `fib_hex` | n-th Fibonnaci number in hex | Integer | Easy | Matrix exponentiation | addition, multiplication | 24 | | `fib_ratio` | n-th modified Fibonnaci number | Rational | - | - | - | 25 | 26 | ## Usage examples 27 | 28 | - Print results: 29 | - Integer: `cargo run -- --lib dashu --lib num --lib malachite --lib ibig --task e -n 100 print` 30 | - Rational: `cargo run -- --lib dashu --lib num --lib malachite --task fib_ratio -n 100 print` 31 | - Decimal Float: `cargo run -- --lib dashu --lib bigdecimal --task e_decimal -n 100 print` 32 | - Run the benchmark: change `print` to `exec` in the commands above and select a larger `n`. 33 | -------------------------------------------------------------------------------- /benchmark/src/e.rs: -------------------------------------------------------------------------------- 1 | use crate::number::Natural; 2 | use std::f64; 3 | 4 | /// n digits of the number e. 5 | pub(crate) fn calculate(n: u32) -> String { 6 | assert!(n > 0); 7 | // Find k such that log_10 k! is approximately n + 50. 8 | // This makes 1 / k! and subsequent terms small enough. 9 | // Use Stirling's approximation: ln k! ~= k ln k - k + 0.5 * ln(2*pi*k). 10 | let k: u32 = binary_search(|k| { 11 | k > 0 && { 12 | let k = k as f64; 13 | let ln_k_factorial = k * k.ln() - k + 0.5 * (f64::consts::TAU * k).ln(); 14 | let log_10_k_factorial = ln_k_factorial / f64::consts::LN_10; 15 | log_10_k_factorial >= (n + 50) as f64 16 | } 17 | }); 18 | 19 | // 1/1! + ... + 1/(k-1)! 20 | let (p, q) = sum_terms::(0, k - 1); 21 | // Add 1/0! = 1. 22 | let p = p + &q; 23 | // e ~= p/q. 24 | // Calculate p/q * 10^(n-1) to get the answer as an integer. 25 | let answer_int = p * T::from(10u32).pow(n - 1) / q; 26 | let mut answer = answer_int.to_string(); 27 | // Insert the decimal period. 28 | answer.insert(1, '.'); 29 | answer 30 | } 31 | 32 | /// a! * (1/(a+1)! + 1/(a+2)! + ... + 1/b!) as a fraction p / q. 33 | /// q = (a+1) * (a+2) * ... * (b-1) * b 34 | /// p = (a+2)...b + (a+3)...b + ... + 1 35 | fn sum_terms(a: u32, b: u32) -> (T, T) { 36 | if b == a + 1 { 37 | (1u32.into(), b.into()) 38 | } else { 39 | let mid = (a + b) / 2; 40 | let (p_left, q_left) = sum_terms::(a, mid); 41 | let (p_right, q_right) = sum_terms::(mid, b); 42 | // p / q = p_left / q_left + a!/mid! * p_right / q_right 43 | // a! / mid! = 1 / q_left 44 | // p / q = (p_left * q_right + p_right) / (q_left * q_right) 45 | (p_left * &q_right + p_right, q_left * q_right) 46 | } 47 | } 48 | 49 | // Find k such that f(k) is true. 50 | fn binary_search bool>(f: F) -> u32 { 51 | let mut a = 0; 52 | let mut b = 1; 53 | while !f(b) { 54 | a = b; 55 | b *= 2; 56 | } 57 | while b - a > 1 { 58 | let m = a + (b - a) / 2; 59 | if f(m) { 60 | b = m; 61 | } else { 62 | a = m; 63 | } 64 | } 65 | b 66 | } 67 | -------------------------------------------------------------------------------- /benchmark/src/fib.rs: -------------------------------------------------------------------------------- 1 | use crate::number::{Natural, Rational}; 2 | 3 | // Using matrix exponentiation: [[1,0],[1,1]]^n = [[fib(n-1), fib(n)], [(fib(n), fib(n+1)]] 4 | // 5 | // If follows that: 6 | // fib(2n) = fib(n) * (2 * fib(n+1) - fib(n)) 7 | // fib(2n+1) = fib(n)^2 + fib(n+1)^2 8 | // fib(2n+2) = fib(2n) + fib(2n+1) 9 | 10 | /// Fibonacci(n) in decimal 11 | pub(crate) fn calculate_decimal(n: u32) -> String { 12 | calculate::(n).to_string() 13 | } 14 | 15 | /// Fibonacci(n) in hexadecimal 16 | pub(crate) fn calculate_hex(n: u32) -> String { 17 | calculate::(n).to_hex() 18 | } 19 | 20 | fn calculate(n: u32) -> T { 21 | let (a, b) = fib::(n / 2); 22 | if n % 2 == 0 { 23 | (T::from(2) * b - &a) * a 24 | } else { 25 | a.mul_ref(&a) + b.mul_ref(&b) 26 | } 27 | } 28 | 29 | // (fib(n), fib(n+1)) 30 | fn fib(n: u32) -> (T, T) { 31 | if n == 0 { 32 | (T::from(0), T::from(1)) 33 | } else { 34 | let (a, b) = fib::(n / 2); 35 | let new_b = a.mul_ref(&a) + b.mul_ref(&b); 36 | let new_a = (T::from(2) * b - &a) * a; 37 | if n % 2 == 0 { 38 | (new_a, new_b) 39 | } else { 40 | let new_c = new_a + &new_b; 41 | (new_b, new_c) 42 | } 43 | } 44 | } 45 | 46 | // Modified fibonacci sequence for benchmarking rational numbers 47 | // F_n = F_{n-1} + 1 / F_{n-2} 48 | pub(crate) fn calculate_rational(n: u32) -> String { 49 | let mut a = T::from_u32(1); 50 | let mut b = T::from_u32(1); 51 | for _ in 0..n { 52 | let next = a + b.recip(); 53 | a = b; 54 | b = next; 55 | } 56 | b.to_string() 57 | } 58 | -------------------------------------------------------------------------------- /benchmark/src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::number::{Float, Natural, Rational}; 2 | use std::{fmt::Debug, str::FromStr}; 3 | 4 | pub(crate) fn calculate_natural(mut n: u32) -> String 5 | where 6 | ::Err: Debug, 7 | { 8 | // generate input 9 | let digits = "1234567890"; 10 | let mut input = String::with_capacity(n as usize); 11 | 12 | while n >= 10 { 13 | input.push_str(digits); 14 | n -= 10; 15 | } 16 | digits.chars().take(n as usize).for_each(|c| input.push(c)); 17 | 18 | // parse, add one, then print 19 | let number = T::from_str(&input).unwrap(); 20 | (number + T::from(1)).to_string() 21 | } 22 | 23 | pub(crate) fn calculate_ratioal(mut n: u32) -> String 24 | where 25 | ::Err: Debug, 26 | { 27 | // generate input 28 | let middle = n as usize / 2; 29 | let digits = "1234567890"; 30 | let mut input = String::with_capacity(n as usize); 31 | 32 | while n >= 10 { 33 | input.push_str(digits); 34 | n -= 10; 35 | } 36 | digits.chars().take(n as usize).for_each(|c| input.push(c)); 37 | input.insert(middle, '/'); 38 | 39 | // parse, add one, then print 40 | let number = T::from_str(&input).unwrap(); 41 | (number + T::from_u32(1)).to_string() 42 | } 43 | 44 | pub(crate) fn calculate_decimal(mut n: u32) -> String 45 | where 46 | ::Err: Debug, 47 | { 48 | // generate input 49 | let middle = n as usize / 2; 50 | let digits = "123456789"; 51 | let mut input = String::with_capacity(n as usize); 52 | 53 | while n >= 10 { 54 | input.push_str(digits); 55 | n -= 10; 56 | } 57 | digits.chars().take(n as usize).for_each(|c| input.push(c)); 58 | input.insert(middle, '.'); 59 | 60 | // parse, add one, then print 61 | let number = T::from_str(&input).unwrap(); 62 | (number + T::from(1)).to_string() 63 | } 64 | -------------------------------------------------------------------------------- /float/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-float" 3 | version = "0.4.3" 4 | authors = ["Jacob Zhong "] 5 | edition = "2021" 6 | description = "A big float library supporting arbitrary precision, arbitrary base and arbitrary rounding mode" 7 | keywords = ["mathematics", "numerics", "floating-point", "decimal", "arbitrary-precision"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-float" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | default = ["std", "num-order"] 21 | std = ["dashu-base/std", "dashu-int/std"] 22 | 23 | # stable dependencies 24 | diesel = ["diesel_v2"] 25 | serde = ["dep:serde", "dashu-int/serde"] 26 | zeroize = ["dep:zeroize", "dashu-int/zeroize"] 27 | num-order = ["dep:num-order", "dep:_num-modular"] 28 | 29 | # unstable dependencies 30 | num-traits = ["num-traits_v02"] 31 | num-traits_v02 = ["dep:num-traits_v02", "dashu-int/num-traits_v02"] 32 | rand = ["rand_v08"] 33 | rand_v08 = ["dep:rand_v08", "dashu-int/rand_v08"] 34 | postgres-types = ["postgres-types_v02"] 35 | postgres-types_v02 = ["dep:postgres-types_v02", "dep:_bytes", "std"] 36 | 37 | [dependencies] 38 | dashu-base = { version = "0.4.1", default-features = false, path = "../base" } 39 | dashu-int = { version = "0.4.1", default-features = false, path = "../integer" } 40 | static_assertions = { version = "1.1" } 41 | 42 | # stable dependencies 43 | rustversion = "1.0.0" 44 | num-order = { optional = true, version = "1.2.0", default-features = false } 45 | serde = { optional = true, version = "1.0.130", default-features = false } 46 | zeroize = { optional = true, version = "1.5.7", default-features = false } 47 | diesel_v1 = { optional = true, version = "1.4.0", package = "diesel", default-features = false, features = ["postgres"]} 48 | diesel_v2 = { optional = true, version = "2.0.0", package = "diesel", default-features = false, features = ["postgres_backend"]} 49 | _bytes = { optional = true, version = "1.0", package = "bytes", default-features = false } 50 | 51 | # unstable dependencies 52 | rand_v08 = { optional = true, version = "0.8.3", package = "rand", default-features = false } 53 | num-traits_v02 = { optional = true, version = "0.2.15", package = "num-traits", default-features = false } 54 | postgres-types_v02 = { optional = true, version = "0.2.4", package = "postgres-types", default-features = false } 55 | _num-modular = { optional = true, version = "0.6.1", package = "num-modular", default-features = false } 56 | 57 | [dev-dependencies] 58 | rand_v08 = { version = "0.8.3", package = "rand" } 59 | postcard = { version = "1.0.2", features = ["alloc"] } 60 | serde_test = { version = "1.0.130" } 61 | serde_json = { version = "1.0" } 62 | postgres = { version = "0.19.4" } 63 | 64 | criterion = { version = "0.5.1", features = ["html_reports"] } 65 | 66 | [[test]] 67 | name = "random" 68 | required-features = ["rand"] 69 | 70 | [[test]] 71 | name = "serde" 72 | required-features = ["serde"] 73 | 74 | [[test]] 75 | name = "num_order" 76 | required-features = ["num-order", "dashu-int/num-order"] 77 | 78 | [[test]] 79 | name = "postgres" 80 | required-features = ["postgres-types", "diesel_v1", "diesel_v2", "diesel_v2/postgres"] 81 | 82 | [[bench]] 83 | name = "primitive" 84 | required-features = ["rand"] 85 | harness = false 86 | -------------------------------------------------------------------------------- /float/README.md: -------------------------------------------------------------------------------- 1 | # dashu-float 2 | 3 | Arbitrary precision floating point number implementation as a part of the `dashu` library. See [Docs.rs](https://docs.rs/dashu-float/latest/dashu_float/) for the full documentation. 4 | 5 | # Features 6 | 7 | - Supports `no_std` and written in pure Rust. 8 | - Support **arbitrary base** and **arbitrary rounding mode**. 9 | - Support efficient **base conversion**. 10 | - Small float numbers are **inlined** on stack. 11 | - Efficient float number **parsing and printing** with base 2~36. 12 | - Supports the **hexadecimal float format** used by C++. 13 | - **Developer friendly** debug printing for float numbers. 14 | 15 | ## Optional dependencies 16 | 17 | * `std` (default): enable `std` support for dependencies. 18 | 19 | ## Performance 20 | 21 | Relevant benchmark will be implemented in the [built-in benchmark](../benchmark/). 22 | 23 | ## License 24 | 25 | See the [top-level readme](../README.md). 26 | 27 | -------------------------------------------------------------------------------- /float/src/consts.rs: -------------------------------------------------------------------------------- 1 | //! Constants calculation 2 | 3 | // to be implemented, should be associated with the Context type 4 | -------------------------------------------------------------------------------- /float/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::{Repr, Word}; 2 | 3 | #[inline] 4 | pub const fn assert_finite(repr: &Repr) { 5 | if repr.is_infinite() { 6 | panic_operate_with_inf() 7 | } 8 | } 9 | 10 | #[inline] 11 | pub const fn assert_finite_operands(lhs: &Repr, rhs: &Repr) { 12 | if lhs.is_infinite() || rhs.is_infinite() { 13 | panic_operate_with_inf() 14 | } 15 | } 16 | 17 | /// Panics when operate with infinities 18 | pub const fn panic_operate_with_inf() -> ! { 19 | panic!("arithmetic operations with the infinity are not allowed!") 20 | } 21 | 22 | /// Panics if precision is set to 0 23 | pub const fn assert_limited_precision(precision: usize) { 24 | if precision == 0 { 25 | panic_unlimited_precision() 26 | } 27 | } 28 | 29 | /// Panics when operate on unlimited precision number 30 | pub const fn panic_unlimited_precision() -> ! { 31 | panic!("precision cannot be 0 (unlimited) for this operation!") 32 | } 33 | 34 | /// Panics when the base of the power operation is negative 35 | pub const fn panic_power_negative_base() -> ! { 36 | panic!("powering on negative bases could result in complex number!") 37 | } 38 | 39 | /// Panics when taking an even order root of an negative number 40 | pub(crate) fn panic_root_negative() -> ! { 41 | panic!("the root is a complex number!") 42 | } 43 | -------------------------------------------------------------------------------- /float/src/iter.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of core::iter traits 2 | 3 | use crate::{fbig::FBig, repr::Word, round::Round}; 4 | use core::{ 5 | iter::{Product, Sum}, 6 | ops::{Add, Mul}, 7 | }; 8 | 9 | // TODO(v0.5): implement precise summation of multiple floats, however, 10 | // this means what only Sum should be implemented, which requires 11 | // narrowing the implementation 12 | impl Sum for FBig 13 | where 14 | Self: Add, 15 | { 16 | fn sum>(iter: I) -> Self { 17 | iter.fold(FBig::ZERO, FBig::add) 18 | } 19 | } 20 | 21 | impl Product for FBig 22 | where 23 | Self: Mul, 24 | { 25 | fn product>(iter: I) -> Self { 26 | iter.fold(FBig::ONE, FBig::mul) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /float/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Jacob Zhong 2 | // 3 | // Licensed under either of 4 | // 5 | // * Apache License, Version 2.0 6 | // (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) 7 | // * MIT license 8 | // (LICENSE-MIT or https://opensource.org/licenses/MIT) 9 | // 10 | // at your option. 11 | // 12 | // Unless you explicitly state otherwise, any contribution intentionally submitted 13 | // for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 14 | // dual licensed as above, without any additional terms or conditions. 15 | 16 | //! A big float library supporting arbitrary precision, arbitrary base and arbitrary rounding mode. 17 | //! 18 | //! The library implements efficient large floating point arithmetic in pure Rust. 19 | //! 20 | //! The main type is [FBig] representing the arbitrary precision floating point numbers, the [DBig] type 21 | //! is an alias supporting decimal floating point numbers. 22 | //! 23 | //! To construct big floats from literals, please use the [`dashu-macro`](https://docs.rs/dashu-macros/latest/dashu_macros/) 24 | //! crate for your convenience. 25 | //! 26 | //! # Examples 27 | //! 28 | //! ``` 29 | //! # use dashu_base::ParseError; 30 | //! use core::str::FromStr; 31 | //! use core::convert::TryFrom; 32 | //! use dashu_float::DBig; 33 | //! 34 | //! // due to the limit of rust generics, the default float type 35 | //! // need to be instantiate explicitly 36 | //! type FBig = dashu_float::FBig; 37 | //! 38 | //! let a = FBig::try_from(-12.34_f32).unwrap(); 39 | //! let b = DBig::from_str("6.022e23")?; 40 | //! let c = DBig::from_parts(271828.into(), -5); 41 | //! let d: DBig = "-0.0123456789".parse()?; 42 | //! let e = 2 * b.ln() + DBig::ONE; 43 | //! let f = &c * d.powi(10.into()) / 7; 44 | //! 45 | //! assert_eq!(a.precision(), 24); // IEEE 754 single has 24 significant bits 46 | //! assert_eq!(b.precision(), 4); // 4 decimal digits 47 | //! 48 | //! assert!(b > c); // comparison is limited in the same base 49 | //! assert!(a.to_decimal().value() < d); 50 | //! assert_eq!(c.to_string(), "2.71828"); 51 | //! 52 | //! // use associated functions of the context to get full result 53 | //! use dashu_base::Approximation::*; 54 | //! use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}}; 55 | //! let ctxt = Context::::new(6); 56 | //! assert_eq!(ctxt.exp(DBig::ONE.repr()), Inexact(c, NoOp)); 57 | //! # Ok::<(), ParseError>(()) 58 | //! ``` 59 | //! 60 | //! # Optional dependencies 61 | //! 62 | //! * `std` (*default*): enable `std` for dependencies. 63 | 64 | #![cfg_attr(not(feature = "std"), no_std)] 65 | 66 | extern crate alloc; 67 | 68 | mod add; 69 | mod cmp; 70 | mod convert; 71 | mod div; 72 | mod error; 73 | mod exp; 74 | mod fbig; 75 | mod fmt; 76 | mod helper_macros; 77 | mod iter; 78 | mod log; 79 | mod mul; 80 | pub mod ops; 81 | mod parse; 82 | mod repr; 83 | mod root; 84 | pub mod round; 85 | mod round_ops; 86 | mod shift; 87 | mod sign; 88 | mod third_party; 89 | mod utils; 90 | 91 | // All the public items from third_party will be exposed 92 | #[allow(unused_imports)] 93 | pub use third_party::*; 94 | 95 | pub use fbig::FBig; 96 | pub use repr::{Context, Repr}; 97 | 98 | /// Multi-precision float number with decimal exponent and [HalfAway][round::mode::HalfAway] rounding mode 99 | pub type DBig = FBig; 100 | 101 | #[doc(hidden)] 102 | pub use dashu_int::Word; // for macros 103 | 104 | // TODO: allow operations with inf, but only panic when the result is nan (inf - inf and inf / inf) 105 | // for division with zero (and other functions that has different limits at zero), 106 | // we might forbidden it because we don't want to support negative zero in this library. 107 | -------------------------------------------------------------------------------- /float/src/math/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of advanced math functions 2 | 3 | // TODO: implement the math functions as associated methods, and add them to FBig through a trait 4 | // REF: https://pkg.go.dev/github.com/ericlagergren/decimal 5 | 6 | enum FpResult { 7 | Normal(Repr), 8 | Overflow, 9 | Underflow, 10 | NaN, 11 | 12 | /// An exact infinite result is obtained from finite inputs, such as 13 | /// divide by zero, logarithm on zero. 14 | Infinite, 15 | } 16 | 17 | impl Context { 18 | fn sin(&self, repr: Repr) -> FpResult { 19 | todo!() 20 | } 21 | } 22 | 23 | trait ContextOps { 24 | fn context(&self) -> &Context; 25 | fn repr(&self) -> &Repr; 26 | 27 | #[inline] 28 | fn sin(&self) -> FpResult { 29 | self.context().sin(self.repr()) 30 | } 31 | } -------------------------------------------------------------------------------- /float/src/ops.rs: -------------------------------------------------------------------------------- 1 | //! Re-exported relevant operator traits from `dashu-base` 2 | 3 | pub use dashu_base::math::{CubicRoot, EstimatedLog2, SquareRoot}; 4 | pub use dashu_base::ring::{DivEuclid, DivRemEuclid, RemEuclid}; 5 | pub use dashu_base::sign::Abs; 6 | -------------------------------------------------------------------------------- /float/src/root.rs: -------------------------------------------------------------------------------- 1 | use dashu_base::{Approximation, Sign, SquareRoot, SquareRootRem, UnsignedAbs}; 2 | use dashu_int::IBig; 3 | 4 | use crate::{ 5 | error::{assert_finite, assert_limited_precision, panic_root_negative}, 6 | fbig::FBig, 7 | repr::{Context, Repr, Word}, 8 | round::{Round, Rounded}, 9 | utils::{shl_digits, split_digits_ref}, 10 | }; 11 | 12 | impl SquareRoot for FBig { 13 | type Output = Self; 14 | #[inline] 15 | fn sqrt(&self) -> Self { 16 | self.context.sqrt(self.repr()).value() 17 | } 18 | } 19 | 20 | impl Context { 21 | /// Calculate the square root of the floating point number. 22 | /// 23 | /// # Examples 24 | /// 25 | /// ``` 26 | /// # use core::str::FromStr; 27 | /// # use dashu_base::ParseError; 28 | /// # use dashu_float::DBig; 29 | /// use dashu_base::Approximation::*; 30 | /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}}; 31 | /// 32 | /// let context = Context::::new(2); 33 | /// let a = DBig::from_str("1.23")?; 34 | /// assert_eq!(context.sqrt(&a.repr()), Inexact(DBig::from_str("1.1")?, NoOp)); 35 | /// # Ok::<(), ParseError>(()) 36 | /// ``` 37 | /// 38 | /// # Panics 39 | /// 40 | /// Panics if the precision is unlimited. 41 | pub fn sqrt(&self, x: &Repr) -> Rounded> { 42 | assert_finite(x); 43 | assert_limited_precision(self.precision); 44 | if x.sign() == Sign::Negative { 45 | panic_root_negative() 46 | } 47 | 48 | // adjust the signifcand so that the exponent is even 49 | let digits = x.digits() as isize; 50 | let shift = self.precision as isize * 2 - (digits & 1) + (x.exponent & 1) - digits; 51 | let (signif, low, low_digits) = if shift > 0 { 52 | (shl_digits::(&x.significand, shift as usize), IBig::ZERO, 0) 53 | } else { 54 | let shift = (-shift) as usize; 55 | let (hi, lo) = split_digits_ref::(&x.significand, shift); 56 | (hi, lo, shift) 57 | }; 58 | 59 | let (root, rem) = signif.unsigned_abs().sqrt_rem(); 60 | let root = Sign::Positive * root; 61 | let exp = (x.exponent - shift) / 2; 62 | 63 | let res = if rem.is_zero() { 64 | Approximation::Exact(root) 65 | } else { 66 | let adjust = R::round_low_part(&root, Sign::Positive, || { 67 | (Sign::Positive * rem) 68 | .cmp(&root) 69 | .then_with(|| (low * 4u8).cmp(&Repr::::BASE.pow(low_digits).into())) 70 | }); 71 | Approximation::Inexact(root + adjust, adjust) 72 | }; 73 | res.map(|signif| Repr::new(signif, exp)) 74 | .and_then(|v| self.repr_round(v)) 75 | .map(|v| FBig::new(v, *self)) 76 | } 77 | } 78 | 79 | // TODO(next): implement cbrt, nth_root 80 | -------------------------------------------------------------------------------- /float/src/shift.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::assert_finite, fbig::FBig, repr::Word, round::Round}; 2 | use core::ops::{Shl, ShlAssign, Shr, ShrAssign}; 3 | 4 | impl Shl for FBig { 5 | type Output = Self; 6 | #[inline] 7 | fn shl(mut self, rhs: isize) -> Self::Output { 8 | assert_finite(&self.repr); 9 | if !self.repr.is_zero() { 10 | self.repr.exponent += rhs; 11 | } 12 | self 13 | } 14 | } 15 | 16 | impl ShlAssign for FBig { 17 | #[inline] 18 | fn shl_assign(&mut self, rhs: isize) { 19 | assert_finite(&self.repr); 20 | if !self.repr.is_zero() { 21 | self.repr.exponent += rhs; 22 | } 23 | } 24 | } 25 | 26 | impl Shr for FBig { 27 | type Output = Self; 28 | #[inline] 29 | fn shr(mut self, rhs: isize) -> Self::Output { 30 | assert_finite(&self.repr); 31 | if !self.repr.is_zero() { 32 | self.repr.exponent -= rhs; 33 | } 34 | self 35 | } 36 | } 37 | 38 | impl ShrAssign for FBig { 39 | #[inline] 40 | fn shr_assign(&mut self, rhs: isize) { 41 | assert_finite(&self.repr); 42 | if !self.repr.is_zero() { 43 | self.repr.exponent -= rhs; 44 | } 45 | self.repr.exponent -= rhs; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /float/src/third_party/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementations for third party crates and traits 2 | 3 | #[cfg(feature = "num-traits_v02")] 4 | mod num_traits; 5 | 6 | #[cfg(feature = "num-order")] 7 | mod num_order; 8 | 9 | #[cfg(feature = "rand_v08")] 10 | pub mod rand; 11 | 12 | #[cfg(feature = "serde")] 13 | mod serde; 14 | 15 | #[cfg(feature = "zeroize")] 16 | mod zeroize; 17 | 18 | #[cfg(any( 19 | feature = "diesel_v1", 20 | feature = "diesel_v2", 21 | feature = "postgres-types" 22 | ))] 23 | mod postgres; 24 | -------------------------------------------------------------------------------- /float/src/third_party/postgres/diesel_v1.rs: -------------------------------------------------------------------------------- 1 | use super::Numeric; 2 | use crate::{fbig::FBig, repr::Repr, round::Round}; 3 | use dashu_base::Sign; 4 | use diesel_v1::{ 5 | deserialize::{self, FromSql}, 6 | pg::{data_types::PgNumeric, Pg}, 7 | serialize::{self, Output, ToSql}, 8 | sql_types::Numeric as DieselNumeric, 9 | }; 10 | use std::io::Write; 11 | 12 | impl FromSql for Numeric { 13 | fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result { 14 | match PgNumeric::from_sql(bytes)? { 15 | PgNumeric::Positive { 16 | weight, 17 | scale, 18 | digits, 19 | } => Ok(Numeric { 20 | weight, 21 | is_inf: false, 22 | sign: Sign::Positive, 23 | dscale: scale, 24 | digits, 25 | }), 26 | PgNumeric::Negative { 27 | weight, 28 | scale, 29 | digits, 30 | } => Ok(Numeric { 31 | weight, 32 | is_inf: false, 33 | sign: Sign::Negative, 34 | dscale: scale, 35 | digits, 36 | }), 37 | PgNumeric::NaN => Err(Box::from("NaN is not supported in dashu")), 38 | } 39 | } 40 | } 41 | 42 | impl FromSql for Repr<10> { 43 | #[inline] 44 | fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result { 45 | Ok(Numeric::from_sql(bytes)?.into()) 46 | } 47 | } 48 | 49 | impl FromSql for FBig { 50 | #[inline] 51 | fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result { 52 | Ok(Numeric::from_sql(bytes)?.into()) 53 | } 54 | } 55 | 56 | fn numeric_to_sql(num: Numeric, out: &mut Output) -> serialize::Result { 57 | let num = match num { 58 | Numeric { 59 | is_inf: false, 60 | sign: Sign::Positive, 61 | weight, 62 | dscale, 63 | digits, 64 | } => PgNumeric::Positive { 65 | weight, 66 | scale: dscale, 67 | digits, 68 | }, 69 | Numeric { 70 | is_inf: false, 71 | sign: Sign::Negative, 72 | weight, 73 | dscale, 74 | digits, 75 | } => PgNumeric::Negative { 76 | weight, 77 | scale: dscale, 78 | digits, 79 | }, 80 | Numeric { is_inf: true, .. } => { 81 | return Err(Box::from("Infinities are not yet supported in diesel")) 82 | } 83 | }; 84 | ToSql::::to_sql(&num, out) 85 | } 86 | 87 | impl ToSql for FBig { 88 | #[inline] 89 | fn to_sql(&self, out: &mut Output) -> serialize::Result { 90 | numeric_to_sql(self.try_into()?, out) 91 | } 92 | } 93 | 94 | impl ToSql for Repr<10> { 95 | #[inline] 96 | fn to_sql(&self, out: &mut Output) -> serialize::Result { 97 | numeric_to_sql(self.try_into()?, out) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /float/src/third_party/postgres/diesel_v2.rs: -------------------------------------------------------------------------------- 1 | use super::Numeric; 2 | use crate::{fbig::FBig, repr::Repr, round::Round}; 3 | use dashu_base::Sign; 4 | use diesel_v2::{ 5 | deserialize::{self, FromSql}, 6 | pg::{data_types::PgNumeric, Pg, PgValue}, 7 | serialize::{self, Output, ToSql}, 8 | sql_types::Numeric as DieselNumeric, 9 | }; 10 | 11 | impl FromSql for Numeric { 12 | #[inline] 13 | fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { 14 | match PgNumeric::from_sql(bytes)? { 15 | PgNumeric::Positive { 16 | weight, 17 | scale, 18 | digits, 19 | } => Ok(Numeric { 20 | weight, 21 | is_inf: false, 22 | sign: Sign::Positive, 23 | dscale: scale, 24 | digits, 25 | }), 26 | PgNumeric::Negative { 27 | weight, 28 | scale, 29 | digits, 30 | } => Ok(Numeric { 31 | weight, 32 | is_inf: false, 33 | sign: Sign::Negative, 34 | dscale: scale, 35 | digits, 36 | }), 37 | PgNumeric::NaN => Err(Box::from("NaN is not supported in dashu")), 38 | } 39 | } 40 | } 41 | 42 | impl FromSql for Repr<10> { 43 | #[inline] 44 | fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { 45 | Ok(Numeric::from_sql(bytes)?.into()) 46 | } 47 | } 48 | 49 | impl FromSql for FBig { 50 | #[inline] 51 | fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { 52 | Ok(Numeric::from_sql(bytes)?.into()) 53 | } 54 | } 55 | 56 | fn numeric_to_sql(num: Numeric, out: &mut Output<'_, '_, Pg>) -> serialize::Result { 57 | let num = match num { 58 | Numeric { 59 | is_inf: false, 60 | sign: Sign::Positive, 61 | weight, 62 | dscale, 63 | digits, 64 | } => PgNumeric::Positive { 65 | weight, 66 | scale: dscale, 67 | digits, 68 | }, 69 | Numeric { 70 | is_inf: false, 71 | sign: Sign::Negative, 72 | weight, 73 | dscale, 74 | digits, 75 | } => PgNumeric::Negative { 76 | weight, 77 | scale: dscale, 78 | digits, 79 | }, 80 | Numeric { is_inf: true, .. } => { 81 | return Err(Box::from("Infinities are not yet supported in diesel")) 82 | } 83 | }; 84 | ToSql::::to_sql(&num, &mut out.reborrow()) 85 | } 86 | 87 | impl ToSql for FBig { 88 | #[inline] 89 | fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { 90 | numeric_to_sql(self.try_into()?, out) 91 | } 92 | } 93 | 94 | impl ToSql for Repr<10> { 95 | #[inline] 96 | fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { 97 | numeric_to_sql(self.try_into()?, out) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /float/src/third_party/zeroize.rs: -------------------------------------------------------------------------------- 1 | //! Implement zeroize traits. 2 | 3 | use crate::{ 4 | fbig::FBig, 5 | repr::{Context, Repr, Word}, 6 | round::Round, 7 | }; 8 | use zeroize::Zeroize; 9 | 10 | impl Zeroize for Repr { 11 | #[inline] 12 | fn zeroize(&mut self) { 13 | self.significand.zeroize(); 14 | self.exponent.zeroize(); 15 | } 16 | } 17 | 18 | impl Zeroize for Context { 19 | #[inline] 20 | fn zeroize(&mut self) { 21 | self.precision.zeroize(); 22 | } 23 | } 24 | 25 | impl Zeroize for FBig { 26 | #[inline] 27 | fn zeroize(&mut self) { 28 | self.repr.zeroize(); 29 | self.context.zeroize(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /float/tests/iter.rs: -------------------------------------------------------------------------------- 1 | use dashu_float::DBig; 2 | type FBig = dashu_float::FBig; 3 | 4 | mod helper_macros; 5 | 6 | #[test] 7 | #[rustfmt::skip::macros(fbig)] 8 | fn test_sum() { 9 | let nums = [ 10 | fbig!(-0x1p2), 11 | fbig!(-0x1p1), 12 | fbig!(-0x1), 13 | fbig!(-0x1p-1), 14 | fbig!(-0x1p-2), 15 | fbig!(0), 16 | fbig!(0x1p-2), 17 | fbig!(0x1p-1), 18 | fbig!(0x1), 19 | fbig!(0x1p1), 20 | fbig!(0x1p2), 21 | ]; 22 | 23 | assert_eq!(nums[..0].iter().sum::(), fbig!(0)); 24 | assert_eq!(nums[..1].iter().sum::(), fbig!(-0x1p2)); 25 | assert_eq!(nums[..2].iter().sum::(), fbig!(-0x3p1)); 26 | assert_eq!(nums[..4].iter().sum::(), fbig!(-0xfp-1)); 27 | assert_eq!(nums.iter().sum::(), fbig!(0x1p-2)); 28 | assert_eq!(nums.into_iter().sum::(), fbig!(0x1p-2)); 29 | 30 | let nums = [ 31 | dbig!(-0001e2), 32 | dbig!(-1e1), 33 | dbig!(-1), 34 | dbig!(-1e-1), 35 | dbig!(-1e-2), 36 | dbig!(0), 37 | dbig!(1e-2), 38 | dbig!(1e-1), 39 | dbig!(1), 40 | dbig!(1e1), 41 | dbig!(1e2), 42 | ]; 43 | 44 | assert_eq!(nums[..0].iter().sum::(), dbig!(0)); 45 | assert_eq!(nums[..1].iter().sum::(), dbig!(-1e2)); 46 | assert_eq!(nums[..2].iter().sum::(), dbig!(-11e1)); 47 | assert_eq!(nums[..4].iter().sum::(), dbig!(-1111e-1)); 48 | assert_eq!(nums.iter().sum::(), dbig!(1e-2)); 49 | assert_eq!(nums.into_iter().sum::(), dbig!(1e-2)); 50 | } 51 | 52 | #[test] 53 | #[rustfmt::skip::macros(fbig)] 54 | fn test_prod() { 55 | let nums = [ 56 | fbig!(-0x1p2), 57 | fbig!(0x1p1), 58 | fbig!(-0x1), 59 | fbig!(0x1p-1), 60 | fbig!(-0x1p-2), 61 | fbig!(0), 62 | ]; 63 | 64 | assert_eq!(nums[..0].iter().product::(), fbig!(0x1)); 65 | assert_eq!(nums[..1].iter().product::(), fbig!(-0x1p2)); 66 | assert_eq!(nums[..2].iter().product::(), fbig!(-0x1p3)); 67 | assert_eq!(nums[..4].iter().product::(), fbig!(0x1p2)); 68 | assert_eq!(nums.iter().product::(), fbig!(0)); 69 | assert_eq!(nums.into_iter().product::(), fbig!(0)); 70 | 71 | let nums = [ 72 | dbig!(-0001e2), 73 | dbig!(1e1), 74 | dbig!(-1), 75 | dbig!(1e-1), 76 | dbig!(-1e-2), 77 | dbig!(0), 78 | ]; 79 | 80 | assert_eq!(nums[..0].iter().product::(), dbig!(1)); 81 | assert_eq!(nums[..1].iter().product::(), dbig!(-1e2)); 82 | assert_eq!(nums[..2].iter().product::(), dbig!(-1e3)); 83 | assert_eq!(nums[..4].iter().product::(), dbig!(1e2)); 84 | assert_eq!(nums.iter().product::(), dbig!(0)); 85 | assert_eq!(nums.into_iter().product::(), dbig!(0)); 86 | } 87 | -------------------------------------------------------------------------------- /float/tests/root.rs: -------------------------------------------------------------------------------- 1 | use dashu_base::Approximation::*; 2 | use dashu_float::{ops::SquareRoot, round::Rounding::*, DBig}; 3 | 4 | mod helper_macros; 5 | 6 | #[test] 7 | #[rustfmt::skip::macros(fbig)] 8 | fn test_sqrt_binary() { 9 | let exact_cases = [ 10 | (fbig!(0), fbig!(0)), 11 | (fbig!(1), fbig!(1)), 12 | (fbig!(0x9), fbig!(0x3)), 13 | (fbig!(0x100), fbig!(0x10)), 14 | ]; 15 | for (x, sqrt) in &exact_cases { 16 | assert_eq!(x.sqrt(), *sqrt); 17 | if let Exact(v) = x.context().sqrt(x.repr()) { 18 | assert_eq!(v, *sqrt); 19 | } else { 20 | panic!("the result should be exact!") 21 | } 22 | } 23 | 24 | let inexact_cases = [ 25 | (fbig!(0x3), fbig!(0xdp-3)), 26 | (fbig!(0x0003), fbig!(0xddb3p-15)), 27 | (fbig!(0x0000000000000003), fbig!(0xddb3d742c265539dp-63)), 28 | ( 29 | fbig!(0x3).with_precision(200).unwrap(), 30 | fbig!(0xddb3d742c265539d92ba16b83c5c1dc492ec1a6629ed23cc63p-199), 31 | ), 32 | (fbig!(0x3000), fbig!(0xddb3p-9)), 33 | (fbig!(0x3000000000000000), fbig!(0xddb3d742c265539dp-33)), 34 | (fbig!(0xf), fbig!(0xfp-2)), 35 | (fbig!(0xffff), fbig!(0xffffp-8)), 36 | ]; 37 | 38 | for (x, root) in &inexact_cases { 39 | assert_eq!(x.sqrt(), *root); 40 | if let Inexact(v, e) = x.context().sqrt(x.repr()) { 41 | assert_eq!(v, *root); 42 | assert_eq!(e, NoOp); 43 | } else { 44 | panic!("the result should be inexact!") 45 | } 46 | } 47 | } 48 | 49 | #[test] 50 | fn test_sqrt_decimal() { 51 | let exact_cases = [ 52 | (dbig!(0), dbig!(0)), 53 | (dbig!(1), dbig!(1)), 54 | (dbig!(9), dbig!(3)), 55 | (dbig!(6561), dbig!(81)), 56 | ]; 57 | for (x, sqrt) in &exact_cases { 58 | assert_eq!(x.sqrt(), *sqrt); 59 | if let Exact(v) = x.context().sqrt(x.repr()) { 60 | assert_eq!(v, *sqrt); 61 | } else { 62 | panic!("the result should be exact!") 63 | } 64 | } 65 | 66 | let inexact_cases = [ 67 | (dbig!(3), dbig!(2), AddOne), 68 | (dbig!(0003), dbig!(1732e-3), NoOp), 69 | (dbig!(0000000000000003), dbig!(1732050807568877e-15), NoOp), 70 | ( 71 | dbig!(3).with_precision(60).unwrap(), 72 | dbig!(173205080756887729352744634150587236694280525381038062805581e-59), 73 | AddOne, 74 | ), 75 | (dbig!(3000), dbig!(5477e-2), NoOp), 76 | (dbig!(3000000000000000), dbig!(5477225575051661e-8), NoOp), 77 | (dbig!(9999), dbig!(9999e-2), NoOp), 78 | (dbig!(9999e-4), dbig!(9999e-4), NoOp), 79 | ]; 80 | for (x, sqrt, rnd) in &inexact_cases { 81 | assert_eq!(x.sqrt(), *sqrt); 82 | if let Inexact(v, e) = x.context().sqrt(x.repr()) { 83 | assert_eq!(v, *sqrt); 84 | assert_eq!(e, *rnd); 85 | } else { 86 | panic!("the result should be inexact!") 87 | } 88 | } 89 | } 90 | 91 | #[test] 92 | #[should_panic] 93 | fn test_sqrt_unlimited_precision() { 94 | let _ = dbig!(2).with_precision(0).unwrap().sqrt(); 95 | } 96 | 97 | #[test] 98 | #[should_panic] 99 | fn test_sqrt_inf() { 100 | let _ = DBig::INFINITY.sqrt(); 101 | } 102 | 103 | #[test] 104 | #[should_panic] 105 | fn test_sqrt_negative() { 106 | let _ = DBig::NEG_ONE.sqrt(); 107 | } 108 | -------------------------------------------------------------------------------- /float/tests/serde.rs: -------------------------------------------------------------------------------- 1 | use dashu_float::{DBig, Repr}; 2 | use postcard::{from_bytes, to_allocvec}; 3 | use serde_json::{from_str, to_string}; 4 | 5 | mod helper_macros; 6 | type FBig = dashu_float::FBig; 7 | 8 | #[test] 9 | #[rustfmt::skip::macros(fbig)] 10 | fn test_fbig_serde() { 11 | let test_numbers = [ 12 | fbig!(0), 13 | fbig!(0x1dp-1), 14 | fbig!(-0x23p-1), 15 | fbig!(0x1234p-2), 16 | fbig!(-0x1234567890123456789p-40), 17 | fbig!(-0x123456789012345678901234567890123456789p-200), 18 | ]; 19 | for float in &test_numbers { 20 | // test binary serialization 21 | let output = to_allocvec(float).unwrap(); 22 | let parsed: FBig = from_bytes(&output).unwrap(); 23 | assert_eq!(&parsed, float); 24 | 25 | // test binary serialization of repr 26 | let output = to_allocvec(float.repr()).unwrap(); 27 | let parsed: Repr<2> = from_bytes(&output).unwrap(); 28 | assert_eq!(&parsed, float.repr()); 29 | 30 | // test string serialization 31 | let output = to_string(float).unwrap(); 32 | let parsed: FBig = from_str(&output).unwrap(); 33 | assert_eq!(&parsed, float); 34 | 35 | // test string serialization of repr 36 | let output = to_string(float.repr()).unwrap(); 37 | let parsed: Repr<2> = from_str(&output).unwrap(); 38 | assert_eq!(&parsed, float.repr()); 39 | } 40 | } 41 | 42 | #[test] 43 | fn test_dbig_serde() { 44 | let test_numbers = [ 45 | dbig!(0), 46 | dbig!(1.3), 47 | dbig!(-2.3), 48 | dbig!(10.99), 49 | dbig!(-123456789.0123456789), 50 | dbig!(-1.2345678901234567890123456789e-100), 51 | ]; 52 | for float in &test_numbers { 53 | // test binary serialization 54 | let output = to_allocvec(float).unwrap(); 55 | let parsed: DBig = from_bytes(&output).unwrap(); 56 | assert_eq!(&parsed, float); 57 | 58 | // test binary serialization of repr 59 | let output = to_allocvec(float.repr()).unwrap(); 60 | let parsed: Repr<10> = from_bytes(&output).unwrap(); 61 | assert_eq!(&parsed, float.repr()); 62 | 63 | // test string serialization 64 | let output = to_string(float).unwrap(); 65 | let parsed: DBig = from_str(&output).unwrap(); 66 | assert_eq!(&parsed, float); 67 | 68 | // test string serialization of repr 69 | let output = to_string(float.repr()).unwrap(); 70 | let parsed: Repr<10> = from_str(&output).unwrap(); 71 | assert_eq!(&parsed, float.repr()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /float/tests/shift.rs: -------------------------------------------------------------------------------- 1 | use dashu_float::DBig; 2 | 3 | mod helper_macros; 4 | 5 | #[test] 6 | #[rustfmt::skip::macros(fbig)] 7 | fn test_shift() { 8 | assert_eq!(fbig!(0x0) << 1, fbig!(0x0)); 9 | assert_eq!(fbig!(0x0) >> 1, fbig!(0x0)); 10 | assert_eq!(fbig!(0x1) << 1, fbig!(0x1p1)); 11 | assert_eq!(fbig!(0x1) >> 1, fbig!(0x1p-1)); 12 | assert_eq!(fbig!(-0x1) << 1, fbig!(-0x1p1)); 13 | assert_eq!(fbig!(-0x1) >> 1, fbig!(-0x1p-1)); 14 | 15 | assert_eq!(dbig!(0) << 1, dbig!(0)); 16 | assert_eq!(dbig!(0) >> 1, dbig!(0)); 17 | assert_eq!(dbig!(1) << 1, dbig!(1e1)); 18 | assert_eq!(dbig!(1) >> 1, dbig!(1e-1)); 19 | assert_eq!(dbig!(-1) << 1, dbig!(-1e1)); 20 | assert_eq!(dbig!(-1) >> 1, dbig!(-1e-1)); 21 | } 22 | 23 | #[test] 24 | #[should_panic] 25 | fn test_shift_inf() { 26 | let _ = DBig::INFINITY >> 1; 27 | } 28 | -------------------------------------------------------------------------------- /guide/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Dashu user guide" 3 | description = "User guide of the dashu arbitrary precision library" 4 | authors = ["Jacob Zhong"] 5 | language = "en" 6 | multilingual = false 7 | src = "src" 8 | -------------------------------------------------------------------------------- /guide/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](./index.md) 4 | 5 | - [Types](./types.md) 6 | - [Construction and Destruction](./construct.md) 7 | - [Conversion](./convert.md) 8 | - [Input and Output](./io/index.md) 9 | - [Parsing](./io/parse.md) 10 | - [Printing](./io/print.md) 11 | - [Serialization](./io/serialize.md) 12 | - [Interoperability](./io/interop.md) 13 | - [Operations](./ops/index.md) 14 | - [Equality and Comparison](./ops/cmp.md) 15 | - [Basic Arithmetics](./ops/basic.md) 16 | - [Exponential and Logarithm](./ops/exp_log.md) 17 | - [Bit Manipulation](./ops/bit.md) 18 | - [Number Theoretic](./ops/num_theory.md) 19 | - [FAQ](./faq.md) 20 | - [Cheatsheet](./cheatsheet.md) 21 | -------------------------------------------------------------------------------- /guide/src/cheatsheet.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmpute/dashu/fbcb8c32671b8a3e80c557b3fec4448f5d36af55/guide/src/cheatsheet.md -------------------------------------------------------------------------------- /guide/src/faq.md: -------------------------------------------------------------------------------- 1 | # Why is the library called `dashu`? 2 | 3 | # Why to use dashu? 4 | 5 | (list the features of dashu and other crates: num-bigint, ibig, malachite) 6 | (reference: https://rkyv.org/feature-comparison.html?highlight=features#feature-matrix) 7 | (maybe need to split the table to integer, float, rational three sections) 8 | -------------------------------------------------------------------------------- /guide/src/index.md: -------------------------------------------------------------------------------- 1 | # The user guide for `dashu` 2 | 3 | Welcome to the `dashu` user guide! `dashu` is a library set of arbitrary precision numbers (aka. big numbers) implemented in Rust. 4 | 5 | The book is a companion to [`dashu`'s API docs](https://docs.rs/dashu/latest/dashu/). It contains a more concise overview of all the functionalities equipped with `dashu` and some examples. 6 | 7 | Please choose from the chapters on the left to jump to individual topics. 8 | 9 | ## Features 10 | 11 | - Pure rust, full `no_std` support. 12 | - Focus on ergonomics & readability, and then efficiency. 13 | - Current MSRV is 1.61 14 | 15 | ## The meta crate 16 | 17 | The crate `dashu` is a meta crate that exposes all the functionalities of the subcrates (`dashu-base`, `dashu-int`, `dashu-float`, `dashu-ratio` and `dashu-macros`). Each subcrate becomes a module in `dashu`, for example, `dashu-int` is re-exported as `dashu::int`. Besides, it creates more readable aliases for the numeric types: 18 | - `dashu::Natural` = `dashu::int::UBig` = `dashu_int::UBig` 19 | - `dashu::Integer` = `dashu::int::IBig` = `dashu_int::IBig` 20 | - `dashu::Ratio` = `dashu::ratio::RBig` = `dashu_ratio::RBig` 21 | - `dashu::Real` = `dashu::float::FBig` = `dashu_float::FBig` 22 | - `dashu::Decimal` = `dashu::float::DBig` = `dashu_float::DBig` 23 | 24 | In this guide, we will use the original names of the numeric types (i.e. `XBig`), but the explanations are also applicable to these re-exported types. 25 | 26 | ## Cargo Features 27 | 28 | Dashu has several optional features defined for cargo that supports various third-party crates. Most of them are not enabled by default. Specially, we use a special naming rule for the features. 29 | - For feature dependencies with stable versions (reached v1.0), we will use `xxx_vyy` to represent its major versions, and `xxx` pointing to one of the major versions. Changing the version which `xxx` is pointing to is regarded as a break change in `dashu` (requiring major version bump). Therefore, when you dependends on `dashu` with these stable features, additional implementations for newer versions in `dashu` will not cause any issues in your code. 30 | - For feature dependencies with only unstable versions (pre v1.0), we will always use `xxx_vyy` to represent each major versions. We will also provide the feature `xxx`, which is an alias of the **newest** version of the crate. Therefore, when you dependends on `dashu` with these unstable features, the upgrade of these dependencies might cause your code to fail. However, we still do not consider this as break changes, because the unstable dependencies will never be enabled by default. If you want to prevent break changes cause by this, please specify which version to use. 31 | 32 | **Example**: In `dashu-float`, the support for the diesel library v1 is under the feature name `diesel`, and the support for v2 is under the feature name `diesel2`. On the other hand, the `rand` crate is not stable yet, even if it's already widely used. Therefore, the support for `rand` v0.7 and v0.8 is under the feature name `rand_v07` and `rand_v08` respectively. The feature name `rand` is currently pointing to `rand_v08`. 33 | 34 | In your Cargo.toml, if you enable `dashu/diesel`, `dashu/diesel2` or `dashu/rand_v07`, there won't be any risk of break changes when `dashu` updates the support for `diesel` v3 or `rand` v0.9 in future. However the risk exists if you enable `rand` instead of `rand_v08`, because `rand` might point to `rand_v09` in future. 35 | 36 | ## License 37 | 38 | Licensed under either [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT license](https://opensource.org/licenses/MIT) at your option. 39 | -------------------------------------------------------------------------------- /guide/src/io/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmpute/dashu/fbcb8c32671b8a3e80c557b3fec4448f5d36af55/guide/src/io/index.md -------------------------------------------------------------------------------- /guide/src/io/interop.md: -------------------------------------------------------------------------------- 1 | Document `from_words`, `as_words`, `to_le_bytes/to_be_bytes` (and `from_*`), `to_digits` -------------------------------------------------------------------------------- /guide/src/io/parse.md: -------------------------------------------------------------------------------- 1 | # Standard Parsing API 2 | 3 | `from_str`, `from_str_radix` 4 | 5 | # Float number parsing 6 | -------------------------------------------------------------------------------- /guide/src/io/print.md: -------------------------------------------------------------------------------- 1 | # Standard Format API 2 | 3 | # Debug Print 4 | 5 | # Rational Number Formatting 6 | -------------------------------------------------------------------------------- /guide/src/io/serialize.md: -------------------------------------------------------------------------------- 1 | ```text 2 | The layout for serialized numbers is protected by the semver. The change of the layout is considered as a break change and a new major version will be published. 3 | ``` 4 | 5 | # Conversion to Bytes 6 | 7 | (use `to_le_bytes`, `to_be_bytes`, `from_le_bytes`, `from_be_bytes`) 8 | 9 | # Serialization with `serde` 10 | 11 | (Use serde for best platform compatibility and memory efficiency. Note that we support the `is_human_readable` option.) 12 | 13 | # Serialization with `rkyv` 14 | 15 | (Use rkyv for best speed.) 16 | 17 | -------------------------------------------------------------------------------- /guide/src/ops/basic.md: -------------------------------------------------------------------------------- 1 | (+-*) (clarify behavior of division and modulo on different types) -------------------------------------------------------------------------------- /guide/src/ops/bit.md: -------------------------------------------------------------------------------- 1 | (clarify that it follows the two's complement rule) 2 | ## Bit Operators 3 | ## Use `UBig` as a Bit Vector 4 | (clear bits, set bit, bit len, chunk bits) -------------------------------------------------------------------------------- /guide/src/ops/cmp.md: -------------------------------------------------------------------------------- 1 | (Comparison is natively enabled only between big numbers, but not for native types due to [`num-bigint`#150](https://github.com/rust-num/num-bigint/issues/150)). To compare with native types, use `NumOrd`) 2 | 3 | # Ordering 4 | 5 | ## Comparison 6 | ## Sign 7 | ## Equality 8 | 9 | # Hashing 10 | 11 | (NumHash) 12 | 13 | -------------------------------------------------------------------------------- /guide/src/ops/exp_log.md: -------------------------------------------------------------------------------- 1 | (including the estimated ones) -------------------------------------------------------------------------------- /guide/src/ops/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmpute/dashu/fbcb8c32671b8a3e80c557b3fec4448f5d36af55/guide/src/ops/index.md -------------------------------------------------------------------------------- /guide/src/ops/num_theory.md: -------------------------------------------------------------------------------- 1 | # Greatest Common Divisor 2 | 3 | and extended gcd 4 | 5 | # Diophantine Approximation 6 | -------------------------------------------------------------------------------- /integer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-int" 3 | version = "0.4.1" 4 | authors = ["Jacob Zhong ", "Tomek Czajka "] 5 | edition = "2021" 6 | description = "A big integer library with good performance" 7 | keywords = ["mathematics", "numerics", "integer", "bigint", "arbitrary-precision"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-int" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | default = ["std", "num-order"] 21 | std = ["dashu-base/std"] 22 | 23 | # unstable dependencies 24 | rand = ["rand_v08"] 25 | num-traits = ["num-traits_v02"] 26 | num-integer = ["num-integer_v01"] 27 | 28 | [dependencies] 29 | dashu-base = { version = "0.4.0", default-features = false, path = "../base" } 30 | cfg-if = { version = "1.0.0" } 31 | static_assertions = { version = "1.1" } 32 | rustversion = { version = "1.0.0" } 33 | num-modular = { version = "0.6.1" } 34 | 35 | # stable dependencies 36 | num-order = { optional = true, version = "1.2.0", default-features = false } 37 | serde = { optional = true, version = "1.0.130", default-features = false } 38 | zeroize = { optional = true, version = "1.5.7", default-features = false } 39 | 40 | # unstable dependencies 41 | rand_v08 = { optional = true, version = "0.8.3", package = "rand", default-features = false } 42 | num-traits_v02 = { optional = true, version = "0.2.14", package = "num-traits", default-features = false } 43 | num-integer_v01 = { optional = true, version = "0.1.45", package = "num-integer", default-features = false } 44 | 45 | [dev-dependencies] 46 | rand_v08 = { version = "0.8.3", package = "rand" } 47 | postcard = { version = "1.0.2", features = ["alloc"] } 48 | serde_test = { version = "1.0.130" } 49 | serde_json = { version = "1.0" } 50 | 51 | criterion = { version = "0.5.1", features = ["html_reports"] } 52 | 53 | [lib] 54 | bench = false 55 | 56 | [[test]] 57 | name = "random" 58 | required-features = ["rand"] 59 | 60 | [[test]] 61 | name = "serde" 62 | required-features = ["serde"] 63 | 64 | [[test]] 65 | name = "num_order" 66 | required-features = ["num-order"] 67 | 68 | [[bench]] 69 | name = "primitive" 70 | required-features = ["rand"] 71 | harness = false 72 | -------------------------------------------------------------------------------- /integer/NOTICE.md: -------------------------------------------------------------------------------- 1 | Following is the copy of the MIT license of ibig-rs crate: 2 | 3 | ``` 4 | MIT License 5 | 6 | Copyright (c) 2020 Tomek Czajka 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | ``` 26 | 27 | Initial modifications (at version 0.1.0) on the `ibig` library: 28 | 29 | 1. The underlying represetation of the UBig is vastly changed, the new representation 30 | supports inline double words and embedded sign bit, and the IBig doesn't support get 31 | the magnitude as reference now. 32 | 2. Operation traits are moved to the `dashu-base` crate. 33 | 3. The trait `NextPowerOfTwo` is changed to `PowerOfTwo` with modified definition. 34 | 4. Bitwise operators with primitive integers between different signedness are removed to 35 | enforce explicitness. However, operators between UBig and IBig are added to reduce 36 | copying overhead. 37 | 5. `AndNot` trait is made private because it's not widely used and the naming doesn't 38 | follow the BitXXX style in the std library. 39 | 6. MSRV is increased to 1.56 40 | 41 | The other modifications from later versions can be found in the CHANGELOG.md 42 | -------------------------------------------------------------------------------- /integer/README.md: -------------------------------------------------------------------------------- 1 | # dashu-int 2 | 3 | Arbitrary precision integer implementation as a part of the `dashu` library. See [Docs.rs](https://docs.rs/dashu-int/latest/dashu_int/) for the full documentation. 4 | 5 | > The majority of the code is based on the [ibig crate](https://github.com/tczajka/ibig-rs). The modification notice based on the the original `ibig` repo is included in the [NOTICE](./NOTICE) file. 6 | 7 | ## Features 8 | 9 | - Supports `no_std` and written in pure Rust. 10 | - Support for both **unsigned** and **signed** big integers. 11 | - Small integers are **inlined** on stack with specialized algorithms. 12 | - **Efficient** implementation for basic arithmetic operations (`+`,`-`,`*`,`/`,`%`,`<<`,`>>`). 13 | - Support **advanced** arithmetic operations including `pow`, `ilog`, `gcd`, `gcd_ext`. 14 | - Bit operations for signed big integers follow the **2's complement rule**. 15 | - **Efficient** implementation for modular arithmetics (e.g. modular powering and inverse). 16 | - Efficient integer **parsing and printing** with base 2~36. 17 | - **Developer friendly** debug printing for big integers. 18 | - **Direct access** to underlying machine word array. 19 | 20 | ## Optional dependencies 21 | 22 | * `std` (default): for `std::error::Error`. 23 | * `num-traits` (default): integral traits. 24 | * `rand` (default): random number generation. 25 | * `serde`: serialization and deserialization. 26 | 27 | ## Performance 28 | 29 | See the [built-in benchmark](../benchmark/). 30 | 31 | ## License 32 | 33 | See the [top-level readme](../README.md). 34 | -------------------------------------------------------------------------------- /integer/examples/factorial.rs: -------------------------------------------------------------------------------- 1 | use dashu_int::UBig; 2 | 3 | // a * (a+1) * ... * (b-1) 4 | fn product(a: u32, b: u32) -> UBig { 5 | if b == a + 1 { 6 | UBig::from(a) 7 | } else { 8 | let mid = a + (b - a) / 2; 9 | product(a, mid) * product(mid, b) 10 | } 11 | } 12 | 13 | fn main() { 14 | let n: u32 = 1000000; 15 | let factorial = product(1, n + 1); 16 | println!("{}! = {:#x}", n, factorial); 17 | } 18 | -------------------------------------------------------------------------------- /integer/src/arch/generic/add.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::word::Word; 2 | 3 | /// Add a + b + carry. 4 | /// 5 | /// Returns (result, overflow). 6 | #[inline] 7 | pub fn add_with_carry(a: Word, b: Word, carry: bool) -> (Word, bool) { 8 | let (sum, c0) = a.overflowing_add(b); 9 | let (sum, c1) = sum.overflowing_add(Word::from(carry)); 10 | (sum, c0 | c1) 11 | } 12 | 13 | /// Subtract a - b - borrow. 14 | /// 15 | /// Returns (result, overflow). 16 | #[inline] 17 | pub fn sub_with_borrow(a: Word, b: Word, borrow: bool) -> (Word, bool) { 18 | let (diff, b0) = a.overflowing_sub(b); 19 | let (diff, b1) = diff.overflowing_sub(Word::from(borrow)); 20 | (diff, b0 | b1) 21 | } 22 | -------------------------------------------------------------------------------- /integer/src/arch/generic/digits.rs: -------------------------------------------------------------------------------- 1 | use crate::{arch::word::Word, primitive::WORD_BYTES, radix::DigitCase}; 2 | 3 | /// Chunk length for digit conversion. 4 | pub const DIGIT_CHUNK_LEN: usize = WORD_BYTES; 5 | 6 | /// Convert raw digits to ASCII. 7 | /// 8 | /// digits must be valid 9 | #[inline] 10 | pub fn digit_chunk_raw_to_ascii(digits: &mut [u8; DIGIT_CHUNK_LEN], digit_case: DigitCase) { 11 | let mut word = Word::from_ne_bytes(*digits); 12 | 13 | // ALL_ONES = 0x01010101... 14 | const ALL_ONES: Word = Word::MAX / 0xff; 15 | 16 | // For digits >= 10, add 'a'-'0' or 'A'-'0' as appropriate. 17 | if digit_case != DigitCase::NoLetters { 18 | // Find digits >= 10, in parallel. 19 | // 0x76 + digit will have the top bit set if digit >= 10. 20 | // letters: 0x01 if digit >= 10. 21 | let letters = ((0x76 * ALL_ONES + word) >> 7) & ALL_ONES; 22 | 23 | word += letters * (digit_case as Word); 24 | } 25 | 26 | // Convert digits to ASCII in parallel. 27 | word += ALL_ONES * (b'0' as Word); 28 | 29 | digits.copy_from_slice(&word.to_ne_bytes()); 30 | } 31 | -------------------------------------------------------------------------------- /integer/src/arch/generic_16_bit/mod.rs: -------------------------------------------------------------------------------- 1 | #[path = "../generic/add.rs"] 2 | pub(crate) mod add; 3 | 4 | #[path = "../generic/digits.rs"] 5 | pub(crate) mod digits; 6 | 7 | pub(crate) mod ntt; 8 | 9 | pub(crate) mod word; 10 | -------------------------------------------------------------------------------- /integer/src/arch/generic_16_bit/ntt.rs: -------------------------------------------------------------------------------- 1 | use crate::mul::ntt::{Prime, NUM_PRIMES}; 2 | 3 | /// Maximum order of the number-theoretic transform. 4 | /// 5 | /// 2^12 * 16 = 2^16 bits. 6 | pub const MAX_ORDER: u32 = 12; 7 | 8 | /// Primes to be used for the number-theoretic transform. 9 | #[allow(dead_code)] 10 | pub const PRIMES: [Prime; NUM_PRIMES] = [ 11 | Prime { 12 | prime: 0x3001, 13 | max_order_root: 0x29, 14 | }, 15 | Prime { 16 | prime: 0xa001, 17 | max_order_root: 0x1c, 18 | }, 19 | Prime { 20 | prime: 0xf001, 21 | max_order_root: 0x13, 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /integer/src/arch/generic_16_bit/word.rs: -------------------------------------------------------------------------------- 1 | /// Machine word. 2 | pub type Word = u16; 3 | 4 | /// Signed machine word. 5 | pub type SignedWord = i16; 6 | 7 | /// Double machine word. 8 | pub type DoubleWord = u32; 9 | 10 | /// Signed double machine word. 11 | pub type SignedDoubleWord = i32; 12 | -------------------------------------------------------------------------------- /integer/src/arch/generic_32_bit/mod.rs: -------------------------------------------------------------------------------- 1 | #[path = "../generic/add.rs"] 2 | pub(crate) mod add; 3 | 4 | #[path = "../generic/digits.rs"] 5 | pub(crate) mod digits; 6 | 7 | pub(crate) mod ntt; 8 | 9 | pub(crate) mod word; 10 | -------------------------------------------------------------------------------- /integer/src/arch/generic_32_bit/ntt.rs: -------------------------------------------------------------------------------- 1 | use crate::mul::ntt::{Prime, NUM_PRIMES}; 2 | 3 | /// Maximum order of the number-theoretic transform. 4 | /// 5 | /// 2^27 * 32 = 2^32 bits. 6 | pub const MAX_ORDER: u32 = 27; 7 | 8 | /// Primes to be used for the number-theoretic transform. 9 | #[allow(dead_code)] 10 | pub const PRIMES: [Prime; NUM_PRIMES] = [ 11 | Prime { 12 | prime: 0xc0000001, 13 | max_order_root: 0x3, 14 | }, 15 | Prime { 16 | prime: 0xd0000001, 17 | max_order_root: 0x79, 18 | }, 19 | Prime { 20 | prime: 0xe8000001, 21 | max_order_root: 0x23, 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /integer/src/arch/generic_32_bit/word.rs: -------------------------------------------------------------------------------- 1 | /// Machine word. 2 | pub type Word = u32; 3 | 4 | /// Signed machine word. 5 | pub type SignedWord = i32; 6 | 7 | /// Double machine word. 8 | pub type DoubleWord = u64; 9 | 10 | /// Signed double machine word. 11 | pub type SignedDoubleWord = i64; 12 | -------------------------------------------------------------------------------- /integer/src/arch/generic_64_bit/mod.rs: -------------------------------------------------------------------------------- 1 | #[path = "../generic/add.rs"] 2 | pub(crate) mod add; 3 | 4 | #[path = "../generic/digits.rs"] 5 | pub(crate) mod digits; 6 | 7 | pub(crate) mod ntt; 8 | 9 | pub(crate) mod word; 10 | -------------------------------------------------------------------------------- /integer/src/arch/generic_64_bit/ntt.rs: -------------------------------------------------------------------------------- 1 | use crate::mul::ntt::{Prime, NUM_PRIMES}; 2 | 3 | /// Maximum order of the number-theoretic transform. 4 | /// 5 | /// 2^57 * 64 = 2^63 bits. 6 | pub const MAX_ORDER: u32 = 57; 7 | 8 | /// Primes to be used for the number-theoretic transform. 9 | #[allow(dead_code)] 10 | pub const PRIMES: [Prime; NUM_PRIMES] = [ 11 | Prime { 12 | prime: 0xbe00000000000001, 13 | max_order_root: 0x37, 14 | }, 15 | Prime { 16 | prime: 0xd800000000000001, 17 | max_order_root: 0x40, 18 | }, 19 | Prime { 20 | prime: 0xf600000000000001, 21 | max_order_root: 0x1ed, 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /integer/src/arch/generic_64_bit/word.rs: -------------------------------------------------------------------------------- 1 | /// Machine word. 2 | pub type Word = u64; 3 | 4 | /// Signed machine word. 5 | pub type SignedWord = i64; 6 | 7 | /// Double machine word. 8 | pub type DoubleWord = u128; 9 | 10 | /// Signed double machine word. 11 | pub type SignedDoubleWord = i128; 12 | -------------------------------------------------------------------------------- /integer/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | //! Architecture dependent functionality. 2 | 3 | use cfg_if::cfg_if; 4 | 5 | pub(crate) use arch_impl::add; 6 | pub(crate) use arch_impl::digits; 7 | pub(crate) use arch_impl::ntt; 8 | pub(crate) use arch_impl::word; 9 | 10 | // Architecture choice. The logic works like this: 11 | // 1. If the configuration option force_bits is set to 16, 32 or 64, use generic__bit. 12 | // 2. Otherwise if target_arch is known, select that architecture. 13 | // 3. Otherwise target_pointer_width is 16 or 32, use generic__bit. 14 | // 4. Otherwise, use generic_64_bit. 15 | cfg_if! { 16 | // Step 1. Check force_bits. 17 | if #[cfg(force_bits = "16")] { 18 | #[path = "generic_16_bit/mod.rs"] 19 | mod arch_impl; 20 | } 21 | else if #[cfg(force_bits = "32")] { 22 | #[path = "generic_32_bit/mod.rs"] 23 | mod arch_impl; 24 | } 25 | else if #[cfg(force_bits = "64")] { 26 | #[path = "generic_64_bit/mod.rs"] 27 | mod arch_impl; 28 | } 29 | // Step 2. Specific architectures. 30 | else if #[cfg(target_arch = "x86")] { 31 | #[path = "x86/mod.rs"] 32 | mod arch_impl; 33 | } 34 | else if #[cfg(target_arch = "x86_64")] { 35 | #[path = "x86_64/mod.rs"] 36 | mod arch_impl; 37 | } 38 | else if #[cfg(any( 39 | target_arch = "arm", 40 | target_arch = "mips", 41 | target_arch = "powerpc", 42 | target_arch = "sparc", 43 | target_arch = "wasm32"))] { 44 | #[path = "generic_32_bit/mod.rs"] 45 | mod arch_impl; 46 | } 47 | else if #[cfg(any( 48 | target_arch = "aarch64", 49 | target_arch = "mips64", 50 | target_arch = "powerpc64"))] { 51 | #[path = "generic_64_bit/mod.rs"] 52 | mod arch_impl; 53 | } 54 | // Step 3. target_pointer_width 16 or 32. 55 | else if #[cfg(target_pointer_width = "16")] { 56 | #[path = "generic_16_bit/mod.rs"] 57 | mod arch_impl; 58 | } 59 | else if #[cfg(target_pointer_width = "32")] { 60 | #[path = "generic_32_bit/mod.rs"] 61 | mod arch_impl; 62 | } 63 | // Step 4. Fall back on generic_64_bit. 64 | else { 65 | #[path = "generic_64_bit/mod.rs"] 66 | mod arch_impl; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /integer/src/arch/x86/add.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::word::Word; 2 | 3 | /// Add a + b + carry. 4 | /// 5 | /// Returns (result, overflow). 6 | #[inline] 7 | pub fn add_with_carry(a: Word, b: Word, carry: bool) -> (Word, bool) { 8 | let mut sum = 0; 9 | let carry = unsafe { core::arch::x86::_addcarry_u32(carry.into(), a, b, &mut sum) }; 10 | (sum, carry != 0) 11 | } 12 | 13 | /// Subtract a - b - borrow. 14 | /// 15 | /// Returns (result, overflow). 16 | #[inline] 17 | pub fn sub_with_borrow(a: Word, b: Word, borrow: bool) -> (Word, bool) { 18 | let mut diff = 0; 19 | let borrow = unsafe { core::arch::x86::_subborrow_u32(borrow.into(), a, b, &mut diff) }; 20 | (diff, borrow != 0) 21 | } 22 | -------------------------------------------------------------------------------- /integer/src/arch/x86/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod add; 2 | 3 | #[path = "../generic/digits.rs"] 4 | pub(crate) mod digits; 5 | 6 | #[path = "../generic_32_bit/ntt.rs"] 7 | pub(crate) mod ntt; 8 | 9 | #[path = "../generic_32_bit/word.rs"] 10 | pub(crate) mod word; 11 | -------------------------------------------------------------------------------- /integer/src/arch/x86_64/add.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::word::Word; 2 | 3 | /// Add a + b + carry. 4 | /// 5 | /// Returns (result, overflow). 6 | #[inline] 7 | pub fn add_with_carry(a: Word, b: Word, carry: bool) -> (Word, bool) { 8 | let mut sum = 0; 9 | // SAFETY: this intrinsic is actually safe 10 | let carry = unsafe { core::arch::x86_64::_addcarry_u64(carry.into(), a, b, &mut sum) }; 11 | (sum, carry != 0) 12 | } 13 | 14 | /// Subtract a - b - borrow. 15 | /// 16 | /// Returns (result, overflow). 17 | #[inline] 18 | pub fn sub_with_borrow(a: Word, b: Word, borrow: bool) -> (Word, bool) { 19 | let mut diff = 0; 20 | // SAFETY: this intrinsic is actually safe 21 | let borrow = unsafe { core::arch::x86_64::_subborrow_u64(borrow.into(), a, b, &mut diff) }; 22 | (diff, borrow != 0) 23 | } 24 | -------------------------------------------------------------------------------- /integer/src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod add; 2 | 3 | #[path = "../generic/digits.rs"] 4 | pub(crate) mod digits; 5 | 6 | #[path = "../generic_64_bit/ntt.rs"] 7 | pub(crate) mod ntt; 8 | 9 | #[path = "../generic_64_bit/word.rs"] 10 | pub(crate) mod word; 11 | -------------------------------------------------------------------------------- /integer/src/div/simple.rs: -------------------------------------------------------------------------------- 1 | //! Simple (School book) division algorithm. 2 | 3 | use crate::{ 4 | add, 5 | arch::word::Word, 6 | cmp, 7 | math::FastDivideNormalized2, 8 | mul, 9 | primitive::{double_word, highest_dword, split_dword}, 10 | }; 11 | 12 | /// Division in place using the simple algorithm. 13 | /// 14 | /// Divide lhs by rhs, replacing the top words of lhs by the quotient and the 15 | /// bottom words of lhs by the remainder. 16 | /// 17 | /// `lhs = [lhs % rhs, lhs / rhs]` 18 | /// 19 | /// Returns carry in the quotient. It is at most 1 because rhs is normalized. 20 | #[must_use] 21 | pub(crate) fn div_rem_in_place( 22 | lhs: &mut [Word], 23 | rhs: &[Word], 24 | fast_div_rhs_top: FastDivideNormalized2, 25 | ) -> bool { 26 | // The Art of Computer Programming, algorithm 4.3.1D. 27 | 28 | let n = rhs.len(); 29 | assert!(n >= 2); 30 | 31 | let lhs_len = lhs.len(); 32 | assert!(lhs_len >= n); 33 | 34 | let quotient_carry = cmp::cmp_same_len(&lhs[lhs_len - n..], rhs).is_ge(); 35 | if quotient_carry { 36 | let overflow = add::sub_same_len_in_place(&mut lhs[lhs_len - n..], rhs); 37 | debug_assert!(!overflow); 38 | } 39 | 40 | // keep track of the position of remainder 41 | let mut rem = lhs; 42 | while rem.len() > n { 43 | let (lhs_top, lhs_lo) = rem.split_last_mut().unwrap(); 44 | 45 | // Get the next digit of quotient 46 | *lhs_top = div_rem_highest_word(*lhs_top, lhs_lo, rhs, fast_div_rhs_top); 47 | 48 | // Shrink the remainder. 49 | rem = lhs_lo; 50 | } 51 | // Quotient is now in lhs[n..] and remainder in lhs[..n]. 52 | quotient_carry 53 | } 54 | 55 | /// Do one step division on lhs by rhs, get the highest word of the quotient. 56 | /// 57 | /// Rhs must be normalized, lhs.len() > rhs.len() and lhs[lhs.len() - rhs.len()..] 58 | /// must be smaller than rhs. 59 | /// 60 | /// The remainder will be put in lhs_lo and the quotient word will be returned. 61 | #[inline] 62 | pub(crate) fn div_rem_highest_word( 63 | lhs_top: Word, 64 | lhs_lo: &mut [Word], 65 | rhs: &[Word], 66 | fast_div_rhs_top: FastDivideNormalized2, 67 | ) -> Word { 68 | let n = rhs.len(); 69 | let (rhs_top, rhs_lo) = rhs.split_last().unwrap(); 70 | 71 | let lhs_lo_len = lhs_lo.len(); 72 | debug_assert!(lhs_lo_len >= n); 73 | debug_assert!(lhs_top 74 | .cmp(rhs_top) 75 | .then(cmp::cmp_same_len(&lhs_lo[lhs_lo_len - rhs_lo.len()..], rhs_lo)) 76 | .is_le()); 77 | 78 | // lhs0 = lhs_top 79 | let (lhs2, lhs1) = split_dword(highest_dword(lhs_lo)); 80 | let lhs01 = double_word(lhs1, lhs_top); 81 | 82 | // Approximate the next word of quotient by 83 | // q = floor([lhs0, lhs1, lhs2] / [rhs0, rhs1]) 84 | // q may be too large (by 1), but never too Small 85 | let mut q = if lhs_top < *rhs_top { 86 | fast_div_rhs_top.div_rem_3by2(lhs2, lhs01).0 87 | } else { 88 | // In this case MAX is accurate (r is already overflown). 89 | Word::MAX 90 | }; 91 | 92 | // Subtract a multiple of rhs. 93 | let mut borrow = mul::sub_mul_word_same_len_in_place(&mut lhs_lo[lhs_lo_len - n..], q, rhs); 94 | 95 | if borrow > lhs_top { 96 | // Unlikely case: q is too large (by 1), add a correction. 97 | q -= 1; 98 | let carry = add::add_same_len_in_place(&mut lhs_lo[lhs_lo_len - n..], rhs); 99 | debug_assert!(carry); 100 | borrow -= 1; 101 | } 102 | debug_assert!(borrow == lhs_top); 103 | 104 | q 105 | } 106 | -------------------------------------------------------------------------------- /integer/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Definitions of panic cases 2 | 3 | /// Panics when division by 0 is happening 4 | pub(crate) const fn panic_divide_by_0() -> ! { 5 | panic!("divisor must not be 0") 6 | } 7 | 8 | /// Panics when try to allocate memory with size exceeding usize range 9 | pub(crate) const fn panic_allocate_too_much() -> ! { 10 | panic!("try to allocate too much memory") 11 | } 12 | 13 | /// Panics when allocation failed 14 | pub(crate) const fn panic_out_of_memory() -> ! { 15 | panic!("out of memory") 16 | } 17 | 18 | /// Panics when the `UBig` result is negative 19 | pub(crate) const fn panic_negative_ubig() -> ! { 20 | panic!("UBig result must not be negative") 21 | } 22 | 23 | /// Panics when trying to do operations on `Modulo` values from different rings. 24 | pub(crate) const fn panic_different_rings() -> ! { 25 | panic!("Modulo values from different rings") 26 | } 27 | 28 | /// Panics when the radix is not supported 29 | pub(crate) fn panic_invalid_radix(radix: u32) -> ! { 30 | panic!("invalid radix: {}, only radix 2-36 are supported", radix); 31 | } 32 | 33 | /// Panics when the base is 0 or 1 in logarithm 34 | pub(crate) fn panic_invalid_log_oprand() -> ! { 35 | panic!("logarithm is not defined for 0, base 0 and base 1!"); 36 | } 37 | 38 | /// Panics when taking the zeroth root of an integer 39 | pub(crate) fn panic_root_zeroth() -> ! { 40 | panic!("finding 0th root is not allowed!") 41 | } 42 | 43 | /// Panics when taking an even order root of an negative integer 44 | pub(crate) fn panic_root_negative() -> ! { 45 | panic!("the root is a complex number!") 46 | } 47 | 48 | /// Panics when taking an inavlid inverse on a modulo number 49 | pub(crate) fn panic_divide_by_invalid_modulo() -> ! { 50 | panic!("Division by a non-invertible Modulo") 51 | } 52 | -------------------------------------------------------------------------------- /integer/src/fmt/digit_writer.rs: -------------------------------------------------------------------------------- 1 | //! Buffered raw digit -> ASCII conversion. 2 | 3 | use crate::{arch, math, radix::DigitCase}; 4 | use core::{convert::TryInto, fmt, str}; 5 | 6 | /// Minimum buffer length. 7 | const BUFFER_LEN_MIN: usize = 32; 8 | 9 | const BUFFER_LEN: usize = math::round_up_usize(BUFFER_LEN_MIN, arch::digits::DIGIT_CHUNK_LEN); 10 | 11 | /// DigitWriter allows writing raw digits and turns them into ASCII. 12 | pub struct DigitWriter<'a> { 13 | buffer: [u8; BUFFER_LEN], 14 | buffer_len: usize, 15 | digit_case: DigitCase, 16 | writer: &'a mut dyn fmt::Write, 17 | } 18 | 19 | impl<'a> DigitWriter<'a> { 20 | pub fn new(writer: &'a mut dyn fmt::Write, digit_case: DigitCase) -> DigitWriter { 21 | DigitWriter { 22 | buffer: [0; BUFFER_LEN], 23 | buffer_len: 0, 24 | digit_case, 25 | writer, 26 | } 27 | } 28 | 29 | /// buf must contain values 0-35, or 0-9 if digit_case is NoLetters. 30 | pub fn write(&mut self, mut buf: &[u8]) -> fmt::Result { 31 | while !buf.is_empty() { 32 | let len = buf.len().min(BUFFER_LEN - self.buffer_len); 33 | let (buf_chunk, buf_remainder) = buf.split_at(len); 34 | buf = buf_remainder; 35 | self.buffer[self.buffer_len..self.buffer_len + len].copy_from_slice(buf_chunk); 36 | self.buffer_len += len; 37 | if self.buffer_len == BUFFER_LEN { 38 | self.flush()?; 39 | } 40 | } 41 | Ok(()) 42 | } 43 | 44 | /// Must call flush to make sure all the data is written. 45 | pub fn flush(&mut self) -> fmt::Result { 46 | let buffer_len_rounded = math::round_up(self.buffer_len, arch::digits::DIGIT_CHUNK_LEN); 47 | self.buffer[self.buffer_len..buffer_len_rounded].fill(0); 48 | for chunk in 49 | self.buffer[..buffer_len_rounded].chunks_exact_mut(arch::digits::DIGIT_CHUNK_LEN) 50 | { 51 | arch::digits::digit_chunk_raw_to_ascii(chunk.try_into().unwrap(), self.digit_case); 52 | } 53 | let b = &self.buffer[..self.buffer_len]; 54 | // SAFETY: the buffer contains only ASCII characters 0-9, a-z, A-Z. 55 | let s = unsafe { str::from_utf8_unchecked(b) }; 56 | self.writer.write_str(s)?; 57 | self.buffer_len = 0; 58 | Ok(()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /integer/src/iter.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of core::iter traits 2 | 3 | use crate::{ibig::IBig, ubig::UBig}; 4 | use core::{ 5 | iter::{Product, Sum}, 6 | ops::{Add, Mul}, 7 | }; 8 | 9 | macro_rules! impl_fold_iter { 10 | ($t:ty, $fold_trait:ident, $fold:ident, $op_trait:ident, $op:ident, $init:ident) => { 11 | impl $fold_trait for $t 12 | where 13 | $t: $op_trait, 14 | { 15 | fn $fold>(iter: I) -> Self { 16 | iter.fold(<$t>::$init, <$t>::$op) 17 | } 18 | } 19 | }; 20 | } 21 | 22 | impl_fold_iter!(UBig, Sum, sum, Add, add, ZERO); 23 | impl_fold_iter!(IBig, Sum, sum, Add, add, ZERO); 24 | impl_fold_iter!(UBig, Product, product, Mul, mul, ONE); 25 | impl_fold_iter!(IBig, Product, product, Mul, mul, ONE); 26 | -------------------------------------------------------------------------------- /integer/src/modular/fmt.rs: -------------------------------------------------------------------------------- 1 | //! Formatting modular rings and modular numbers. 2 | 3 | use super::repr::Reduced; 4 | use core::fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex}; 5 | 6 | macro_rules! impl_fmt_for_modulo { 7 | ($t:ident) => { 8 | impl $t for Reduced<'_> { 9 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 10 | $t::fmt(&self.residue(), f)?; 11 | f.write_str(" (mod ")?; 12 | $t::fmt(&self.modulus(), f)?; 13 | f.write_str(")") 14 | } 15 | } 16 | }; 17 | } 18 | 19 | impl_fmt_for_modulo!(Display); 20 | impl_fmt_for_modulo!(Binary); 21 | impl_fmt_for_modulo!(Octal); 22 | impl_fmt_for_modulo!(LowerHex); 23 | impl_fmt_for_modulo!(UpperHex); 24 | 25 | impl Debug for Reduced<'_> { 26 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 27 | let residue = self.residue(); 28 | let modulus = self.modulus(); 29 | if f.alternate() { 30 | f.debug_struct("Reduced") 31 | .field("residue", &residue) 32 | .field("modulus", &modulus) 33 | .finish() 34 | } else { 35 | Debug::fmt(&residue, f)?; 36 | f.write_str(" (mod ")?; 37 | Debug::fmt(&self.modulus(), f)?; 38 | f.write_str(")") 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /integer/src/modular/mod.rs: -------------------------------------------------------------------------------- 1 | //! Modular arithmetic. 2 | //! 3 | //! Modular arithmetic is performed on [Reduced] values attached to a [ConstDivisor][crate::div_const::ConstDivisor]. 4 | //! 5 | //! Trying to mix different [ConstDivisor][crate::fast_div::ConstDivisor] instances (even with the same modulus!) will cause a panic. 6 | //! 7 | //! # Examples 8 | //! 9 | //! ``` 10 | //! use dashu_int::{fast_div::ConstDivisor, UBig}; 11 | //! 12 | //! let ring = ConstDivisor::new(UBig::from(10000u32)); 13 | //! let x = ring.reduce(12345); 14 | //! let y = ring.reduce(55443); 15 | //! assert_eq!(format!("{}", x - y), "6902 (mod 10000)"); 16 | //! ``` 17 | 18 | pub use convert::IntoRing; 19 | pub use repr::Reduced; 20 | 21 | mod add; 22 | pub(crate) mod convert; 23 | mod div; 24 | mod fmt; 25 | mod mul; 26 | mod pow; 27 | mod reducer; 28 | pub(crate) mod repr; 29 | 30 | // TODO: Also support Montgomery form reductions, use relaxed form described 31 | // in https://cetinkayakoc.net/docs/j56.pdf and https://eprint.iacr.org/2011/239.pdf 32 | -------------------------------------------------------------------------------- /integer/src/mul/helpers.rs: -------------------------------------------------------------------------------- 1 | //! Helper functions for multiplication algorithms. 2 | 3 | use crate::{ 4 | add, 5 | arch::word::{SignedWord, Word}, 6 | memory::Memory, 7 | mul, Sign, 8 | }; 9 | 10 | /// c += sign * a * b 11 | /// 12 | /// Splits a into chunks of chunk_len, using regular multiplication for the remainder if any. 13 | /// 14 | /// Returns carry. 15 | pub fn add_signed_mul_split_into_chunks( 16 | mut c: &mut [Word], 17 | sign: Sign, 18 | mut a: &[Word], 19 | b: &[Word], 20 | chunk_len: usize, 21 | memory: &mut Memory, 22 | f_add_signed_mul_chunk: F, 23 | ) -> SignedWord 24 | where 25 | F: Fn(&mut [Word], Sign, &[Word], &[Word], &mut Memory) -> SignedWord, 26 | { 27 | debug_assert!(a.len() >= b.len() && c.len() == a.len() + b.len()); 28 | debug_assert!(b.len() <= chunk_len); 29 | 30 | let n = b.len(); 31 | let mut carry_n = 0; // at c[n] 32 | while a.len() >= chunk_len { 33 | let (a_lo, a_hi) = a.split_at(chunk_len); 34 | // Propagate carry_n 35 | carry_n = add::add_signed_word_in_place(&mut c[n..chunk_len + n], carry_n); 36 | carry_n += f_add_signed_mul_chunk(&mut c[..chunk_len + n], sign, a_lo, b, memory); 37 | a = a_hi; 38 | c = &mut c[chunk_len..]; 39 | } 40 | // Propagate carry_n 41 | let mut carry = add::add_signed_word_in_place(&mut c[n..], carry_n); 42 | if a.len() >= b.len() { 43 | carry += mul::add_signed_mul(c, sign, a, b, memory); 44 | } else if !a.is_empty() { 45 | carry += mul::add_signed_mul(c, sign, b, a, memory); 46 | } 47 | carry 48 | } 49 | -------------------------------------------------------------------------------- /integer/src/mul/simple.rs: -------------------------------------------------------------------------------- 1 | //! Simple multiplication algorithm. 2 | 3 | use crate::{ 4 | arch::{ 5 | self, 6 | word::{SignedWord, Word}, 7 | }, 8 | memory::Memory, 9 | mul::{self, helpers}, 10 | Sign::{self, *}, 11 | }; 12 | 13 | /// Split larger length into chunks of CHUNK_LEN..2 * CHUNK_LEN for memory locality. 14 | const CHUNK_LEN: usize = 1024; 15 | 16 | /// Max supported Smaller factor length. 17 | pub const MAX_SMALLER_LEN: usize = CHUNK_LEN; 18 | 19 | /// c += sign * a * b 20 | /// Simple method: O(a.len() * b.len()). 21 | /// 22 | /// Returns carry. 23 | pub fn add_signed_mul( 24 | c: &mut [Word], 25 | sign: Sign, 26 | a: &[Word], 27 | b: &[Word], 28 | memory: &mut Memory, 29 | ) -> SignedWord { 30 | debug_assert!(a.len() >= b.len() && c.len() == a.len() + b.len()); 31 | debug_assert!(b.len() <= MAX_SMALLER_LEN); 32 | if a.len() <= CHUNK_LEN { 33 | add_signed_mul_chunk(c, sign, a, b, memory) 34 | } else { 35 | helpers::add_signed_mul_split_into_chunks( 36 | c, 37 | sign, 38 | a, 39 | b, 40 | CHUNK_LEN, 41 | memory, 42 | add_signed_mul_chunk, 43 | ) 44 | } 45 | } 46 | 47 | /// c += sign * a * b 48 | /// Simple method: O(a.len() * b.len()). 49 | /// 50 | /// Returns carry. 51 | pub fn add_signed_mul_same_len( 52 | c: &mut [Word], 53 | sign: Sign, 54 | a: &[Word], 55 | b: &[Word], 56 | memory: &mut Memory, 57 | ) -> SignedWord { 58 | debug_assert!(a.len() == b.len() && c.len() == a.len() + b.len()); 59 | debug_assert!(b.len() <= MAX_SMALLER_LEN); 60 | add_signed_mul_chunk(c, sign, a, b, memory) 61 | } 62 | 63 | /// c += sign * a * b 64 | /// Simple method: O(a.len() * b.len()). 65 | /// 66 | /// Returns carry. 67 | fn add_signed_mul_chunk( 68 | c: &mut [Word], 69 | sign: Sign, 70 | a: &[Word], 71 | b: &[Word], 72 | _memory: &mut Memory, 73 | ) -> SignedWord { 74 | debug_assert!(a.len() >= b.len() && c.len() == a.len() + b.len()); 75 | debug_assert!(a.len() <= CHUNK_LEN); 76 | 77 | match sign { 78 | Positive => SignedWord::from(add_mul_chunk(c, a, b)), 79 | Negative => -SignedWord::from(sub_mul_chunk(c, a, b)), 80 | } 81 | } 82 | 83 | /// c += a * b 84 | /// Simple method: O(a.len() * b.len()). 85 | /// 86 | /// Returns carry. 87 | fn add_mul_chunk(c: &mut [Word], a: &[Word], b: &[Word]) -> bool { 88 | debug_assert!(a.len() >= b.len() && c.len() == a.len() + b.len()); 89 | debug_assert!(a.len() < 2 * CHUNK_LEN); 90 | let mut carry = false; 91 | for (i, m) in b.iter().enumerate() { 92 | let carry_word = mul::add_mul_word_same_len_in_place(&mut c[i..i + a.len()], *m, a); 93 | let (carry_word, carry_next) = arch::add::add_with_carry(c[i + a.len()], carry_word, carry); 94 | c[i + a.len()] = carry_word; 95 | carry = carry_next; 96 | } 97 | carry 98 | } 99 | 100 | /// c -= a * b 101 | /// Simple method: O(a.len() * b.len()). 102 | /// 103 | /// Returns borrow. 104 | fn sub_mul_chunk(c: &mut [Word], a: &[Word], b: &[Word]) -> bool { 105 | debug_assert!(a.len() >= b.len() && c.len() == a.len() + b.len()); 106 | debug_assert!(a.len() < 2 * CHUNK_LEN); 107 | let mut borrow = false; 108 | for (i, m) in b.iter().enumerate() { 109 | let borrow_word = mul::sub_mul_word_same_len_in_place(&mut c[i..i + a.len()], *m, a); 110 | let (borrow_word, borrow_next) = 111 | arch::add::sub_with_borrow(c[i + a.len()], borrow_word, borrow); 112 | c[i + a.len()] = borrow_word; 113 | borrow = borrow_next; 114 | } 115 | borrow 116 | } 117 | -------------------------------------------------------------------------------- /integer/src/ops.rs: -------------------------------------------------------------------------------- 1 | //! Re-exported relevant operator traits from `dashu-base` 2 | 3 | pub use dashu_base::bit::{BitTest, PowerOfTwo}; 4 | pub use dashu_base::math::{CubicRoot, EstimatedLog2, SquareRoot}; 5 | pub use dashu_base::ring::{ 6 | CubicRootRem, DivEuclid, DivRem, DivRemAssign, DivRemEuclid, ExtendedGcd, Gcd, RemEuclid, 7 | SquareRootRem, 8 | }; 9 | pub use dashu_base::sign::{Abs, UnsignedAbs}; 10 | -------------------------------------------------------------------------------- /integer/src/parse/power_two.rs: -------------------------------------------------------------------------------- 1 | //! Parse in a power-of-two radix. 2 | 3 | use crate::{ 4 | arch::word::Word, 5 | buffer::Buffer, 6 | primitive::{WORD_BITS, WORD_BITS_USIZE}, 7 | radix::{self, Digit}, 8 | repr::Repr, 9 | ubig::UBig, 10 | }; 11 | 12 | use dashu_base::ParseError; 13 | 14 | /// Parse an unsigned string to [UBig]. 15 | pub fn parse(src: &str, radix: Digit) -> Result { 16 | debug_assert!(radix::is_radix_valid(radix) && radix.is_power_of_two()); 17 | 18 | let digits_per_word = (WORD_BITS / radix.trailing_zeros()) as usize; 19 | if src.len() <= digits_per_word { 20 | Ok(parse_word(src, radix)?.into()) 21 | } else { 22 | parse_large(src, radix) 23 | } 24 | } 25 | 26 | /// Parse an unsigned string to `Word`. 27 | /// 28 | /// The length of the string must be at most digits_per_word(radix). 29 | fn parse_word(src: &str, radix: Digit) -> Result { 30 | debug_assert!(radix::is_radix_valid(radix) && radix.is_power_of_two()); 31 | debug_assert!(src.len() <= (WORD_BITS / radix.trailing_zeros()) as usize); 32 | 33 | let log_radix = radix.trailing_zeros(); 34 | let mut word = 0; 35 | let mut bits = 0; 36 | for byte in src.as_bytes().iter().rev() { 37 | if *byte == b'_' { 38 | continue; 39 | } 40 | let digit = radix::digit_from_ascii_byte(*byte, radix).ok_or(ParseError::InvalidDigit)?; 41 | word |= (digit as Word) << bits; 42 | bits += log_radix; 43 | } 44 | Ok(word) 45 | } 46 | 47 | /// Parse an unsigned string to [UBig]. 48 | /// 49 | /// The result will usually not fit in a single word. 50 | fn parse_large(src: &str, radix: Digit) -> Result { 51 | debug_assert!(radix::is_radix_valid(radix) && radix.is_power_of_two()); 52 | 53 | let log_radix = radix.trailing_zeros(); 54 | #[allow(clippy::redundant_closure)] 55 | let num_bits = src 56 | .len() 57 | .checked_mul(log_radix as usize) 58 | .expect("the number to be parsed is too large"); 59 | let mut buffer = Buffer::allocate((num_bits - 1) / WORD_BITS_USIZE + 1); 60 | let mut bits = 0; 61 | let mut word = 0; 62 | for byte in src.as_bytes().iter().rev() { 63 | if *byte == b'_' { 64 | continue; 65 | } 66 | let digit = radix::digit_from_ascii_byte(*byte, radix).ok_or(ParseError::InvalidDigit)?; 67 | word |= (digit as Word) << bits; 68 | let new_bits = bits + log_radix; 69 | if new_bits >= WORD_BITS { 70 | buffer.push(word); 71 | word = (digit as Word) >> (WORD_BITS - bits); 72 | bits = new_bits - WORD_BITS; 73 | } else { 74 | bits = new_bits; 75 | } 76 | } 77 | if bits > 0 { 78 | buffer.push(word); 79 | } 80 | Ok(UBig(Repr::from_buffer(buffer))) 81 | } 82 | -------------------------------------------------------------------------------- /integer/src/remove.rs: -------------------------------------------------------------------------------- 1 | use crate::ubig::UBig; 2 | use alloc::vec; 3 | use dashu_base::{DivRem, PowerOfTwo}; 4 | 5 | impl UBig { 6 | /// Divide out all multiples of the factor from the integer, 7 | /// returns the exponent of the removed factor. 8 | /// 9 | /// For self = 0 or factor = 0 or 1, this method returns None. 10 | pub fn remove(&mut self, factor: &UBig) -> Option { 11 | if self.is_zero() || factor.is_zero() || factor.is_one() { 12 | return None; 13 | } 14 | 15 | // shortcut for power of 2 16 | if factor.is_power_of_two() { 17 | let bits = factor.trailing_zeros().unwrap(); 18 | let exp = self.trailing_zeros().unwrap() / bits; 19 | *self >>= exp * bits; 20 | return Some(exp); 21 | } 22 | 23 | let (mut q, r) = (&*self).div_rem(factor); 24 | if !r.is_zero() { 25 | return Some(0); 26 | } 27 | 28 | // first stage, division with exponentially growing factors 29 | let mut exp = 1; 30 | let mut pows = vec![factor.sqr()]; 31 | loop { 32 | let last = pows.last().unwrap(); 33 | let (new_q, r) = (&q).div_rem(last); 34 | if !r.is_zero() { 35 | break; 36 | } 37 | 38 | exp += 1 << pows.len(); 39 | q = new_q; 40 | let next_sq = last.sqr(); 41 | pows.push(next_sq); 42 | } 43 | 44 | // second stage, division from highest power to the lowest 45 | while let Some(last) = pows.pop() { 46 | let (new_q, r) = (&q).div_rem(last); 47 | if r.is_zero() { 48 | exp += 1 << (pows.len() + 1); 49 | q = new_q; 50 | } 51 | } 52 | 53 | // last division 54 | let (new_q, r) = (&q).div_rem(factor); 55 | if r.is_zero() { 56 | exp += 1; 57 | q = new_q; 58 | } 59 | 60 | *self = q; 61 | Some(exp) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /integer/src/shift.rs: -------------------------------------------------------------------------------- 1 | //! Bit shift functions. 2 | 3 | use crate::{ 4 | arch::word::Word, 5 | math::shr_word, 6 | primitive::{extend_word, split_dword, WORD_BITS}, 7 | }; 8 | 9 | /// Shift left by less than WORD_BITS in place. 10 | /// Returns carry. 11 | pub fn shl_in_place(words: &mut [Word], shift: u32) -> Word { 12 | debug_assert!(shift < WORD_BITS); 13 | if shift == 0 { 14 | return 0; 15 | } 16 | let mut carry = 0; 17 | for word in words { 18 | let (new_word, new_carry) = split_dword(extend_word(*word) << shift); 19 | *word = new_word | carry; 20 | carry = new_carry; 21 | } 22 | carry 23 | } 24 | 25 | /// Shift right by at most WORD_BITS in place. 26 | /// Returns shifted bits in the high bits of a Word. 27 | #[inline] 28 | pub fn shr_in_place(words: &mut [Word], shift: u32) -> Word { 29 | debug_assert!(shift <= WORD_BITS); 30 | if shift == WORD_BITS { 31 | shr_in_place_one_word(words) 32 | } else { 33 | shr_in_place_with_carry(words, shift, 0) 34 | } 35 | } 36 | 37 | /// Shift right by less than WORD_BITS in place. 38 | /// An optional carry could be provided from a higher word. 39 | /// Returns shifted bits in the high bits of a Word. 40 | pub fn shr_in_place_with_carry(words: &mut [Word], shift: u32, mut carry: Word) -> Word { 41 | debug_assert!(shift < WORD_BITS); 42 | if shift == 0 { 43 | debug_assert_eq!(carry, 0); 44 | return 0; 45 | } 46 | for word in words.iter_mut().rev() { 47 | let (new_word, new_carry) = shr_word(*word, shift); 48 | *word = new_word | carry; 49 | carry = new_carry; 50 | } 51 | carry 52 | } 53 | 54 | /// Shift right by WORD_BITS in place 55 | pub fn shr_in_place_one_word(words: &mut [Word]) -> Word { 56 | // SAFETY: the ptr and len all comes from the slice, so it's safe 57 | unsafe { 58 | let ptr = words.as_mut_ptr(); 59 | let rem = ptr.read(); 60 | ptr.copy_from(ptr.add(1), words.len() - 1); 61 | ptr.add(words.len() - 1).write(0); 62 | rem 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /integer/src/sign.rs: -------------------------------------------------------------------------------- 1 | //! Operators on the sign of [IBig]. 2 | 3 | use crate::{ 4 | ibig::IBig, 5 | ops::{Abs, UnsignedAbs}, 6 | ubig::UBig, 7 | }; 8 | use core::ops::{Mul, MulAssign, Neg}; 9 | use dashu_base::{Sign, Signed}; 10 | 11 | impl IBig { 12 | /// A number representing the sign of `self`. 13 | /// 14 | /// * [IBig::ONE] if the number is positive 15 | /// * [IBig::ZERO] if the number is zero 16 | /// * [IBig::NEG_ONE] if the number is negative 17 | /// 18 | /// # Examples 19 | /// ``` 20 | /// # use dashu_int::IBig; 21 | /// assert_eq!(IBig::from(-500).signum(), IBig::from(-1)); 22 | /// ``` 23 | #[inline] 24 | pub const fn signum(&self) -> IBig { 25 | IBig(self.0.signum()) 26 | } 27 | } 28 | 29 | impl Neg for UBig { 30 | type Output = IBig; 31 | 32 | #[inline] 33 | fn neg(self) -> IBig { 34 | IBig(self.0.neg()) 35 | } 36 | } 37 | 38 | impl Neg for IBig { 39 | type Output = IBig; 40 | 41 | #[inline] 42 | fn neg(self) -> IBig { 43 | IBig(self.0.neg()) 44 | } 45 | } 46 | 47 | impl Neg for &UBig { 48 | type Output = IBig; 49 | 50 | #[inline] 51 | fn neg(self) -> IBig { 52 | IBig(self.0.clone().neg()) 53 | } 54 | } 55 | 56 | impl Neg for &IBig { 57 | type Output = IBig; 58 | 59 | #[inline] 60 | fn neg(self) -> IBig { 61 | IBig(self.0.clone().neg()) 62 | } 63 | } 64 | 65 | impl Abs for IBig { 66 | type Output = IBig; 67 | 68 | #[inline] 69 | fn abs(self) -> IBig { 70 | IBig(self.0.with_sign(Sign::Positive)) 71 | } 72 | } 73 | 74 | impl Abs for &IBig { 75 | type Output = IBig; 76 | 77 | #[inline] 78 | fn abs(self) -> IBig { 79 | IBig(self.0.clone().with_sign(Sign::Positive)) 80 | } 81 | } 82 | 83 | impl UnsignedAbs for IBig { 84 | type Output = UBig; 85 | 86 | #[inline] 87 | fn unsigned_abs(self) -> UBig { 88 | UBig(self.0.with_sign(Sign::Positive)) 89 | } 90 | } 91 | 92 | impl UnsignedAbs for &IBig { 93 | type Output = UBig; 94 | 95 | #[inline] 96 | fn unsigned_abs(self) -> UBig { 97 | UBig(self.0.clone().with_sign(Sign::Positive)) 98 | } 99 | } 100 | 101 | impl Mul for UBig { 102 | type Output = IBig; 103 | 104 | #[inline] 105 | fn mul(self, rhs: Sign) -> Self::Output { 106 | IBig(self.0.with_sign(rhs)) 107 | } 108 | } 109 | 110 | impl Mul for Sign { 111 | type Output = IBig; 112 | 113 | #[inline] 114 | fn mul(self, rhs: UBig) -> Self::Output { 115 | IBig(rhs.0.with_sign(self)) 116 | } 117 | } 118 | 119 | impl Mul for IBig { 120 | type Output = IBig; 121 | 122 | #[inline] 123 | fn mul(self, rhs: Sign) -> Self::Output { 124 | let sign = self.sign() * rhs; 125 | IBig(self.0.with_sign(sign)) 126 | } 127 | } 128 | 129 | impl Mul for Sign { 130 | type Output = IBig; 131 | 132 | #[inline] 133 | fn mul(self, rhs: IBig) -> Self::Output { 134 | let sign = self * rhs.sign(); 135 | IBig(rhs.0.with_sign(sign)) 136 | } 137 | } 138 | 139 | impl MulAssign for IBig { 140 | #[inline] 141 | fn mul_assign(&mut self, rhs: Sign) { 142 | *self = core::mem::take(self) * rhs; 143 | } 144 | } 145 | 146 | impl Signed for IBig { 147 | #[inline] 148 | fn sign(&self) -> Sign { 149 | self.0.sign() 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /integer/src/sqr/mod.rs: -------------------------------------------------------------------------------- 1 | //! Square. 2 | 3 | use alloc::alloc::Layout; 4 | 5 | use crate::{ 6 | arch::word::Word, 7 | helper_macros::debug_assert_zero, 8 | memory::{self, Memory}, 9 | mul, Sign, 10 | }; 11 | 12 | mod simple; 13 | 14 | /// If operand length <= this, simple squaring will be used. 15 | const MAX_LEN_SIMPLE: usize = 30; 16 | 17 | pub fn memory_requirement_exact(len: usize) -> Layout { 18 | if len <= MAX_LEN_SIMPLE { 19 | memory::zero_layout() 20 | } else { 21 | mul::memory_requirement_up_to(2 * len, len) 22 | } 23 | } 24 | 25 | /// b = a * a, b must be filled with zeros. 26 | pub fn sqr(b: &mut [Word], a: &[Word], memory: &mut Memory) { 27 | debug_assert!(a.len() >= 2, "use native multiplication when a is small"); 28 | debug_assert!(b.len() == a.len() * 2); 29 | debug_assert!(b.iter().all(|&v| v == 0)); 30 | 31 | if a.len() <= MAX_LEN_SIMPLE { 32 | simple::square(b, a); 33 | } else { 34 | debug_assert_zero!(mul::add_signed_mul_same_len(b, Sign::Positive, a, a, memory)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integer/src/sqr/simple.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | arch::{ 3 | self, 4 | word::{DoubleWord, Word}, 5 | }, 6 | math::mul_add_2carry, 7 | mul, 8 | primitive::{double_word, split_dword}, 9 | }; 10 | 11 | pub fn square(b: &mut [Word], a: &[Word]) { 12 | debug_assert!(b.len() == a.len() * 2); 13 | 14 | /* 15 | * A simple algorithm for squaring 16 | * 17 | * let B = 2^WORD_BITS 18 | * take a = a0 + a1*B + a2*B^2 + a3*B^3 as an example 19 | * to calculate a^2 = (a0 + a1*B + a2*B^2 + a3*B^3) ^ 2 20 | * 21 | * first 22 | * b += a0 * (a1 + a2*B + a3*B^2) * B 23 | * b += a1 * (a2 + a3*B) * B^3 24 | * b += a2 * a3 * B^5 25 | * 26 | * then 27 | * b = b * 2 + (a0^2 + a1^2*B^2 + a2^2*B^4 + a3^2*B^6) 28 | * the square and shifting can be fused in a single run 29 | * 30 | */ 31 | 32 | // first step (triangular part) 33 | let mut c0 = false; 34 | let mut offset = 1; 35 | let mut a_cur = a; 36 | while let Some((m, new_cur)) = a_cur.split_first() { 37 | a_cur = new_cur; 38 | let carry = 39 | mul::add_mul_word_same_len_in_place(&mut b[offset..offset + a_cur.len()], *m, a_cur); 40 | let b_top = &mut b[offset + a_cur.len()]; 41 | let (new_top, carry_next) = arch::add::add_with_carry(*b_top, carry, c0); 42 | *b_top = new_top; 43 | c0 = carry_next; 44 | offset += 2; 45 | } 46 | 47 | // second step (diagonal part) 48 | let (mut c1, mut c2) = (false, false); 49 | for (m, b01) in a.iter().zip(b.chunks_exact_mut(2)) { 50 | let b0 = b01.first().unwrap(); 51 | let b1 = b01.last().unwrap(); 52 | 53 | // new [b0, b1] = m^2 + 2 * [b0, b1] + c1 + c2 54 | let (s0, s1) = mul_add_2carry(*m, *m, *b0, *b0); 55 | let s = double_word(s0, s1); 56 | let wb1 = double_word(0, *b1); 57 | let (s, oc1) = s.overflowing_add(wb1 + c1 as DoubleWord); 58 | let (s, oc2) = s.overflowing_add(wb1 + c2 as DoubleWord); 59 | let (s0, s1) = split_dword(s); 60 | 61 | *b01.first_mut().unwrap() = s0; 62 | *b01.last_mut().unwrap() = s1; 63 | c1 = oc1; 64 | c2 = oc2; 65 | } 66 | 67 | // aggregate carry bits 68 | *b.last_mut().unwrap() += c0 as Word + c1 as Word + c2 as Word; 69 | } 70 | -------------------------------------------------------------------------------- /integer/src/third_party/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementations for third party crates and traits 2 | 3 | #[cfg(feature = "num-integer_v01")] 4 | mod num_integer; 5 | 6 | #[cfg(feature = "num-order")] 7 | mod num_order; 8 | 9 | #[cfg(feature = "num-traits_v02")] 10 | mod num_traits; 11 | 12 | #[cfg(feature = "rand_v08")] 13 | pub mod rand; 14 | 15 | #[cfg(feature = "serde")] 16 | mod serde; 17 | 18 | #[cfg(feature = "zeroize")] 19 | mod zeroize; 20 | -------------------------------------------------------------------------------- /integer/src/third_party/zeroize.rs: -------------------------------------------------------------------------------- 1 | //! Implement zeroize traits. 2 | 3 | use zeroize::Zeroize; 4 | 5 | use crate::{ 6 | buffer::Buffer, 7 | repr::{Repr, TypedRepr}, 8 | IBig, UBig, 9 | }; 10 | 11 | impl Zeroize for Buffer { 12 | fn zeroize(&mut self) { 13 | self.as_full_slice().zeroize(); 14 | self.truncate(0) 15 | } 16 | } 17 | 18 | impl Zeroize for Repr { 19 | fn zeroize(&mut self) { 20 | self.as_full_slice().zeroize(); 21 | self.clone_from(&Repr::zero()); 22 | } 23 | } 24 | 25 | impl Zeroize for TypedRepr { 26 | #[inline] 27 | fn zeroize(&mut self) { 28 | if let TypedRepr::Large(buffer) = self { 29 | buffer.zeroize() 30 | } 31 | *self = TypedRepr::Small(0) 32 | } 33 | } 34 | 35 | impl Zeroize for UBig { 36 | #[inline] 37 | fn zeroize(&mut self) { 38 | self.0.zeroize() 39 | } 40 | } 41 | 42 | impl Zeroize for IBig { 43 | #[inline] 44 | fn zeroize(&mut self) { 45 | self.0.zeroize() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /integer/tests/cmp.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::cmp_owned)] 2 | 3 | use core::cmp::Ordering; 4 | 5 | use dashu_base::{AbsOrd, Sign}; 6 | use dashu_int::{IBig, UBig}; 7 | 8 | mod helper_macros; 9 | 10 | #[test] 11 | fn test_eq_and_cmp() { 12 | assert_eq!(ubig!(0), UBig::from_words(&[])); 13 | assert_eq!(ubig!(0), UBig::from_words(&[0])); 14 | assert_eq!(ibig!(0), IBig::from_parts(Sign::Positive, UBig::from_words(&[]))); 15 | assert_eq!(ibig!(0), IBig::from_parts(Sign::Positive, UBig::from_words(&[0]))); 16 | assert_eq!(ubig!(500), UBig::from_words(&[500])); 17 | assert_eq!(ibig!(500), IBig::from_parts(Sign::Positive, UBig::from_words(&[500]))); 18 | assert_eq!(ibig!(-500), IBig::from_parts(Sign::Negative, UBig::from_words(&[500]))); 19 | assert_eq!(ubig!(500).cmp(&ubig!(500)), Ordering::Equal); 20 | 21 | assert!(ubig!(100) < ubig!(500)); 22 | assert!(ubig!(500) > ubig!(100)); 23 | assert!(ubig!(0x10000000000000000) > ubig!(100)); 24 | assert!(ubig!(100) < ubig!(0x100000000000000000000000000000000)); 25 | assert!( 26 | ubig!(0x100000000000000020000000000000003) < ubig!(0x100000000000000030000000000000002) 27 | ); 28 | assert!( 29 | ubig!(0x100000000000000030000000000000002) > ubig!(0x100000000000000020000000000000003) 30 | ); 31 | assert_eq!( 32 | ubig!(0x100000000000000030000000000000002).cmp(&ubig!(0x100000000000000030000000000000002)), 33 | Ordering::Equal 34 | ); 35 | 36 | assert_eq!(ibig!(500).cmp(&ibig!(500)), Ordering::Equal); 37 | assert_eq!(ibig!(-500).cmp(&ibig!(-500)), Ordering::Equal); 38 | assert!(ibig!(5) < ibig!(10)); 39 | assert!(ibig!(10) > ibig!(5)); 40 | assert!(ibig!(-5) < ibig!(10)); 41 | assert!(ibig!(-15) < ibig!(10)); 42 | assert!(ibig!(10) > ibig!(-5)); 43 | assert!(ibig!(10) > ibig!(-15)); 44 | assert!(ibig!(-10) < ibig!(-5)); 45 | assert!(ibig!(-5) > ibig!(-10)); 46 | } 47 | 48 | #[test] 49 | fn test_abs_ord() { 50 | // assert!(ubig!(12).abs_eq(&ubig!(12))); 51 | // assert!(ubig!(12).abs_eq(&ibig!(-12))); 52 | // assert!(ibig!(-12).abs_eq(&ubig!(12))); 53 | // assert!(ibig!(12).abs_eq(&ibig!(-12))); 54 | 55 | assert!(ibig!(-12).abs_cmp(&ubig!(10)).is_ge()); 56 | assert!(ibig!(-12).abs_cmp(&ubig!(12)).is_eq()); 57 | assert!(ibig!(-12).abs_cmp(&ubig!(14)).is_le()); 58 | assert!(ubig!(12).abs_cmp(&ibig!(-10)).is_ge()); 59 | assert!(ubig!(12).abs_cmp(&ibig!(-12)).is_eq()); 60 | assert!(ubig!(12).abs_cmp(&ibig!(-14)).is_le()); 61 | } 62 | -------------------------------------------------------------------------------- /integer/tests/const_div.rs: -------------------------------------------------------------------------------- 1 | use dashu_base::{DivRem, DivRemAssign, Sign::Negative}; 2 | use dashu_int::fast_div::ConstDivisor; 3 | 4 | mod helper_macros; 5 | 6 | #[test] 7 | fn test_value() { 8 | let ring = ConstDivisor::new(ubig!(100)); 9 | assert_eq!(ring.value(), ubig!(100)); 10 | 11 | let ring = ConstDivisor::new(ubig!(10).pow(100)); 12 | assert_eq!(ring.value(), ubig!(10).pow(100)); 13 | } 14 | 15 | #[test] 16 | fn test_const_div() { 17 | let divisors = [ 18 | ubig!(1), 19 | ubig!(2), 20 | ubig!(3), 21 | ubig!(127), 22 | ubig!(12345), 23 | (ubig!(1) << 32) - ubig!(1), 24 | (ubig!(1) << 64) - ubig!(1), 25 | (ubig!(1) << 128) - ubig!(1), 26 | (ubig!(1) << 256) - ubig!(1), 27 | ]; 28 | 29 | let numbers = [ 30 | ubig!(0), 31 | ubig!(1), 32 | ubig!(2), 33 | ubig!(3), 34 | ubig!(3).pow(5), 35 | ubig!(3).pow(10), 36 | ubig!(3).pow(20), 37 | ubig!(3).pow(40), 38 | ubig!(3).pow(80), 39 | ubig!(3).pow(120), 40 | ubig!(3).pow(160), 41 | ]; 42 | 43 | for d in &divisors { 44 | let const_d = &(ConstDivisor::new(d.clone())); 45 | for n in &numbers { 46 | assert_eq!(n / d, n / const_d); 47 | assert_eq!(n / d, n.clone() / const_d); 48 | assert_eq!(n % d, n % const_d); 49 | assert_eq!(n % d, n.clone() % const_d); 50 | assert_eq!(n.div_rem(d), n.div_rem(const_d)); 51 | assert_eq!(n.div_rem(d), n.clone().div_rem(const_d)); 52 | 53 | let mut x = n.clone(); 54 | x /= const_d; 55 | assert_eq!(x, n / d); 56 | 57 | let mut x = n.clone(); 58 | x %= const_d; 59 | assert_eq!(x, n % d); 60 | 61 | let mut x = n.clone(); 62 | assert_eq!(x.div_rem_assign(const_d), n % d); 63 | assert_eq!(x, n / d); 64 | 65 | let i = &(Negative * n.clone()); 66 | assert_eq!(i / d, i / const_d); 67 | assert_eq!(i / d, i.clone() / const_d); 68 | assert_eq!(i % d, i % const_d); 69 | assert_eq!(i % d, i.clone() % const_d); 70 | // assert_eq!(i.div_rem(d), n.div_rem(const_d)); 71 | // assert_eq!(i.div_rem(d), n.clone().div_rem(const_d)); 72 | 73 | let mut x = i.clone(); 74 | x /= const_d; 75 | assert_eq!(x, i / d); 76 | 77 | let mut x = i.clone(); 78 | x %= const_d; 79 | assert_eq!(x, i % d); 80 | 81 | let mut x = i.clone(); 82 | assert_eq!(x.div_rem_assign(const_d), i % d); 83 | assert_eq!(x, i / d); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /integer/tests/hash.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::hash_map::DefaultHasher, 3 | hash::{Hash, Hasher}, 4 | }; 5 | 6 | mod helper_macros; 7 | 8 | fn hash(x: &T) -> u64 9 | where 10 | T: Hash, 11 | { 12 | let mut hasher = DefaultHasher::new(); 13 | x.hash(&mut hasher); 14 | hasher.finish() 15 | } 16 | 17 | #[test] 18 | fn test_hash() { 19 | let h = hash(&(ubig!(1) << 1000)); 20 | for i in 0..=1000 { 21 | let h2 = hash(&(ubig!(1) << i << (1000 - i))); 22 | assert_eq!(h2, h); 23 | } 24 | 25 | let h3 = hash(&(ubig!(2) << 1000)); 26 | assert_ne!(h3, h); 27 | } 28 | -------------------------------------------------------------------------------- /integer/tests/helper_macros.rs: -------------------------------------------------------------------------------- 1 | //! Helper macros for constructing numbers 2 | //! 3 | //! These macros are the simpler versions of the ones in `dashu-macros`, meant 4 | //! to be used in testing only. 5 | //! 6 | //! These macros rely on string parsing, so do not use 7 | //! these macros when testing string parsing! 8 | 9 | #[macro_export] 10 | macro_rules! ubig { 11 | ($val:tt) => {{ 12 | const STR: &::core::primitive::str = ::core::stringify!($val); 13 | ::core::result::Result::expect( 14 | ::dashu_int::UBig::from_str_with_radix_prefix(STR), 15 | "invalid number", 16 | ) 17 | .0 18 | }}; 19 | ($val:tt base $radix:literal) => {{ 20 | const STR: &::core::primitive::str = ::core::stringify!($val); 21 | let s = 22 | ::core::option::Option::unwrap_or(::core::primitive::str::strip_prefix(STR, "_"), STR); 23 | ::core::result::Result::expect( 24 | ::dashu_int::UBig::from_str_radix(s, $radix), 25 | "invalid number", 26 | ) 27 | }}; 28 | } 29 | 30 | #[macro_export] 31 | macro_rules! ibig { 32 | (- $val:tt) => { 33 | - <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from($crate::ubig!($val)) 34 | }; 35 | (- $val:tt base $radix:literal) => { 36 | - <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from( 37 | $crate::ubig!($val base $radix) 38 | ) 39 | }; 40 | ($val:tt) => { 41 | <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from($crate::ubig!($val)) 42 | }; 43 | ($val:tt base $radix:literal) => { 44 | <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from( 45 | $crate::ubig!($val base $radix) 46 | ) 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /integer/tests/iter.rs: -------------------------------------------------------------------------------- 1 | use dashu_int::{IBig, UBig}; 2 | 3 | mod helper_macros; 4 | 5 | #[test] 6 | fn test_sum() { 7 | let nums = [ 8 | ubig!(0), 9 | ubig!(1), 10 | ubig!(10), 11 | ubig!(100), 12 | ubig!(10000), 13 | ubig!(100000000), 14 | ubig!(10000000000000000), 15 | ubig!(100000000000000000000000000000000), 16 | ]; 17 | 18 | assert_eq!(nums[..0].iter().sum::(), ubig!(0)); 19 | assert_eq!(nums[..1].iter().sum::(), ubig!(0)); 20 | assert_eq!(nums[..2].iter().sum::(), ubig!(1)); 21 | assert_eq!(nums[..4].iter().sum::(), ubig!(111)); 22 | assert_eq!(nums.iter().sum::(), ubig!(100000000000000010000000100010111)); 23 | assert_eq!(nums.iter().sum::(), ibig!(100000000000000010000000100010111)); 24 | assert_eq!(nums.into_iter().sum::(), ubig!(100000000000000010000000100010111)); 25 | 26 | let nums = [ 27 | ibig!(0), 28 | ibig!(-1), 29 | ibig!(10), 30 | ibig!(-100), 31 | ibig!(10000), 32 | ibig!(-100000000), 33 | ibig!(10000000000000000), 34 | ibig!(-100000000000000000000000000000000), 35 | ]; 36 | 37 | assert_eq!(nums[..0].iter().sum::(), ibig!(0)); 38 | assert_eq!(nums[..1].iter().sum::(), ibig!(0)); 39 | assert_eq!(nums[..2].iter().sum::(), ibig!(-1)); 40 | assert_eq!(nums[..4].iter().sum::(), ibig!(-91)); 41 | assert_eq!(nums.iter().sum::(), ibig!(-99999999999999990000000099990091)); 42 | assert_eq!(nums.into_iter().sum::(), ibig!(-99999999999999990000000099990091)); 43 | } 44 | 45 | #[test] 46 | fn test_prod() { 47 | assert_eq!((1..4u8).map(UBig::from).product::(), ubig!(6)); 48 | assert_eq!((1..10u8).map(UBig::from).product::(), ubig!(362880)); 49 | assert_eq!((1..10u8).map(UBig::from).product::(), ibig!(362880)); 50 | assert_eq!((0..4u8).map(UBig::from).product::(), ubig!(0)); 51 | assert_eq!((0..10u8).map(UBig::from).product::(), ubig!(0)); 52 | assert_eq!((0..10u8).map(UBig::from).product::(), ibig!(0)); 53 | 54 | assert_eq!((-4..-1).map(IBig::from).product::(), ibig!(-24)); 55 | assert_eq!((-10..-1).map(IBig::from).product::(), ibig!(-3628800)); 56 | assert_eq!((-4..4).map(IBig::from).product::(), ibig!(0)); 57 | assert_eq!((-10..10).map(IBig::from).product::(), ibig!(0)); 58 | } 59 | -------------------------------------------------------------------------------- /integer/tests/num_order.rs: -------------------------------------------------------------------------------- 1 | use dashu_int::{IBig, UBig}; 2 | use num_order::{NumHash, NumOrd}; 3 | 4 | mod helper_macros; 5 | 6 | #[test] 7 | fn test_ord_with_float() { 8 | assert!(ubig!(0).num_eq(&0f32)); 9 | assert!(ubig!(0).num_eq(&-0f32)); 10 | assert!(ibig!(0).num_eq(&0f32)); 11 | assert!(ibig!(0).num_eq(&-0f32)); 12 | assert!(ubig!(1).num_eq(&1f32)); 13 | assert!(ibig!(1).num_eq(&1f32)); 14 | assert!(ubig!(1).num_ne(&-1f32)); 15 | assert!(ibig!(-1).num_eq(&-1f32)); 16 | 17 | assert!(ubig!(1).num_gt(&-1f32)); 18 | assert!(ibig!(1).num_gt(&-1f32)); 19 | assert!(ibig!(-1).num_gt(&-1.0001f32)); 20 | assert!(ibig!(-100000).num_gt(&-100001f32)); 21 | assert!(1f32.num_gt(&ibig!(-1))); 22 | assert!((-1.0001).num_le(&ibig!(-1))); 23 | } 24 | 25 | #[test] 26 | fn test_ord_between_ubig_ibig() { 27 | assert!(ubig!(500).num_eq(&ibig!(500))); 28 | assert!(ubig!(500).num_ne(&ibig!(-500))); 29 | assert!(ibig!(500).num_eq(&ubig!(500))); 30 | assert!(ibig!(-500).num_ne(&ubig!(500))); 31 | 32 | assert!(ubig!(500).num_gt(&ibig!(499))); 33 | assert!(ibig!(500).num_gt(&ubig!(499))); 34 | assert!(ubig!(500).num_gt(&ibig!(-500))); 35 | assert!(ibig!(-500).num_le(&ubig!(500))); 36 | } 37 | 38 | #[test] 39 | fn test_hash() { 40 | fn hash(value: &T) -> u64 { 41 | use std::collections::hash_map::DefaultHasher; 42 | use std::hash::Hasher; 43 | let mut hasher = DefaultHasher::new(); 44 | value.num_hash(&mut hasher); 45 | hasher.finish() 46 | } 47 | 48 | // trivial cases 49 | assert_eq!(hash(&ubig!(0)), hash(&ibig!(0))); 50 | assert_eq!(hash(&ubig!(1)), hash(&ibig!(1))); 51 | assert_ne!(hash(&ubig!(1)), hash(&ibig!(-1))); 52 | 53 | // small numbers 54 | let small_cases = [ 55 | 12i64, 56 | -123, 57 | 1234, 58 | -12345, 59 | 123456, 60 | -12345678, 61 | 1234567890, 62 | -12345678901234, 63 | 1234567890123456789, 64 | ]; 65 | for v in small_cases { 66 | let i = IBig::from(v); 67 | assert_eq!(hash(&v), hash(&i)); 68 | 69 | if let Ok(u) = UBig::try_from(v) { 70 | assert_eq!(hash(&u), hash(&v)); 71 | assert_eq!(hash(&u), hash(&i)); 72 | } 73 | } 74 | 75 | // large numbers 76 | let big_cases = [1e10f64, -1e20, 1e30, -1e40, 1e60, -1e100]; 77 | for v in big_cases { 78 | let i = IBig::try_from(v).unwrap(); 79 | assert_eq!(hash(&v), hash(&i)); 80 | 81 | if let Ok(u) = UBig::try_from(v) { 82 | assert_eq!(hash(&u), hash(&v)); 83 | assert_eq!(hash(&u), hash(&i)); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /integer/tests/pow.rs: -------------------------------------------------------------------------------- 1 | mod helper_macros; 2 | 3 | #[test] 4 | fn test_pow_ubig() { 5 | let test_cases = [ 6 | // trivial cases 7 | (ubig!(0), 0, ubig!(1)), 8 | (ubig!(100), 0, ubig!(1)), 9 | (ubig!(0), 1, ubig!(0)), 10 | (ubig!(100), 1, ubig!(100)), 11 | (ubig!(0), 2, ubig!(0)), 12 | (ubig!(100), 2, ubig!(10000)), 13 | (ubig!(0), 100, ubig!(0)), 14 | (ubig!(1), 100, ubig!(1)), 15 | // pow by shifting 16 | (ubig!(2), 10, ubig!(1) << 10), 17 | (ubig!(64), 10, ubig!(1) << 60), 18 | (ubig!(2), 100, ubig!(1) << 100), 19 | (ubig!(64), 100, ubig!(1) << 600), 20 | // small bases 21 | (ubig!(7), 10, ubig!(282475249)), 22 | (ubig!(14), 10, ubig!(282475249) << 10), 23 | ( 24 | ubig!(7), 25 | 100, 26 | ubig!(3234476509624757991344647769100216810857203198904625400933895331391691459636928060001), 27 | ), 28 | ( 29 | ubig!(14), 30 | 100, 31 | ubig!(3234476509624757991344647769100216810857203198904625400933895331391691459636928060001) 32 | << 100, 33 | ), 34 | (ubig!(123), 13, ubig!(1474913153392179474539944683)), 35 | ( 36 | ubig!(123), 37 | 123, 38 | ubig!(114374367934617190099880295228066276746218078451850229775887975052369504785666896446606568365201542169649974727730628842345343196581134895919942820874449837212099476648958359023796078549041949007807220625356526926729664064846685758382803707100766740220839267), 39 | ), 40 | ( 41 | (ubig!(1) << 70) - 1u8, 42 | 10, 43 | ubig!(0xffffffffffffffffd80000000000000002cfffffffffffffffe20000000000000000d1fffffffffffffffc10000000000000000d1fffffffffffffffe200000000000000002cffffffffffffffffd800000000000000001), 44 | ), 45 | // large bases 46 | ( 47 | (ubig!(1) << 250) - 1u8, 48 | 3, 49 | (ubig!(1) << 750) - (ubig!(3) << 500) + (ubig!(3) << 250) - 1u8, 50 | ), 51 | ]; 52 | 53 | for (a, b, c) in &test_cases { 54 | assert_eq!(a.pow(*b), *c); 55 | } 56 | } 57 | 58 | #[test] 59 | fn test_pow_ibig() { 60 | let test_cases = [ 61 | (ibig!(0), 0, ibig!(1)), 62 | (ibig!(0), 12, ibig!(0)), 63 | (ibig!(0), 13, ibig!(0)), 64 | (ibig!(7), 2, ibig!(49)), 65 | (ibig!(7), 3, ibig!(343)), 66 | (ibig!(-7), 2, ibig!(49)), 67 | (ibig!(-7), 3, ibig!(-343)), 68 | ]; 69 | 70 | for (a, b, c) in &test_cases { 71 | assert_eq!(a.pow(*b), *c); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /integer/tests/remove.rs: -------------------------------------------------------------------------------- 1 | mod helper_macros; 2 | 3 | #[test] 4 | fn test_remove() { 5 | let mut zero = ubig!(0); 6 | assert_eq!(zero.remove(&ubig!(0)), None); 7 | assert_eq!(zero.remove(&ubig!(1)), None); 8 | 9 | let mut one = ubig!(1); 10 | assert_eq!(one.remove(&ubig!(0)), None); 11 | assert_eq!(one.remove(&ubig!(1)), None); 12 | 13 | for i in 0..32 { 14 | for b in [ubig!(2), ubig!(3), ubig!(10), ubig!(16)] { 15 | let mut a = b.clone().pow(i) * 5u8; 16 | assert_eq!(a.remove(&b), Some(i)); 17 | assert_eq!(a, ubig!(5)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /integer/tests/sign.rs: -------------------------------------------------------------------------------- 1 | use dashu_int::{ 2 | ops::{Abs, UnsignedAbs}, 3 | Sign, 4 | }; 5 | 6 | mod helper_macros; 7 | 8 | #[test] 9 | #[allow(clippy::double_neg)] 10 | fn test_neg() { 11 | assert_eq!(-ubig!(123), ibig!(-123)); 12 | assert_eq!(-ibig!(123), ibig!(-123)); 13 | assert_eq!(-ibig!(-123), ibig!(123)); 14 | assert_eq!(-ibig!(0), ibig!(0)); 15 | 16 | assert_eq!(-&ubig!(123), ibig!(-123)); 17 | assert_eq!(-&ibig!(123), ibig!(-123)); 18 | assert_eq!(-&ibig!(0), ibig!(0)); 19 | } 20 | 21 | #[test] 22 | fn test_abs() { 23 | assert_eq!(ibig!(123).abs(), ibig!(123)); 24 | assert_eq!(ibig!(-123).abs(), ibig!(123)); 25 | 26 | assert_eq!((&ibig!(-123)).abs(), ibig!(123)); 27 | } 28 | 29 | #[test] 30 | fn test_unsigned_abs() { 31 | assert_eq!(ibig!(123).unsigned_abs(), ubig!(123)); 32 | assert_eq!(ibig!(-123).unsigned_abs(), ubig!(123)); 33 | 34 | assert_eq!((&ibig!(-123)).unsigned_abs(), ubig!(123)); 35 | } 36 | 37 | #[test] 38 | fn test_signum() { 39 | assert_eq!(ibig!(-500).signum(), ibig!(-1)); 40 | assert_eq!(ibig!(0).signum(), ibig!(0)); 41 | assert_eq!(ibig!(500).signum(), ibig!(1)); 42 | } 43 | 44 | #[test] 45 | fn test_mul() { 46 | assert_eq!(Sign::Positive * ubig!(0), ibig!(0)); 47 | assert_eq!(Sign::Negative * ubig!(0), ibig!(0)); 48 | assert_eq!(Sign::Positive * ubig!(123), ibig!(123)); 49 | assert_eq!(Sign::Negative * ubig!(123), ibig!(-123)); 50 | 51 | assert_eq!(Sign::Positive * ibig!(0), ibig!(0)); 52 | assert_eq!(Sign::Negative * ibig!(0), ibig!(0)); 53 | assert_eq!(Sign::Positive * ibig!(123), ibig!(123)); 54 | assert_eq!(Sign::Negative * ibig!(123), ibig!(-123)); 55 | assert_eq!(Sign::Positive * ibig!(-123), ibig!(-123)); 56 | assert_eq!(Sign::Negative * ibig!(-123), ibig!(123)); 57 | } 58 | -------------------------------------------------------------------------------- /macros/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.1 4 | 5 | - Add `static_ubig!` and `static_ibig!` macros to support static integer creation ([#38](https://github.com/cmpute/dashu/issues/38)). 6 | - Add `static_fbig!` macro to support static float numbers creation. 7 | - Add `static_rbig!` macro to support static rational numbers creation. 8 | 9 | ## 0.4.0 10 | 11 | - Remove the `embedded` feature ([#18](https://github.com/cmpute/dashu/pull/18)). 12 | 13 | ## 0.3.1 14 | 15 | - Fix the problem of `ibig` and `rbig` using incorrect crate names. 16 | 17 | ## 0.3.0 18 | 19 | - Now only numbers that fit in `u32`s can be created in a const context. (Previously any numbers fit in `DoubleWord`s is permitted.) 20 | - Add feature `embedded` to improve ergonomics when embedded in the `dashu` meta crate. 21 | 22 | ## 0.2.0 (Initial release) 23 | 24 | - Support creating integers and floats from literals. 25 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-macros" 3 | version = "0.4.1" 4 | authors = ["Jacob Zhong "] 5 | edition = "2021" 6 | description = "Procedure macros for creating big numbers" 7 | keywords = ["mathematics", "numerics", "arbitrary-precision"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-macros" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [lib] 20 | proc-macro = true 21 | 22 | [dependencies] 23 | dashu-base = { version = "0.4.0", default-features = false, path = "../base" } 24 | dashu-int = { version = "0.4.1", default-features = false, path = "../integer" } 25 | dashu-float = { version = "0.4.2", default-features = false, path = "../float" } 26 | dashu-ratio = { version = "0.4.1", default-features = false, path = "../rational" } 27 | 28 | quote = "1" 29 | proc-macro2 = "1" 30 | paste = "1.0" 31 | rustversion = "1.0" 32 | -------------------------------------------------------------------------------- /macros/README.md: -------------------------------------------------------------------------------- 1 | # dashu-macros 2 | 3 | Utility macros to create number literals. See [Docs.rs](https://docs.rs/dashu-macros/latest/dashu_macros/) for the full documentation. 4 | 5 | # Features 6 | 7 | - Support creating **big integers** with literals using `ubig!` and `ibig!`. 8 | - Support creating **big floats** with literals using `fbig!` and `dbig!`. 9 | - Support creating **big rationals** with literals using `rbig!`. 10 | - All macros can be used to create **const** numbers if they are small enough. 11 | 12 | ## License 13 | 14 | See the [top-level readme](../README.md). 15 | -------------------------------------------------------------------------------- /macros/docs/dbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision float number ([dashu_float::DBig]) with base 10 rounding to the nearest. 2 | 3 | ```rust 4 | # use dashu_macros::dbig; 5 | let a = dbig!(12.001); 6 | let b = dbig!(7.42e-3); // exponent in base 2 can be specified using `Bxx` 7 | 8 | // underscores can be used to separate digits 9 | let c = dbig!(3.141_592_653_589_793_238); 10 | ``` 11 | 12 | The generated float has precision determined by length of digits in the input literal. 13 | ```rust 14 | # use dashu_macros::dbig; 15 | let a = dbig!(12.001); // 5 decimal digits 16 | assert_eq!(a.precision(), 5); 17 | 18 | let b = dbig!(003.1200e-2); // 7 decimal digits 19 | assert_eq!(b.precision(), 7); 20 | assert_eq!(b.digits(), 3); // 312 only has 3 effective digits 21 | ``` 22 | 23 | For numbers whose significands are small enough (fit in a [u32]), 24 | the literal can be assigned to a constant. 25 | ```rust 26 | # use dashu_macros::dbig; 27 | use dashu_float::DBig; 28 | 29 | const A: DBig = dbig!(-1.201); 30 | const B: DBig = dbig!(1234_5678e-100); 31 | const C: DBig = dbig!(-1e100000); 32 | ``` -------------------------------------------------------------------------------- /macros/docs/fbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision float number ([dashu_float::FBig]) with base 2 rounding towards zero. 2 | 3 | This macro only accepts binary or hexadecimal literals. It doesn't allow decimal literals because 4 | the conversion is not always lossless. Therefore if you want to create an [FBig][dashu_float::FBig] 5 | instance with decimal literals, use the [dbig!] macro and then change the radix with 6 | [with_base][dashu_float::FBig::with_base]. 7 | 8 | ```rust 9 | # use dashu_macros::fbig; 10 | let a = fbig!(11.001); // digits in base 2, equal to 3.125 in decimal 11 | let b = fbig!(1.101B-3); // exponent in base 2 can be specified using `Bxx` 12 | let c = fbig!(-0x1a7f); // digits in base 16 13 | let d = fbig!(0x03.efp-2); // equal to 0.9833984375 in decimal 14 | 15 | // underscores can be used to separate digits 16 | let e = fbig!(0xa54653ca_67376856_5b41f775.f00c1782_d6947d55p-33); 17 | 18 | // Due to the limitation of Rust literal syntax, the hexadecimal literal 19 | // with floating point requires an underscore prefix if the first digit is 20 | // not a decimal digit. 21 | let f = fbig!(-_0xae.1f); 22 | let g = fbig!(-0xae1fp-8); 23 | assert_eq!(f, g); 24 | let h = fbig!(-0x12._34); 25 | let i = fbig!(-_0x12.34); 26 | assert_eq!(h, i); 27 | ``` 28 | 29 | The generated float has precision determined by length of digits in the input literal. 30 | 31 | ```rust 32 | # use dashu_macros::fbig; 33 | let a = fbig!(11.001); // 5 binary digits 34 | assert_eq!(a.precision(), 5); 35 | 36 | let b = fbig!(0x0003.ef00p-2); // 8 hexadecimal digits = 32 binary digits 37 | assert_eq!(b.precision(), 32); 38 | assert_eq!(b.digits(), 10); // 0x3ef only has 10 effective bits 39 | ``` 40 | 41 | For numbers that are small enough (significand fits in a [u32]), 42 | the literal can be assigned to a constant. 43 | 44 | ```rust 45 | # use dashu_macros::fbig; 46 | use dashu_float::FBig; 47 | 48 | const A: FBig = fbig!(-1001.10); 49 | const B: FBig = fbig!(0x123); 50 | const C: FBig = fbig!(-0xffff_ffffp-127); 51 | ``` -------------------------------------------------------------------------------- /macros/docs/ibig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision signed integer ([dashu_int::IBig]) 2 | 3 | Usually just pass use a numeric literal. This works for bases 2, 8, 10 or 16 using standard 4 | prefixes: 5 | ```rust 6 | # use dashu_macros::ibig; 7 | let a = ibig!(-100); 8 | let b = ibig!(0b101); 9 | let c = ibig!(-0o202); 10 | let d = ibig!(0x2ff); 11 | let e = ibig!(314159265358979323846264338327950288419716939937); 12 | 13 | // underscores can be used to separate digits 14 | let f = ibig!(-0x5a4653ca_67376856_5b41f775_d6947d55_cf3813d1); 15 | ``` 16 | 17 | For an arbitrary base, add `base N`: 18 | ```rust 19 | # use dashu_macros::ibig; 20 | let g = ibig!(-a3gp1 base 32); 21 | 22 | // it might be necessary to put a underscore to prevent 23 | // Rust from recognizing some digits as prefix or exponent 24 | let h = ibig!(-_100ef base 32); 25 | let i = ibig!(_0b102 base 32); 26 | let j = ibig!(b102 base 32); 27 | assert_eq!(i, j); 28 | ``` 29 | 30 | For numbers that are small enough (fits in a [u32]), the literal can 31 | be assigned to a constant. 32 | ```rust 33 | # use dashu_macros::ibig; 34 | use dashu_int::IBig; 35 | 36 | const A: IBig = ibig!(-123); 37 | const B: IBig = ibig!(0x123); 38 | const C: IBig = ibig!(-0xffff_ffff); 39 | ``` 40 | 41 | Please use the [static_ibig!][crate::static_ibig!] macro if you want to create a big static number. -------------------------------------------------------------------------------- /macros/docs/rbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision rational number ([dashu_ratio::RBig] or [dashu_ratio::Relaxed]). 2 | 3 | ```rust 4 | # use dashu_macros::rbig; 5 | let a = rbig!(22/7); 6 | let b = rbig!(~-1/13); // use `~` to create a relaxed rational number 7 | let c = rbig!(0x3c/0x5e); 8 | let d = rbig!(~0xff/dd); // the prefix of denomiator can be omitted 9 | let e = rbig!(-2); // denominators can be omitted for integers 10 | 11 | // underscores can be used to separate digits 12 | let f = rbig!(107_241/35_291); 13 | ``` 14 | 15 | For an arbitrary base, add `base N`: 16 | ```rust 17 | # use dashu_macros::rbig; 18 | let g = rbig!(a3/gp1 base 32); 19 | 20 | // it might be necessary to put a underscore to prevent 21 | // Rust from recognizing some digits as prefix or exponent 22 | let h = rbig!(~_100ef/_5ge base 32); 23 | let i = rbig!(_0b102/_0h2 base 32); 24 | let j = rbig!(b102/h2 base 32); 25 | assert_eq!(i, j); 26 | ``` 27 | 28 | For numbers whose the numerator and denominator are small enough (fit in [u32]), 29 | the literal can be assigned to a constant. 30 | 31 | ```rust 32 | # use dashu_macros::rbig; 33 | use dashu_ratio::{RBig, Relaxed}; 34 | 35 | const A: RBig = rbig!(-1/2); 36 | const B: Relaxed = rbig!(~3355/15); 37 | ``` -------------------------------------------------------------------------------- /macros/docs/static_dbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision float number ([dashu_float::DBig]), with base 10 rounding to the nearest, as a static reference. 2 | 3 | The syntax of this macro is the same as the [dbig!][crate::dbig!], but the macro generates a **reference to** a immutable static `DBig` instance. Due to the limitation of const generics, the generated `DBig` instance will take as much as 4x (static) memory as a normal one, to support cross-platform definitions. Besides, the generated float number will have a unlimited precision. Please remember to set a precision before any operations between two static numbers. 4 | 5 | This macro is available only after Rust 1.64. -------------------------------------------------------------------------------- /macros/docs/static_fbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision float number ([dashu_float::FBig]), with base 2 rounding towards zero, as a static reference. 2 | 3 | The syntax of this macro is the same as the [fbig!][crate::fbig!], but the macro generates a **reference to** a immutable static `FBig` instance. Due to the limitation of const generics, the generated `FBig` instance will take as much as 4x (static) memory as a normal one, to support cross-platform definitions. Besides, the generated float number will have a unlimited precision. Please remember to set a precision before any operations between two static numbers. 4 | 5 | This macro is available only after Rust 1.64. -------------------------------------------------------------------------------- /macros/docs/static_ibig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision signed integer ([dashu_int::IBig]) as a static reference. 2 | 3 | The syntax of this macro is the same as the [ibig!][crate::ibig!], but the macro generates a **reference to** a immutable static `IBig` instance. Due to the limitation of const generics, the generated `IBig` instance can take as much as 4x (static) memory as a normal one, to support cross-platform definitions. 4 | 5 | This macro is available only after Rust 1.64. -------------------------------------------------------------------------------- /macros/docs/static_rbig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision rational number ([dashu_ratio::RBig] or [dashu_ratio::Relaxed]) as a static reference. 2 | 3 | The syntax of this macro is the same as the [rbig!][crate::rbig!], but the macro generates a **reference to** a immutable static `RBig` or `Relaxed` instance. Due to the limitation of const generics, the generated instance can take as much as 4x (static) memory as a normal one, to support cross-platform definitions. 4 | 5 | This macro is available only after Rust 1.64. -------------------------------------------------------------------------------- /macros/docs/static_ubig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision unsigned integer ([dashu_int::UBig]) as a static reference. 2 | 3 | The syntax of this macro is the same as the [ubig!][crate::ubig!], but the macro generates a **reference to** a immutable static `UBig` instance. Due to the limitation of const generics, the generated `UBig` instance will take as much as 4x (static) memory as a normal one, to support cross-platform definitions. 4 | 5 | This macro is available only after Rust 1.64. -------------------------------------------------------------------------------- /macros/docs/ubig.md: -------------------------------------------------------------------------------- 1 | Create an arbitrary precision unsigned integer ([dashu_int::UBig]) 2 | 3 | Usually just pass use a numeric literal. This works for bases 2, 8, 10 or 16 using standard 4 | prefixes: 5 | ```rust 6 | # use dashu_macros::ubig; 7 | let a = ubig!(100); 8 | let b = ubig!(0b101); 9 | let c = ubig!(0o202); 10 | let d = ubig!(0x2ff); 11 | let e = ubig!(314159265358979323846264338327950288419716939937); 12 | 13 | // underscores can be used to separate digits 14 | let f = ubig!(0x5a4653ca_67376856_5b41f775_d6947d55_cf3813d1); 15 | ``` 16 | 17 | For an arbitrary base, add `base N`: 18 | ```rust 19 | # use dashu_macros::ubig; 20 | let g = ubig!(a3gp1 base 32); 21 | 22 | // it might be necessary to put a underscore to prevent 23 | // Rust from recognizing some digits as prefix or exponent 24 | let h = ubig!(_100ef base 32); 25 | let i = ubig!(_0b102 base 32); 26 | let j = ubig!(b102 base 32); 27 | assert_eq!(i, j); 28 | ``` 29 | 30 | For numbers that are small enough (fits in a [u32]), the literal can 31 | be assigned to a constant. 32 | ```rust 33 | # use dashu_macros::ubig; 34 | use dashu_int::UBig; 35 | 36 | const A: UBig = ubig!(123); 37 | const B: UBig = ubig!(0x123); 38 | const C: UBig = ubig!(0xffff_ffff); 39 | ``` 40 | 41 | Please use the [static_ubig!][crate::static_ubig!] macro if you want to create a big static number. -------------------------------------------------------------------------------- /macros/src/parse/mod.rs: -------------------------------------------------------------------------------- 1 | //! Parse number from raw literals 2 | 3 | mod common; 4 | pub mod float; 5 | pub mod int; 6 | pub mod ratio; 7 | -------------------------------------------------------------------------------- /python/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | - TODO: support pickle through __reduce__ 4 | - TODO: support as much dunder methods as possible: https://docs.cython.org/en/latest/src/userguide/special_methods.html#special-methods -------------------------------------------------------------------------------- /python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-python" 3 | version = "0.0.1" 4 | authors = ["Jacob Zhong "] 5 | edition = "2021" 6 | description = "Python binding for the dashu numeric types" 7 | keywords = ["mathematics", "numerics"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-python" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [dependencies] 20 | dashu-base = { version = "0.4.0", path = "../base", features = ["std"]} 21 | dashu-int = { version = "0.4.1", path = "../integer", features = ["std", "num-order"] } 22 | dashu-float = { version = "0.4.2", path = "../float", features = ["std", "num-order"] } 23 | dashu-ratio = { version = "0.4.1", path = "../rational", features = ["std", "num-order", "dashu-float"] } 24 | num-order = "1.2.0" 25 | 26 | _num-modular = { optional = true, version = "0.6.1", package = "num-modular", default-features = false } 27 | 28 | [dependencies.pyo3] 29 | version = "0.20" 30 | features = ["extension-module"] 31 | 32 | [lib] 33 | name = "dashu" 34 | crate-type = ["cdylib"] 35 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # dashu-python 2 | 3 | Python binding to the numeric types in the dashu crates, based on PyO3. 4 | -------------------------------------------------------------------------------- /python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "dashu-rs" 7 | requires-python = ">=3.7" 8 | classifiers = [ 9 | "Programming Language :: Rust", 10 | "Programming Language :: Python :: Implementation :: CPython", 11 | "Programming Language :: Python :: Implementation :: PyPy", 12 | ] -------------------------------------------------------------------------------- /python/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod convert; 2 | mod float; 3 | mod int; 4 | mod ratio; 5 | mod types; 6 | mod utils; 7 | mod words; 8 | 9 | use pyo3::prelude::*; 10 | 11 | /// A Python module implemented in Rust. The name of this function must match 12 | /// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to 13 | /// import the module. 14 | #[pymodule] 15 | fn dashu(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 16 | m.add_class::()?; 17 | m.add_class::()?; 18 | m.add_class::()?; 19 | m.add_class::()?; 20 | m.add_class::()?; 21 | m.add_class::()?; 22 | m.add_class::()?; 23 | 24 | m.add_function(wrap_pyfunction!(utils::auto, m)?)?; 25 | m.add_function(wrap_pyfunction!(utils::autos, m)?)?; 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /python/src/ratio.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::DefaultHasher; 2 | use std::hash::Hasher; 3 | 4 | use dashu_ratio::RBig; 5 | use num_order::NumHash; 6 | use pyo3::{exceptions::PyTypeError, intern, prelude::*}; 7 | 8 | use crate::{ 9 | convert::{convert_from_rbig, parse_error_to_py, parse_to_rbig}, 10 | types::RPy, 11 | }; 12 | 13 | const ERRMSG_RBIG_WRONG_SRC_TYPE: &str = 14 | "only Fraction instances or strings can be used to construct an RBig instance"; 15 | 16 | #[pymethods] 17 | impl RPy { 18 | #[new] 19 | fn __new__(ob: &PyAny) -> PyResult { 20 | let py = ob.py(); 21 | let fractions = py.import(intern!(py, "fractions"))?; 22 | if ob.is_instance(fractions.getattr(intern!(py, "Fraction"))?)? { 23 | // create from fractions.Fraction 24 | Ok(RPy(parse_to_rbig(ob)?)) 25 | } else if let Ok(s) = ob.extract() { 26 | // create from string 27 | let d = RBig::from_str_with_radix_prefix(s).map(|v| v.0); 28 | Ok(RPy(d.map_err(parse_error_to_py)?)) 29 | } else if let Ok(obj) = as FromPyObject>::extract(ob) { 30 | Ok(RPy(obj.0.clone())) 31 | } else { 32 | Err(PyTypeError::new_err(ERRMSG_RBIG_WRONG_SRC_TYPE)) 33 | } 34 | } 35 | fn unwrap(&self, py: Python) -> PyResult { 36 | convert_from_rbig(&self.0, py) 37 | } 38 | 39 | fn __repr__(&self) -> String { 40 | format!("", self.0) 41 | } 42 | fn __str__(&self) -> String { 43 | format!("{}", self.0) 44 | } 45 | fn __format__(&self) { 46 | todo!() 47 | } 48 | fn __hash__(&self) -> u64 { 49 | let mut hasher = DefaultHasher::new(); 50 | self.0.num_hash(&mut hasher); 51 | hasher.finish() 52 | } 53 | 54 | fn __float__(&self) -> f64 { 55 | self.0.to_f64_fast() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /python/src/types.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use std::os::raw::{c_double, c_longlong, c_ulonglong}; 3 | 4 | use dashu_base::Sign; 5 | use dashu_float::DBig; 6 | use dashu_int::{IBig, UBig}; 7 | use dashu_ratio::RBig; 8 | type FBig = dashu_float::FBig; 9 | 10 | #[pyclass] 11 | pub enum PySign { 12 | Positive, 13 | Negative, 14 | } 15 | 16 | impl From for PySign { 17 | #[inline] 18 | fn from(value: Sign) -> Self { 19 | match value { 20 | Sign::Positive => Self::Positive, 21 | Sign::Negative => Self::Negative, 22 | } 23 | } 24 | } 25 | 26 | /// This struct is used for representing [UBig] in Python 27 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 28 | #[pyclass(name = "UBig")] 29 | pub struct UPy(pub UBig); 30 | 31 | impl From for UPy { 32 | fn from(n: UBig) -> Self { 33 | UPy(n) 34 | } 35 | } 36 | 37 | /// This struct is used for representing [IBig] in Python 38 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 39 | #[pyclass(name = "IBig")] 40 | pub struct IPy(pub IBig); 41 | 42 | impl From for IPy { 43 | fn from(n: IBig) -> Self { 44 | IPy(n) 45 | } 46 | } 47 | 48 | /// This struct is used for representing [FBig] in Python 49 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 50 | #[pyclass(name = "FBig")] 51 | pub struct FPy(pub FBig); 52 | 53 | impl From for FPy { 54 | fn from(n: FBig) -> Self { 55 | FPy(n) 56 | } 57 | } 58 | 59 | /// This struct is used for representing [DBig] in Python 60 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 61 | #[pyclass(name = "DBig")] 62 | pub struct DPy(pub DBig); 63 | 64 | impl From for DPy { 65 | fn from(n: DBig) -> Self { 66 | DPy(n) 67 | } 68 | } 69 | 70 | /// This struct is used for representing [RBig] in Python 71 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 72 | #[pyclass(name = "RBig")] 73 | pub struct RPy(pub RBig); 74 | 75 | impl From for RPy { 76 | fn from(n: RBig) -> Self { 77 | RPy(n) 78 | } 79 | } 80 | 81 | #[pyclass(name = "Words")] 82 | pub struct PyWords(pub std::vec::Vec); 83 | 84 | /// An input type that accepts all possible numeric types from Python 85 | /// 86 | /// Notes: 87 | /// - Variants starting with 'B': big numbers 88 | /// - Variants starting with 'OB': owned big numbers 89 | pub enum UniInput<'a> { 90 | Uint(c_ulonglong), // from int 91 | Int(c_longlong), // from int 92 | BUint(PyRef<'a, UPy>), 93 | BInt(PyRef<'a, IPy>), 94 | OBInt(IBig), // from int 95 | Float(c_double), // from float 96 | BFloat(PyRef<'a, FPy>), 97 | BDecimal(PyRef<'a, DPy>), 98 | OBDecimal(DBig), // from decimal.Decimal 99 | BRational(PyRef<'a, RPy>), 100 | OBRational(RBig), // from fractions.Fraction 101 | } 102 | -------------------------------------------------------------------------------- /python/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use crate::{ 4 | convert::{conversion_error_to_py, parse_error_to_py}, 5 | types::*, 6 | }; 7 | use dashu_base::{Signed, UnsignedAbs}; 8 | use dashu_float::{DBig, FBig}; 9 | use dashu_int::{IBig, UBig}; 10 | use dashu_ratio::RBig; 11 | use pyo3::prelude::*; 12 | 13 | /// Convert input automatically to corresponding dashu type: 14 | /// (int -> UBig/IBig, float -> FBig, decimal -> DBig, fraction -> RBig) 15 | #[pyfunction] 16 | pub fn auto(ob: UniInput, py: Python<'_>) -> PyResult { 17 | use UniInput::*; 18 | 19 | // shrink IBig to UBig if necessary 20 | let fit_ibig = |i: IBig| { 21 | if i.is_negative() { 22 | IPy(i).into_py(py) 23 | } else { 24 | UPy(i.unsigned_abs()).into_py(py) 25 | } 26 | }; 27 | 28 | // TODO: shrink each type to the minimal representation (FBig/RBig -> IBig -> UBig) 29 | let obj = match ob { 30 | Uint(v) => UPy(v.into()).into_py(py), 31 | Int(v) => fit_ibig(v.into()), 32 | BUint(v) => v.clone().into_py(py), 33 | BInt(v) => fit_ibig(v.0.clone()), 34 | OBInt(v) => fit_ibig(v), 35 | Float(v) => match v.try_into() { 36 | Ok(big) => FPy(big).into_py(py), 37 | Err(e) => { 38 | return Err(conversion_error_to_py(e)); 39 | } 40 | }, 41 | BFloat(v) => v.clone().into_py(py), 42 | BDecimal(v) => v.clone().into_py(py), 43 | OBDecimal(v) => DPy(v).into_py(py), 44 | BRational(v) => v.clone().into_py(py), 45 | OBRational(v) => RPy(v).into_py(py), 46 | }; 47 | Ok(obj) 48 | } 49 | 50 | /// Convert input string to corresponding dashu type. 51 | /// The type is heuristically determined 52 | #[pyfunction] 53 | pub fn autos(s: &str, py: Python<'_>) -> PyResult { 54 | let obj = if s.contains('/') { 55 | RPy(RBig::from_str_with_radix_prefix(s) 56 | .map_err(parse_error_to_py)? 57 | .0) 58 | .into_py(py) 59 | } else if s.contains(['p', 'P']) { 60 | FPy(FBig::from_str(s).map_err(parse_error_to_py)?).into_py(py) 61 | } else if s.contains('.') || (!s.contains("0x") && s.contains(['e', 'E'])) { 62 | DPy(DBig::from_str(s).map_err(parse_error_to_py)?).into_py(py) 63 | } else if s.contains('-') { 64 | IPy(IBig::from_str_with_radix_prefix(s) 65 | .map_err(parse_error_to_py)? 66 | .0) 67 | .into_py(py) 68 | } else { 69 | UPy(UBig::from_str_with_radix_prefix(s) 70 | .map_err(parse_error_to_py)? 71 | .0) 72 | .into_py(py) 73 | }; 74 | Ok(obj) 75 | } 76 | 77 | // TODO: split_dword, double_word, etc. 78 | -------------------------------------------------------------------------------- /python/tests/test_convert.py: -------------------------------------------------------------------------------- 1 | from dashu import * 2 | 3 | def test_int_conversions(): 4 | # small cases 5 | test_cases = [ 6 | (0, UBig), 7 | (1, UBig), 8 | (-1, IBig), 9 | (0xffffffffffffffffffff, UBig), 10 | (-0xffffffffffffffffffff, IBig), 11 | ] 12 | 13 | # large cases 14 | for i in range(6): 15 | v = (-3)**(9**i) 16 | test_cases.append((v, UBig if v >= 0 else IBig)) 17 | 18 | # testing 19 | for v, t in test_cases: 20 | if v < 0: # test constructors 21 | _ = IBig(v) 22 | else: 23 | _ = UBig(v), IBig(v) 24 | assert type(auto(v)) == t # test result type of auto 25 | 26 | if __name__ == "__main__": 27 | test_int_conversions() -------------------------------------------------------------------------------- /python/tests/test_int.py: -------------------------------------------------------------------------------- 1 | from dashu import * 2 | 3 | def test_bit_ops(): 4 | ##### getters ##### 5 | n = UBig(12) # 0b1100 6 | assert not n[0] and not n[1] and n[2] and n[3] 7 | assert int(n[:2]) == 0 and int(n[2:]) == 3 8 | assert int(n[:3]) == 4 and int(n[3:]) == 1 9 | 10 | ##### setters ##### 11 | n = UBig(12) 12 | n[0] = True 13 | assert int(n) == 0b1101 14 | n[10] = True 15 | assert int(n) == 0b10000001101 16 | 17 | n = UBig(12) 18 | n[:2] = True 19 | assert int(n) == 0b1111 20 | n[2:] = False 21 | assert int(n) == 0b0011 22 | 23 | n = UBig(12) 24 | n[1:3] = True 25 | assert int(n) == 0b1110 26 | 27 | ##### delete ##### 28 | n = UBig(12) 29 | del n[0] 30 | assert int(n) == 0b110 31 | 32 | n = UBig(12) 33 | del n[2] 34 | assert int(n) == 0b100 35 | 36 | n = UBig(12) 37 | del n[:2] 38 | assert int(n) == 0b11 39 | 40 | n = UBig(12) 41 | del n[2:] 42 | assert int(n) == 0b00 43 | 44 | n = UBig(12) 45 | del n[1:3] 46 | assert int(n) == 0b10 47 | 48 | ## misc 49 | n = UBig("0x3bc0495b81ab5b422d2b18c7e61a2309cf548cda8fbbd18d41aded48711b72d6") 50 | assert n[:32] == n & (2**32 - 1) 51 | assert n[:64] == n & (2**64 - 1) 52 | assert n[:128] == n & (2**128 - 1) 53 | 54 | def test_conversion_to_chunks(): 55 | n = UBig("0x123456789abcdef") 56 | assert UBig.from_chunks(n.to_chunks(10), 10) == n 57 | 58 | if __name__ == "__main__": 59 | # test_bit_ops() 60 | test_conversion_to_chunks() 61 | -------------------------------------------------------------------------------- /python/tests/test_words.py: -------------------------------------------------------------------------------- 1 | from dashu import * 2 | 3 | slice_list = [ 4 | slice(None, 0), # [:0] 5 | slice(None, 1), # [:1] 6 | slice(None, -1), # [:-1] 7 | slice(0, None), # [0:] 8 | slice(1, None), # [1:] 9 | slice(-1, None), # [-1:] 10 | 11 | slice(None, None, 2), # [::2] 12 | slice(None, 0, 2), # [:0:2] 13 | slice(None, 1, 2), # [:1:2] 14 | slice(None, -1, 2), # [:-1:2] 15 | slice(0, None, 2), # [0::2] 16 | slice(1, None, 2), # [1::2] 17 | slice(-1, None, 2), # [-1::2] 18 | 19 | slice(None, None, -2), # [::-2] 20 | slice(None, 0, -2), # [:0:-2] 21 | slice(None, 1, -2), # [:1:-2] 22 | slice(None, -1, -2), # [:-1:-2] 23 | slice(0, None, -2), # [0::-2] 24 | slice(1, None, -2), # [1::-2] 25 | slice(-1, None, -2), # [-1::-2] 26 | ] 27 | 28 | def test_words_get(): 29 | n = UBig(3 ** 300) 30 | words = n.to_words() 31 | words_list = list(words) 32 | 33 | # single index 34 | assert words[0] == words_list[0] 35 | assert words[1] == words_list[1] 36 | assert words[-1] == words_list[-1] 37 | 38 | # slice index 39 | for sl in slice_list: 40 | assert list(words[sl]) == words_list[sl], "{} => {}, {}".format(sl, words, words_list) 41 | 42 | def test_words_set(): 43 | n = UBig(3 ** 300) 44 | 45 | # single index 46 | words = n.to_words() 47 | words_list = list(words) 48 | words[0], words_list[0] = 0, 0 49 | words[1], words_list[1] = 1, 1 50 | words[-1], words_list[-1] = 2, 2 51 | assert list(words) == words_list 52 | 53 | # slice index 54 | for sl in slice_list: 55 | words = n.to_words() 56 | words_list = list(words) 57 | values = list(range(len(words))) 58 | 59 | words[sl] = values[sl] 60 | words_list[sl] = values[sl] 61 | assert list(words) == words_list, "{} => {}, {}".format(sl, words, words_list) 62 | 63 | def test_words_del(): 64 | n = UBig(3 ** 300) 65 | 66 | # single index 67 | words = n.to_words() 68 | words_list = list(words) 69 | del words[0]; del words_list[0] 70 | del words[1]; del words_list[1] 71 | del words[-1]; del words_list[-1] 72 | assert list(words) == words_list 73 | 74 | # slice index 75 | for sl in slice_list: 76 | words = n.to_words() 77 | words_list = list(words) 78 | 79 | del words[sl]; del words_list[sl] 80 | assert list(words) == words_list, "{} => {}, {}".format(sl, words, words_list) 81 | 82 | if __name__ == "__main__": 83 | test_words_get() 84 | test_words_set() 85 | test_words_del() 86 | -------------------------------------------------------------------------------- /rational/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - Implement `Div` and `Div` for `UBig`/`IBig` 6 | - Bugfix of division between big rational and big integers. 7 | 8 | ## 0.4.1 9 | 10 | - Implement `AbsOrd` between `RBig`/`Relaxed` and `UBig`/`IBig`/`FBig`. 11 | - Implement `NumOrd` between `RBig`/`Relaxed` and primitive integers / floats. 12 | - Add `RBig::as_relaxed` to use an `RBig` as an `Relaxed` instance. 13 | - Method `RBig::to_float` now will enforce the precision of the output even for zero ([#45](https://github.com/cmpute/dashu/issues/45)). 14 | 15 | ## 0.4.0 16 | 17 | ### Add 18 | 19 | - Add `is_int` to `RBig` 20 | - Implement `num-order::NumOrd` between `RBig`/`Relaxed` and `UBig`/`IBig`/`FBig`. 21 | - Implement `num-order::NumHash` for `RBig` and `Relaxed` 22 | - Implement `simpliest_from_float` for `RBig`. 23 | 24 | ### Change 25 | 26 | - Now feature `num-traits` and `rand` are not enabled by default, feature `num-order` is enabled instead. 27 | - Fix the bug in `is_one` of `RBig` and `Relaxed`. 28 | - `RBig::square` and `Relaxed::square` are renamed to `sqr` 29 | 30 | ### Remove 31 | 32 | - `PartialOrd` is no longer implemented between `RBig` and `Relaxed`. Please use `num-order::NumOrd` instead. 33 | 34 | ## 0.3.2 35 | 36 | Fix the Bug in multiplication between `RBig` and `IBig`. 37 | 38 | ## 0.3.1 39 | 40 | - Implement `Sum` and `Product` traits for `RBig` and `Relaxed`. 41 | - Implement `Rem` trait for `RBig` and `Relaxed`. 42 | - Implement `dashu_base::{Abs, Inverse, DivEuclid, RemEuclid, DivRemEuclid, EstimatedLog2}` traits for `RBig` and `Relaxed`. 43 | - Implement `rand::distributions::uniform::SampleUniform` for `RBig`. 44 | - Implement `serde::{Serialize, Deserialize}` for `RBig` and `Relaxed` 45 | - Implement `num_traits::{Zero, One, Num, Signed, FromPrimitive, ToPrimitive, Pow, Euclid}` for `RBig` and `Relaxed` 46 | - Add `cubic()`, `pow()` for `RBig` and `Relaxed`. 47 | - Add `round()` for `RBig` and `Relaxed`. 48 | - Add support of random rational numbers generation through `Uniform01` and `UniformRBig`. 49 | - Add `rand_v08` and `num-traits_v02` feature flags to prevent breaking changes due to dependency updates in future 50 | - Fix the bug in number comparison. 51 | - Re-export operation traits through the `ops` module. 52 | 53 | ## 0.3.0 (Initial release) 54 | 55 | - Support basic arithmetic operations and numeric conversion. 56 | - Support Diophatine approximations. 57 | -------------------------------------------------------------------------------- /rational/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dashu-ratio" 3 | version = "0.4.1" 4 | authors = ["Jacob Zhong "] 5 | edition = "2021" 6 | description = "A big rational library with good performance" 7 | keywords = ["mathematics", "numerics", "rational", "bigrat", "arbitrary-precision"] 8 | categories = ["mathematics", "no-std"] 9 | license = "MIT OR Apache-2.0" 10 | repository = "https://github.com/cmpute/dashu" 11 | homepage = "https://github.com/cmpute/dashu" 12 | documentation = "https://docs.rs/dashu-ratio" 13 | readme = "README.md" 14 | rust-version = "1.61" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | default = ["std", "num-order", "dashu-float"] 21 | std = ["dashu-base/std", "dashu-int/std"] 22 | 23 | # stable dependencies 24 | serde = ["dep:serde", "dashu-int/serde"] 25 | zeroize = ["dep:zeroize", "dashu-int/zeroize"] 26 | num-order = ["dep:num-order", "dep:_num-modular"] 27 | 28 | # unstable dependencies 29 | num-traits = ["num-traits_v02"] 30 | num-traits_v02 = ["dep:num-traits_v02", "dashu-int/num-traits_v02"] 31 | rand = ["rand_v08"] 32 | rand_v08 = ["dep:rand_v08", "dashu-int/rand_v08"] 33 | 34 | [dependencies] 35 | dashu-base = { version = "0.4.0", default-features = false, path = "../base" } 36 | dashu-int = { version = "0.4.0", default-features = false, path = "../integer" } 37 | dashu-float = { version = "0.4.0", default-features = false, optional = true, path = "../float" } 38 | 39 | # stable dependencies 40 | rustversion = "1.0.0" 41 | num-order = { optional = true, version = "1.2.0", default-features = false } 42 | serde = { optional = true, version = "1.0.130", default-features = false } 43 | zeroize = { optional = true, version = "1.5.7", default-features = false } 44 | 45 | # unstable dependencies 46 | rand_v08 = { optional = true, version = "0.8.3", package = "rand", default-features = false } 47 | num-traits_v02 = { optional = true, version = "0.2.15", package = "num-traits", default-features = false } 48 | _num-modular = { optional = true, version = "0.6.1", package = "num-modular", default-features = false } 49 | 50 | [dev-dependencies] 51 | rand_v08 = { version = "0.8.3", package = "rand" } 52 | postcard = { version = "1.0.2", features = ["alloc"] } 53 | serde_test = { version = "1.0.130" } 54 | serde_json = { version = "1.0" } 55 | 56 | criterion = { version = "0.5.1", features = ["html_reports"] } 57 | 58 | [lib] 59 | bench = false 60 | 61 | [[test]] 62 | name = "random" 63 | required-features = ["rand"] 64 | 65 | [[test]] 66 | name = "serde" 67 | required-features = ["serde"] 68 | 69 | [[test]] 70 | name = "dashu_float" 71 | required-features = ["dashu-float"] 72 | 73 | [[test]] 74 | name = "cmp" 75 | required-features = ["dashu-float"] 76 | 77 | [[test]] 78 | name = "num_order" 79 | required-features = ["num-order", "dashu-float", "dashu-int/num-order"] 80 | 81 | [[bench]] 82 | name = "primitive" 83 | required-features = ["rand"] 84 | harness = false 85 | -------------------------------------------------------------------------------- /rational/README.md: -------------------------------------------------------------------------------- 1 | # dashu-ratio 2 | 3 | Arbitrary precision rational implementation as a part of the `dashu` library. See [Docs.rs](https://docs.rs/dashu-ratio/latest/dashu_ratio/) for the full documentation. 4 | 5 | ## Features 6 | 7 | - Supports `no_std` and written in pure Rust. 8 | - Support a **relaxed** verion of rational numbers for **fast computation**. 9 | - Support for **Diophantine Approximation** of floating point numbers. 10 | - Rational numbers with small numerators and denominators are **inlined** on stack. 11 | - Efficient integer **parsing and printing** with base 2~36. 12 | - **Developer friendly** debug printing for float numbers. 13 | 14 | ## Optional dependencies 15 | 16 | * `std` (default): enable `std` support for dependencies. 17 | 18 | ## Performance 19 | 20 | Relevant benchmark will be implemented in the [built-in benchmark](../benchmark/). 21 | 22 | ## License 23 | 24 | See the [top-level readme](../README.md). 25 | -------------------------------------------------------------------------------- /rational/benches/primitive.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks. 2 | //! Run: cargo bench -p dashu-float --bench primitive --features rand -- --quick 3 | 4 | use criterion::{ 5 | criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration, 6 | }; 7 | use dashu_base::Sign; 8 | use dashu_int::{IBig, UBig}; 9 | use dashu_ratio::RBig; 10 | use rand_v08::prelude::*; 11 | use std::ops::*; 12 | 13 | const SEED: u64 = 1; 14 | 15 | fn random_rbig(bits: usize, rng: &mut R) -> RBig 16 | where 17 | R: Rng + ?Sized, 18 | { 19 | let sign = Sign::from(rng.gen_bool(0.5)); 20 | let numerator = 21 | IBig::from_parts(sign, rng.gen_range(UBig::ONE << (bits - 1)..UBig::ONE << bits)); 22 | let denominator = rng.gen_range(UBig::ONE << (bits - 1)..UBig::ONE << bits); 23 | RBig::from_parts(numerator, denominator) 24 | } 25 | 26 | macro_rules! add_binop_benchmark { 27 | ($name:ident, $method:ident, $max_log_bits:literal) => { 28 | fn $name(criterion: &mut Criterion) { 29 | let mut rng = StdRng::seed_from_u64(SEED); 30 | let mut group = criterion.benchmark_group(stringify!($name)); 31 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 32 | 33 | for log_bits in 1..=$max_log_bits { 34 | let bits = 10usize.pow(log_bits); 35 | let a = random_rbig(bits, &mut rng); 36 | let b = random_rbig(bits, &mut rng) + &a; // make b > a so that sub won't underflow 37 | group.bench_with_input( 38 | BenchmarkId::from_parameter(bits), 39 | &(a, b), 40 | |bencher, (ta, tb)| bencher.iter(|| tb.$method(ta)), 41 | ); 42 | } 43 | 44 | group.finish(); 45 | } 46 | }; 47 | } 48 | 49 | add_binop_benchmark!(rbig_add, add, 6); 50 | add_binop_benchmark!(rbig_sub, sub, 6); 51 | add_binop_benchmark!(rbig_mul, mul, 6); 52 | add_binop_benchmark!(rbig_div, div, 6); 53 | 54 | criterion_group!(benches, rbig_add, rbig_sub, rbig_mul, rbig_div,); 55 | 56 | criterion_main!(benches); 57 | -------------------------------------------------------------------------------- /rational/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Definitions of panic cases 2 | 3 | pub const fn panic_divide_by_0() -> ! { 4 | panic!("Divisor or denominator must not be zero!") 5 | } 6 | -------------------------------------------------------------------------------- /rational/src/fmt.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | rbig::{RBig, Relaxed}, 3 | repr::Repr, 4 | }; 5 | use core::fmt::{self, Write}; 6 | 7 | impl fmt::Debug for Repr { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | if f.alternate() { 10 | f.debug_struct("Repr") 11 | .field("numerator", &format_args!("{:#?}", &self.numerator)) 12 | .field("denominator", &format_args!("{:#?}", &self.denominator)) 13 | .finish() 14 | } else { 15 | f.write_fmt(format_args!("{:?} / {:?}", &self.numerator, &self.denominator)) 16 | } 17 | } 18 | } 19 | 20 | impl fmt::Display for Repr { 21 | #[inline] 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | if self.denominator.is_one() { 24 | fmt::Display::fmt(&self.numerator, f) 25 | } else { 26 | fmt::Display::fmt(&self.numerator, f)?; 27 | f.write_char('/')?; 28 | fmt::Display::fmt(&self.denominator, f) 29 | } 30 | } 31 | } 32 | 33 | impl fmt::Debug for RBig { 34 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 35 | if f.alternate() { 36 | f.debug_struct("RBig") 37 | .field("numerator", &format_args!("{:#?}", self.numerator())) 38 | .field("denominator", &format_args!("{:#?}", self.denominator())) 39 | .finish() 40 | } else { 41 | f.write_fmt(format_args!("{:?} / {:?}", self.numerator(), self.denominator())) 42 | } 43 | } 44 | } 45 | 46 | impl fmt::Display for RBig { 47 | #[inline] 48 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 49 | fmt::Display::fmt(&self.0, f) 50 | } 51 | } 52 | 53 | impl fmt::Debug for Relaxed { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | if f.alternate() { 56 | f.debug_struct("Relaxed") 57 | .field("numerator", &format_args!("{:#?}", self.numerator())) 58 | .field("denominator", &format_args!("{:#?}", self.denominator())) 59 | .finish() 60 | } else { 61 | f.write_fmt(format_args!("{:?} / {:?}", self.numerator(), self.denominator())) 62 | } 63 | } 64 | } 65 | 66 | impl fmt::Display for Relaxed { 67 | #[inline] 68 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 69 | fmt::Display::fmt(&self.0, f) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rational/src/iter.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of core::iter traits 2 | 3 | use crate::rbig::{RBig, Relaxed}; 4 | use core::{ 5 | iter::{Product, Sum}, 6 | ops::{Add, Mul}, 7 | }; 8 | 9 | macro_rules! impl_fold_iter { 10 | ($t:ty, $fold_trait:ident, $fold:ident, $op_trait:ident, $op:ident, $init:ident) => { 11 | impl $fold_trait for $t 12 | where 13 | $t: $op_trait, 14 | { 15 | fn $fold>(iter: I) -> Self { 16 | iter.fold(<$t>::$init, <$t>::$op) 17 | } 18 | } 19 | }; 20 | } 21 | 22 | impl_fold_iter!(RBig, Sum, sum, Add, add, ZERO); 23 | impl_fold_iter!(Relaxed, Sum, sum, Add, add, ZERO); 24 | impl_fold_iter!(RBig, Product, product, Mul, mul, ONE); 25 | impl_fold_iter!(Relaxed, Product, product, Mul, mul, ONE); 26 | -------------------------------------------------------------------------------- /rational/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Jacob Zhong 2 | // 3 | // Licensed under either of 4 | // 5 | // * Apache License, Version 2.0 6 | // (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) 7 | // * MIT license 8 | // (LICENSE-MIT or https://opensource.org/licenses/MIT) 9 | // 10 | // at your option. 11 | // 12 | // Unless you explicitly state otherwise, any contribution intentionally submitted 13 | // for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 14 | // dual licensed as above, without any additional terms or conditions. 15 | 16 | //! A big rational library with good performance. 17 | //! 18 | //! The library implements efficient arithmetic and conversion functions in pure Rust. 19 | //! 20 | //! The two main rational types are [RBig] and [Relaxed]. Both of them represent the 21 | //! rational number as a pair of integers (numerator and denominator) and their APIs 22 | //! are mostly the same. However only with [RBig], the numerator and denominator are 23 | //! reduced so that they don't have common divisors other than one. Therefore, [Relaxed] 24 | //! sometimes can be much faster if you don't care about a reduced representation of 25 | //! the rational number. However, benchmarking is always recommended before choosing 26 | //! which representation to use. 27 | //! 28 | //! To construct big rationals from literals, please use the [`dashu-macro`](https://docs.rs/dashu-macros/latest/dashu_macros/) 29 | //! crate for your convenience. 30 | //! 31 | //! # Examples 32 | //! 33 | //! ``` 34 | //! # use dashu_base::ParseError; 35 | //! use dashu_int::{IBig, UBig}; 36 | //! use dashu_ratio::{RBig, Relaxed}; 37 | //! 38 | //! let a = RBig::from_parts((-12).into(), 34u8.into()); 39 | //! let b = RBig::from_str_radix("-azz/ep", 36).unwrap(); 40 | //! let c = RBig::try_from(3.1415926f32).unwrap(); // c = 6588397 / 2097152 (lossless) 41 | //! let c2 = RBig::simplest_from_f32(3.1415926).unwrap(); // c2 = 51808 / 16491 42 | //! assert_eq!(c2.numerator(), &IBig::from(51808)); 43 | //! 44 | //! assert_eq!(c.to_string(), "6588397/2097152"); 45 | //! let d = RBig::simplest_from_f32(22./7.).unwrap(); 46 | //! assert_eq!(d.to_string(), "22/7"); // round trip to the original literal 47 | //! 48 | //! // for Relaxed, only the common divisor 2 is removed 49 | //! let e: Relaxed = "-3228/1224".parse()?; // d = -807 / 306 50 | //! assert_eq!(e.numerator(), &IBig::from(-807)); 51 | //! let f: RBig = e.clone().canonicalize(); // e = -269 / 102 52 | //! assert_eq!(f.numerator(), &IBig::from(-269)); 53 | //! # Ok::<(), ParseError>(()) 54 | //! ``` 55 | 56 | #![cfg_attr(not(feature = "std"), no_std)] 57 | 58 | mod add; 59 | mod cmp; 60 | mod convert; 61 | mod div; 62 | mod error; 63 | mod fmt; 64 | mod helper_macros; 65 | mod mul; 66 | pub mod ops; 67 | mod parse; 68 | mod rbig; 69 | mod repr; 70 | mod round; 71 | mod sign; 72 | mod simplify; 73 | mod third_party; 74 | 75 | // All the public items from third_party will be exposed 76 | #[allow(unused_imports)] 77 | pub use third_party::*; 78 | 79 | pub use rbig::{RBig, Relaxed}; 80 | 81 | #[doc(hidden)] 82 | pub use dashu_int::Word; // for macros 83 | -------------------------------------------------------------------------------- /rational/src/ops.rs: -------------------------------------------------------------------------------- 1 | //! Re-exported relevant operator traits from `dashu-base` 2 | 3 | pub use dashu_base::math::EstimatedLog2; 4 | pub use dashu_base::ring::{DivEuclid, DivRemEuclid, RemEuclid}; 5 | pub use dashu_base::sign::Abs; 6 | -------------------------------------------------------------------------------- /rational/src/third_party/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementations for third party crates and traits 2 | 3 | #[cfg(feature = "dashu-float")] 4 | mod dashu_float; 5 | 6 | #[cfg(feature = "rand_v08")] 7 | pub mod rand; 8 | 9 | #[cfg(feature = "num-traits_v02")] 10 | mod num_traits; 11 | 12 | #[cfg(feature = "num-order")] 13 | mod num_order; 14 | 15 | #[cfg(feature = "serde")] 16 | mod serde; 17 | 18 | #[cfg(feature = "zeroize")] 19 | mod zeroize; 20 | -------------------------------------------------------------------------------- /rational/src/third_party/zeroize.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | rbig::{RBig, Relaxed}, 3 | repr::Repr, 4 | }; 5 | use zeroize::Zeroize; 6 | 7 | impl Zeroize for Repr { 8 | #[inline] 9 | fn zeroize(&mut self) { 10 | self.numerator.zeroize(); 11 | self.denominator.zeroize(); 12 | } 13 | } 14 | 15 | impl Zeroize for RBig { 16 | #[inline] 17 | fn zeroize(&mut self) { 18 | self.0.zeroize() 19 | } 20 | } 21 | 22 | impl Zeroize for Relaxed { 23 | #[inline] 24 | fn zeroize(&mut self) { 25 | self.0.zeroize() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /rational/tests/dashu_float.rs: -------------------------------------------------------------------------------- 1 | use dashu_base::Approximation::*; 2 | use dashu_float::{ 3 | round::{mode::*, Rounding::*}, 4 | DBig, 5 | }; 6 | use dashu_ratio::RBig; 7 | use std::str::FromStr; 8 | 9 | mod helper_macros; 10 | 11 | type FBin = dashu_float::FBig; 12 | 13 | #[test] 14 | fn test_to_float_decimal() { 15 | assert_eq!(rbig!(0).to_float(1), Exact(DBig::ZERO)); 16 | assert_eq!(rbig!(0).to_float::(20).unwrap().precision(), 20); 17 | assert_eq!(rbig!(1).to_float(1), Exact(DBig::ONE)); 18 | assert_eq!(rbig!(-1).to_float(1), Exact(DBig::NEG_ONE)); 19 | assert_eq!(rbig!(1 / 2).to_float(1), Exact(DBig::from_str("0.5").unwrap())); 20 | assert_eq!(rbig!(2 / 5).to_float(1), Exact(DBig::from_str("0.4").unwrap())); 21 | assert_eq!(rbig!(9 / 100).to_float(1), Exact(DBig::from_str("0.09").unwrap())); 22 | assert_eq!(rbig!(21 / 33).to_float(4), Inexact(DBig::from_str("0.6364").unwrap(), AddOne)); 23 | assert_eq!( 24 | rbig!(2 / 33333333).to_float(4), 25 | Inexact(DBig::from_str("6.000e-8").unwrap(), NoOp) 26 | ); 27 | assert_eq!( 28 | rbig!(22222222 / 3).to_float(4), 29 | Inexact(DBig::from_str("7.407e6").unwrap(), NoOp) 30 | ); 31 | } 32 | 33 | #[test] 34 | fn test_to_float_binary() { 35 | assert_eq!(rbig!(0).to_float(1), Exact(FBin::ZERO)); 36 | assert_eq!(rbig!(0).to_float::(20).unwrap().precision(), 20); 37 | assert_eq!(rbig!(1).to_float(1), Exact(FBin::ONE)); 38 | assert_eq!(rbig!(-1).to_float(1), Exact(FBin::NEG_ONE)); 39 | assert_eq!(rbig!(1 / 2).to_float(1), Exact(FBin::from_str("0x1p-1").unwrap())); 40 | assert_eq!(rbig!(2 / 5).to_float(1), Inexact(FBin::from_str("0x1p-2").unwrap(), NoOp)); 41 | assert_eq!(rbig!(9 / 100).to_float(4), Inexact(FBin::from_str("0xbp-7").unwrap(), NoOp)); 42 | } 43 | 44 | #[test] 45 | fn test_from_float_binary() { 46 | assert_eq!(RBig::simplest_from_float(&FBin::ZERO), Some(rbig!(0))); 47 | assert_eq!(RBig::simplest_from_float(&FBin::ONE), Some(rbig!(1))); 48 | assert_eq!(RBig::simplest_from_float(&FBin::NEG_ONE), Some(rbig!(-1))); 49 | assert_eq!(RBig::simplest_from_float(&FBin::INFINITY), None); 50 | assert_eq!(RBig::simplest_from_float(&FBin::NEG_INFINITY), None); 51 | 52 | let f = FBin::from(3) / FBin::from(7); 53 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(2 / 5))); 54 | let f = FBin::from(3) / FBin::from(7).with_precision(4).unwrap(); 55 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(3 / 7))); 56 | let f = FBin::from(-3) / FBin::from(7).with_precision(16).unwrap(); 57 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(-3 / 7))); 58 | } 59 | 60 | #[test] 61 | fn test_from_float_decimal() { 62 | assert_eq!(RBig::simplest_from_float(&DBig::ZERO), Some(rbig!(0))); 63 | assert_eq!(RBig::simplest_from_float(&DBig::ONE), Some(rbig!(1))); 64 | assert_eq!(RBig::simplest_from_float(&DBig::NEG_ONE), Some(rbig!(-1))); 65 | assert_eq!(RBig::simplest_from_float(&DBig::INFINITY), None); 66 | assert_eq!(RBig::simplest_from_float(&DBig::NEG_INFINITY), None); 67 | 68 | let f = DBig::from(3) / DBig::from(7); 69 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(2 / 5))); 70 | let f = DBig::from(3) / DBig::from(7).with_precision(4).unwrap(); 71 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(3 / 7))); 72 | let f = DBig::from(-3) / DBig::from(7).with_precision(10).unwrap(); 73 | assert_eq!(RBig::simplest_from_float(&f), Some(rbig!(-3 / 7))); 74 | } 75 | -------------------------------------------------------------------------------- /rational/tests/helper_macros.rs: -------------------------------------------------------------------------------- 1 | //! Helper macros for constructing numbers 2 | //! 3 | //! These macros are the simpler versions of the ones in `dashu-macros`, meant 4 | //! to be used in testing only. 5 | //! 6 | 7 | #[macro_export] 8 | macro_rules! ubig { 9 | ($val:tt) => {{ 10 | const STR: &::core::primitive::str = ::core::stringify!($val); 11 | ::core::result::Result::expect( 12 | ::dashu_int::UBig::from_str_with_radix_prefix(STR), 13 | "invalid number", 14 | ) 15 | .0 16 | }}; 17 | ($val:tt base $radix:literal) => {{ 18 | const STR: &::core::primitive::str = ::core::stringify!($val); 19 | let s = 20 | ::core::option::Option::unwrap_or(::core::primitive::str::strip_prefix(STR, "_"), STR); 21 | ::core::result::Result::expect( 22 | ::dashu_int::UBig::from_str_radix(s, $radix), 23 | "invalid number", 24 | ) 25 | }}; 26 | } 27 | 28 | #[macro_export] 29 | macro_rules! ibig { 30 | (- $val:tt) => { 31 | - <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from($crate::ubig!($val)) 32 | }; 33 | (- $val:tt base $radix:literal) => { 34 | - <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from( 35 | $crate::ubig!($val base $radix) 36 | ) 37 | }; 38 | ($val:tt) => { 39 | <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from($crate::ubig!($val)) 40 | }; 41 | ($val:tt base $radix:literal) => { 42 | <::dashu_int::IBig as ::core::convert::From<::dashu_int::UBig>>::from( 43 | $crate::ubig!($val base $radix) 44 | ) 45 | }; 46 | } 47 | 48 | /// Create a RBig instance from literal 49 | #[macro_export] 50 | macro_rules! rbig { 51 | ($val:tt) => { 52 | ::dashu_ratio::RBig::from_parts(ibig!($val), ::dashu_int::UBig::ONE) 53 | }; 54 | (-$val:tt) => { 55 | ::dashu_ratio::RBig::from_parts(ibig!(-$val), ::dashu_int::UBig::ONE) 56 | }; 57 | ($num:tt / $den:tt) => { 58 | ::dashu_ratio::RBig::from_parts(ibig!($num), ubig!($den)) 59 | }; 60 | (-$num:tt / $den:tt) => { 61 | ::dashu_ratio::RBig::from_parts(ibig!(-$num), ubig!($den)) 62 | }; 63 | (~$val:tt) => { 64 | ::dashu_ratio::Relaxed::from_parts(ibig!($val), ::dashu_int::UBig::ONE) 65 | }; 66 | (~-$val:tt) => { 67 | ::dashu_ratio::Relaxed::from_parts(ibig!(-$val), ::dashu_int::UBig::ONE) 68 | }; 69 | (~$num:tt / $den:tt) => { 70 | ::dashu_ratio::Relaxed::from_parts(ibig!($num), ubig!($den)) 71 | }; 72 | (~-$num:tt / $den:tt) => { 73 | ::dashu_ratio::Relaxed::from_parts(ibig!(-$num), ubig!($den)) 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /rational/tests/io.rs: -------------------------------------------------------------------------------- 1 | use dashu_base::ParseError; 2 | use dashu_ratio::{RBig, Relaxed}; 3 | 4 | mod helper_macros; 5 | 6 | #[test] 7 | fn test_rbig_format() { 8 | assert_eq!(format!("{}", rbig!(0)), "0"); 9 | assert_eq!(format!("{}", rbig!(1)), "1"); 10 | assert_eq!(format!("{}", rbig!(-1)), "-1"); 11 | assert_eq!(format!("{}", rbig!(-3)), "-3"); 12 | assert_eq!(format!("{}", rbig!(1 / 3)), "1/3"); 13 | assert_eq!(format!("{}", rbig!(-1 / 3)), "-1/3"); 14 | assert_eq!(format!("{}", rbig!(12 / 15)), "4/5"); 15 | assert_eq!(format!("{}", RBig::from_parts(ibig!(-1) << 200, (ubig!(1) << 200) - ubig!(1))), 16 | "-1606938044258990275541962092341162602522202993782792835301376/1606938044258990275541962092341162602522202993782792835301375"); 17 | } 18 | 19 | #[test] 20 | fn test_relaxed_format() { 21 | assert_eq!(format!("{}", rbig!(~0)), "0"); 22 | assert_eq!(format!("{}", rbig!(~1)), "1"); 23 | assert_eq!(format!("{}", rbig!(~-1)), "-1"); 24 | assert_eq!(format!("{}", rbig!(~-3)), "-3"); 25 | assert_eq!(format!("{}", rbig!(~1/3)), "1/3"); 26 | assert_eq!(format!("{}", rbig!(~-1/3)), "-1/3"); 27 | assert_eq!(format!("{}", rbig!(~12/15)), "12/15"); 28 | assert_eq!(format!("{}", Relaxed::from_parts(ibig!(-1) << 200, (ubig!(1) << 200) - ubig!(1))), 29 | "-1606938044258990275541962092341162602522202993782792835301376/1606938044258990275541962092341162602522202993782792835301375"); 30 | } 31 | 32 | #[test] 33 | fn test_rbig_from_str_radix() { 34 | assert_eq!(RBig::from_str_radix("", 2).unwrap_err(), ParseError::NoDigits); 35 | assert_eq!(RBig::from_str_radix("+", 2).unwrap_err(), ParseError::NoDigits); 36 | assert_eq!(RBig::from_str_radix("/", 2).unwrap_err(), ParseError::NoDigits); 37 | assert_eq!(RBig::from_str_radix("2/", 10).unwrap_err(), ParseError::NoDigits); 38 | assert_eq!(RBig::from_str_radix("/2", 10).unwrap_err(), ParseError::NoDigits); 39 | assert_eq!(RBig::from_str_radix("1//2", 10).unwrap_err(), ParseError::InvalidDigit); 40 | assert_eq!(RBig::from_str_radix("0", 2).unwrap(), rbig!(0)); 41 | assert_eq!(RBig::from_str_radix("1", 2).unwrap(), rbig!(1)); 42 | assert_eq!(RBig::from_str_radix("-1", 2).unwrap(), rbig!(-1)); 43 | assert_eq!(RBig::from_str_radix("1/2", 10).unwrap(), rbig!(1 / 2)); 44 | assert_eq!(RBig::from_str_radix("-1/2", 10).unwrap(), rbig!(-1 / 2)); 45 | assert_eq!(RBig::from_str_radix("+1/-2", 10).unwrap(), rbig!(-1 / 2)); 46 | assert_eq!(RBig::from_str_radix("-1/-2", 10).unwrap(), rbig!(1 / 2)); 47 | } 48 | 49 | #[test] 50 | fn test_relaxed_from_str_radix() { 51 | assert_eq!(Relaxed::from_str_radix("", 2).unwrap_err(), ParseError::NoDigits); 52 | assert_eq!(Relaxed::from_str_radix("+", 2).unwrap_err(), ParseError::NoDigits); 53 | assert_eq!(Relaxed::from_str_radix("/", 2).unwrap_err(), ParseError::NoDigits); 54 | assert_eq!(Relaxed::from_str_radix("2/", 10).unwrap_err(), ParseError::NoDigits); 55 | assert_eq!(Relaxed::from_str_radix("/2", 10).unwrap_err(), ParseError::NoDigits); 56 | assert_eq!(Relaxed::from_str_radix("1//2", 10).unwrap_err(), ParseError::InvalidDigit); 57 | assert_eq!(Relaxed::from_str_radix("0", 2).unwrap(), rbig!(~0)); 58 | assert_eq!(Relaxed::from_str_radix("1", 2).unwrap(), rbig!(~1)); 59 | assert_eq!(Relaxed::from_str_radix("-1", 2).unwrap(), rbig!(~-1)); 60 | assert_eq!(Relaxed::from_str_radix("1/2", 10).unwrap(), rbig!(~1/2)); 61 | assert_eq!(Relaxed::from_str_radix("-1/2", 10).unwrap(), rbig!(~-1/2)); 62 | assert_eq!(Relaxed::from_str_radix("+1/-2", 10).unwrap(), rbig!(~-1/2)); 63 | assert_eq!(Relaxed::from_str_radix("-1/-2", 10).unwrap(), rbig!(~1/2)); 64 | } 65 | -------------------------------------------------------------------------------- /rational/tests/mul.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::Debug, 3 | ops::{Mul, MulAssign}, 4 | }; 5 | 6 | mod helper_macros; 7 | 8 | fn test_mul<'a, T>(a: &'a T, b: &'a T, c: &'a T) 9 | where 10 | T: Mul, 11 | T: Mul<&'a T, Output = T>, 12 | &'a T: Mul, 13 | &'a T: Mul<&'a T, Output = T>, 14 | T: MulAssign, 15 | T: MulAssign<&'a T>, 16 | T: Clone, 17 | T: Debug, 18 | T: Eq, 19 | { 20 | assert_eq!(a * b, *c); 21 | assert_eq!(a.clone() * b, *c); 22 | assert_eq!(a * b.clone(), *c); 23 | assert_eq!(a.clone() * b.clone(), *c); 24 | 25 | let mut x = a.clone(); 26 | x *= b; 27 | assert_eq!(x, *c); 28 | 29 | let mut x = a.clone(); 30 | x *= b.clone(); 31 | assert_eq!(x, *c); 32 | } 33 | 34 | #[test] 35 | fn test_mul_rbig() { 36 | let test_cases = [ 37 | (rbig!(0), rbig!(0), rbig!(0)), 38 | (rbig!(1), rbig!(1), rbig!(1)), 39 | (rbig!(1), rbig!(-1), rbig!(-1)), 40 | (rbig!(-1), rbig!(-1), rbig!(1)), 41 | (rbig!(1 / 2), rbig!(-2 / 3), rbig!(-1 / 3)), 42 | (rbig!(10 / 9), rbig!(15 / 4), rbig!(25 / 6)), 43 | ]; 44 | 45 | for (a, b, c) in &test_cases { 46 | test_mul(a, b, c); 47 | } 48 | } 49 | 50 | #[test] 51 | fn test_mul_relaxed() { 52 | let test_cases = [ 53 | (rbig!(~0), rbig!(~0), rbig!(~0)), 54 | (rbig!(~1), rbig!(~1), rbig!(~1)), 55 | (rbig!(~1), rbig!(~-1), rbig!(~-1)), 56 | (rbig!(~-1), rbig!(~-1), rbig!(~1)), 57 | (rbig!(~1/2), rbig!(~-2/3), rbig!(~-1/3)), 58 | (rbig!(~10/9), rbig!(~15/4), rbig!(~75/18)), 59 | ]; 60 | 61 | for (a, b, c) in &test_cases { 62 | test_mul(a, b, c); 63 | } 64 | } 65 | 66 | #[test] 67 | fn test_add_with_ibig() { 68 | let test_cases = [ 69 | (rbig!(0), ibig!(0), rbig!(0)), 70 | (rbig!(1), ibig!(1), rbig!(1)), 71 | (rbig!(1), ibig!(-1), rbig!(-1)), 72 | (rbig!(-1), ibig!(-1), rbig!(1)), 73 | (rbig!(1 / 2), ibig!(-2), rbig!(-1)), 74 | (rbig!(1 / 2), ibig!(-4), rbig!(-2)), 75 | (rbig!(10 / 7), ibig!(7), rbig!(10)), 76 | (rbig!(-7 / 6), ibig!(9), rbig!(-21 / 2)), 77 | ]; 78 | 79 | for (a, b, c) in &test_cases { 80 | assert_eq!(a * b, *c); 81 | assert_eq!(b * a, *c); 82 | 83 | let r = &a.clone().relax(); 84 | assert_eq!(r * b, c.clone().relax()); 85 | assert_eq!(b * r, c.clone().relax()); 86 | } 87 | } 88 | 89 | #[test] 90 | fn test_mul_with_int() { 91 | assert_eq!(rbig!(0) * ubig!(1), rbig!(0)); 92 | assert_eq!(rbig!(~0) * ubig!(1), rbig!(~0)); 93 | assert_eq!(rbig!(1) * ubig!(1), rbig!(1)); 94 | assert_eq!(rbig!(~1) * ubig!(1), rbig!(~1)); 95 | assert_eq!(rbig!(-1 / 2) * ibig!(-2), rbig!(1)); 96 | assert_eq!(rbig!(~-1/2) * ibig!(-2), rbig!(~1)); 97 | assert_eq!(rbig!(5 / 12) * ibig!(-3), rbig!(-5 / 4)); 98 | assert_eq!(rbig!(~5/12) * ibig!(-3), rbig!(~-5/4)); 99 | 100 | assert_eq!(ubig!(0) * rbig!(1), rbig!(0)); 101 | assert_eq!(ubig!(0) * rbig!(~1), rbig!(~0)); 102 | assert_eq!(ubig!(1) * rbig!(1), rbig!(1)); 103 | assert_eq!(ubig!(1) * rbig!(~1), rbig!(~1)); 104 | assert_eq!(ibig!(-2) * rbig!(-1 / 2), rbig!(1)); 105 | assert_eq!(ibig!(-2) * rbig!(~-1/2), rbig!(~1)); 106 | assert_eq!(ibig!(-3) * rbig!(5 / 12), rbig!(-5 / 4)); 107 | assert_eq!(ibig!(-3) * rbig!(~5/12), rbig!(~-5/4)); 108 | } 109 | -------------------------------------------------------------------------------- /rational/tests/round.rs: -------------------------------------------------------------------------------- 1 | mod helper_macros; 2 | 3 | #[test] 4 | fn test_ceil_floor() { 5 | let test_cases = [ 6 | // (ratio, ceil, floor) 7 | (rbig!(~0), ibig!(0), ibig!(0)), 8 | (rbig!(~1), ibig!(1), ibig!(1)), 9 | (rbig!(~-1), ibig!(-1), ibig!(-1)), 10 | (rbig!(~10), ibig!(10), ibig!(10)), 11 | (rbig!(~-10), ibig!(-10), ibig!(-10)), 12 | (rbig!(~4/2), ibig!(2), ibig!(2)), 13 | (rbig!(~4/3), ibig!(2), ibig!(1)), 14 | (rbig!(~-4/3), ibig!(-1), ibig!(-2)), 15 | (rbig!(~0xffff/0xfe), ibig!(259), ibig!(258)), 16 | (rbig!(~-0xffff/0xfe), ibig!(-258), ibig!(-259)), 17 | (rbig!(~0xfffe/0xff), ibig!(257), ibig!(256)), 18 | (rbig!(~-0xfffe/0xff), ibig!(-256), ibig!(-257)), 19 | ]; 20 | 21 | for (ratio, ceil, floor) in test_cases { 22 | assert_eq!(ratio.ceil(), ceil); 23 | assert_eq!(ratio.floor(), floor); 24 | 25 | let ratio = ratio.canonicalize(); 26 | assert_eq!(ratio.ceil(), ceil); 27 | assert_eq!(ratio.floor(), floor); 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_trunc_fract() { 33 | let test_cases = [ 34 | // (ratio, trunc, fract) 35 | (rbig!(~0), ibig!(0), rbig!(~0)), 36 | (rbig!(~1), ibig!(1), rbig!(~0)), 37 | (rbig!(~10), ibig!(10), rbig!(~0)), 38 | (rbig!(~4/2), ibig!(2), rbig!(~0)), 39 | (rbig!(~4/3), ibig!(1), rbig!(~1/3)), 40 | (rbig!(~0xffff/0xfe), ibig!(258), rbig!(~3/0xfe)), 41 | (rbig!(~0xfffe/0xff), ibig!(256), rbig!(~0xfe/0xff)), 42 | ]; 43 | 44 | for (ratio, trunc, fract) in test_cases { 45 | assert_eq!(ratio.trunc(), trunc); 46 | assert_eq!(ratio.fract(), fract); 47 | assert_eq!((-&ratio).trunc(), -&trunc); 48 | assert_eq!((-&ratio).fract(), -&fract); 49 | assert_eq!(ratio.clone().split_at_point(), (trunc.clone(), fract.clone())); 50 | assert_eq!((-ratio.clone()).split_at_point(), (-trunc.clone(), -fract.clone())); 51 | 52 | let ratio = ratio.canonicalize(); 53 | let fract = fract.canonicalize(); 54 | assert_eq!(ratio.trunc(), trunc); 55 | assert_eq!(ratio.fract(), fract); 56 | assert_eq!((-&ratio).trunc(), -&trunc); 57 | assert_eq!((-&ratio).fract(), -&fract); 58 | assert_eq!(ratio.clone().split_at_point(), (trunc.clone(), fract.clone())); 59 | assert_eq!((-ratio.clone()).split_at_point(), (-trunc.clone(), -fract.clone())); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rational/tests/serde.rs: -------------------------------------------------------------------------------- 1 | use dashu_ratio::{RBig, Relaxed}; 2 | use postcard::{from_bytes, to_allocvec}; 3 | use serde_json::{from_str, to_string}; 4 | 5 | mod helper_macros; 6 | 7 | #[test] 8 | fn test_rbig_serde() { 9 | let test_numbers = [ 10 | rbig!(0), 11 | rbig!(1 / 3), 12 | rbig!(-2 / 3), 13 | rbig!(10 / 99), 14 | rbig!(-0xffffffffffffffff / 0xfffffffffffffffe), 15 | rbig!(-0xfffffffffffffffffffffffffffffffe / 0xffffffffffffffffffffffffffffffff), 16 | ]; 17 | for ratio in &test_numbers { 18 | // test binary serialization 19 | let output = to_allocvec(ratio).unwrap(); 20 | let parsed: RBig = from_bytes(&output).unwrap(); 21 | assert_eq!(&parsed, ratio); 22 | 23 | // test string serialization 24 | let output = to_string(ratio).unwrap(); 25 | let parsed: RBig = from_str(&output).unwrap(); 26 | assert_eq!(&parsed, ratio); 27 | } 28 | } 29 | 30 | #[test] 31 | fn test_relaxed_serde() { 32 | let test_numbers = [ 33 | rbig!(~0), 34 | rbig!(~1/3), 35 | rbig!(~-2/3), 36 | rbig!(~33/99), 37 | rbig!(~-0xffffffffffffffff/0xfffffffffffffffe), 38 | rbig!(~-0xfffffffffffffffffffffffffffffffe/0xffffffffffffffffffffffffffffffff), 39 | ]; 40 | for ratio in &test_numbers { 41 | // test binary serialization 42 | let output = to_allocvec(ratio).unwrap(); 43 | let parsed: Relaxed = from_bytes(&output).unwrap(); 44 | assert_eq!(&parsed, ratio); 45 | 46 | // test string serialization 47 | let output = to_string(ratio).unwrap(); 48 | let parsed: Relaxed = from_str(&output).unwrap(); 49 | assert_eq!(&parsed, ratio); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | fn_call_width = 80 -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The meta crate that re-exports all `dashu` numeric types. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | /// Defintions of common traits 6 | pub mod base { 7 | pub use dashu_base::*; 8 | } 9 | 10 | /// Arbitrary precision integer number 11 | pub mod integer { 12 | pub use dashu_int::*; 13 | } 14 | 15 | /// Arbitrary precision floating point number 16 | pub mod float { 17 | pub use dashu_float::*; 18 | } 19 | 20 | /// Arbitrary precision rational number 21 | pub mod rational { 22 | pub use dashu_ratio::*; 23 | } 24 | 25 | #[doc(hidden)] 26 | pub use dashu_macros as __dashu_macros; 27 | 28 | #[macro_export] 29 | #[doc = include_str!("macro-docs/ubig.md")] 30 | macro_rules! ubig { 31 | ($($t:tt)+) => { 32 | $crate::__dashu_macros::ubig_embedded!($($t)+) 33 | } 34 | } 35 | 36 | #[macro_export] 37 | #[rustversion::since(1.64)] 38 | #[doc = include_str!("macro-docs/static_ubig.md")] 39 | macro_rules! static_ubig { 40 | ($($t:tt)+) => { 41 | $crate::__dashu_macros::static_ubig_embedded!($($t)+) 42 | } 43 | } 44 | 45 | #[macro_export] 46 | #[doc = include_str!("macro-docs/ibig.md")] 47 | macro_rules! ibig { 48 | ($($t:tt)+) => { 49 | $crate::__dashu_macros::ibig_embedded!($($t)+) 50 | } 51 | } 52 | 53 | #[macro_export] 54 | #[rustversion::since(1.64)] 55 | #[doc = include_str!("macro-docs/static_ibig.md")] 56 | macro_rules! static_ibig { 57 | ($($t:tt)+) => { 58 | $crate::__dashu_macros::static_ibig_embedded!($($t)+) 59 | } 60 | } 61 | 62 | #[macro_export] 63 | #[doc = include_str!("macro-docs/fbig.md")] 64 | macro_rules! fbig { 65 | ($($t:tt)+) => { 66 | $crate::__dashu_macros::fbig_embedded!($($t)+) 67 | } 68 | } 69 | 70 | #[macro_export] 71 | #[rustversion::since(1.64)] 72 | #[doc = include_str!("macro-docs/static_fbig.md")] 73 | macro_rules! static_fbig { 74 | ($($t:tt)+) => { 75 | $crate::__dashu_macros::static_fbig_embedded!($($t)+) 76 | } 77 | } 78 | 79 | #[macro_export] 80 | #[doc = include_str!("macro-docs/dbig.md")] 81 | macro_rules! dbig { 82 | ($($t:tt)+) => { 83 | $crate::__dashu_macros::dbig_embedded!($($t)+) 84 | } 85 | } 86 | 87 | #[macro_export] 88 | #[rustversion::since(1.64)] 89 | #[doc = include_str!("macro-docs/static_dbig.md")] 90 | macro_rules! static_dbig { 91 | ($($t:tt)+) => { 92 | $crate::__dashu_macros::static_dbig_embedded!($($t)+) 93 | } 94 | } 95 | 96 | #[macro_export] 97 | #[doc = include_str!("macro-docs/rbig.md")] 98 | macro_rules! rbig { 99 | ($($t:tt)+) => { 100 | $crate::__dashu_macros::rbig_embedded!($($t)+) 101 | } 102 | } 103 | 104 | #[macro_export] 105 | #[rustversion::since(1.64)] 106 | #[doc = include_str!("macro-docs/static_rbig.md")] 107 | macro_rules! static_rbig { 108 | ($($t:tt)+) => { 109 | $crate::__dashu_macros::static_rbig_embedded!($($t)+) 110 | } 111 | } 112 | 113 | /// A verbose alias for [UBig][dashu_int::UBig] 114 | pub type Natural = dashu_int::UBig; 115 | 116 | /// A verbose alias for [IBig][dashu_int::IBig] 117 | pub type Integer = dashu_int::IBig; 118 | 119 | /// A verbose alias for [FBig][dashu_float::FBig] (base 2, rounding towards zero) 120 | pub type Real = dashu_float::FBig; 121 | 122 | /// A verbose alias for [DBig][dashu_float::DBig] (base 10, rounding to the nearest) 123 | pub type Decimal = dashu_float::DBig; 124 | 125 | /// A verbose alias for [RBig][dashu_ratio::RBig] 126 | pub type Rational = dashu_ratio::RBig; 127 | -------------------------------------------------------------------------------- /src/macro-docs: -------------------------------------------------------------------------------- 1 | ../macros/docs -------------------------------------------------------------------------------- /tests/import.rs: -------------------------------------------------------------------------------- 1 | //! Test for importing items from dashu, and do basic operations 2 | 3 | use dashu::{rational::Relaxed, *}; 4 | 5 | #[test] 6 | #[rustfmt::skip::macros(fbig)] 7 | fn test_macros() { 8 | // small numbers 9 | const A: Natural = ubig!(1234); 10 | const B: Integer = ibig!(-1234); 11 | assert_eq!(A + B, ibig!(0)); 12 | 13 | const C: Real = fbig!(0x1234p-4); 14 | const D: Decimal = dbig!(12.34); 15 | assert!(C.to_decimal().value() > D); 16 | 17 | const E: Rational = rbig!(2 / 5); 18 | const F: Relaxed = rbig!(~2/7); 19 | assert!(E.relax() > F); 20 | 21 | // large numbers 22 | let a = ubig!(0xfffffffffffffffffffffffffffffffffffffffffffffffe); 23 | let b = ibig!(-0xffffffffffffffffffffffffffffffffffffffffffffffff); 24 | assert_eq!(a + b, ibig!(-1)); 25 | 26 | let c = fbig!(0xffffffffffffffffffffffffffffffffffffffffffffffffp-192); 27 | let d = dbig!(999999999999999999999999999999999999999999999999999999999999e-60); 28 | assert!(c < d.to_binary().value()); 29 | 30 | let e = rbig!( 31 | 0xfffffffffffffffffffffffffffffffffffffffffffffffe 32 | / 0xffffffffffffffffffffffffffffffffffffffffffffffff 33 | ); 34 | let f = rbig!(~ 35 | 999999999999999999999999999999999999999999999999999999999998 36 | / 999999999999999999999999999999999999999999999999999999999999); 37 | assert!(e < f.canonicalize()); 38 | } 39 | 40 | #[test] 41 | #[rustversion::since(1.64)] 42 | #[rustfmt::skip::macros(static_fbig)] 43 | fn test_static_macros() { 44 | static SA: &Natural = static_ubig!(1234); 45 | static SB: &Integer = static_ibig!(-1234); 46 | assert_eq!(SA + SB, ibig!(0)); 47 | 48 | static SC: &Real = static_fbig!(0x1234p-4); 49 | static SD: &Decimal = static_dbig!(12.34); 50 | assert!(SC.to_decimal().value() > *SD); 51 | 52 | static SE: &Rational = static_rbig!(2 / 5); 53 | static SF: &Relaxed = static_rbig!(~2/7); 54 | assert!(SE.as_relaxed() > SF); 55 | 56 | static BA: &Natural = static_ubig!(0xfffffffffffffffffffffffffffffffffffffffffffffffe); 57 | static BB: &Integer = static_ibig!(-0xffffffffffffffffffffffffffffffffffffffffffffffff); 58 | assert_eq!(BA + BB, ibig!(-1)); 59 | 60 | static BC: &Real = static_fbig!(0xffffffffffffffffffffffffffffffffffffffffffffffffp-192); 61 | static BD: &Decimal = 62 | static_dbig!(999999999999999999999999999999999999999999999999999999999999e-60); 63 | assert!(*BC < BD.clone().with_base_and_precision(200).value()); 64 | 65 | static BE: &Rational = static_rbig!( 66 | 0xfffffffffffffffffffffffffffffffffffffffffffffffe 67 | / 0xffffffffffffffffffffffffffffffffffffffffffffffff 68 | ); 69 | static BF: &Relaxed = static_rbig!(~ 70 | 999999999999999999999999999999999999999999999999999999999998 71 | / 999999999999999999999999999999999999999999999999999999999999); 72 | assert!(*BE < BF.clone().canonicalize()); 73 | } 74 | --------------------------------------------------------------------------------