diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/rust_decimal | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/rust_decimal')
41 files changed, 17259 insertions, 0 deletions
diff --git a/third_party/rust/rust_decimal/.cargo-checksum.json b/third_party/rust/rust_decimal/.cargo-checksum.json new file mode 100644 index 0000000000..3973992719 --- /dev/null +++ b/third_party/rust/rust_decimal/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"BUILD.md":"302c5260cb6eb87efc1d968b42f082ea3569cca084c0cbcc1821a8670235c19a","CHANGELOG.md":"f4eab626b23c7a76e17e2601e7e6fc920090134f59411472c0eaab5b06bd62d1","CODE_OF_CONDUCT.md":"64765f10290cfce7191b4208cb21698b708a118568f5016602cccc304846a09a","CONTRIBUTING.md":"471d6281fb5038e17e32d3b4450aacf542a396709605aa170e07d3971d70b9c1","Cargo.toml":"70a6cc3be824c2c135094b3f217262d86d16a3bc613acf9a12c71cb317002bec","LICENSE":"f8218253704e32441cafea1b9b3bcb2c6a3c51c5553cd8513d179290202bccb2","Makefile.toml":"d8d548ac651ce87294bafb6f55bedd1e6e78ce6332594963f6d02db9d7452d13","README.md":"c65eef618921de6d55955f7ec88c5fdde88a2b8ccdcab6e7fd07def01d90ab14","benches/comparison.rs":"e8b14531b129acb8ffca2dd44565d9160f510ec38abd32fc49fd4e2386067e25","benches/lib_benches.rs":"899ecdd258350ab1c49218b8f0213bbbc2ae937093bd0afa46be7eb1a28afd72","build.rs":"ae5b0e60460390f8411e19d7721524f492a8a988132c74f05867a261b513d54e","rustfmt.toml":"f33bda44a494d17c95b7bc1b3dd88c203030b75be766f3a7f9b63ef45d960bb0","src/arithmetic_impls.rs":"87a87bc46214ba7f239dfafcffb6ca05453f2ff07ed36e94792548d508da7a9d","src/constants.rs":"5a31626a234e4bb1f06752d7db6ebb39a543c5e0df1e929dd0032689ef7aaa1f","src/decimal.rs":"f4f9883e1196b1b316dc7fcaaf94f8b06963ab1d26d56274caaf793afca582b2","src/error.rs":"27cdf052d2a956b81cfe0bf512354e3e0772fa9fe5a2a9e8b2246a53ed58ad28","src/fuzz.rs":"86c07d8d541b9ee92a51275993b686902712f94c30785ba93f38997f3569e700","src/lib.rs":"efff31de36fdfd0b8959982e4dc42eda6512f5fa5bb66fe25eb5ba948f0af6f2","src/maths.rs":"f82016c5ae8406103835a3fedebeb1360cfe3cc40ac4ab28e13da89636c6782c","src/mysql.rs":"3352e1d72561b022427539fc5025f5f01f00be65ebf6492761668effc9e1eeeb","src/ops.rs":"4d426a35f73b0b69cbceee6e01c6eff59b8cc09aab7c885570aea52d8f258f66","src/ops/add.rs":"a85b6214be92a5563e8bb4a936275d8de094507d77384c9184d63583a78b3f55","src/ops/array.rs":"8a900c845e52843f34ae19742a5886cd8becf54ddd6d1923fd0fbd9588c605e9","src/ops/cmp.rs":"95437401daf93d60425c388fc243e52ad5570cfe6c5d818b5aa144759a5f2ef1","src/ops/common.rs":"6d48ecfa4796a38cb9d4c48f50ffed5d1ee79ba8e168a175615b6fe97194b7c2","src/ops/div.rs":"6b1e90b383293eb51f20f22846002a61f17211f7791860d4e9d6f82ad940fb87","src/ops/legacy.rs":"08bc45454b1815a592cc2fd02df8c50bec36c831ab7af098018e29dfc81f0ec4","src/ops/mul.rs":"b0bb69461b4934cb53c49e105d21da8aa661e1215e8797b8fdc64564df431d92","src/ops/rem.rs":"125d64e9425effd01d4ff400f811f43ef76bf846b6823195059648fdf004b592","src/postgres.rs":"34e35b73cb55fa5f303251f3e074c4e30509973900810a9f423f80294effcc05","src/postgres/common.rs":"7f52920a83f9c5081f7b7a626ef4cff65bbaef51133c7eaf364318212d4aed15","src/postgres/diesel.rs":"fb2d7783d279730a6c77458c6bb58f839e9b48d8209ed2e8395e2f72d7032c87","src/postgres/driver.rs":"dfc5001e4d6235e8ce535066887a910681581e9e1aedffd73b265c065dbc497c","src/rand.rs":"382f057f4a8752a6028afbecd3cb27422d530c6aa0142ddc04b698b501f8f9db","src/rocket.rs":"4d05f292281e4d463abeba603b07ea460065cb1b8ec1c6dafdb4e41f7b89e828","src/serde.rs":"b3fda74ee7ec9317e074f2d271a59ae6024c2a324beef1169d5df1747dad9ba2","src/str.rs":"ca8ce5a61fc7d940355a8f1154492a62d06a8c2f825061044a1d422354d6c5ea","tests/decimal_tests.rs":"6e06f4f9558e983c1e39875508f5e96341284664e0f245b2b265587c3b5ab3a0","tests/macros.rs":"f4e1ade99bf8a7aaf2a2d4ee557df5b0b32266a349daf59b2e8b1ae7bc72599c","tests/version-numbers.rs":"73301b7bfe500eada5ede66f0dce89bd3e354af50a8e7a123b02931cd5eb8e16"},"package":"e13cf35f7140155d02ba4ec3294373d513a3c7baa8364c162b030e33c61520a8"}
\ No newline at end of file diff --git a/third_party/rust/rust_decimal/BUILD.md b/third_party/rust/rust_decimal/BUILD.md new file mode 100644 index 0000000000..2e46288332 --- /dev/null +++ b/third_party/rust/rust_decimal/BUILD.md @@ -0,0 +1,112 @@ +Developing Rust Decimal +======================= + +* [Setup](#setup) +* [Building](#building) +* [Library Versioning](#library-versioning) +* [Formatting / Code Style](#formatting--code-style) +* [Testing](#testing) +* [Fuzzing](#fuzzing) +* [Benchmarking](#benchmarking) +* [Code Coverage](#code-coverage) + +## Setup + +Rust Decimal leverages [cargo make](https://github.com/sagiegurari/cargo-make) to ensure a consistent build/test/release +approach. This also handles installing any additional dependencies in order to execute various commands, making getting set +up and going relatively straight forward and easy. + +Installing this can be done using `cargo`: + +```shell +cargo install --force cargo-make +``` + +Once this has been installed, common tasks can be initiated with the `makers` command. + +## Building + +**Useful Make Commands:** `makers build` + +Building this library doesn't require any special commands and can be performed using a standard `cargo build`. + +### Building no-std + +Builds by default leverage `std`. To disable this functionality you can build Rust Decimal without any default features +enabled: + +```shell +cargo build --no-default-features +``` + +## Library Versioning + +**Useful Make Commands:** `makers outdated` + +The general rule of thumb with Rust Decimal is to ensure that downstream dependencies are kept up to date, so long as they +align with the minimum requirements of the Rust Decimal build system. As a helpful measure, a `makers` command has been created +to identify any outdated dependencies within the project. On a regular basis this command is run and results evaluated for potential +updates to be applied. + +Please note: because this is a library, the `Cargo.lock` file is not committed into the repository. This means that `cargo update` +should be run before running `makers outdated`. + +## Formatting / Code Style + +**Useful Make Commands:** `makers format`, `makers clippy` + +To maintain consistent styling across the project, both `clippy` and `cargo fmt` are used. Github actions will +check for consistent styling upon pull request however it is recommended that both of these commands are run before creating +a PR. + +## Testing + +**Useful Make Commands:** `makers test-all`, `makers test-no-std`, `makers test-maths`, `makers test-serde`, `makers test-db` + +Testing is a critical part of the Rust Decimal development lifecycle. It helps ensure that the library is behaving correctly under +a mixture of different inputs and feature configurations. + +There are many different test configurations defined in `Makefile.toml` to help ensure that common test cases are easy to execute locally. +For the full complete list, please review `Makefile.toml` directly for all `test-*` tasks. Some useful test tasks are listed below: + +* `test-all`: Tests all test configurations against all features +* `test-default`: Test the default configuration +* `test-no-std`: Test the library against a `no-std` build +* `test-maths`: Test the maths feature of the library using both legacy and default ops. +* `test-serde`: Test serialization and deserialization using a mixture of serde features +* `test-db`: Test a variety of database logic changes, including `diesel` and `postgres` configurations. + +When adding new features, please make sure to generate new tasks within the `Makefile.toml` to ensure continued +coverage of your feature going forward. + +### Generated tests + +Rust Decimal includes a number of generated tests that have been generated outside the scope of this project. These are +effectively CSV files that use randomized data for the `lo`, `mid` and `hi` portions of the Decimal number. + +Modification of these files is restricted and should not be committed. If you would like to add further generated tests then +please create new files as you see fit. + +## Fuzzing + +**Useful Make Commands:** `makers fuzz` + +Rust Decimal includes some fuzz testing support also to help capture a wider range of testing scenarios. These can be +run by using `makers fuzz`. + +## Benchmarking + +**Useful Make Commands:** `makers bench` + +In order to ensure that Rust Decimal runs quickly, a number of benchmarks have been created and can be executed using +`makers bench`. + +When adding new benchmarking configurations, please ensure that you add a corresponding `Makefile.toml` task to capture the +updated configuration. + +## Code Coverage + +**Useful Make Commands:** `makers coverage`, `makers codecov-open` + +Limited code coverage support has been added to Rust Decimal using `gcov`. A code coverage report +can be generated by running `makers coverage`. Once generated, the report can be opened using `makers codecov-open`. diff --git a/third_party/rust/rust_decimal/CHANGELOG.md b/third_party/rust/rust_decimal/CHANGELOG.md new file mode 100644 index 0000000000..6844a9af2b --- /dev/null +++ b/third_party/rust/rust_decimal/CHANGELOG.md @@ -0,0 +1,669 @@ +# Version History + +## 1.28.1 + +### Fixed + +* Bumped `borsh` and `criterion` dependencies to the latest versions. ([#568](https://github.com/paupino/rust-decimal/pull/568)) +* Removed erroneous debug statements in `serde-with-str` feature. ([#571](https://github.com/paupino/rust-decimal/pull/571)) + +Thanks [@attila-lin](https://github.com/attila-lin) for your help bumping dependencies. + +## 1.28.0 + +### Added + +* Implement `TryFrom<&str>` for `Decimal` ([#560](https://github.com/paupino/rust-decimal/pull/560)) + +### Fixed + +* Explicit string deserialize for `Option<Decimal>` when using `serde-with-str` ([#565](https://github.com/paupino/rust-decimal/pull/565)) +* Fix for `rescale` preventing `Decimal::ZERO` to be rescaled to an invalid precision ([#566](https://github.com/paupino/rust-decimal/pull/566)) + +### Credit + +Thank you to [@c410-f3r](https://github.com/c410-f3r) for your diligent help adding features to this library! + +## 1.27.0 + +### Added + +* Added `Copy`/`Clone` traits for `rkvy::Archive` ([#539](https://github.com/paupino/rust-decimal/pull/539)) +* Support precision for scientific notation formatting ([#555](https://github.com/paupino/rust-decimal/pull/555), [#557](https://github.com/paupino/rust-decimal/pull/557)) + +### Fixed + +* Fixed edge case parsing of strings whereby it would incorrectly error instead of round ([#541](https://github.com/paupino/rust-decimal/pull/541)) + +### Changed + +* Update diesel feature to use release version ([#546](https://github.com/paupino/rust-decimal/pull/546)) +* Updated documentation to include `cargo-edit` instructions ([#538](https://github.com/paupino/rust-decimal/pull/538)) +* Start using new feature resolver for cargo dependencies ([#551](https://github.com/paupino/rust-decimal/pull/551)) +* Optional serde deserialization avoids using `deserialize_any` except in explicit use cases ([#558](https://github.com/paupino/rust-decimal/pull/558)) + +### Credit + +This release wouldn't have been possible without the help of the community. Special call out and thank you to [@icy-ux](https://github.com/icy-ux), [@CAD97](https://github.com/CAD97), [@nicksenger](https://github.com/nicksenger), +[@c410-f3r](https://github.com/c410-f3r) and [@yongqli](https://github.com/yongqli). + +## 1.26.1 + +### Fixed + +* Fixes `docs.rs` [failing to compile](https://docs.rs/crate/rust_decimal/1.26.0/builds/605522) due to mutually exclusive `diesel1` and `diesel2` features. The previous `compile_error!` + was in fact an [anti-pattern](https://doc.rust-lang.org/cargo/reference/features.html#mutually-exclusive-features). Consequently, + if both `diesel1` and `diesel2` features are specified then it will favor `diesel2`. This helps prevent any conflict scenarios + and ultimately resolves the downstream `docs.rs` issue. + +## 1.26.0 + +### Added + +* `serde-with-*` features now support `Option<T>` variants ([#524](https://github.com/paupino/rust-decimal/pull/524)). +* Implementation support for `core::iter::Product` ([#525](https://github.com/paupino/rust-decimal/pull/525)). +* Diesel `2.0.0-rc.1` support via `db-diesel2-mysql` and `db-diesel2-postgres` features. ([#533](https://github.com/paupino/rust-decimal/pull/533) and ([#532](https://github.com/paupino/rust-decimal/pull/532))). + +### Fixed + +* Fixes a silent overflow in `from_str` when parsing `Decimal::MAX` with a fractional portion. ([#530](https://github.com/paupino/rust-decimal/pull/530)). + +### Credit + +Thank you to [@Guara92](https://github.com/Guara92) who made the initial `diesel2` PR which made parallel implementation a breeze! Also, once again, thank you +to [@c410-f3r](https://github.com/c410-f3r) for implementing the missing `Product` trait! + +## 1.25.0 + +### Added + +* Expands `rand` support to implement `SampleUniform` to allow for `rng.gen_range` calls ([#519](https://github.com/paupino/rust-decimal/pull/519)) +* Adds [`rkyv` framework](https://github.com/rkyv/rkyv) support for zero-cost (de)serialization ([#520](https://github.com/paupino/rust-decimal/pull/520)) + +A big thank you to [@lukesneeringer](https://github.com/lukesneeringer) and [@dovahcrow](https://github.com/dovahcrow) for your help building +features for this release. + +## 1.24.0 + +* Introduces new feature `rand` which allows for random number generation within `rust_decimal` ([#517](https://github.com/paupino/rust-decimal/pull/517)). +* Upgrade to Rust 2021 and remove unnecessary dependencies to improve build speed ([#513](https://github.com/paupino/rust-decimal/pull/513), [#516](https://github.com/paupino/rust-decimal/pull/516)) +* Some test scoping improvements to ensure we capture as many test combinations as possible ([#503](https://github.com/paupino/rust-decimal/pull/503), [#504](https://github.com/paupino/rust-decimal/pull/504)). + +A big thank you to [@c410-f3r](https://github.com/c410-f3r) for their contributions to this release! + + +## 1.23.1 + +Fixes issue with `no_std` compatability introduced in `1.23.0` ([#501](https://github.com/paupino/rust-decimal/pull/501)). + +## 1.23.0 + +* Add `BorschSchema` support for the `borsh` feature ([#498](https://github.com/paupino/rust-decimal/pull/498)). +* Implement `TryFrom` for Decimal primitive types ([#493](https://github.com/paupino/rust-decimal/pull/493)). +* Fix `to_i64` to support `i64::MIN` ([#496](https://github.com/paupino/rust-decimal/pull/496)). +* Implement `Inv` from `num_traits` ([#495](https://github.com/paupino/rust-decimal/pull/495)). +* Some minor housekeeping tasks ([#487](https://github.com/paupino/rust-decimal/pull/487), [#490](https://github.com/paupino/rust-decimal/pull/490)). + +Thank you [@turion](https://github.com/turion), [@arthurprs](https://github.com/arthurprs) and [@jnitard](https://github.com/jnitard) for your help with this release. + + +## 1.22.0 + +* Add support for `borsh` serialization/deserialization ([#478](https://github.com/paupino/rust-decimal/pull/478)). +* Fixes an issue with `serde-with-str` where it wasn't behaving the same as `serde-str` when using `bincode` ([#484](https://github.com/paupino/rust-decimal/pull/484)). +* Added `must_use` to `is_sign_negative` and `unpack` ([#482](https://github.com/paupino/rust-decimal/pull/482)). +* Minor documentation fixes. + +Thank you [@jkbpvsc](https://github.com/jkbpvsc) for your help in this release. + +## 1.21.0 + +* Saturating op variants have been added: `saturating_add`, `saturating_sub` and `saturating_mul` ([#464](https://github.com/paupino/rust-decimal/pull/464)) +* Fixes issue with `log10` values `0 < x < 1` ([#469](https://github.com/paupino/rust-decimal/pull/469)) +* Various documentation fixes/cleanup. + +Thank you [@c410-f3r](https://github.com/c410-f3r) for your work in this release! + +## 1.20.0 + +* Additional fuzz testing for deserialize ([#452](https://github.com/paupino/rust-decimal/pull/452)) +* Documentation fix for rounding strategy ([#458](https://github.com/paupino/rust-decimal/pull/458)) +* `from_str` is now over 4x faster, utilizing const generics and TCO ([#456](https://github.com/paupino/rust-decimal/pull/456)) +* Fixed `from_str` issue with rounding issues when too many digits in source string. ([#453](https://github.com/paupino/rust-decimal/issues/453)) +* New `serde-with` functionality for greater configurability when using `serde` ([#459](https://github.com/paupino/rust-decimal/pull/459)) +* Various maintenance tasks ([#460](https://github.com/paupino/rust-decimal/pull/460)) + +This is truly a collaborative release and has some significant contributions from the community. A huge thank +you to everyone involved: + +* [@chris-cantor](https://github.com/chris-cantor) +* [@TradingTomatillo](https://github.com/TradingTomatillo) +* [@c410-f3r](https://github.com/c410-f3r) +* [@ShigotoMitame](https://github.com/ShigotoMitame) +* [@luke-brown](https://github.com/luke-brown) + +## 1.19.0 + +This is a minor dot release and contains library updates ([#334](https://github.com/paupino/rust-decimal/pull/334)) and +expanded scope in `prelude` to include `num_traits::Signed` ([#450](https://github.com/paupino/rust-decimal/pull/450)). +While subtle, it also expands the way towards some bigger improvements including variable precision. + +Of a larger note, the minimum Rust compiler version has been updated so we can start utilizing various compiler features +(such as const generics). The miminum compiler version is now `1.51.0`. + +Thank you to [@c410-f3r](https://github.com/c410-f3r) and [@jracollins](https://github.com/jracollins) for your contributions +to this release. + +## 1.18.0 + +* Fixes integer handling for `to_f64` when scale is set to `0`. [#443](https://github.com/paupino/rust-decimal/pull/443). + Thank you [@anatols](https://github.com/anatols) for creating this fix. +* Add support for Rocket Forms using the feature flag `rocket-traits`. [#445](https://github.com/paupino/rust-decimal/pull/445). + Thank you [@Misterio77](https://github.com/Misterio77) for putting this together. +* Add Diesel MySQL support, enabled by the feature flag `db-diesel-mysq`. [#446](https://github.com/paupino/rust-decimal/pull/446). + +## 1.17.0 + +* Fixes panic when display precision was specified greater than 30. [#428](https://github.com/paupino/rust-decimal/issues/428). +* Binds `deserialize` so that invalid scale values cannot be set. This may be a breaking change for some users leveraging + this in a `const` function. [#428](https://github.com/paupino/rust-decimal/issues/428). +* Fixes an issue in `round_sf` whereby integral values that started with a 0 or 1 would output incorrect rounded numbers. [#430](https://github.com/paupino/rust-decimal/issues/430), [#437](https://github.com/paupino/rust-decimal/issues/437). +* Adds `from_f32_retain` and `from_f64_retain` functions which allow parsing a floating point number and retaining any excess precision. [#438](https://github.com/paupino/rust-decimal/issues/438). + +## 1.16.0 + +* Implement `sin`/`cos`/`tan` functions which are enabled within the `maths` feature. [#413](https://github.com/paupino/rust-decimal/pull/413). +* Implement `round_sf` and `round_sf_with_strategy` to enable significant figure rounding. [#421](https://github.com/paupino/rust-decimal/pull/421) +* Remove unnecessary `std` feature from `arrayvec` usage [#417](https://github.com/paupino/rust-decimal/pull/417). +* Adhoc benchmarking and fuzz tooling improvements [#412](https://github.com/paupino/rust-decimal/pull/412), + [#415](https://github.com/paupino/rust-decimal/pull/415), [#416](https://github.com/paupino/rust-decimal/pull/416), + [#418](https://github.com/paupino/rust-decimal/pull/418). + +Thank you to [@c410-f3r](https://github.com/c410-f3r) for all your help with fuzz and benchmarking improvements! + +## 1.15.0 + +A minor bug and feature release which adds a couple of new functions as well as cleans up some documentation: + +* Support for serializing to float without converting to float via the `serde-arbitrary-precision` feature. + [#402](https://github.com/paupino/rust-decimal/issues/402). Thanks [@JamesHinshelwood](https://github.com/JamesHinshelwood)! + for finding and fixing this! +* Add `log10` support to the `maths` feature. Please note that `ln` and `log10` will now panic on invalid input since both + functions have a `checked_*` equivalent. This is the preferred approach going forward, however if you would like to re-enable + the previous behavior please use the `maths-nopanic` feature. [#397](https://github.com/paupino/rust-decimal/issues/397). +* Added further constants to the `Decimal` library including `TWO`, `TEN`, `ONE_HUNDRED`, `ONE_THOUSAND`, and `NEGATIVE_ONE`. + [#400](https://github.com/paupino/rust-decimal/issues/400). +* Fixes serialization issue for `-0` whereby `-` would be output [#406](https://github.com/paupino/rust-decimal/pull/406). Thanks + [@edwardycl](https://github.com/edwardycl)! +* Fixes float rounding before return in `to_f64`. [#401](https://github.com/paupino/rust-decimal/issues/401). +* Added `BUILD.md` file to help people get set up with Rust Decimal locally and cleaned up some + documentation examples. + +## 1.14.3 + +Fixes an issue [#398](https://github.com/paupino/rust-decimal/issues/398) where `Decimal::ZERO.ln()` would panic rather than returning `Decimal::ZERO`. This +aligns the behavior with calling `ln` on negative decimals. + +Thank you to [@SebRollen](https://github.com/SebRollen) for finding and fixing this. + +## 1.14.2 + +Fixes an overflow issue during division under some specific circumstances. ([#392](https://github.com/paupino/rust-decimal/issues/392)) + +## 1.14.1 + +A bug fix release following on from `1.14.0`: + +* Fixes an issue whereby in some cases when subtracting a 64 bit `Decimal` a negating overflow would occur during underflow. + [#384](https://github.com/paupino/rust-decimal/issues/384). Thank you to [@c410-f3r](https://github.com/c410-f3r) for finding + this as part of fuzz testing. +* Fixes an issue with `exp` whereby negative values lost accuracy due to inability to converge. + [#378](https://github.com/paupino/rust-decimal/issues/378). Thank you to [@schungx](https://github.com/schungx) for + finding this and proposing a fix. +* Fixes some documentation issues. + +## 1.14.0 + +* Added `checked_exp` and `checked_norm_pdf` functions [#375](https://github.com/paupino/rust-decimal/pull/375). +* Fixes bug in division under certain circumstances whereby overflow would occur during rounding. [#377](https://github.com/paupino/rust-decimal/pull/377) +* Documentation improvements + +Thank you to [@falsetru](https://github.com/falsetru), [@schungx](https://github.com/schungx) and [@blasrodri](https://github.com/blasrodri) for your +help with this release! + +## 1.13.0 + +This is a minor update to the library providing a few new features and one breaking change (I'm not using semver properly here +sorry). + +* `#[must_use]` added to functions to provide additional compiler hints. +* `try_from_i128_with_scale` function added to safely handle `i128` overflow errors. +* New `c-repr` feature added which will ensure that `#[repr(C)]` is used on the `Decimal` type. Thanks [@jean-airoldie](https://github.com/jean-airoldie). +* Small improvements to `from_scientific`. It now supports a wider range of values as well has slightly faster performance. +* Support for negative and decimal `pow` functions. This is *breaking* since `powi(u64)` has been renamed to `powi(i64)`. If you want to + continue using `u64` arguments then please use `powu(u64)`. The fractional functions should be considered experimental for the time being + and may have subtle issues that still need ironing out. Functions are now: + * `powi`, `checked_powi` - When the exponent is a signed integer. + * `powu`, `checked_powu` - When the exponent is an unsigned integer. + * `powf`, `checked_powf` - When the exponent is a floating point number. Please note, numbers with a fractional component + will use an approximation function. + * `powd`, `checked_powd` - When the exponent is a `Decimal`. Please note, numbers with a fractional component will use + an approximation function. + +## 1.12.4 + +Adds `num_traits::One` back to `rust_decimal::prelude` to prevent unnecessary downstream dependency breakages. Thanks [@spearman](https://github.com/spearman). + +## 1.12.3 + +Fixes an issue [#361](https://github.com/paupino/rust-decimal/issues/361) when rounding a small number towards zero. + +## 1.12.2 + +Fixes small regression whereby `0 - 0` was producing `-0`. Thank you [@KonishchevDmitry](https://github.com/KonishchevDmitry) for +providing a swift fix ([#356](https://github.com/paupino/rust-decimal/pull/356)). + +## 1.12.1 + +Added `num_traits::Zero` back to `rust_decimal::prelude` to prevent unnecessary downstream dependency breakages. + +## 1.12.0 + +This version releases faster operation support for `add`, `sub`, `cmp`, `rem` and `mul` to match the renewed `div` strategy. +It does this by leveraging 64 bit support when it makes sense, while attempting to still keep 32 bit optimizations in place. +To ensure correct functionality, thousands more tests were included to cover a wide variety of different scenarios +and bit combinations. Compared to previous operations, we get the following speed improvements: +* `add` - up to 2.2x faster +* `div` - up to 428x faster +* `mul` - up to 1.8x faster +* `rem` - up to 1.08x faster +* `sub` - up to 2.5x faster + +Of course, if old functionality is desired, it can be re-enabled by using the `legacy-ops` feature. + +Other improvements include: +* Remove unnecessary `String` allocation when parsing a scientific number format. Thanks [@thomcc](https://github.com/thomcc) for the fix [#350](https://github.com/paupino/rust-decimal/pull/350). +* Fixes overflow bug with `sqrt` when using the smallest possible representable number. [#349](https://github.com/paupino/rust-decimal/pull/349). +* Some minor optimizations in the `maths` feature. Future work will involve speeding up this feature by keeping operations + in an internal format until required. +* Added associated constants for `MIN`, `MAX` and `ZERO`. Deprecated `min_value()` and `max_value()` in favor of these new + constants. +* `-0` now gets corrected to `0`. During operation rewrite I needed to consider operations such as `-0 * 2` - in cases like + this I opted towards `0` always being the right number and `-0` being superfluous (since `+0 == -0`). Consequently, parsing + `-0` etc _in general_ will automatically be parsed as `0`. Of course, this _may_ be a breaking change so if this + functionality is required then please create an issue with the use case described. +* Small breaking change by renaming `is_negative` to `negative` in `UnpackedDecimal`. +* Some internal housekeeping was made to help make way for version 2.0 improvements. + +## 1.11.1 + +This is a documentation only release and has no new functionality included. Thank you [@c410-f3r](https://github.com/c410-f3r) for the documentation fix. + +## 1.11.0 + +This release includes a number of bug fixes and ergonomic improvements. + +* Mathematical functionality is now behind a feature flag. This should help optimize library size when functions such as + `log` and `pow` are not required (e.g. simple financial applications). Mathematical functionality is now behind the `maths` + feature flag. [#321](https://github.com/paupino/rust-decimal/pull/321). +* Numerous test coverage improvements to ensure broader coverage. [#322](https://github.com/paupino/rust-decimal/pull/322), + [#323](https://github.com/paupino/rust-decimal/pull/323) +* Various documentation improvements. [#324](https://github.com/paupino/rust-decimal/pull/324), [#342](https://github.com/paupino/rust-decimal/pull/342) +* Fixes `u128` and `i128` parsing. [#332](https://github.com/paupino/rust-decimal/pull/332) +* Implemented `Checked*` traits from `num_traits`. [#333](https://github.com/paupino/rust-decimal/pull/333). Thank you + [@teoxoy](https://github.com/teoxoy) +* Added `checked_powi` function to `maths` feature. [#336](https://github.com/paupino/rust-decimal/pull/336) +* Updated `from_parts` to avoid accidental scale clobbering. [#337](https://github.com/paupino/rust-decimal/pull/337) +* Added support for the `Arbitrary` trait for `rust-fuzz` support. This is behind the feature flag `rust-fuzz`. + [#338](https://github.com/paupino/rust-decimal/pull/338) +* Fixes `e^-1` returning an incorrect approximation. [#339](https://github.com/paupino/rust-decimal/pull/339) +* Revamp of `RoundingStrategy` naming and documentation ([#340](https://github.com/paupino/rust-decimal/pull/340)). + The old naming was ambiguous in interpretation - the new naming + convention follows guidance from other libraries to ensure an easy to follow scheme. The `RoundingStrategy` enum now + includes: + * `MidpointNearestEven` (previously `BankersRounding`) + * `MidpointAwayFromZero` (previously `RoundHalfUp`) + * `MidpointTowardZero` (previously `RoundHalfDown`) + * `ToZero` (previously `RoundDown`) + * `AwayFromZero` (previously `RoundUp`) + * `ToNegativeInfinity` - new rounding strategy + * `ToPositiveInfinity` - new rounding strategy +* Added function to access `mantissa` directly. [#341](https://github.com/paupino/rust-decimal/pull/341) +* Added a feature to `rust_decimal_macros` to make re-exporting the macro from a downstream crate more approachable. + Enabling the `reexportable` feature will ensure that the generated code doesn't require `rust_decimal` to be exposed at + the root level. [#343](https://github.com/paupino/rust-decimal/pull/343) + +## 1.10.3 + +* Fixes bug in bincode serialization where a negative symbol causes a buffer overflow (#317). + +## 1.10.2 + +* Fixes a bug introduced in division whereby certain values when using a large remainder cause an incorrect results (#314). + +## 1.10.1 + +* Fixes bug introduced in `neg` whereby sign would always be turned negative as opposed to being correctly negated. + +Thank you [KonishchevDmitry](https://github.com/KonishchevDmitry) for finding and fixing this. + +## 1.10.0 + +* Upgrade `postgres` to `0.19` and `tokio-postgres` to `0.7`. +* Faster `serde` serialization by preventing heap allocation. +* Alternative division algorithm which provides significant speed improvements. The new algorithms are enabled by default, + but can be disabled with the feature: `legacy-ops`. Further work to improve other operations will + be made available in future versions. +* Add `TryFrom` for `f32`/`f64` to/from Decimal + +Thank you for the the community help and support for making this release happen, in particular: +[jean-airoldie](https://github.com/jean-airoldie), [gakonst](https://github.com/gakonst), [okaneco](https://github.com/okaneco) and +[c410-f3r](https://github.com/c410-f3r). + +## 1.9.0 + +* Added arbitrary precision support for `serde_json` deserialization (#283) +* Add `u128` and `i128` `FromPrimitive` overrides to prevent default implementation kicking in. Also adds default `From` + interceptors to avoid having to use trait directly. (#282) +* Alias `serde-bincode` as `serde-str` to make usage clearer (#279) +* Adds scientific notation to format strings via `UpperExp` and `LowerExp` traits. (#271) +* Upgrade `tokio-postgres` and `postgres` libraries. +* Add statistical function support for `powi`, `sqrt`, `exp`, `norm_cdf`, `norm_pdf`, `ln` & `erf` (#281, #287) +* Allow `sum` across immutable references (#280) + +Thank you for all the community help and support with this release, in particular [xilec](https://github.com/xilec), +[remkade](https://github.com/remkade) and [Anders429](https://github.com/Anders429). + +## 1.8.1 + +Make `std` support the default to prevent breaking downstream library dependencies. To enable `no_std` support please set +default features to false and opt-in to any required components. e.g. + +``` +rust_decimal = { default-features = false, version = "1.8.0" } +``` + + +## 1.8.0 + +* `no_std` support added to Rust Decimal by default. `std` isn't required to use Rust Decimal, however can be enabled by + using the `std` feature. [#190](https://github.com/paupino/rust-decimal/issues/190) +* Fixes issue with Decimal sometimes losing precision through `to_f64`. [#267](https://github.com/paupino/rust-decimal/issues/267). +* Add `Clone`, `Copy`, `PartialEq` and `Eq` derives to `RoundingStrategy`. +* Remove Proc Macro hack due to procedural macros as expressions being stabilized. +* Minor optimizations + +Thank you to [@c410-f3r](https://github.com/c410-f3r), [@smessmer](https://github.com/smessmer) and [@KiChjang](https://github.com/KiChjang). + +## 1.7.0 + +* Enables `bincode` support via the feature `serde-bincode`. This provides a long term fix for a regression + that was introduced in version `0.6.5` (tests now cover this case!). [Issue 43](https://github.com/paupino/rust-decimal/issues/43). +* Fixes issue where `rescale` on zero would not have an affect. This was due to an early exit condition which failed to + set the new scale. [Issue 253](https://github.com/paupino/rust-decimal/issues/253). +* Add `min` and `max` functions, similar to what `f32` and `f64` provide. Thank you [@michalsieron](https://github.com/michalsieron). +* Updates documentation for `is_sign_positive` and `is_sign_negative` to specify that the sign bit is being checked. + +Please note: feature naming conventions have been modified, however backwards compatible aliases have been created where +necessary. It's highly recommended that you move over to the new naming conventions as these aliases may be removed at a +later date. + +## 1.6.0 + +* Fixes issue with PostgreSQL conversions whereby certain inputs would cause unexpected + outputs. [Issue 241](https://github.com/paupino/rust-decimal/issues/241). +* Fixes issue with `from_str_radix` whereby rounding logic would kick in too early, + especially with radix less than 10. [Issue 242](https://github.com/paupino/rust-decimal/issues/242). +* Fixes issue whereby `from_str` (implicity `from_str_radix`) would panic when there was overflow + and overflow significant digit was < 5. [Issue 246](https://github.com/paupino/rust-decimal/issues/246). +* Make `bytes` and `byteorder` optional since they're only used in the `postgres` feature and tests. +* Fix edge case in `from_i128_with_scale` when `i128::MIN` was provided. + +Thank you to [@serejkaaa512](https://github.com/serejkaaa512), [@AbsurdlySuspicious](https://github.com/AbsurdlySuspicious) and [@0e4ef622]((https://github.com/0e4ef622)) for your contributions! + +## 1.5.0 + +* Added additional `RoundStrategy` abilities: `RoundUp` to always round up and `RoundDown` to always round down. +* Updated prelude to include expected structs and traits by default. + +Special thank you to [@jean-airoldie](https://github.com/jean-airoldie) for adding the additional rounding strategies and to [@pfrenssen](https://github.com/pfrenssen) for fixing an +issue in the README. + +## 1.4.1 + +* Performance improvements for `to_f64` when using a scale > 0. + +Special thank you to [@hengchu](https://github.com/hengchu) who discovered and resolved the issue! + +## 1.4.0 + +* Allow uppercase "E" in scientific notation. +* Allow scientific notation in `dec!` macro. +* Deprecate `set_sign` and replace with `set_sign_positive` and `set_sign_negative`. This is intended + to improve the readability of the API. +* Fixes precision issue when parsing `f64` values. The base 2 mantissa of the float was assuming guaranteed accuracy + of 53 bit precision, however 52 bit precision is more accurate (`f64` only). +* Removes deprecated usage of `Error::description`. + +## 1.3.0 + +* Replace `num` dependency with `num_trait` - implemented `Signed` and `Num` traits. + +## 1.2.1 + +* Fixes issue whereby overflow would occur reading from PostgreSQL with high precision. The library now + handles this by rounding high precision numbers as they're read as opposed to crashing (similar to other + underflow situations e.g. 1/3). + +## 1.2.0 + +* Retain trailing zeros from PostgreSQL. This ensures that the scale is maintained when serialized into the Decimal type. +* Fixes issue where -0 != 0 (these are now equivalent - thank you @hengchu for discovering). +* Improve hashing function so that the following property is true: `k1 == k2 -> hash(k1) == hash(k2)` +* Update normalize function so that -0 normalizes to 0. + +Special thanks to @hathawsh for their help in this release! + +## 1.1.0 + +* Update to Postgres 0.17 and add postgres async/await support via `tokio-pg` +* Added option for serializing decimals as float via `serde-float` + +Special thanks to @pimeys and @kaibyao! + +## 1.0.3 + +Updates dependencies to prevent build issues. + +## 1.0.2 + +Bug fix release: + +* Fixes issue where scaling logic produced incorrect results when one arm was a high precision zero. Thank you @KonishchevDmitry! + +## 1.0.1 + +Bug fix release: + +* Fixes issue where `ToSql` was incorrectly calculating weight when whole portion = numeric portion. +* Fixes issue where `Decimal::new` incorrectly handled `i64::max_value()` and `i64::min_value()`. +* Fixes issue where `rem` operation incorrectly returned results when `scale` was required. + +## 1.0.0 + +This release represents the start of semantic versioning and allows the library to start making fundamental improvements under +the guise of V2.0. Leading up to that I expect to release 1.x versions which will include adding +various mathematical functions such as `pow`, `ln`, `log10` etc. + +Version `1.0.0` does come with some new features: + +* Checked Operations! This implements `checked_add`, `checked_sub`, `checked_mul`, `checked_div` and `checked_rem`. +* Fixes overflow from `max_value()` and `min_value()` for `i32` and `i64`. +* Minor documentation improvements and test coverage. + +Special thanks to @0e4ef622 for their help with this release! + +## 0.11.3 + +* Add prelude to help num trait inclusion (`use rust_decimal::prelude::*`) +* Add `Default` trait to the library. This is equivalent to using `Decimal::zero()` +* Added assignment operators for references. + +Special thanks to @jean-airoldie for his help with this release! + +## 0.11.2 + +* Fall back to `from_scientific` when `from_str` fails during deserialization. Thanks @mattjbray! +* Added basic `Sum` trait implementation + +## 0.11.1 + +* Fixes a bug in `floor` and `ceil` where negative numbers were incorrectly handled. + +## 0.11.0 + +* Macros are now supported on stable. This does use a [hack](https://github.com/dtolnay/proc-macro-hack) for the meantime +so due diligence is required before usage. +* Fixes issue when parsing strings where an underscore preceded a decimal point. +* `const_fn` support via a feature flag. In the future this will be the default option however in order to support older +compiler versions is behind a feature flag. + +## 0.10.2 + +* Macros (nightly) now output structural data as opposed to serialized data. This is fully backwards compatible and results in some minor performance improvements. Also, removed feature gate so that it can be compiled in stable. +* Fixes a string parsing bug when given highly significant numbers that require rounding. + +## 0.10.1 + +* Bumped dependencies to remove some legacy serialization requirements. + +## 0.10.0 + +Special thanks to @xilec, @snd and @AndrewSpeed for their help with this release. + +* New rounding strategies introduced via `round_dp_with_strategy`. Previously default rounding support used bankers rounding by default whereas now you can choose to round the half way point either up or down. +* PostgreSQL write performance improved so that it is at least 3 times faster than the previous implementation. +* `Debug` trait now outputs the actual decimal number by default to make it more useful within consuming libraries (e.g. `criterion.rs`). To get something similar to the previous functionality you can use the `unpack` argument - this is likely for core `rust-decimal` library maintainers. +* Various other performance improvements for common operations such as `rescale`, `sub` and `div`. + +## 0.9.1 + +* Performance optimization for `add`. + +## 0.9.0 + +* Introduces the `Neg` trait to support the ability to use `-decimal_variable`. +* Fixes bug with underflow on addition. + +## 0.8.1 + +This release updates the published documentation only and is a no-op for functionality. + +## 0.8.0 + +* Introduces `from_scientific` allowing parsing of scientific notation into the Decimal type. +* Fixes a bug when formatting a number with a leading zero's. + +## 0.7.2 + +* Fixes bug in `rescale` whereby scaling which invoked rounding incorrectly set the new scale for the left/right sides. + +## 0.7.1 + +* Fixes bug in `cmp` whereby two negatives would return an incorrect result. +* Further documentation examples +* Small improvements in division logic +* New `abs`, `floor` and `ceil` functions. + +## 0.7.0 + +This is a minor version bump as we slowly build our way towards 1.0. Thank you for everyone's support and help as we get there! This has a few notable changes - also introducing a few new interfaces which is the reason for the version bump: + +* `from_parts` function to allow effective creation of `Decimal`'s without requiring binary serialization. An example of this benefit is with the lazy static group initializers for Postgres. +* `normalize` function to allow stripping trailing zero's easily. +* `trunc` function allows truncation of a number without any rounding. This effectively "truncates" the fractional part of the number. +* `fract` function returns the fractional part of the number without the integral. +* Minor improvements in some iterator logic, utilizing the compiler for further optimizations. +* Fixes issue in string parsing logic whereby `_` would cause numbers to be incorrectly identified. +* Many improvements to `mul`. Numbers utilizing the `lo` portion of the decimal only will now be shortcut and bigger numbers will now correctly overflow. True overflows will still panic, however large underflows will now be rounded as necessary as opposed to panicing. +* `Hash` was implemented by convention in `0.6.5` however is reimplemented explicitly in `0.7.0` for effectiveness. +* PostgreSQL read performance improved by pre-caching groups and leveraging `normalize` (i.e. avoiding strings). Further optimizations can be made in write however require some `div` optimizations first. +* Added short circuit write improvement for zero in PostgreSQL writes. +* Benchmarks are now recorded per build so we can start tracking where slow downs have occurred. This does mean there is a performance hit on Travis builds however hopefully the pay off will make it worthwhile. + +## 0.6.5 + +Fixes issue with rescale sometimes causing a silent overflow which led to incorrect results during addition, subtraction and compare. Consequently Decimal now rounds the most significant number so that these operations work successfully. + +In addition, Decimal now derive's the `Hash` trait so that it can be used for indexing. + +## 0.6.4 + +Fixes silent overflow errors when parsing highly significant strings. `from_str` will now round in these scenario's, similar to oleaut32 behavior. + +## 0.6.3 + +Fixes a regression in ordering where by different scales would be rescaled towards losing precision instead of increasing precision. Have added numerous test suites to help cover more issues like this in the future. +Also fixes an issue in parsing invalid strings whereby the precision exceeded our maximum precision. Previously, this would work with unintended results however this now returns an Error returned from `FromStr`. + +## 0.6.2 + +Fixes an issue with division of rational numbers allowing results greater than `MAX_PRECISION`. This would ultimately cause issues for future operations on this number. +In addition, in some cases transitive operations would not be equal due to overflow being lost. + +## 0.6.1 + +This minor release is purely to expose `rust_decimal_macros` for use on the nightly channel. Documentation has been updated accordingly. + +## 0.6.0 + +This release has a few major changes to the internal workings of the `Decimal` implementation and consequently comes with a number of performance improvements. + +* Floats can now be parsed into a `Decimal` type using `from_f32` and `from_f64`. +* `add`, `sub`, `mul` run roughly 1500% faster than before. +* `div` run's roughly 1000% faster than before with room for future improvement. +* Also get significant speed improvements with `cmp`, `rescale`, `round_dp` and some string manipulations. +* Implemented `*Assign` traits for simpler usage. +* Removed `BigInt` and `BigUint` as being intermediary data types. + +## 0.5.2 + +Minor bug fix to prevent a `panic` from overflow during comparison of high significant digit decimals. + +## 0.5.1 + +Minor bux fix to prevent `panic` upon parsing an empty string. + +## 0.5.0 + +* Removes postgres from default feature set. +* `bincode` support for serde +* Better support for format strings +* Benchmarks added to tests + +## 0.4.2 + +Fixes bug in `cmp` whereby negative's were not being compared correctly. + +## 0.4.1 + +Minor bug fix to support creating negative numbers using the default constructor. + +## 0.4.0 + +This release is a stylistic cleanup however does include some minor changes that may break existing builds. + +### Changed +* Serde is now optional. You can enable Serde support within `features` using the keyword `serde`. +* Serde now returns errors on invalid input as opposed to `0`. +* `f64` conversion support has been added. +* Update Postgres dependency to use v0.15. + +## 0.3.1 + +This is a documentation release that should help with discoverability and usage. + +## 0.3.0 + +### Changed +* Removed trait `ToDecimal` and replaced with builtin [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) trait ([`#12`](https://github.com/paupino/rust-decimal/pull/12)) diff --git a/third_party/rust/rust_decimal/CODE_OF_CONDUCT.md b/third_party/rust/rust_decimal/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..c86080edf9 --- /dev/null +++ b/third_party/rust/rust_decimal/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at paul@form1.co.nz. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/third_party/rust/rust_decimal/CONTRIBUTING.md b/third_party/rust/rust_decimal/CONTRIBUTING.md new file mode 100644 index 0000000000..fc8aa310ca --- /dev/null +++ b/third_party/rust/rust_decimal/CONTRIBUTING.md @@ -0,0 +1,40 @@ +# Contributing to Rust Decimal + +Rust Decimal welcomes contributions from everyone. Here are the guidelines if you are +thinking of helping us: + +## Contributions + +Contributions to Rust Decimal or its dependencies should be made in the form of GitHub +pull requests. Each pull request will be reviewed by a core contributor +(someone with permission to land patches) and either landed in the main tree or +given feedback for changes that would be required. All contributions should +follow this format, even those from core contributors. + +Should you wish to work on an issue, please claim it first by commenting on +the GitHub issue that you want to work on it. This is to prevent duplicated +efforts from contributors on the same issue. + +## Pull Request Checklist + +- Branch from the master branch and, if needed, rebase to the current master + branch before submitting your pull request. If it doesn't merge cleanly with + master you may be asked to rebase your changes. + +- If your patch is not getting reviewed or you need a specific person to review + it, you can @-reply a reviewer asking for a review in the pull request or inside a + comment. + +- Add tests relevant to the fixed bug or new feature. + +## Conduct + +In all Rust Decimal related forums, we follow the [Rust Code of +Conduct](https://www.rust-lang.org/conduct.html). For escalation or moderation of +issues, please contact Paul (paul@form1.co.nz) instead of the Rust +moderation team. + +## Communication + +Opening tickets on the +[paupino/rust-decimal](https://github.com/paupino/rust-decimal) project is the preferred method of communication.
\ No newline at end of file diff --git a/third_party/rust/rust_decimal/Cargo.toml b/third_party/rust/rust_decimal/Cargo.toml new file mode 100644 index 0000000000..1bbbd9ba57 --- /dev/null +++ b/third_party/rust/rust_decimal/Cargo.toml @@ -0,0 +1,249 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.60" +name = "rust_decimal" +version = "1.28.1" +authors = ["Paul Mason <paul@form1.co.nz>"] +build = "build.rs" +exclude = ["tests/generated/*"] +description = "Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations." +documentation = "https://docs.rs/rust_decimal/" +readme = "./README.md" +keywords = [ + "decimal", + "financial", + "fixed", + "precision", + "number", +] +categories = [ + "science", + "mathematics", + "data-structures", +] +license = "MIT" +repository = "https://github.com/paupino/rust-decimal" + +[package.metadata.docs.rs] +all-features = true + +[[bench]] +name = "comparison" +path = "benches/comparison.rs" +harness = false + +[dependencies.arbitrary] +version = "1.0" +optional = true +default-features = false + +[dependencies.arrayvec] +version = "0.7" +default-features = false + +[dependencies.borsh] +version = "0.10.0" +optional = true +default-features = false + +[dependencies.bytecheck] +version = "0.6" +optional = true +default-features = false + +[dependencies.byteorder] +version = "1.0" +optional = true +default-features = false + +[dependencies.bytes] +version = "1.0" +optional = true +default-features = false + +[dependencies.diesel1] +version = "1.0" +optional = true +default-features = false +package = "diesel" + +[dependencies.diesel2] +version = "2.0" +optional = true +default-features = false +package = "diesel" + +[dependencies.num-traits] +version = "0.2" +features = ["i128"] +default-features = false + +[dependencies.postgres] +version = "0.19" +optional = true +default-features = false + +[dependencies.rand] +version = "0.8" +optional = true +default-features = false + +[dependencies.rkyv] +version = "0.7" +features = [ + "size_32", + "std", +] +optional = true +default-features = false + +[dependencies.rocket] +version = "0.5.0-rc.1" +optional = true +default-features = false + +[dependencies.serde] +version = "1.0" +optional = true +default-features = false + +[dependencies.serde_json] +version = "1.0" +optional = true +default-features = false + +[dependencies.tokio-postgres] +version = "0.7" +optional = true +default-features = false + +[dev-dependencies.bincode] +version = "1.0" +default-features = false + +[dev-dependencies.bytes] +version = "1.0" +default-features = false + +[dev-dependencies.criterion] +version = "0.4.0" +default-features = false + +[dev-dependencies.csv] +version = "1" + +[dev-dependencies.futures] +version = "0.3" +default-features = false + +[dev-dependencies.rand] +version = "0.8" +features = ["getrandom"] +default-features = false + +[dev-dependencies.serde] +version = "1.0" +features = ["derive"] +default-features = false + +[dev-dependencies.serde_json] +version = "1.0" + +[dev-dependencies.tokio] +version = "1.0" +features = [ + "macros", + "rt-multi-thread", + "test-util", +] +default-features = false + +[dev-dependencies.version-sync] +version = "0.9" +features = [ + "html_root_url_updated", + "markdown_deps_updated", +] +default-features = false + +[features] +c-repr = [] +db-diesel-mysql = ["db-diesel1-mysql"] +db-diesel-postgres = ["db-diesel1-postgres"] +db-diesel1-mysql = [ + "diesel1/mysql", + "std", +] +db-diesel1-postgres = [ + "diesel1/postgres", + "std", +] +db-diesel2-mysql = [ + "diesel2/mysql", + "std", +] +db-diesel2-postgres = [ + "diesel2/postgres", + "std", +] +db-postgres = [ + "byteorder", + "bytes", + "postgres", + "std", +] +db-tokio-postgres = [ + "byteorder", + "bytes", + "postgres", + "std", + "tokio-postgres", +] +default = [ + "serde", + "std", +] +legacy-ops = [] +maths = [] +maths-nopanic = ["maths"] +rkyv-safe = [ + "bytecheck", + "rkyv/validation", +] +rocket-traits = ["rocket"] +rust-fuzz = ["arbitrary"] +serde = ["dep:serde"] +serde-arbitrary-precision = ["serde-with-arbitrary-precision"] +serde-bincode = ["serde-str"] +serde-float = ["serde-with-float"] +serde-str = ["serde-with-str"] +serde-with-arbitrary-precision = [ + "serde", + "serde_json/arbitrary_precision", + "serde_json/std", +] +serde-with-float = ["serde"] +serde-with-str = ["serde"] +std = [ + "arrayvec/std", + "borsh?/std", + "bytecheck?/std", + "byteorder?/std", + "bytes?/std", + "rand?/std", + "rkyv?/std", + "serde?/std", + "serde_json?/std", +] +tokio-pg = ["db-tokio-postgres"] diff --git a/third_party/rust/rust_decimal/LICENSE b/third_party/rust/rust_decimal/LICENSE new file mode 100644 index 0000000000..68364efa4e --- /dev/null +++ b/third_party/rust/rust_decimal/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Paul Mason + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/rust_decimal/Makefile.toml b/third_party/rust/rust_decimal/Makefile.toml new file mode 100644 index 0000000000..3e6c89d633 --- /dev/null +++ b/third_party/rust/rust_decimal/Makefile.toml @@ -0,0 +1,280 @@ +[config] +default_to_workspace = false + +[env] +FUZZ_RUNS = 1000 + +[tasks.build] +command = "cargo" +args = ["build"] + +[tasks.format] +workspace = true +install_crate = "rustfmt" +command = "cargo" +args = ["fmt", "--", "--emit=files"] + +[tasks.clippy] +workspace = true +install_crate = "clippy" +command = "cargo" +args = ["clippy"] + +[tasks.outdated] +install_crate = "cargo-outdated" +command = "cargo" +args = ["outdated", "-R"] + +[tasks.bench] +toolchain = "nightly" +command = "cargo" +args = ["bench", "${@}"] + +[tasks.bench-maths] +toolchain = "nightly" +command = "cargo" +args = ["bench", "--features", "maths", "${@}"] + +[tasks.bench-legacy] +toolchain = "nightly" +command = "cargo" +args = ["bench", "--no-default-features", "--features", "serde,std,legacy-ops", "${@}"] + +[tasks.benchcmp] +dependencies = [ + "benchcmp-legacy", + "benchcmp-default" +] +install_crate = "benchcmp" +command = "cargo" +args = ["benchcmp", "target/legacy.bench", "target/default.bench"] + +[tasks.benchcmp-default] +script = "cargo +nightly bench > target/default.bench" + +[tasks.benchcmp-legacy] +script = "cargo +nightly bench --no-default-features --features serde,std,legacy-ops > target/legacy.bench" + +[tasks.coverage] +dependencies = ["codecov-clean"] +env = { "CARGO_INCREMENTAL" = "0", "RUSTFLAGS" = "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort", "RUSTDOCFLAGS" = "-Cpanic=abort" } +run_task = "codecov-grcov" + +[tasks.codecov-grcov] +dependencies = ["codecov-build", "codecov-test"] +command = "grcov" +args = [".", "-s", ".", "--binary-path", "./target/debug/", "-t", "html", "--branch", "--ignore-not-existing", "-o", "./target/debug/coverage/"] + +[tasks.codecov-open] +command = "open" +args = [ "./target/debug/coverage/index.html" ] + +[tasks.codecov-clean] +toolchain = "nightly" +command = "cargo" +args = [ "clean" ] + +[tasks.codecov-build] +toolchain = "nightly" +command = "cargo" +args = [ "build", "-p", "rust_decimal", "--features=default" ] + +[tasks.codecov-test] +toolchain = "nightly" +command = "cargo" +args = [ "test", "-p", "rust_decimal", "--features=default" ] + +# Always test no-std with std tests +[tasks.test] +dependencies = ["test-no-std"] +command = "cargo" +args = ["test"] + +[tasks.fuzz] +dependencies = [ + "fuzz-arithmetic", + "fuzz-constructors" +] + +[tasks.fuzz-arithmetic] +toolchain = "nightly" +install_crate = "cargo-fuzz" +command = "cargo" +args = ["fuzz", "run", "arithmetic", "--", "-runs=${FUZZ_RUNS}"] + +[tasks.fuzz-constructors] +toolchain = "nightly" +install_crate = "cargo-fuzz" +command = "cargo" +args = ["fuzz", "run", "constructors", "--", "-runs=${FUZZ_RUNS}"] + +[tasks.test-all] +dependencies = [ + "test-no-std", + "test-default", + "test-legacy-ops", + "test-maths", + "test-misc", + "test-db", + "test-rocket-traits", + "test-serde", + "test-macros" +] + +[tasks.test-db] +dependencies = [ + "test-db-mysql-all", + "test-db-postgres-all" +] + +[tasks.test-serde] +dependencies = [ + "test-serde-float", + "test-serde-str", + "test-serde-str-float", + "test-serde-arbitrary-precision", + "test-serde-arbitrary-precision-float", + "test-serde-with-arbitrary-precision", + "test-serde-with-float", + "test-serde-with-str", +] + +[tasks.test-macros] +workspace = true + +[tasks.test-no-std] +command = "cargo" +args = ["test", "--no-default-features"] + +[tasks.test-default] +command = "cargo" +args = ["test", "--workspace", "--features=default"] + +[tasks.test-legacy-ops] +command = "cargo" +args = ["test", "--workspace", "--features=legacy-ops"] + +[tasks.test-maths] +dependencies = [ + "test-maths-default", + "test-maths-legacy", + "test-maths-nopanic", +] + +[tasks.test-maths-default] +command = "cargo" +args = ["test", "--workspace", "--no-default-features", "--features=maths", "maths", "--", "--skip", "generated"] + +[tasks.test-maths-legacy] +command = "cargo" +args = ["test", "--workspace", "--no-default-features", "--features=maths,legacy-ops", "maths", "--", "--skip", "generated"] + +[tasks.test-maths-nopanic] +command = "cargo" +args = ["test", "--workspace", "--no-default-features", "--features=maths-nopanic", "maths", "--", "--skip", "generated"] + +[tasks.test-misc] +dependencies = [ + "test-rust-fuzz", + "test-borsh", + "test-rkyv" +] + +[tasks.test-rust-fuzz] +command = "cargo" +args = ["test", "--workspace", "--no-default-features", "--features=rust-fuzz", "rust_fuzz", "--", "--skip", "generated"] + +[tasks.test-db-diesel] +dependencies = [ + "test-db-diesel1-mysql", + "test-db-diesel1-postgres", + "test-db-diesel2-mysql", + "test-db-diesel2-postgres", +] + +[tasks.test-db-mysql-all] +dependencies = [ + "test-db-diesel1-mysql", + "test-db-diesel2-mysql", +] + +[tasks.test-db-diesel1-mysql] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-diesel1-mysql", "mysql", "--", "--skip", "generated"] + +[tasks.test-db-diesel2-mysql] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-diesel2-mysql", "mysql", "--", "--skip", "generated"] + +[tasks.test-db-postgres-all] +dependencies = [ + "test-db-postgres", + "test-db-tokio-postgres", + "test-db-diesel1-postgres", + "test-db-diesel2-postgres" +] + +[tasks.test-db-postgres] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-postgres", "postgres", "--", "--skip", "generated"] + +[tasks.test-db-tokio-postgres] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-tokio-postgres", "postgres", "--", "--skip", "generated"] + +[tasks.test-db-diesel1-postgres] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-diesel1-postgres", "postgres", "--", "--skip", "generated"] + +[tasks.test-db-diesel2-postgres] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=db-diesel2-postgres", "postgres", "--", "--skip", "generated"] + +[tasks.test-rocket-traits] +command = "cargo" +args = ["test", "--workspace", "--features=rocket-traits"] + +[tasks.test-serde-float] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-float", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-str] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-str", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-str-float] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-str,serde-float", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-arbitrary-precision] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-arbitrary-precision", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-arbitrary-precision-float] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-arbitrary-precision,serde-float", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-with-arbitrary-precision] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-with-arbitrary-precision", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-with-float] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-with-float", "serde", "--", "--skip", "generated"] + +[tasks.test-serde-with-str] +command = "cargo" +args = ["test", "--workspace", "--tests", "--features=serde-with-str", "serde", "--", "--skip", "generated"] + +[tasks.test-borsh] +command = "cargo" +args = ["test", "--workspace", "--features=borsh", "--", "--skip", "generated"] + +[tasks.test-rkyv] +command = "cargo" +args = ["test", "--workspace", "--features=rkyv", "--features=rkyv-safe", "--", "--skip", "generated"] + +[tasks.test-rand] +command = "cargo" +args = ["test", "--workspace", "--features=rand", "--", "--skip", "generated"] + diff --git a/third_party/rust/rust_decimal/README.md b/third_party/rust/rust_decimal/README.md new file mode 100644 index 0000000000..9620cc38e5 --- /dev/null +++ b/third_party/rust/rust_decimal/README.md @@ -0,0 +1,302 @@ +# Decimal   [![Build Status]][actions] [![Latest Version]][crates.io] [![Docs Badge]][docs] + +[Build Status]: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fpaupino%2Frust-decimal%2Fbadge&label=build&logo=none +[actions]: https://actions-badge.atrox.dev/paupino/rust-decimal/goto +[Latest Version]: https://img.shields.io/crates/v/rust-decimal.svg +[crates.io]: https://crates.io/crates/rust-decimal +[Docs Badge]: https://docs.rs/rust_decimal/badge.svg +[docs]: https://docs.rs/rust_decimal + +A Decimal number implementation written in pure Rust suitable for financial calculations that require significant integral and fractional digits with no round-off errors. + +The binary representation consists of a 96 bit integer number, a scaling factor used to specify the decimal fraction and a 1 bit sign. Because of this representation, trailing zeros are preserved and may be exposed when in string form. These can be truncated using the `normalize` or `round_dp` functions. + +## Installing + +Using [`cargo-edit`](https://crates.io/crates/cargo-edit): + +```sh +$ cargo add rust_decimal +``` + +In addition, if you would like to use the optimized macro for convenient creation of decimals: + +```sh +$ cargo add rust_decimal_macros +``` + +Alternatively, you can edit your `Cargo.toml` directly and run `cargo update`: + +```toml +[dependencies] +rust_decimal = "1.28" +rust_decimal_macros = "1.28" +``` + +## Usage + +Decimal numbers can be created in a few distinct ways. The easiest and most efficient method of creating a Decimal is to use the procedural macro within the `rust_decimal_macros` crate: + +```rust +// Procedural macros need importing directly +use rust_decimal_macros::dec; + +let number = dec!(-1.23) + dec!(3.45); +assert_eq!(number, dec!(2.22)); +assert_eq!(number.to_string(), "2.22"); +``` + +Alternatively you can also use one of the Decimal number convenience functions ([see the docs](https://docs.rs/rust_decimal/) for more details): + +```rust +// Using the prelude can help importing trait based functions (e.g. core::str::FromStr). +use rust_decimal::prelude::*; + +// Using an integer followed by the decimal points +let scaled = Decimal::new(202, 2); +assert_eq!("2.02", scaled.to_string()); + +// From a 128 bit integer +let balance = Decimal::from_i128_with_scale(5_897_932_384_626_433_832, 2); +assert_eq!("58979323846264338.32", balance.to_string()); + +// From a string representation +let from_string = Decimal::from_str("2.02").unwrap(); +assert_eq!("2.02", from_string.to_string()); + +// From a string representation in a different base +let from_string_base16 = Decimal::from_str_radix("ffff", 16).unwrap(); +assert_eq!("65535", from_string_base16.to_string()); + +// From scientific notation +let sci = Decimal::from_scientific("9.7e-7").unwrap(); +assert_eq!("0.00000097", sci.to_string()); + +// Using the `Into` trait +let my_int: Decimal = 3_i32.into(); +assert_eq!("3", my_int.to_string()); + +// Using the raw decimal representation +let pi = Decimal::from_parts(1_102_470_952, 185_874_565, 1_703_060_790, false, 28); +assert_eq!("3.1415926535897932384626433832", pi.to_string()); +``` + +Once you have instantiated your `Decimal` number you can perform calculations with it just like any other number: + +```rust +use rust_decimal::prelude::*; +use rust_decimal_macros::dec; + +let amount = dec!(25.12); +let tax_percentage = dec!(0.085); +let total = amount + (amount * tax_percentage).round_dp(2); +assert_eq!(total, dec!(27.26)); +``` + +## Features + +**Behavior / Functionality** + +* [borsh](#borsh) +* [c-repr](#c-repr) +* [legacy-ops](#legacy-ops) +* [maths](#maths) +* [rkyv](#rkyv) +* [rocket-traits](#rocket-traits) +* [rust-fuzz](#rust-fuzz) +* [std](#std) + +**Database** + +* [db-postgres](#db-postgres) +* [db-tokio-postgres](#db-tokio-postgres) +* [db-diesel-postgres](#db-diesel-postgres) +* [db-diesel-mysql](#db-diesel-mysql) + +**Serde** + +* [serde-float](#serde-float) +* [serde-str](#serde-str) +* [serde-arbitrary-precision](#serde-arbitrary-precision) +* [serde-with-float](#serde-with-float) +* [serde-with-str](#serde-with-str) +* [serde-with-arbitrary-precision](#serde-with-arbitrary-precision) + +### `borsh` + +Enables [Borsh](https://borsh.io/) serialization for `Decimal`. + +### `c-repr` + +Forces `Decimal` to use `[repr(C)]`. The corresponding target layout is 128 bit aligned. + +### `db-postgres` + +Enables a PostgreSQL communication module. It allows for reading and writing the `Decimal` +type by transparently serializing/deserializing into the `NUMERIC` data type within PostgreSQL. + +### `db-tokio-postgres` + +Enables the tokio postgres module allowing for async communication with PostgreSQL. + +### `db-diesel-postgres` + +Enable `diesel` PostgreSQL support. By default, this enables version `1.4` of `diesel`. If you wish to use the `2.0` +version of `diesel` then you can do so by using the feature `db-diesel2-postgres`. Please note, if both features are +enabled then version 2 will supersede version 1. + +### `db-diesel-mysql` + +Enable `diesel` MySQL support. By default, this enables version `1.4` of `diesel`. If you wish to use the `2.0` +version of `diesel` then you can do so by using the feature `db-diesel2-mysql`. Please note, if both features are +enabled then version 2 will supersede version 1. + +### `legacy-ops` + +**Warning:** This is deprecated and will be removed from a future versions. + +As of `1.10` the algorithms used to perform basic operations have changed which has benefits of significant speed improvements. +To maintain backwards compatibility this can be opted out of by enabling the `legacy-ops` feature. + +### `maths` + +The `maths` feature enables additional complex mathematical functions such as `pow`, `ln`, `enf`, `exp` etc. +Documentation detailing the additional functions can be found on the +[`MathematicalOps`](https://docs.rs/rust_decimal/latest/rust_decimal/trait.MathematicalOps.html) trait. + +Please note that `ln` and `log10` will panic on invalid input with `checked_ln` and `checked_log10` the preferred functions +to curb against this. When the `maths` feature was first developed the library would return `0` on invalid input. To re-enable this +non-panicking behavior, please use the feature: `maths-nopanic`. + +### `rand` + +Implements `rand::distributions::Distribution<Decimal>` to allow the creation of random instances. + +Note: When using `rand::Rng` trait to generate a decimal between a range of two other decimals, the scale of the randomly-generated +decimal will be the same as the scale of the input decimals (or, if the inputs have different scales, the higher of the two). + +### `rkyv` +Enables [rkyv](https://github.com/rkyv/rkyv) serialization for `Decimal`. +Supports rkyv's safe API when the `rkyv-safe` feature is enabled as well. + +### `rocket-traits` + +Enable support for Rocket forms by implementing the `FromFormField` trait. + +### `rust-fuzz` + +Enable `rust-fuzz` support by implementing the `Arbitrary` trait. + +### `serde-float` + +**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data +level. + +Enable this so that JSON serialization of `Decimal` types are sent as a float instead of a string (default). + +e.g. with this turned on, JSON serialization would output: +```json +{ + "value": 1.234 +} +``` + +### `serde-str` + +**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data +level. + +This is typically useful for `bincode` or `csv` like implementations. + +Since `bincode` does not specify type information, we need to ensure that a type hint is provided in order to +correctly be able to deserialize. Enabling this feature on its own will force deserialization to use `deserialize_str` +instead of `deserialize_any`. + +If, for some reason, you also have `serde-float` enabled then this will use `deserialize_f64` as a type hint. Because +converting to `f64` _loses_ precision, it's highly recommended that you do NOT enable this feature when working with +`bincode`. That being said, this will only use 8 bytes so is slightly more efficient in terms of storage size. + +### `serde-arbitrary-precision` + +**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data +level. + +This is used primarily with `serde_json` and consequently adds it as a "weak dependency". This supports the +`arbitrary_precision` feature inside `serde_json` when parsing decimals. + +This is recommended when parsing "float" looking data as it will prevent data loss. + +### `serde-with-float` + +Enable this to access the module for serializing `Decimal` types to a float. This can be use in `struct` definitions like so: + +```rust +#[derive(Serialize, Deserialize)] +pub struct FloatExample { + #[serde(with = "rust_decimal::serde::float")] + value: Decimal, +} +``` +```rust +#[derive(Serialize, Deserialize)] +pub struct OptionFloatExample { + #[serde(with = "rust_decimal::serde::float_option")] + value: Option<Decimal>, +} +``` + +### `serde-with-str` + +Enable this to access the module for serializing `Decimal` types to a `String`. This can be use in `struct` definitions like so: + +```rust +#[derive(Serialize, Deserialize)] +pub struct StrExample { + #[serde(with = "rust_decimal::serde::str")] + value: Decimal, +} +``` +```rust +#[derive(Serialize, Deserialize)] +pub struct OptionStrExample { + #[serde(with = "rust_decimal::serde::str_option")] + value: Option<Decimal>, +} +``` + +### `serde-with-arbitrary-precision` + +Enable this to access the module for serializing `Decimal` types to a `String`. This can be use in `struct` definitions like so: + +```rust +#[derive(Serialize, Deserialize)] +pub struct ArbitraryExample { + #[serde(with = "rust_decimal::serde::arbitrary_precision")] + value: Decimal, +} +``` +```rust +#[derive(Serialize, Deserialize)] +pub struct OptionArbitraryExample { + #[serde(with = "rust_decimal::serde::arbitrary_precision_option")] + value: Option<Decimal>, +} +``` + +### `std` + +Enable `std` library support. This is enabled by default, however in the future will be opt in. For now, to support `no_std` +libraries, this crate can be compiled with `--no-default-features`. + +## Building + +Please refer to the [Build document](BUILD.md) for more information on building and testing Rust Decimal. + +## Minimum Rust Compiler Version + +The current _minimum_ compiler version is [`1.60.0`](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1600-2022-04-07) +which was released on `2022-04-07`. + +This library maintains support for rust compiler versions that are 4 minor versions away from the current stable rust compiler version. +For example, if the current stable compiler version is `1.50.0` then we will guarantee support up to and including `1.46.0`. +Of note, we will only update the minimum supported version if and when required. diff --git a/third_party/rust/rust_decimal/benches/comparison.rs b/third_party/rust/rust_decimal/benches/comparison.rs new file mode 100644 index 0000000000..d6bf29adba --- /dev/null +++ b/third_party/rust/rust_decimal/benches/comparison.rs @@ -0,0 +1,50 @@ +//! See how `rust-decimal` performs compared to native floating numbers. + +use criterion::{ + black_box, criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, +}; +use rust_decimal::Decimal; + +const DECIMAL_2_01: Decimal = Decimal::from_parts(201, 0, 0, false, 2); +const DECIMAL_13_7: Decimal = Decimal::from_parts(137, 0, 0, false, 1); +const F64_2_01: f64 = 2.01; +const F64_13_7: f64 = 13.7; + +macro_rules! add_benchmark_group { + ($criterion:expr, $f:ident, $op:tt) => { + fn $f<M, const N: usize>(group: &mut BenchmarkGroup<'_, M>) + where + M: Measurement, + { + group.bench_with_input(BenchmarkId::new("f64 (diff)", N), &N, |ben, _| { + ben.iter(|| black_box(F64_2_01 $op F64_13_7)) + }); + + group.bench_with_input(BenchmarkId::new("f64 (equal)", N), &N, |ben, _| { + ben.iter(|| black_box(F64_2_01 $op F64_2_01)) + }); + + group.bench_with_input(BenchmarkId::new("rust-decimal (diff)", N), &N, |ben, _| { + ben.iter(|| black_box(DECIMAL_2_01 $op DECIMAL_13_7)) + }); + + group.bench_with_input(BenchmarkId::new("rust-decimal (equal)", N), &N, |ben, _| { + ben.iter(|| black_box(DECIMAL_2_01 $op DECIMAL_2_01)) + }); + } + + let mut group = $criterion.benchmark_group(stringify!($f)); + $f::<_, 100>(&mut group); + group.finish(); + }; +} + +fn criterion_benchmark(c: &mut Criterion) { + add_benchmark_group!(c, addition, +); + add_benchmark_group!(c, division, /); + add_benchmark_group!(c, multiplication, *); + add_benchmark_group!(c, subtraction, -); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/third_party/rust/rust_decimal/benches/lib_benches.rs b/third_party/rust/rust_decimal/benches/lib_benches.rs new file mode 100644 index 0000000000..5757c91c84 --- /dev/null +++ b/third_party/rust/rust_decimal/benches/lib_benches.rs @@ -0,0 +1,359 @@ +#![feature(test)] + +extern crate test; + +use bincode::Options as _; +use core::str::FromStr; +use rust_decimal::Decimal; + +macro_rules! bench_decimal_op { + ($name:ident, $op:tt, $x:expr, $y:expr) => { + #[bench] + fn $name(b: &mut ::test::Bencher) { + let x = Decimal::from_str($x).unwrap(); + let y = Decimal::from_str($y).unwrap(); + b.iter(|| { + let result = x $op y; + ::test::black_box(result); + }); + } + } +} + +macro_rules! bench_fold_op { + ($name:ident, $op:tt, $init:expr, $count:expr) => { + #[bench] + fn $name(b: &mut ::test::Bencher) { + fn fold(values: &[Decimal]) -> Decimal { + let mut acc: Decimal = $init.into(); + for value in values { + acc = acc $op value; + } + acc + } + + let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect()); + b.iter(|| { + let result = fold(&values); + ::test::black_box(result); + }); + } + } +} + +/* Add */ +bench_decimal_op!(add_self, +, "2.01", "2.01"); +bench_decimal_op!(add_simple, +, "2", "1"); +bench_decimal_op!(add_one, +, "2.01", "1"); +bench_decimal_op!(add_two, +, "2.01", "2"); +bench_decimal_op!(add_one_hundred, +, "2.01", "100"); +bench_decimal_op!(add_point_zero_one, +, "2.01", "0.01"); +bench_decimal_op!(add_negative_point_five, +, "2.01", "-0.5"); +bench_decimal_op!(add_pi, +, "2.01", "3.1415926535897932384626433832"); +bench_decimal_op!(add_negative_pi, +, "2.01", "-3.1415926535897932384626433832"); + +bench_fold_op!(add_10k, +, 0, 10_000); + +/* Sub */ +bench_decimal_op!(sub_self, -, "2.01", "2.01"); +bench_decimal_op!(sub_simple, -, "2", "1"); +bench_decimal_op!(sub_one, -, "2.01", "1"); +bench_decimal_op!(sub_two, -, "2.01", "2"); +bench_decimal_op!(sub_one_hundred, -, "2.01", "100"); +bench_decimal_op!(sub_point_zero_one, -, "2.01", "0.01"); +bench_decimal_op!(sub_negative_point_five, -, "2.01", "-0.5"); +bench_decimal_op!(sub_pi, -, "2.01", "3.1415926535897932384626433832"); +bench_decimal_op!(sub_negative_pi, -, "2.01", "-3.1415926535897932384626433832"); + +bench_fold_op!(sub_10k, -, 5_000_000, 10_000); + +/* Mul */ +bench_decimal_op!(mul_one, *, "2.01", "1"); +bench_decimal_op!(mul_two, *, "2.01", "2"); +bench_decimal_op!(mul_one_hundred, *, "2.01", "100"); +bench_decimal_op!(mul_point_zero_one, *, "2.01", "0.01"); +bench_decimal_op!(mul_negative_point_five, *, "2.01", "-0.5"); +bench_decimal_op!(mul_pi, *, "2.01", "3.1415926535897932384626433832"); +bench_decimal_op!(mul_negative_pi, *, "2.01", "-3.1415926535897932384626433832"); + +bench_fold_op!(mul_25, *, Decimal::from_str("1.1").unwrap(), 25); + +/* Div */ +bench_decimal_op!(div_one, /, "2.01", "1"); +bench_decimal_op!(div_two, /, "2.01", "2"); +bench_decimal_op!(div_one_hundred, /, "2.01", "100"); +bench_decimal_op!(div_point_zero_one, /, "2.01", "0.01"); +bench_decimal_op!(div_negative_point_five, /, "2.01", "-0.5"); +bench_decimal_op!(div_pi, /, "2.01", "3.1415926535897932384626433832"); +bench_decimal_op!(div_negative_pi, /, "2.01", "-3.1415926535897932384626433832"); +bench_decimal_op!(div_no_underflow, /, "1.02343545345", "0.35454343453"); +bench_fold_op!(div_10k, /, Decimal::MAX, 10_000); +bench_fold_op!(rem_10k, %, Decimal::MAX, 10_000); + +/* Iteration */ +struct DecimalIterator { + count: usize, +} + +impl DecimalIterator { + fn new() -> DecimalIterator { + DecimalIterator { count: 0 } + } +} + +impl Iterator for DecimalIterator { + type Item = Decimal; + + fn next(&mut self) -> Option<Decimal> { + self.count += 1; + if self.count < 6 { + Some(Decimal::new(314, 2)) + } else { + None + } + } +} + +#[bench] +fn iterator_individual(b: &mut ::test::Bencher) { + b.iter(|| { + let mut result = Decimal::new(0, 0); + let iterator = DecimalIterator::new(); + for i in iterator { + result += i; + } + ::test::black_box(result); + }); +} + +#[bench] +fn iterator_product(b: &mut ::test::Bencher) { + b.iter(|| { + let result: Decimal = DecimalIterator::new().product(); + ::test::black_box(result); + }); +} + +#[bench] +fn iterator_sum(b: &mut ::test::Bencher) { + b.iter(|| { + let result: Decimal = DecimalIterator::new().sum(); + ::test::black_box(result); + }); +} + +const SAMPLE_STRS: &[&str] = &[ + "3950.123456", + "3950", + "0.1", + "0.01", + "0.001", + "0.0001", + "0.00001", + "0.000001", + "1", + "-100", + "-123.456", + "119996.25", + "1000000", + "9999999.99999", + "12340.56789", +]; + +#[bench] +fn serialize_bincode(b: &mut test::Bencher) { + let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect(); + + b.iter(|| { + for d in &decimals { + let bytes = bincode::options().serialize(d).unwrap(); + test::black_box(bytes); + } + }) +} + +#[cfg(feature = "serde-str")] +#[bench] +fn deserialize_bincode(b: &mut test::Bencher) { + let payloads: Vec<Vec<u8>> = SAMPLE_STRS + .iter() + .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap()) + .collect(); + + b.iter(|| { + for payload in &payloads { + let decimal: Decimal = bincode::options().deserialize(payload).unwrap(); + test::black_box(decimal); + } + }) +} + +#[bench] +fn decimal_from_str(b: &mut test::Bencher) { + b.iter(|| { + for s in SAMPLE_STRS { + let result = Decimal::from_str(s).unwrap(); + test::black_box(result); + } + }) +} + +#[bench] +fn decimal_to_string(b: &mut test::Bencher) { + let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect(); + + b.iter(|| { + for s in decimals.iter() { + let string = s.to_string(); + test::black_box(string); + } + }) +} + +#[cfg(feature = "postgres")] +#[bench] +fn to_from_sql(b: &mut ::test::Bencher) { + use bytes::BytesMut; + use postgres::types::{FromSql, Kind, ToSql, Type}; + + let samples: Vec<Decimal> = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect()); + let t = Type::new("".into(), 0, Kind::Simple, "".into()); + let mut bytes: BytesMut = BytesMut::with_capacity(100).into(); + + b.iter(|| { + for _ in 0..100 { + for sample in &samples { + bytes.clear(); + sample.to_sql(&t, &mut bytes).unwrap(); + let result = Decimal::from_sql(&t, &bytes).unwrap(); + ::test::black_box(result); + } + } + }); +} + +#[cfg(feature = "maths")] +mod maths { + use rust_decimal::prelude::*; + + #[bench] + fn powi(b: &mut ::test::Bencher) { + // These exponents have to be fairly small because multiplcation overflows easily + let samples = &[ + (Decimal::from_str("36.7").unwrap(), 5), + (Decimal::from_str("0.00000007").unwrap(), 5), + (Decimal::from(2), 64), + (Decimal::from_str("8819287.19276555").unwrap(), 3), + (Decimal::from_str("-8819287.19276555").unwrap(), 3), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.0.powi(sample.1); + ::test::black_box(result); + } + }); + } + + #[bench] + fn sqrt(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from_str("36.7").unwrap(), + Decimal::from_str("0.00000007").unwrap(), + Decimal::from(2), + Decimal::from_str("8819287.19276555").unwrap(), + Decimal::from_str("-8819287.19276555").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.sqrt(); + ::test::black_box(result); + } + }); + } + + #[bench] + fn exp(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from_str("3.7").unwrap(), + Decimal::from_str("0.07").unwrap(), + Decimal::from(2), + Decimal::from_str("8.19").unwrap(), + Decimal::from_str("-8.19").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.exp(); + ::test::black_box(result); + } + }); + } + + #[bench] + fn norm_cdf(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from_str("3.7").unwrap(), + Decimal::from_str("0.007").unwrap(), + Decimal::from(2), + Decimal::from_str("1.19").unwrap(), + Decimal::from_str("-1.19").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.norm_cdf(); + ::test::black_box(result); + } + }); + } + + #[bench] + fn norm_pdf(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from_str("3.7").unwrap(), + Decimal::from_str("0.007").unwrap(), + Decimal::from(2), + Decimal::from_str("1.19").unwrap(), + Decimal::from_str("-1.19").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.norm_pdf(); + ::test::black_box(result); + } + }); + } + + #[bench] + fn ln(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from_str("36.7").unwrap(), + Decimal::from_str("0.00000007").unwrap(), + Decimal::from(2), + Decimal::from_str("8819287.19").unwrap(), + Decimal::from_str("-8819287.19").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.ln(); + ::test::black_box(result); + } + }); + } + + #[bench] + fn erf(b: &mut ::test::Bencher) { + let samples = &[ + Decimal::from(0), + Decimal::from(1), + Decimal::from_str("-0.98717").unwrap(), + Decimal::from_str("0.07").unwrap(), + Decimal::from_str("0.1111").unwrap(), + Decimal::from_str("0.4").unwrap(), + ]; + b.iter(|| { + for sample in samples.iter() { + let result = sample.erf(); + ::test::black_box(result); + } + }); + } +} diff --git a/third_party/rust/rust_decimal/build.rs b/third_party/rust/rust_decimal/build.rs new file mode 100644 index 0000000000..604a77c792 --- /dev/null +++ b/third_party/rust/rust_decimal/build.rs @@ -0,0 +1,58 @@ +use std::fmt::Write; +use std::{fs, path::PathBuf}; + +fn main() { + println!("cargo:rerun-if-changed=README.md"); + let readme = fs::read_to_string("README.md").unwrap(); + let output = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("README-lib.md"); + let contents = prepare(&readme).unwrap(); + fs::write(output, contents).unwrap(); +} + +fn prepare(readme: &str) -> Result<String, Box<dyn std::error::Error>> { + // This is a naive implementation to get things off the ground. + // We just do a few things for this at the moment: + // 1. Strip header stuff + // 2. Replace the build document link + // 3. Replace serde examples with ignore flags (to avoid feature flagging configuration in docs) + let mut cleaned = String::new(); + let mut body = false; + let mut feature_section = false; + let mut feature = String::new(); + for line in readme.lines() { + if !body { + if line.starts_with("[docs]") { + body = true; + } + continue; + } + + // Add the line as is, unless it contains "(BUILD.md)" + if line.contains("(BUILD.md)") { + write!( + cleaned, + "{}", + &line.replace( + "(BUILD.md)", + "(https://github.com/paupino/rust-decimal/blob/master/BUILD.md)", + ) + )?; + } else if feature_section && line.starts_with("```rust") { + // This is a bit naive, but it's to make the Serde examples cleaner. Should probably + // be a bit more "defensive" here. + writeln!(cleaned, "```rust")?; + writeln!(cleaned, "# use rust_decimal::Decimal;")?; + writeln!(cleaned, "# use serde::{{Serialize, Deserialize}};")?; + write!(cleaned, "# #[cfg(features = \"{}\")]", feature)?; + } else { + if !feature_section && line.starts_with("## Features") { + feature_section = true; + } else if feature_section && line.starts_with("### ") { + feature = line.replace("### ", "").replace('`', ""); + } + write!(cleaned, "{}", line)?; + } + writeln!(cleaned)?; + } + Ok(cleaned) +} diff --git a/third_party/rust/rust_decimal/rustfmt.toml b/third_party/rust/rust_decimal/rustfmt.toml new file mode 100644 index 0000000000..866c756105 --- /dev/null +++ b/third_party/rust/rust_decimal/rustfmt.toml @@ -0,0 +1 @@ +max_width = 120
\ No newline at end of file diff --git a/third_party/rust/rust_decimal/src/arithmetic_impls.rs b/third_party/rust/rust_decimal/src/arithmetic_impls.rs new file mode 100644 index 0000000000..81cfa305d2 --- /dev/null +++ b/third_party/rust/rust_decimal/src/arithmetic_impls.rs @@ -0,0 +1,329 @@ +// #[rustfmt::skip] is being used because `rustfmt` poorly formats `#[doc = concat!(..)]`. See +// https://github.com/rust-lang/rustfmt/issues/5062 for more information. + +use crate::{decimal::CalculationResult, ops, Decimal}; +use core::ops::{Add, Div, Mul, Rem, Sub}; +use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub, Inv}; + +// Macros and `Decimal` implementations + +#[rustfmt::skip] +macro_rules! impl_checked { + ($long:literal, $short:literal, $fun:ident, $impl:ident) => { + #[doc = concat!( + "Checked ", + $long, + ". Computes `self ", + $short, + " other`, returning `None` if overflow occurred." + )] + #[inline(always)] + #[must_use] + pub fn $fun(self, other: Decimal) -> Option<Decimal> { + match ops::$impl(&self, &other) { + CalculationResult::Ok(result) => Some(result), + _ => None, + } + } + }; +} + +#[rustfmt::skip] +macro_rules! impl_saturating { + ($long:literal, $short:literal, $fun:ident, $impl:ident, $cmp:ident) => { + #[doc = concat!( + "Saturating ", + $long, + ". Computes `self ", + $short, + " other`, saturating at the relevant upper or lower boundary.", + )] + #[inline(always)] + #[must_use] + pub fn $fun(self, other: Decimal) -> Decimal { + if let Some(elem) = self.$impl(other) { + elem + } else { + $cmp(&self, &other) + } + } + }; +} + +macro_rules! impl_checked_and_saturating { + ( + $op_long:literal, + $op_short:literal, + $checked_fun:ident, + $checked_impl:ident, + + $saturating_fun:ident, + $saturating_cmp:ident + ) => { + impl_checked!($op_long, $op_short, $checked_fun, $checked_impl); + impl_saturating!( + $op_long, + $op_short, + $saturating_fun, + $checked_fun, + $saturating_cmp + ); + }; +} + +impl Decimal { + impl_checked_and_saturating!( + "addition", + "+", + checked_add, + add_impl, + saturating_add, + if_a_is_positive_then_max + ); + impl_checked_and_saturating!( + "multiplication", + "*", + checked_mul, + mul_impl, + saturating_mul, + if_xnor_then_max + ); + impl_checked_and_saturating!( + "subtraction", + "-", + checked_sub, + sub_impl, + saturating_sub, + if_a_is_positive_then_max + ); + + impl_checked!("division", "/", checked_div, div_impl); + impl_checked!("remainder", "%", checked_rem, rem_impl); +} + +// Macros and trait implementations + +macro_rules! forward_all_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + forward_val_val_binop!(impl $imp for $res, $method); + forward_ref_val_binop!(impl $imp for $res, $method); + forward_val_ref_binop!(impl $imp for $res, $method); + }; +} + +macro_rules! forward_ref_val_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl<'a> $imp<$res> for &'a $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + self.$method(&other) + } + } + }; +} + +macro_rules! forward_val_ref_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl<'a> $imp<&'a $res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: &$res) -> $res { + (&self).$method(other) + } + } + }; +} + +macro_rules! forward_val_val_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl $imp<$res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + (&self).$method(&other) + } + } + }; +} + +forward_all_binop!(impl Add for Decimal, add); +impl<'a, 'b> Add<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline(always)] + fn add(self, other: &Decimal) -> Decimal { + match ops::add_impl(self, other) { + CalculationResult::Ok(sum) => sum, + _ => panic!("Addition overflowed"), + } + } +} + +impl CheckedAdd for Decimal { + #[inline] + fn checked_add(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_add(*self, *v) + } +} + +impl CheckedSub for Decimal { + #[inline] + fn checked_sub(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_sub(*self, *v) + } +} + +impl CheckedMul for Decimal { + #[inline] + fn checked_mul(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_mul(*self, *v) + } +} + +impl CheckedDiv for Decimal { + #[inline] + fn checked_div(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_div(*self, *v) + } +} + +impl CheckedRem for Decimal { + #[inline] + fn checked_rem(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_rem(*self, *v) + } +} + +impl Inv for Decimal { + type Output = Self; + + #[inline] + fn inv(self) -> Self { + Decimal::ONE / self + } +} + +forward_all_binop!(impl Div for Decimal, div); +impl<'a, 'b> Div<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn div(self, other: &Decimal) -> Decimal { + match ops::div_impl(self, other) { + CalculationResult::Ok(quot) => quot, + CalculationResult::Overflow => panic!("Division overflowed"), + CalculationResult::DivByZero => panic!("Division by zero"), + } + } +} + +forward_all_binop!(impl Mul for Decimal, mul); +impl<'a, 'b> Mul<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn mul(self, other: &Decimal) -> Decimal { + match ops::mul_impl(self, other) { + CalculationResult::Ok(prod) => prod, + _ => panic!("Multiplication overflowed"), + } + } +} + +forward_all_binop!(impl Rem for Decimal, rem); +impl<'a, 'b> Rem<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn rem(self, other: &Decimal) -> Decimal { + match ops::rem_impl(self, other) { + CalculationResult::Ok(rem) => rem, + CalculationResult::Overflow => panic!("Division overflowed"), + CalculationResult::DivByZero => panic!("Division by zero"), + } + } +} + +forward_all_binop!(impl Sub for Decimal, sub); +impl<'a, 'b> Sub<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline(always)] + fn sub(self, other: &Decimal) -> Decimal { + match ops::sub_impl(self, other) { + CalculationResult::Ok(sum) => sum, + _ => panic!("Subtraction overflowed"), + } + } +} + +// This function signature is expected by `impl_saturating`, thus the reason of `_b`. +#[inline(always)] +const fn if_a_is_positive_then_max(a: &Decimal, _b: &Decimal) -> Decimal { + if a.is_sign_positive() { + Decimal::MAX + } else { + Decimal::MIN + } +} + +// Used by saturating multiplications. +// +// If the `a` and `b` combination represents a XNOR bit operation, returns MAX. Otherwise, +// returns MIN. +#[inline(always)] +const fn if_xnor_then_max(a: &Decimal, b: &Decimal) -> Decimal { + match (a.is_sign_positive(), b.is_sign_positive()) { + (true, true) => Decimal::MAX, + (true, false) => Decimal::MIN, + (false, true) => Decimal::MIN, + (false, false) => Decimal::MAX, + } +} + +#[cfg(test)] +mod tests { + use crate::Decimal; + + #[test] + fn checked_methods_have_correct_output() { + assert_eq!(Decimal::MAX.checked_add(Decimal::MAX), None); + assert_eq!(Decimal::MAX.checked_add(Decimal::MIN), Some(Decimal::ZERO)); + assert_eq!(Decimal::MAX.checked_div(Decimal::ZERO), None); + assert_eq!(Decimal::MAX.checked_mul(Decimal::MAX), None); + assert_eq!(Decimal::MAX.checked_mul(Decimal::MIN), None); + assert_eq!(Decimal::MAX.checked_rem(Decimal::ZERO), None); + assert_eq!(Decimal::MAX.checked_sub(Decimal::MAX), Some(Decimal::ZERO)); + assert_eq!(Decimal::MAX.checked_sub(Decimal::MIN), None); + + assert_eq!(Decimal::MIN.checked_add(Decimal::MAX), Some(Decimal::ZERO)); + assert_eq!(Decimal::MIN.checked_add(Decimal::MIN), None); + assert_eq!(Decimal::MIN.checked_div(Decimal::ZERO), None); + assert_eq!(Decimal::MIN.checked_mul(Decimal::MAX), None); + assert_eq!(Decimal::MIN.checked_mul(Decimal::MIN), None); + assert_eq!(Decimal::MIN.checked_rem(Decimal::ZERO), None); + assert_eq!(Decimal::MIN.checked_sub(Decimal::MAX), None); + assert_eq!(Decimal::MIN.checked_sub(Decimal::MIN), Some(Decimal::ZERO)); + } + + #[test] + fn saturated_methods_have_correct_output() { + assert_eq!(Decimal::MAX.saturating_add(Decimal::MAX), Decimal::MAX); + assert_eq!(Decimal::MAX.saturating_add(Decimal::MIN), Decimal::ZERO); + assert_eq!(Decimal::MAX.saturating_mul(Decimal::MAX), Decimal::MAX); + assert_eq!(Decimal::MAX.saturating_mul(Decimal::MIN), Decimal::MIN); + assert_eq!(Decimal::MAX.saturating_sub(Decimal::MAX), Decimal::ZERO); + assert_eq!(Decimal::MAX.saturating_sub(Decimal::MIN), Decimal::MAX); + + assert_eq!(Decimal::MIN.saturating_add(Decimal::MAX), Decimal::ZERO); + assert_eq!(Decimal::MIN.saturating_add(Decimal::MIN), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_mul(Decimal::MAX), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_mul(Decimal::MIN), Decimal::MAX); + assert_eq!(Decimal::MIN.saturating_sub(Decimal::MAX), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_sub(Decimal::MIN), Decimal::ZERO); + } +} diff --git a/third_party/rust/rust_decimal/src/constants.rs b/third_party/rust/rust_decimal/src/constants.rs new file mode 100644 index 0000000000..59f3366587 --- /dev/null +++ b/third_party/rust/rust_decimal/src/constants.rs @@ -0,0 +1,72 @@ +// Sign mask for the flags field. A value of zero in this bit indicates a +// positive Decimal value, and a value of one in this bit indicates a +// negative Decimal value. +pub const SIGN_MASK: u32 = 0x8000_0000; +pub const UNSIGN_MASK: u32 = 0x4FFF_FFFF; + +// Scale mask for the flags field. This byte in the flags field contains +// the power of 10 to divide the Decimal value by. The scale byte must +// contain a value between 0 and 28 inclusive. +pub const SCALE_MASK: u32 = 0x00FF_0000; +pub const U8_MASK: u32 = 0x0000_00FF; +pub const U32_MASK: u64 = u32::MAX as _; + +// Number of bits scale is shifted by. +pub const SCALE_SHIFT: u32 = 16; +// Number of bits sign is shifted by. +pub const SIGN_SHIFT: u32 = 31; + +// The maximum string buffer size used for serialization purposes. 31 is optimal, however we align +// to the byte boundary for simplicity. +pub const MAX_STR_BUFFER_SIZE: usize = 32; + +// The maximum supported precision +pub const MAX_PRECISION: u8 = 28; +#[cfg(not(feature = "legacy-ops"))] +// u8 to i32 is infallible, therefore, this cast will never overflow +pub const MAX_PRECISION_I32: i32 = MAX_PRECISION as _; +// u8 to u32 is infallible, therefore, this cast will never overflow +pub const MAX_PRECISION_U32: u32 = MAX_PRECISION as _; +// 79,228,162,514,264,337,593,543,950,335 +pub const MAX_I128_REPR: i128 = 0x0000_0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; + +// Fast access for 10^n where n is 0-9 +pub const POWERS_10: [u32; 10] = [ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, +]; +// Fast access for 10^n where n is 1-19 +pub const BIG_POWERS_10: [u64; 19] = [ + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000, +]; + +#[cfg(not(feature = "legacy-ops"))] +// The maximum power of 10 that a 32 bit integer can store +pub const MAX_I32_SCALE: i32 = 9; +#[cfg(not(feature = "legacy-ops"))] +// The maximum power of 10 that a 64 bit integer can store +pub const MAX_I64_SCALE: u32 = 19; +#[cfg(not(feature = "legacy-ops"))] +pub const U32_MAX: u64 = u32::MAX as u64; + +// Determines potential overflow for 128 bit operations +pub const OVERFLOW_U96: u128 = 1u128 << 96; +pub const WILL_OVERFLOW_U64: u64 = u64::MAX / 10 - u8::MAX as u64; +pub const BYTES_TO_OVERFLOW_U64: usize = 18; // We can probably get away with less diff --git a/third_party/rust/rust_decimal/src/decimal.rs b/third_party/rust/rust_decimal/src/decimal.rs new file mode 100644 index 0000000000..e98f7b4e4e --- /dev/null +++ b/third_party/rust/rust_decimal/src/decimal.rs @@ -0,0 +1,2578 @@ +use crate::constants::{ + MAX_I128_REPR, MAX_PRECISION_U32, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, SIGN_SHIFT, U32_MASK, U8_MASK, + UNSIGN_MASK, +}; +use crate::ops; +use crate::Error; +use core::{ + cmp::{Ordering::Equal, *}, + fmt, + hash::{Hash, Hasher}, + iter::{Product, Sum}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, + str::FromStr, +}; + +// Diesel configuration +#[cfg(feature = "diesel2")] +use diesel::deserialize::FromSqlRow; +#[cfg(feature = "diesel2")] +use diesel::expression::AsExpression; +#[cfg(any(feature = "diesel1", feature = "diesel2"))] +use diesel::sql_types::Numeric; + +#[allow(unused_imports)] // It's not actually dead code below, but the compiler thinks it is. +#[cfg(not(feature = "std"))] +use num_traits::float::FloatCore; +use num_traits::{FromPrimitive, Num, One, Signed, ToPrimitive, Zero}; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +/// The smallest value that can be represented by this decimal type. +const MIN: Decimal = Decimal { + flags: 2_147_483_648, + lo: 4_294_967_295, + mid: 4_294_967_295, + hi: 4_294_967_295, +}; + +/// The largest value that can be represented by this decimal type. +const MAX: Decimal = Decimal { + flags: 0, + lo: 4_294_967_295, + mid: 4_294_967_295, + hi: 4_294_967_295, +}; + +const ZERO: Decimal = Decimal { + flags: 0, + lo: 0, + mid: 0, + hi: 0, +}; +const ONE: Decimal = Decimal { + flags: 0, + lo: 1, + mid: 0, + hi: 0, +}; +const TWO: Decimal = Decimal { + flags: 0, + lo: 2, + mid: 0, + hi: 0, +}; +const TEN: Decimal = Decimal { + flags: 0, + lo: 10, + mid: 0, + hi: 0, +}; +const ONE_HUNDRED: Decimal = Decimal { + flags: 0, + lo: 100, + mid: 0, + hi: 0, +}; +const ONE_THOUSAND: Decimal = Decimal { + flags: 0, + lo: 1000, + mid: 0, + hi: 0, +}; +const NEGATIVE_ONE: Decimal = Decimal { + flags: 2147483648, + lo: 1, + mid: 0, + hi: 0, +}; + +/// `UnpackedDecimal` contains unpacked representation of `Decimal` where each component +/// of decimal-format stored in it's own field +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UnpackedDecimal { + pub negative: bool, + pub scale: u32, + pub hi: u32, + pub mid: u32, + pub lo: u32, +} + +/// `Decimal` represents a 128 bit representation of a fixed-precision decimal number. +/// The finite set of values of type `Decimal` are of the form m / 10<sup>e</sup>, +/// where m is an integer such that -2<sup>96</sup> < m < 2<sup>96</sup>, and e is an integer +/// between 0 and 28 inclusive. +#[derive(Clone, Copy)] +#[cfg_attr( + all(feature = "diesel1", not(feature = "diesel2")), + derive(FromSqlRow, AsExpression), + sql_type = "Numeric" +)] +#[cfg_attr(feature = "diesel2", derive(FromSqlRow, AsExpression), diesel(sql_type = Numeric))] +#[cfg_attr(feature = "c-repr", repr(C))] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshDeserialize, borsh::BorshSerialize, borsh::BorshSchema) +)] +#[cfg_attr( + feature = "rkyv", + derive(Archive, Deserialize, Serialize), + archive(compare(PartialEq)), + archive_attr(derive(Clone, Copy, Debug)) +)] +#[cfg_attr(feature = "rkyv-safe", archive_attr(derive(bytecheck::CheckBytes)))] +pub struct Decimal { + // Bits 0-15: unused + // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale + // Bits 24-30: unused + // Bit 31: the sign of the Decimal value, 0 meaning positive and 1 meaning negative. + flags: u32, + // The lo, mid, hi, and flags fields contain the representation of the + // Decimal value as a 96-bit integer. + hi: u32, + lo: u32, + mid: u32, +} + +/// `RoundingStrategy` represents the different rounding strategies that can be used by +/// `round_dp_with_strategy`. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum RoundingStrategy { + /// When a number is halfway between two others, it is rounded toward the nearest even number. + /// Also known as "Bankers Rounding". + /// e.g. + /// 6.5 -> 6, 7.5 -> 8 + MidpointNearestEven, + /// When a number is halfway between two others, it is rounded toward the nearest number that + /// is away from zero. e.g. 6.4 -> 6, 6.5 -> 7, -6.5 -> -7 + MidpointAwayFromZero, + /// When a number is halfway between two others, it is rounded toward the nearest number that + /// is toward zero. e.g. 6.4 -> 6, 6.5 -> 6, -6.5 -> -6 + MidpointTowardZero, + /// The number is always rounded toward zero. e.g. -6.8 -> -6, 6.8 -> 6 + ToZero, + /// The number is always rounded away from zero. e.g. -6.8 -> -7, 6.8 -> 7 + AwayFromZero, + /// The number is always rounded towards negative infinity. e.g. 6.8 -> 6, -6.8 -> -7 + ToNegativeInfinity, + /// The number is always rounded towards positive infinity. e.g. 6.8 -> 7, -6.8 -> -6 + ToPositiveInfinity, + + /// When a number is halfway between two others, it is rounded toward the nearest even number. + /// e.g. + /// 6.5 -> 6, 7.5 -> 8 + #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointNearestEven instead")] + BankersRounding, + /// Rounds up if the value >= 5, otherwise rounds down, e.g. 6.5 -> 7 + #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointAwayFromZero instead")] + RoundHalfUp, + /// Rounds down if the value =< 5, otherwise rounds up, e.g. 6.5 -> 6, 6.51 -> 7 1.4999999 -> 1 + #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::MidpointTowardZero instead")] + RoundHalfDown, + /// Always round down. + #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::ToZero instead")] + RoundDown, + /// Always round up. + #[deprecated(since = "1.11.0", note = "Please use RoundingStrategy::AwayFromZero instead")] + RoundUp, +} + +#[allow(dead_code)] +impl Decimal { + /// The smallest value that can be represented by this decimal type. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::MIN, dec!(-79_228_162_514_264_337_593_543_950_335)); + /// ``` + pub const MIN: Decimal = MIN; + /// The largest value that can be represented by this decimal type. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::MAX, dec!(79_228_162_514_264_337_593_543_950_335)); + /// ``` + pub const MAX: Decimal = MAX; + /// A constant representing 0. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::ZERO, dec!(0)); + /// ``` + pub const ZERO: Decimal = ZERO; + /// A constant representing 1. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::ONE, dec!(1)); + /// ``` + pub const ONE: Decimal = ONE; + /// A constant representing -1. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::NEGATIVE_ONE, dec!(-1)); + /// ``` + pub const NEGATIVE_ONE: Decimal = NEGATIVE_ONE; + /// A constant representing 2. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::TWO, dec!(2)); + /// ``` + pub const TWO: Decimal = TWO; + /// A constant representing 10. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::TEN, dec!(10)); + /// ``` + pub const TEN: Decimal = TEN; + /// A constant representing 100. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::ONE_HUNDRED, dec!(100)); + /// ``` + pub const ONE_HUNDRED: Decimal = ONE_HUNDRED; + /// A constant representing 1000. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::ONE_THOUSAND, dec!(1000)); + /// ``` + pub const ONE_THOUSAND: Decimal = ONE_THOUSAND; + + /// A constant representing Ï€ as 3.1415926535897932384626433833 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::PI, dec!(3.1415926535897932384626433833)); + /// ``` + #[cfg(feature = "maths")] + pub const PI: Decimal = Decimal { + flags: 1835008, + lo: 1102470953, + mid: 185874565, + hi: 1703060790, + }; + /// A constant representing Ï€/2 as 1.5707963267948966192313216916 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::HALF_PI, dec!(1.5707963267948966192313216916)); + /// ``` + #[cfg(feature = "maths")] + pub const HALF_PI: Decimal = Decimal { + flags: 1835008, + lo: 2698719124, + mid: 92937282, + hi: 851530395, + }; + /// A constant representing Ï€/4 as 0.7853981633974483096156608458 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::QUARTER_PI, dec!(0.7853981633974483096156608458)); + /// ``` + #[cfg(feature = "maths")] + pub const QUARTER_PI: Decimal = Decimal { + flags: 1835008, + lo: 1349359562, + mid: 2193952289, + hi: 425765197, + }; + /// A constant representing 2Ï€ as 6.2831853071795864769252867666 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::TWO_PI, dec!(6.2831853071795864769252867666)); + /// ``` + #[cfg(feature = "maths")] + pub const TWO_PI: Decimal = Decimal { + flags: 1835008, + lo: 2204941906, + mid: 371749130, + hi: 3406121580, + }; + /// A constant representing Euler's number (e) as 2.7182818284590452353602874714 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::E, dec!(2.7182818284590452353602874714)); + /// ``` + #[cfg(feature = "maths")] + pub const E: Decimal = Decimal { + flags: 1835008, + lo: 2239425882, + mid: 3958169141, + hi: 1473583531, + }; + /// A constant representing the inverse of Euler's number (1/e) as 0.3678794411714423215955237702 + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// assert_eq!(Decimal::E_INVERSE, dec!(0.3678794411714423215955237702)); + /// ``` + #[cfg(feature = "maths")] + pub const E_INVERSE: Decimal = Decimal { + flags: 1835008, + lo: 2384059206, + mid: 2857938002, + hi: 199427844, + }; + + /// Returns a `Decimal` with a 64 bit `m` representation and corresponding `e` scale. + /// + /// # Arguments + /// + /// * `num` - An i64 that represents the `m` portion of the decimal number + /// * `scale` - A u32 representing the `e` portion of the decimal number. + /// + /// # Panics + /// + /// This function panics if `scale` is > 28. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let pi = Decimal::new(3141, 3); + /// assert_eq!(pi.to_string(), "3.141"); + /// ``` + #[must_use] + pub fn new(num: i64, scale: u32) -> Decimal { + match Self::try_new(num, scale) { + Err(e) => panic!("{}", e), + Ok(d) => d, + } + } + + /// Checked version of `Decimal::new`. Will return `Err` instead of panicking at run-time. + /// + /// # Example + /// + /// ```rust + /// # use rust_decimal::Decimal; + /// # + /// let max = Decimal::try_new(i64::MAX, u32::MAX); + /// assert!(max.is_err()); + /// ``` + pub const fn try_new(num: i64, scale: u32) -> crate::Result<Decimal> { + if scale > MAX_PRECISION_U32 { + return Err(Error::ScaleExceedsMaximumPrecision(scale)); + } + let flags: u32 = scale << SCALE_SHIFT; + if num < 0 { + let pos_num = num.wrapping_neg() as u64; + return Ok(Decimal { + flags: flags | SIGN_MASK, + hi: 0, + lo: (pos_num & U32_MASK) as u32, + mid: ((pos_num >> 32) & U32_MASK) as u32, + }); + } + Ok(Decimal { + flags, + hi: 0, + lo: (num as u64 & U32_MASK) as u32, + mid: ((num as u64 >> 32) & U32_MASK) as u32, + }) + } + + /// Creates a `Decimal` using a 128 bit signed `m` representation and corresponding `e` scale. + /// + /// # Arguments + /// + /// * `num` - An i128 that represents the `m` portion of the decimal number + /// * `scale` - A u32 representing the `e` portion of the decimal number. + /// + /// # Panics + /// + /// This function panics if `scale` is > 28 or if `num` exceeds the maximum supported 96 bits. + /// + /// # Example + /// + /// ```rust + /// # use rust_decimal::Decimal; + /// # + /// let pi = Decimal::from_i128_with_scale(3141i128, 3); + /// assert_eq!(pi.to_string(), "3.141"); + /// ``` + #[must_use] + pub fn from_i128_with_scale(num: i128, scale: u32) -> Decimal { + match Self::try_from_i128_with_scale(num, scale) { + Ok(d) => d, + Err(e) => panic!("{}", e), + } + } + + /// Checked version of `Decimal::from_i128_with_scale`. Will return `Err` instead + /// of panicking at run-time. + /// + /// # Example + /// + /// ```rust + /// # use rust_decimal::Decimal; + /// # + /// let max = Decimal::try_from_i128_with_scale(i128::MAX, u32::MAX); + /// assert!(max.is_err()); + /// ``` + pub const fn try_from_i128_with_scale(num: i128, scale: u32) -> crate::Result<Decimal> { + if scale > MAX_PRECISION_U32 { + return Err(Error::ScaleExceedsMaximumPrecision(scale)); + } + let mut neg = false; + let mut wrapped = num; + if num > MAX_I128_REPR { + return Err(Error::ExceedsMaximumPossibleValue); + } else if num < -MAX_I128_REPR { + return Err(Error::LessThanMinimumPossibleValue); + } else if num < 0 { + neg = true; + wrapped = -num; + } + let flags: u32 = flags(neg, scale); + Ok(Decimal { + flags, + lo: (wrapped as u64 & U32_MASK) as u32, + mid: ((wrapped as u64 >> 32) & U32_MASK) as u32, + hi: ((wrapped as u128 >> 64) as u64 & U32_MASK) as u32, + }) + } + + /// Returns a `Decimal` using the instances constituent parts. + /// + /// # Arguments + /// + /// * `lo` - The low 32 bits of a 96-bit integer. + /// * `mid` - The middle 32 bits of a 96-bit integer. + /// * `hi` - The high 32 bits of a 96-bit integer. + /// * `negative` - `true` to indicate a negative number. + /// * `scale` - A power of 10 ranging from 0 to 28. + /// + /// # Caution: Undefined behavior + /// + /// While a scale greater than 28 can be passed in, it will be automatically capped by this + /// function at the maximum precision. The library opts towards this functionality as opposed + /// to a panic to ensure that the function can be treated as constant. This may lead to + /// undefined behavior in downstream applications and should be treated with caution. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); + /// assert_eq!(pi.to_string(), "3.1415926535897932384626433832"); + /// ``` + #[must_use] + pub const fn from_parts(lo: u32, mid: u32, hi: u32, negative: bool, scale: u32) -> Decimal { + Decimal { + lo, + mid, + hi, + flags: flags( + if lo == 0 && mid == 0 && hi == 0 { + false + } else { + negative + }, + scale % (MAX_PRECISION_U32 + 1), + ), + } + } + + #[must_use] + pub(crate) const fn from_parts_raw(lo: u32, mid: u32, hi: u32, flags: u32) -> Decimal { + if lo == 0 && mid == 0 && hi == 0 { + Decimal { + lo, + mid, + hi, + flags: flags & SCALE_MASK, + } + } else { + Decimal { flags, hi, lo, mid } + } + } + + /// Returns a `Result` which if successful contains the `Decimal` constitution of + /// the scientific notation provided by `value`. + /// + /// # Arguments + /// + /// * `value` - The scientific notation of the `Decimal`. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// # fn main() -> Result<(), rust_decimal::Error> { + /// let value = Decimal::from_scientific("9.7e-7")?; + /// assert_eq!(value.to_string(), "0.00000097"); + /// # Ok(()) + /// # } + /// ``` + pub fn from_scientific(value: &str) -> Result<Decimal, Error> { + const ERROR_MESSAGE: &str = "Failed to parse"; + + let mut split = value.splitn(2, |c| c == 'e' || c == 'E'); + + let base = split.next().ok_or_else(|| Error::from(ERROR_MESSAGE))?; + let exp = split.next().ok_or_else(|| Error::from(ERROR_MESSAGE))?; + + let mut ret = Decimal::from_str(base)?; + let current_scale = ret.scale(); + + if let Some(stripped) = exp.strip_prefix('-') { + let exp: u32 = stripped.parse().map_err(|_| Error::from(ERROR_MESSAGE))?; + ret.set_scale(current_scale + exp)?; + } else { + let exp: u32 = exp.parse().map_err(|_| Error::from(ERROR_MESSAGE))?; + if exp <= current_scale { + ret.set_scale(current_scale - exp)?; + } else if exp > 0 { + use crate::constants::BIG_POWERS_10; + + // This is a case whereby the mantissa needs to be larger to be correctly + // represented within the decimal type. A good example is 1.2E10. At this point, + // we've parsed 1.2 as the base and 10 as the exponent. To represent this within a + // Decimal type we effectively store the mantissa as 12,000,000,000 and scale as + // zero. + if exp > MAX_PRECISION_U32 { + return Err(Error::ScaleExceedsMaximumPrecision(exp)); + } + let mut exp = exp as usize; + // Max two iterations. If exp is 1 then it needs to index position 0 of the array. + while exp > 0 { + let pow; + if exp >= BIG_POWERS_10.len() { + pow = BIG_POWERS_10[BIG_POWERS_10.len() - 1]; + exp -= BIG_POWERS_10.len(); + } else { + pow = BIG_POWERS_10[exp - 1]; + exp = 0; + } + + let pow = Decimal { + flags: 0, + lo: pow as u32, + mid: (pow >> 32) as u32, + hi: 0, + }; + match ret.checked_mul(pow) { + Some(r) => ret = r, + None => return Err(Error::ExceedsMaximumPossibleValue), + }; + } + ret.normalize_assign(); + } + } + Ok(ret) + } + + /// Converts a string slice in a given base to a decimal. + /// + /// The string is expected to be an optional + sign followed by digits. + /// Digits are a subset of these characters, depending on radix, and will return an error if outside + /// the expected range: + /// + /// * 0-9 + /// * a-z + /// * A-Z + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// # fn main() -> Result<(), rust_decimal::Error> { + /// assert_eq!(Decimal::from_str_radix("A", 16)?.to_string(), "10"); + /// # Ok(()) + /// # } + /// ``` + pub fn from_str_radix(str: &str, radix: u32) -> Result<Self, crate::Error> { + if radix == 10 { + crate::str::parse_str_radix_10(str) + } else { + crate::str::parse_str_radix_n(str, radix) + } + } + + /// Parses a string slice into a decimal. If the value underflows and cannot be represented with the + /// given scale then this will return an error. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # use rust_decimal::Error; + /// # + /// # fn main() -> Result<(), rust_decimal::Error> { + /// assert_eq!(Decimal::from_str_exact("0.001")?.to_string(), "0.001"); + /// assert_eq!(Decimal::from_str_exact("0.00000_00000_00000_00000_00000_001")?.to_string(), "0.0000000000000000000000000001"); + /// assert_eq!(Decimal::from_str_exact("0.00000_00000_00000_00000_00000_0001"), Err(Error::Underflow)); + /// # Ok(()) + /// # } + /// ``` + pub fn from_str_exact(str: &str) -> Result<Self, crate::Error> { + crate::str::parse_str_radix_10_exact(str) + } + + /// Returns the scale of the decimal number, otherwise known as `e`. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let num = Decimal::new(1234, 3); + /// assert_eq!(num.scale(), 3u32); + /// ``` + #[inline] + #[must_use] + pub const fn scale(&self) -> u32 { + ((self.flags & SCALE_MASK) >> SCALE_SHIFT) as u32 + } + + /// Returns the mantissa of the decimal number. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// use rust_decimal_macros::dec; + /// + /// let num = dec!(-1.2345678); + /// assert_eq!(num.mantissa(), -12345678i128); + /// assert_eq!(num.scale(), 7); + /// ``` + #[must_use] + pub const fn mantissa(&self) -> i128 { + let raw = (self.lo as i128) | ((self.mid as i128) << 32) | ((self.hi as i128) << 64); + if self.is_sign_negative() { + -raw + } else { + raw + } + } + + /// Returns true if this Decimal number is equivalent to zero. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// let num = Decimal::ZERO; + /// assert!(num.is_zero()); + /// ``` + #[must_use] + pub const fn is_zero(&self) -> bool { + self.lo == 0 && self.mid == 0 && self.hi == 0 + } + + /// An optimized method for changing the sign of a decimal number. + /// + /// # Arguments + /// + /// * `positive`: true if the resulting decimal should be positive. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let mut one = Decimal::ONE; + /// one.set_sign(false); + /// assert_eq!(one.to_string(), "-1"); + /// ``` + #[deprecated(since = "1.4.0", note = "please use `set_sign_positive` instead")] + pub fn set_sign(&mut self, positive: bool) { + self.set_sign_positive(positive); + } + + /// An optimized method for changing the sign of a decimal number. + /// + /// # Arguments + /// + /// * `positive`: true if the resulting decimal should be positive. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let mut one = Decimal::ONE; + /// one.set_sign_positive(false); + /// assert_eq!(one.to_string(), "-1"); + /// ``` + #[inline(always)] + pub fn set_sign_positive(&mut self, positive: bool) { + if positive { + self.flags &= UNSIGN_MASK; + } else { + self.flags |= SIGN_MASK; + } + } + + /// An optimized method for changing the sign of a decimal number. + /// + /// # Arguments + /// + /// * `negative`: true if the resulting decimal should be negative. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let mut one = Decimal::ONE; + /// one.set_sign_negative(true); + /// assert_eq!(one.to_string(), "-1"); + /// ``` + #[inline(always)] + pub fn set_sign_negative(&mut self, negative: bool) { + self.set_sign_positive(!negative); + } + + /// An optimized method for changing the scale of a decimal number. + /// + /// # Arguments + /// + /// * `scale`: the new scale of the number + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// # fn main() -> Result<(), rust_decimal::Error> { + /// let mut one = Decimal::ONE; + /// one.set_scale(5)?; + /// assert_eq!(one.to_string(), "0.00001"); + /// # Ok(()) + /// # } + /// ``` + pub fn set_scale(&mut self, scale: u32) -> Result<(), Error> { + if scale > MAX_PRECISION_U32 { + return Err(Error::ScaleExceedsMaximumPrecision(scale)); + } + self.flags = (scale << SCALE_SHIFT) | (self.flags & SIGN_MASK); + Ok(()) + } + + /// Modifies the `Decimal` towards the desired scale, attempting to do so without changing the + /// underlying number itself. + /// + /// Setting the scale to something less then the current `Decimal`s scale will + /// cause the newly created `Decimal` to perform rounding using the `MidpointAwayFromZero` strategy. + /// + /// Scales greater than the maximum precision that can be represented by `Decimal` will be + /// automatically rounded to either `Decimal::MAX_PRECISION` or the maximum precision that can + /// be represented with the given mantissa. + /// + /// # Arguments + /// * `scale`: The desired scale to use for the new `Decimal` number. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// use rust_decimal_macros::dec; + /// + /// // Rescaling to a higher scale preserves the value + /// let mut number = dec!(1.123); + /// assert_eq!(number.scale(), 3); + /// number.rescale(6); + /// assert_eq!(number.to_string(), "1.123000"); + /// assert_eq!(number.scale(), 6); + /// + /// // Rescaling to a lower scale forces the number to be rounded + /// let mut number = dec!(1.45); + /// assert_eq!(number.scale(), 2); + /// number.rescale(1); + /// assert_eq!(number.to_string(), "1.5"); + /// assert_eq!(number.scale(), 1); + /// + /// // This function never fails. Consequently, if a scale is provided that is unable to be + /// // represented using the given mantissa, then the maximum possible scale is used. + /// let mut number = dec!(11.76470588235294); + /// assert_eq!(number.scale(), 14); + /// number.rescale(28); + /// // A scale of 28 cannot be represented given this mantissa, however it was able to represent + /// // a number with a scale of 27 + /// assert_eq!(number.to_string(), "11.764705882352940000000000000"); + /// assert_eq!(number.scale(), 27); + /// ``` + pub fn rescale(&mut self, scale: u32) { + let mut array = [self.lo, self.mid, self.hi]; + let mut value_scale = self.scale(); + ops::array::rescale_internal(&mut array, &mut value_scale, scale); + self.lo = array[0]; + self.mid = array[1]; + self.hi = array[2]; + self.flags = flags(self.is_sign_negative(), value_scale); + } + + /// Returns a serialized version of the decimal number. + /// The resulting byte array will have the following representation: + /// + /// * Bytes 1-4: flags + /// * Bytes 5-8: lo portion of `m` + /// * Bytes 9-12: mid portion of `m` + /// * Bytes 13-16: high portion of `m` + #[must_use] + pub const fn serialize(&self) -> [u8; 16] { + [ + (self.flags & U8_MASK) as u8, + ((self.flags >> 8) & U8_MASK) as u8, + ((self.flags >> 16) & U8_MASK) as u8, + ((self.flags >> 24) & U8_MASK) as u8, + (self.lo & U8_MASK) as u8, + ((self.lo >> 8) & U8_MASK) as u8, + ((self.lo >> 16) & U8_MASK) as u8, + ((self.lo >> 24) & U8_MASK) as u8, + (self.mid & U8_MASK) as u8, + ((self.mid >> 8) & U8_MASK) as u8, + ((self.mid >> 16) & U8_MASK) as u8, + ((self.mid >> 24) & U8_MASK) as u8, + (self.hi & U8_MASK) as u8, + ((self.hi >> 8) & U8_MASK) as u8, + ((self.hi >> 16) & U8_MASK) as u8, + ((self.hi >> 24) & U8_MASK) as u8, + ] + } + + /// Deserializes the given bytes into a decimal number. + /// The deserialized byte representation must be 16 bytes and adhere to the following convention: + /// + /// * Bytes 1-4: flags + /// * Bytes 5-8: lo portion of `m` + /// * Bytes 9-12: mid portion of `m` + /// * Bytes 13-16: high portion of `m` + #[must_use] + pub fn deserialize(bytes: [u8; 16]) -> Decimal { + // We can bound flags by a bitwise mask to correspond to: + // Bits 0-15: unused + // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale + // Bits 24-30: unused + // Bit 31: the sign of the Decimal value, 0 meaning positive and 1 meaning negative. + let mut raw = Decimal { + flags: ((bytes[0] as u32) | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24) + & 0x801F_0000, + lo: (bytes[4] as u32) | (bytes[5] as u32) << 8 | (bytes[6] as u32) << 16 | (bytes[7] as u32) << 24, + mid: (bytes[8] as u32) | (bytes[9] as u32) << 8 | (bytes[10] as u32) << 16 | (bytes[11] as u32) << 24, + hi: (bytes[12] as u32) | (bytes[13] as u32) << 8 | (bytes[14] as u32) << 16 | (bytes[15] as u32) << 24, + }; + // Scale must be bound to maximum precision. Only two values can be greater than this + if raw.scale() > MAX_PRECISION_U32 { + let mut bits = raw.mantissa_array3(); + let remainder = match raw.scale() { + 29 => crate::ops::array::div_by_1x(&mut bits, 1), + 30 => crate::ops::array::div_by_1x(&mut bits, 2), + 31 => crate::ops::array::div_by_1x(&mut bits, 3), + _ => 0, + }; + if remainder >= 5 { + ops::array::add_one_internal(&mut bits); + } + raw.lo = bits[0]; + raw.mid = bits[1]; + raw.hi = bits[2]; + raw.flags = flags(raw.is_sign_negative(), MAX_PRECISION_U32); + } + raw + } + + /// Returns `true` if the decimal is negative. + #[deprecated(since = "0.6.3", note = "please use `is_sign_negative` instead")] + #[must_use] + pub fn is_negative(&self) -> bool { + self.is_sign_negative() + } + + /// Returns `true` if the decimal is positive. + #[deprecated(since = "0.6.3", note = "please use `is_sign_positive` instead")] + #[must_use] + pub fn is_positive(&self) -> bool { + self.is_sign_positive() + } + + /// Returns `true` if the sign bit of the decimal is negative. + /// + /// # Example + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// assert_eq!(true, Decimal::new(-1, 0).is_sign_negative()); + /// assert_eq!(false, Decimal::new(1, 0).is_sign_negative()); + /// ``` + #[inline(always)] + #[must_use] + pub const fn is_sign_negative(&self) -> bool { + self.flags & SIGN_MASK > 0 + } + + /// Returns `true` if the sign bit of the decimal is positive. + /// + /// # Example + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// assert_eq!(false, Decimal::new(-1, 0).is_sign_positive()); + /// assert_eq!(true, Decimal::new(1, 0).is_sign_positive()); + /// ``` + #[inline(always)] + #[must_use] + pub const fn is_sign_positive(&self) -> bool { + self.flags & SIGN_MASK == 0 + } + + /// Returns the minimum possible number that `Decimal` can represent. + #[deprecated(since = "1.12.0", note = "Use the associated constant Decimal::MIN")] + #[must_use] + pub const fn min_value() -> Decimal { + MIN + } + + /// Returns the maximum possible number that `Decimal` can represent. + #[deprecated(since = "1.12.0", note = "Use the associated constant Decimal::MAX")] + #[must_use] + pub const fn max_value() -> Decimal { + MAX + } + + /// Returns a new `Decimal` integral with no fractional portion. + /// This is a true truncation whereby no rounding is performed. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let pi = Decimal::new(3141, 3); + /// let trunc = Decimal::new(3, 0); + /// // note that it returns a decimal + /// assert_eq!(pi.trunc(), trunc); + /// ``` + #[must_use] + pub fn trunc(&self) -> Decimal { + let mut scale = self.scale(); + if scale == 0 { + // Nothing to do + return *self; + } + let mut working = [self.lo, self.mid, self.hi]; + while scale > 0 { + // We're removing precision, so we don't care about overflow + if scale < 10 { + ops::array::div_by_u32(&mut working, POWERS_10[scale as usize]); + break; + } else { + ops::array::div_by_u32(&mut working, POWERS_10[9]); + // Only 9 as this array starts with 1 + scale -= 9; + } + } + Decimal { + lo: working[0], + mid: working[1], + hi: working[2], + flags: flags(self.is_sign_negative(), 0), + } + } + + /// Returns a new `Decimal` representing the fractional portion of the number. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let pi = Decimal::new(3141, 3); + /// let fract = Decimal::new(141, 3); + /// // note that it returns a decimal + /// assert_eq!(pi.fract(), fract); + /// ``` + #[must_use] + pub fn fract(&self) -> Decimal { + // This is essentially the original number minus the integral. + // Could possibly be optimized in the future + *self - self.trunc() + } + + /// Computes the absolute value of `self`. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let num = Decimal::new(-3141, 3); + /// assert_eq!(num.abs().to_string(), "3.141"); + /// ``` + #[must_use] + pub fn abs(&self) -> Decimal { + let mut me = *self; + me.set_sign_positive(true); + me + } + + /// Returns the largest integer less than or equal to a number. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let num = Decimal::new(3641, 3); + /// assert_eq!(num.floor().to_string(), "3"); + /// ``` + #[must_use] + pub fn floor(&self) -> Decimal { + let scale = self.scale(); + if scale == 0 { + // Nothing to do + return *self; + } + + // Opportunity for optimization here + let floored = self.trunc(); + if self.is_sign_negative() && !self.fract().is_zero() { + floored - ONE + } else { + floored + } + } + + /// Returns the smallest integer greater than or equal to a number. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let num = Decimal::new(3141, 3); + /// assert_eq!(num.ceil().to_string(), "4"); + /// let num = Decimal::new(3, 0); + /// assert_eq!(num.ceil().to_string(), "3"); + /// ``` + #[must_use] + pub fn ceil(&self) -> Decimal { + let scale = self.scale(); + if scale == 0 { + // Nothing to do + return *self; + } + + // Opportunity for optimization here + if self.is_sign_positive() && !self.fract().is_zero() { + self.trunc() + ONE + } else { + self.trunc() + } + } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let x = Decimal::new(1, 0); + /// let y = Decimal::new(2, 0); + /// assert_eq!(y, x.max(y)); + /// ``` + #[must_use] + pub fn max(self, other: Decimal) -> Decimal { + if self < other { + other + } else { + self + } + } + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// let x = Decimal::new(1, 0); + /// let y = Decimal::new(2, 0); + /// assert_eq!(x, x.min(y)); + /// ``` + #[must_use] + pub fn min(self, other: Decimal) -> Decimal { + if self > other { + other + } else { + self + } + } + + /// Strips any trailing zero's from a `Decimal` and converts -0 to 0. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # fn main() -> Result<(), rust_decimal::Error> { + /// let number = Decimal::from_str("3.100")?; + /// assert_eq!(number.normalize().to_string(), "3.1"); + /// # Ok(()) + /// # } + /// ``` + #[must_use] + pub fn normalize(&self) -> Decimal { + let mut result = *self; + result.normalize_assign(); + result + } + + /// An in place version of `normalize`. Strips any trailing zero's from a `Decimal` and converts -0 to 0. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # fn main() -> Result<(), rust_decimal::Error> { + /// let mut number = Decimal::from_str("3.100")?; + /// assert_eq!(number.to_string(), "3.100"); + /// number.normalize_assign(); + /// assert_eq!(number.to_string(), "3.1"); + /// # Ok(()) + /// # } + /// ``` + pub fn normalize_assign(&mut self) { + if self.is_zero() { + self.flags = 0; + return; + } + + let mut scale = self.scale(); + if scale == 0 { + return; + } + + let mut result = self.mantissa_array3(); + let mut working = self.mantissa_array3(); + while scale > 0 { + if ops::array::div_by_u32(&mut working, 10) > 0 { + break; + } + scale -= 1; + result.copy_from_slice(&working); + } + self.lo = result[0]; + self.mid = result[1]; + self.hi = result[2]; + self.flags = flags(self.is_sign_negative(), scale); + } + + /// Returns a new `Decimal` number with no fractional portion (i.e. an integer). + /// Rounding currently follows "Bankers Rounding" rules. e.g. 6.5 -> 6, 7.5 -> 8 + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # + /// // Demonstrating bankers rounding... + /// let number_down = Decimal::new(65, 1); + /// let number_up = Decimal::new(75, 1); + /// assert_eq!(number_down.round().to_string(), "6"); + /// assert_eq!(number_up.round().to_string(), "8"); + /// ``` + #[must_use] + pub fn round(&self) -> Decimal { + self.round_dp(0) + } + + /// Returns a new `Decimal` number with the specified number of decimal points for fractional + /// portion. + /// Rounding is performed using the provided [`RoundingStrategy`] + /// + /// # Arguments + /// * `dp`: the number of decimal points to round to. + /// * `strategy`: the [`RoundingStrategy`] to use. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::{Decimal, RoundingStrategy}; + /// # use rust_decimal_macros::dec; + /// # + /// let tax = dec!(3.4395); + /// assert_eq!(tax.round_dp_with_strategy(2, RoundingStrategy::MidpointAwayFromZero).to_string(), "3.44"); + /// ``` + #[must_use] + pub fn round_dp_with_strategy(&self, dp: u32, strategy: RoundingStrategy) -> Decimal { + // Short circuit for zero + if self.is_zero() { + return Decimal { + lo: 0, + mid: 0, + hi: 0, + flags: flags(self.is_sign_negative(), dp), + }; + } + + let old_scale = self.scale(); + + // return early if decimal has a smaller number of fractional places than dp + // e.g. 2.51 rounded to 3 decimal places is 2.51 + if old_scale <= dp { + return *self; + } + + let mut value = [self.lo, self.mid, self.hi]; + let mut value_scale = self.scale(); + let negative = self.is_sign_negative(); + + value_scale -= dp; + + // Rescale to zero so it's easier to work with + while value_scale > 0 { + if value_scale < 10 { + ops::array::div_by_u32(&mut value, POWERS_10[value_scale as usize]); + value_scale = 0; + } else { + ops::array::div_by_u32(&mut value, POWERS_10[9]); + value_scale -= 9; + } + } + + // Do some midpoint rounding checks + // We're actually doing two things here. + // 1. Figuring out midpoint rounding when we're right on the boundary. e.g. 2.50000 + // 2. Figuring out whether to add one or not e.g. 2.51 + // For this, we need to figure out the fractional portion that is additional to + // the rounded number. e.g. for 0.12345 rounding to 2dp we'd want 345. + // We're doing the equivalent of losing precision (e.g. to get 0.12) + // then increasing the precision back up to 0.12000 + let mut offset = [self.lo, self.mid, self.hi]; + let mut diff = old_scale - dp; + + while diff > 0 { + if diff < 10 { + ops::array::div_by_u32(&mut offset, POWERS_10[diff as usize]); + break; + } else { + ops::array::div_by_u32(&mut offset, POWERS_10[9]); + // Only 9 as this array starts with 1 + diff -= 9; + } + } + + let mut diff = old_scale - dp; + + while diff > 0 { + if diff < 10 { + ops::array::mul_by_u32(&mut offset, POWERS_10[diff as usize]); + break; + } else { + ops::array::mul_by_u32(&mut offset, POWERS_10[9]); + // Only 9 as this array starts with 1 + diff -= 9; + } + } + + let mut decimal_portion = [self.lo, self.mid, self.hi]; + ops::array::sub_by_internal(&mut decimal_portion, &offset); + + // If the decimal_portion is zero then we round based on the other data + let mut cap = [5, 0, 0]; + for _ in 0..(old_scale - dp - 1) { + ops::array::mul_by_u32(&mut cap, 10); + } + let order = ops::array::cmp_internal(&decimal_portion, &cap); + + #[allow(deprecated)] + match strategy { + RoundingStrategy::BankersRounding | RoundingStrategy::MidpointNearestEven => { + match order { + Ordering::Equal => { + if (value[0] & 1) == 1 { + ops::array::add_one_internal(&mut value); + } + } + Ordering::Greater => { + // Doesn't matter about the decimal portion + ops::array::add_one_internal(&mut value); + } + _ => {} + } + } + RoundingStrategy::RoundHalfDown | RoundingStrategy::MidpointTowardZero => { + if let Ordering::Greater = order { + ops::array::add_one_internal(&mut value); + } + } + RoundingStrategy::RoundHalfUp | RoundingStrategy::MidpointAwayFromZero => { + // when Ordering::Equal, decimal_portion is 0.5 exactly + // when Ordering::Greater, decimal_portion is > 0.5 + match order { + Ordering::Equal => { + ops::array::add_one_internal(&mut value); + } + Ordering::Greater => { + // Doesn't matter about the decimal portion + ops::array::add_one_internal(&mut value); + } + _ => {} + } + } + RoundingStrategy::RoundUp | RoundingStrategy::AwayFromZero => { + if !ops::array::is_all_zero(&decimal_portion) { + ops::array::add_one_internal(&mut value); + } + } + RoundingStrategy::ToPositiveInfinity => { + if !negative && !ops::array::is_all_zero(&decimal_portion) { + ops::array::add_one_internal(&mut value); + } + } + RoundingStrategy::ToNegativeInfinity => { + if negative && !ops::array::is_all_zero(&decimal_portion) { + ops::array::add_one_internal(&mut value); + } + } + RoundingStrategy::RoundDown | RoundingStrategy::ToZero => (), + } + + Decimal::from_parts(value[0], value[1], value[2], negative, dp) + } + + /// Returns a new `Decimal` number with the specified number of decimal points for fractional portion. + /// Rounding currently follows "Bankers Rounding" rules. e.g. 6.5 -> 6, 7.5 -> 8 + /// + /// # Arguments + /// * `dp`: the number of decimal points to round to. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// # use rust_decimal_macros::dec; + /// # + /// let pi = dec!(3.1415926535897932384626433832); + /// assert_eq!(pi.round_dp(2).to_string(), "3.14"); + /// ``` + #[must_use] + pub fn round_dp(&self, dp: u32) -> Decimal { + self.round_dp_with_strategy(dp, RoundingStrategy::MidpointNearestEven) + } + + /// Returns `Some(Decimal)` number rounded to the specified number of significant digits. If + /// the resulting number is unable to be represented by the `Decimal` number then `None` will + /// be returned. + /// When the number of significant figures of the `Decimal` being rounded is greater than the requested + /// number of significant digits then rounding will be performed using `MidpointNearestEven` strategy. + /// + /// # Arguments + /// * `digits`: the number of significant digits to round to. + /// + /// # Remarks + /// A significant figure is determined using the following rules: + /// 1. Non-zero digits are always significant. + /// 2. Zeros between non-zero digits are always significant. + /// 3. Leading zeros are never significant. + /// 4. Trailing zeros are only significant if the number contains a decimal point. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// use rust_decimal_macros::dec; + /// + /// let value = dec!(305.459); + /// assert_eq!(value.round_sf(0), Some(dec!(0))); + /// assert_eq!(value.round_sf(1), Some(dec!(300))); + /// assert_eq!(value.round_sf(2), Some(dec!(310))); + /// assert_eq!(value.round_sf(3), Some(dec!(305))); + /// assert_eq!(value.round_sf(4), Some(dec!(305.5))); + /// assert_eq!(value.round_sf(5), Some(dec!(305.46))); + /// assert_eq!(value.round_sf(6), Some(dec!(305.459))); + /// assert_eq!(value.round_sf(7), Some(dec!(305.4590))); + /// assert_eq!(Decimal::MAX.round_sf(1), None); + /// + /// let value = dec!(0.012301); + /// assert_eq!(value.round_sf(3), Some(dec!(0.0123))); + /// ``` + #[must_use] + pub fn round_sf(&self, digits: u32) -> Option<Decimal> { + self.round_sf_with_strategy(digits, RoundingStrategy::MidpointNearestEven) + } + + /// Returns `Some(Decimal)` number rounded to the specified number of significant digits. If + /// the resulting number is unable to be represented by the `Decimal` number then `None` will + /// be returned. + /// When the number of significant figures of the `Decimal` being rounded is greater than the requested + /// number of significant digits then rounding will be performed using the provided [RoundingStrategy]. + /// + /// # Arguments + /// * `digits`: the number of significant digits to round to. + /// * `strategy`: if required, the rounding strategy to use. + /// + /// # Remarks + /// A significant figure is determined using the following rules: + /// 1. Non-zero digits are always significant. + /// 2. Zeros between non-zero digits are always significant. + /// 3. Leading zeros are never significant. + /// 4. Trailing zeros are only significant if the number contains a decimal point. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::{Decimal, RoundingStrategy}; + /// use rust_decimal_macros::dec; + /// + /// let value = dec!(305.459); + /// assert_eq!(value.round_sf_with_strategy(0, RoundingStrategy::ToZero), Some(dec!(0))); + /// assert_eq!(value.round_sf_with_strategy(1, RoundingStrategy::ToZero), Some(dec!(300))); + /// assert_eq!(value.round_sf_with_strategy(2, RoundingStrategy::ToZero), Some(dec!(300))); + /// assert_eq!(value.round_sf_with_strategy(3, RoundingStrategy::ToZero), Some(dec!(305))); + /// assert_eq!(value.round_sf_with_strategy(4, RoundingStrategy::ToZero), Some(dec!(305.4))); + /// assert_eq!(value.round_sf_with_strategy(5, RoundingStrategy::ToZero), Some(dec!(305.45))); + /// assert_eq!(value.round_sf_with_strategy(6, RoundingStrategy::ToZero), Some(dec!(305.459))); + /// assert_eq!(value.round_sf_with_strategy(7, RoundingStrategy::ToZero), Some(dec!(305.4590))); + /// assert_eq!(Decimal::MAX.round_sf_with_strategy(1, RoundingStrategy::ToZero), Some(dec!(70000000000000000000000000000))); + /// + /// let value = dec!(0.012301); + /// assert_eq!(value.round_sf_with_strategy(3, RoundingStrategy::AwayFromZero), Some(dec!(0.0124))); + /// ``` + #[must_use] + pub fn round_sf_with_strategy(&self, digits: u32, strategy: RoundingStrategy) -> Option<Decimal> { + if self.is_zero() || digits == 0 { + return Some(Decimal::ZERO); + } + + // We start by grabbing the mantissa and figuring out how many significant figures it is + // made up of. We do this by just dividing by 10 and checking remainders - effectively + // we're performing a naive log10. + let mut working = self.mantissa_array3(); + let mut mantissa_sf = 0; + while !ops::array::is_all_zero(&working) { + let _remainder = ops::array::div_by_u32(&mut working, 10u32); + mantissa_sf += 1; + if working[2] == 0 && working[1] == 0 && working[0] == 1 { + mantissa_sf += 1; + break; + } + } + let scale = self.scale(); + + match digits.cmp(&mantissa_sf) { + Ordering::Greater => { + // If we're requesting a higher number of significant figures, we rescale + let mut array = [self.lo, self.mid, self.hi]; + let mut value_scale = scale; + ops::array::rescale_internal(&mut array, &mut value_scale, scale + digits - mantissa_sf); + Some(Decimal { + lo: array[0], + mid: array[1], + hi: array[2], + flags: flags(self.is_sign_negative(), value_scale), + }) + } + Ordering::Less => { + // We're requesting a lower number of significant digits. + let diff = mantissa_sf - digits; + // If the diff is greater than the scale we're focused on the integral. Otherwise, we can + // just round. + if diff > scale { + use crate::constants::BIG_POWERS_10; + // We need to adjust the integral portion. This also should be rounded, consequently + // we reduce the number down, round it, and then scale back up. + // E.g. If we have 305.459 scaling to a sf of 2 - we first reduce the number + // down to 30.5459, round it to 31 and then scale it back up to 310. + // Likewise, if we have 12301 scaling to a sf of 3 - we first reduce the number + // down to 123.01, round it to 123 and then scale it back up to 12300. + let mut num = *self; + let mut exp = (diff - scale) as usize; + while exp > 0 { + let pow; + if exp >= BIG_POWERS_10.len() { + pow = Decimal::from(BIG_POWERS_10[BIG_POWERS_10.len() - 1]); + exp -= BIG_POWERS_10.len(); + } else { + pow = Decimal::from(BIG_POWERS_10[exp - 1]); + exp = 0; + } + num = num.checked_div(pow)?; + } + let mut num = num.round_dp_with_strategy(0, strategy).trunc(); + let mut exp = (mantissa_sf - digits - scale) as usize; + while exp > 0 { + let pow; + if exp >= BIG_POWERS_10.len() { + pow = Decimal::from(BIG_POWERS_10[BIG_POWERS_10.len() - 1]); + exp -= BIG_POWERS_10.len(); + } else { + pow = Decimal::from(BIG_POWERS_10[exp - 1]); + exp = 0; + } + num = num.checked_mul(pow)?; + } + Some(num) + } else { + Some(self.round_dp_with_strategy(scale - diff, strategy)) + } + } + Ordering::Equal => { + // Case where significant figures = requested significant digits. + Some(*self) + } + } + } + + /// Convert `Decimal` to an internal representation of the underlying struct. This is useful + /// for debugging the internal state of the object. + /// + /// # Important Disclaimer + /// This is primarily intended for library maintainers. The internal representation of a + /// `Decimal` is considered "unstable" for public use. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::Decimal; + /// use rust_decimal_macros::dec; + /// + /// let pi = dec!(3.1415926535897932384626433832); + /// assert_eq!(format!("{:?}", pi), "3.1415926535897932384626433832"); + /// assert_eq!(format!("{:?}", pi.unpack()), "UnpackedDecimal { \ + /// negative: false, scale: 28, hi: 1703060790, mid: 185874565, lo: 1102470952 \ + /// }"); + /// ``` + #[must_use] + pub const fn unpack(&self) -> UnpackedDecimal { + UnpackedDecimal { + negative: self.is_sign_negative(), + scale: self.scale(), + hi: self.hi, + lo: self.lo, + mid: self.mid, + } + } + + #[inline(always)] + pub(crate) const fn lo(&self) -> u32 { + self.lo + } + + #[inline(always)] + pub(crate) const fn mid(&self) -> u32 { + self.mid + } + + #[inline(always)] + pub(crate) const fn hi(&self) -> u32 { + self.hi + } + + #[inline(always)] + pub(crate) const fn flags(&self) -> u32 { + self.flags + } + + #[inline(always)] + pub(crate) const fn mantissa_array3(&self) -> [u32; 3] { + [self.lo, self.mid, self.hi] + } + + #[inline(always)] + pub(crate) const fn mantissa_array4(&self) -> [u32; 4] { + [self.lo, self.mid, self.hi, 0] + } + + /// Parses a 32-bit float into a Decimal number whilst retaining any non-guaranteed precision. + /// + /// Typically when a float is parsed in Rust Decimal, any excess bits (after ~7.22 decimal points for + /// f32 as per IEEE-754) are removed due to any digits following this are considered an approximation + /// at best. This function bypasses this additional step and retains these excess bits. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// // Usually floats are parsed leveraging float guarantees. i.e. 0.1_f32 => 0.1 + /// assert_eq!("0.1", Decimal::from_f32(0.1_f32).unwrap().to_string()); + /// + /// // Sometimes, we may want to represent the approximation exactly. + /// assert_eq!("0.100000001490116119384765625", Decimal::from_f32_retain(0.1_f32).unwrap().to_string()); + /// ``` + pub fn from_f32_retain(n: f32) -> Option<Self> { + from_f32(n, false) + } + + /// Parses a 64-bit float into a Decimal number whilst retaining any non-guaranteed precision. + /// + /// Typically when a float is parsed in Rust Decimal, any excess bits (after ~15.95 decimal points for + /// f64 as per IEEE-754) are removed due to any digits following this are considered an approximation + /// at best. This function bypasses this additional step and retains these excess bits. + /// + /// # Example + /// + /// ``` + /// # use rust_decimal::prelude::*; + /// # + /// // Usually floats are parsed leveraging float guarantees. i.e. 0.1_f64 => 0.1 + /// assert_eq!("0.1", Decimal::from_f64(0.1_f64).unwrap().to_string()); + /// + /// // Sometimes, we may want to represent the approximation exactly. + /// assert_eq!("0.1000000000000000055511151231", Decimal::from_f64_retain(0.1_f64).unwrap().to_string()); + /// ``` + pub fn from_f64_retain(n: f64) -> Option<Self> { + from_f64(n, false) + } +} + +impl Default for Decimal { + /// Returns the default value for a `Decimal` (equivalent to `Decimal::ZERO`). [Read more] + /// + /// [Read more]: core::default::Default#tymethod.default + #[inline] + fn default() -> Self { + ZERO + } +} + +pub(crate) enum CalculationResult { + Ok(Decimal), + Overflow, + DivByZero, +} + +#[inline] +const fn flags(neg: bool, scale: u32) -> u32 { + (scale << SCALE_SHIFT) | ((neg as u32) << SIGN_SHIFT) +} + +macro_rules! integer_docs { + ( true ) => { + " by truncating and returning the integer component" + }; + ( false ) => { + "" + }; +} + +// #[doc] attributes are formatted poorly with rustfmt so skip for now. +// See https://github.com/rust-lang/rustfmt/issues/5062 for more information. +#[rustfmt::skip] +macro_rules! impl_try_from_decimal { + ($TInto:ty, $conversion_fn:path, $additional_docs:expr) => { + #[doc = concat!( + "Try to convert a `Decimal` to `", + stringify!($TInto), + "`", + $additional_docs, + ".\n\nCan fail if the `Decimal` is out of range for `", + stringify!($TInto), + "`.", + )] + impl TryFrom<Decimal> for $TInto { + type Error = crate::Error; + + #[inline] + fn try_from(t: Decimal) -> Result<Self, Error> { + $conversion_fn(&t).ok_or_else(|| Error::ConversionTo(stringify!($TInto).into())) + } + } + }; +} + +impl_try_from_decimal!(f32, Decimal::to_f32, integer_docs!(false)); +impl_try_from_decimal!(f64, Decimal::to_f64, integer_docs!(false)); +impl_try_from_decimal!(isize, Decimal::to_isize, integer_docs!(true)); +impl_try_from_decimal!(i8, Decimal::to_i8, integer_docs!(true)); +impl_try_from_decimal!(i16, Decimal::to_i16, integer_docs!(true)); +impl_try_from_decimal!(i32, Decimal::to_i32, integer_docs!(true)); +impl_try_from_decimal!(i64, Decimal::to_i64, integer_docs!(true)); +impl_try_from_decimal!(i128, Decimal::to_i128, integer_docs!(true)); +impl_try_from_decimal!(usize, Decimal::to_usize, integer_docs!(true)); +impl_try_from_decimal!(u8, Decimal::to_u8, integer_docs!(true)); +impl_try_from_decimal!(u16, Decimal::to_u16, integer_docs!(true)); +impl_try_from_decimal!(u32, Decimal::to_u32, integer_docs!(true)); +impl_try_from_decimal!(u64, Decimal::to_u64, integer_docs!(true)); +impl_try_from_decimal!(u128, Decimal::to_u128, integer_docs!(true)); + +// #[doc] attributes are formatted poorly with rustfmt so skip for now. +// See https://github.com/rust-lang/rustfmt/issues/5062 for more information. +#[rustfmt::skip] +macro_rules! impl_try_from_primitive { + ($TFrom:ty, $conversion_fn:path $(, $err:expr)?) => { + #[doc = concat!( + "Try to convert a `", + stringify!($TFrom), + "` into a `Decimal`.\n\nCan fail if the value is out of range for `Decimal`." + )] + impl TryFrom<$TFrom> for Decimal { + type Error = crate::Error; + + #[inline] + fn try_from(t: $TFrom) -> Result<Self, Error> { + $conversion_fn(t) $( .ok_or_else(|| $err) )? + } + } + }; +} + +impl_try_from_primitive!(f32, Self::from_f32, Error::ConversionTo("Decimal".into())); +impl_try_from_primitive!(f64, Self::from_f64, Error::ConversionTo("Decimal".into())); +impl_try_from_primitive!(&str, core::str::FromStr::from_str); + +macro_rules! impl_from { + ($T:ty, $from_ty:path) => { + /// + /// Conversion to `Decimal`. + /// + impl core::convert::From<$T> for Decimal { + #[inline] + fn from(t: $T) -> Self { + $from_ty(t).unwrap() + } + } + }; +} + +impl_from!(isize, FromPrimitive::from_isize); +impl_from!(i8, FromPrimitive::from_i8); +impl_from!(i16, FromPrimitive::from_i16); +impl_from!(i32, FromPrimitive::from_i32); +impl_from!(i64, FromPrimitive::from_i64); +impl_from!(usize, FromPrimitive::from_usize); +impl_from!(u8, FromPrimitive::from_u8); +impl_from!(u16, FromPrimitive::from_u16); +impl_from!(u32, FromPrimitive::from_u32); +impl_from!(u64, FromPrimitive::from_u64); + +impl_from!(i128, FromPrimitive::from_i128); +impl_from!(u128, FromPrimitive::from_u128); + +impl Zero for Decimal { + fn zero() -> Decimal { + ZERO + } + + fn is_zero(&self) -> bool { + self.is_zero() + } +} + +impl One for Decimal { + fn one() -> Decimal { + ONE + } +} + +impl Signed for Decimal { + fn abs(&self) -> Self { + self.abs() + } + + fn abs_sub(&self, other: &Self) -> Self { + if self <= other { + ZERO + } else { + self.abs() + } + } + + fn signum(&self) -> Self { + if self.is_zero() { + ZERO + } else { + let mut value = ONE; + if self.is_sign_negative() { + value.set_sign_negative(true); + } + value + } + } + + fn is_positive(&self) -> bool { + self.is_sign_positive() + } + + fn is_negative(&self) -> bool { + self.is_sign_negative() + } +} + +impl Num for Decimal { + type FromStrRadixErr = Error; + + fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { + Decimal::from_str_radix(str, radix) + } +} + +impl FromStr for Decimal { + type Err = Error; + + fn from_str(value: &str) -> Result<Decimal, Self::Err> { + crate::str::parse_str_radix_10(value) + } +} + +impl FromPrimitive for Decimal { + fn from_i32(n: i32) -> Option<Decimal> { + let flags: u32; + let value_copy: i64; + if n >= 0 { + flags = 0; + value_copy = n as i64; + } else { + flags = SIGN_MASK; + value_copy = -(n as i64); + } + Some(Decimal { + flags, + lo: value_copy as u32, + mid: 0, + hi: 0, + }) + } + + fn from_i64(n: i64) -> Option<Decimal> { + let flags: u32; + let value_copy: i128; + if n >= 0 { + flags = 0; + value_copy = n as i128; + } else { + flags = SIGN_MASK; + value_copy = -(n as i128); + } + Some(Decimal { + flags, + lo: value_copy as u32, + mid: (value_copy >> 32) as u32, + hi: 0, + }) + } + + fn from_i128(n: i128) -> Option<Decimal> { + let flags; + let unsigned; + if n >= 0 { + unsigned = n as u128; + flags = 0; + } else { + unsigned = -n as u128; + flags = SIGN_MASK; + }; + // Check if we overflow + if unsigned >> 96 != 0 { + return None; + } + Some(Decimal { + flags, + lo: unsigned as u32, + mid: (unsigned >> 32) as u32, + hi: (unsigned >> 64) as u32, + }) + } + + fn from_u32(n: u32) -> Option<Decimal> { + Some(Decimal { + flags: 0, + lo: n, + mid: 0, + hi: 0, + }) + } + + fn from_u64(n: u64) -> Option<Decimal> { + Some(Decimal { + flags: 0, + lo: n as u32, + mid: (n >> 32) as u32, + hi: 0, + }) + } + + fn from_u128(n: u128) -> Option<Decimal> { + // Check if we overflow + if n >> 96 != 0 { + return None; + } + Some(Decimal { + flags: 0, + lo: n as u32, + mid: (n >> 32) as u32, + hi: (n >> 64) as u32, + }) + } + + fn from_f32(n: f32) -> Option<Decimal> { + // By default, we remove excess bits. This allows 0.1_f64 == dec!(0.1). + from_f32(n, true) + } + + fn from_f64(n: f64) -> Option<Decimal> { + // By default, we remove excess bits. This allows 0.1_f64 == dec!(0.1). + from_f64(n, true) + } +} + +#[inline] +fn from_f64(n: f64, remove_excess_bits: bool) -> Option<Decimal> { + // Handle the case if it is NaN, Infinity or -Infinity + if !n.is_finite() { + return None; + } + + // It's a shame we can't use a union for this due to it being broken up by bits + // i.e. 1/11/52 (sign, exponent, mantissa) + // See https://en.wikipedia.org/wiki/IEEE_754-1985 + // n = (sign*-1) * 2^exp * mantissa + // Decimal of course stores this differently... 10^-exp * significand + let raw = n.to_bits(); + let positive = (raw >> 63) == 0; + let biased_exponent = ((raw >> 52) & 0x7FF) as i32; + let mantissa = raw & 0x000F_FFFF_FFFF_FFFF; + + // Handle the special zero case + if biased_exponent == 0 && mantissa == 0 { + let mut zero = ZERO; + if !positive { + zero.set_sign_negative(true); + } + return Some(zero); + } + + // Get the bits and exponent2 + let mut exponent2 = biased_exponent - 1023; + let mut bits = [ + (mantissa & 0xFFFF_FFFF) as u32, + ((mantissa >> 32) & 0xFFFF_FFFF) as u32, + 0u32, + ]; + if biased_exponent == 0 { + // Denormalized number - correct the exponent + exponent2 += 1; + } else { + // Add extra hidden bit to mantissa + bits[1] |= 0x0010_0000; + } + + // The act of copying a mantissa as integer bits is equivalent to shifting + // left the mantissa 52 bits. The exponent is reduced to compensate. + exponent2 -= 52; + + // Convert to decimal + base2_to_decimal(&mut bits, exponent2, positive, true, remove_excess_bits) +} + +#[inline] +fn from_f32(n: f32, remove_excess_bits: bool) -> Option<Decimal> { + // Handle the case if it is NaN, Infinity or -Infinity + if !n.is_finite() { + return None; + } + + // It's a shame we can't use a union for this due to it being broken up by bits + // i.e. 1/8/23 (sign, exponent, mantissa) + // See https://en.wikipedia.org/wiki/IEEE_754-1985 + // n = (sign*-1) * 2^exp * mantissa + // Decimal of course stores this differently... 10^-exp * significand + let raw = n.to_bits(); + let positive = (raw >> 31) == 0; + let biased_exponent = ((raw >> 23) & 0xFF) as i32; + let mantissa = raw & 0x007F_FFFF; + + // Handle the special zero case + if biased_exponent == 0 && mantissa == 0 { + let mut zero = ZERO; + if !positive { + zero.set_sign_negative(true); + } + return Some(zero); + } + + // Get the bits and exponent2 + let mut exponent2 = biased_exponent - 127; + let mut bits = [mantissa, 0u32, 0u32]; + if biased_exponent == 0 { + // Denormalized number - correct the exponent + exponent2 += 1; + } else { + // Add extra hidden bit to mantissa + bits[0] |= 0x0080_0000; + } + + // The act of copying a mantissa as integer bits is equivalent to shifting + // left the mantissa 23 bits. The exponent is reduced to compensate. + exponent2 -= 23; + + // Convert to decimal + base2_to_decimal(&mut bits, exponent2, positive, false, remove_excess_bits) +} + +fn base2_to_decimal( + bits: &mut [u32; 3], + exponent2: i32, + positive: bool, + is64: bool, + remove_excess_bits: bool, +) -> Option<Decimal> { + // 2^exponent2 = (10^exponent2)/(5^exponent2) + // = (5^-exponent2)*(10^exponent2) + let mut exponent5 = -exponent2; + let mut exponent10 = exponent2; // Ultimately, we want this for the scale + + while exponent5 > 0 { + // Check to see if the mantissa is divisible by 2 + if bits[0] & 0x1 == 0 { + exponent10 += 1; + exponent5 -= 1; + + // We can divide by 2 without losing precision + let hi_carry = bits[2] & 0x1 == 1; + bits[2] >>= 1; + let mid_carry = bits[1] & 0x1 == 1; + bits[1] = (bits[1] >> 1) | if hi_carry { SIGN_MASK } else { 0 }; + bits[0] = (bits[0] >> 1) | if mid_carry { SIGN_MASK } else { 0 }; + } else { + // The mantissa is NOT divisible by 2. Therefore the mantissa should + // be multiplied by 5, unless the multiplication overflows. + exponent5 -= 1; + + let mut temp = [bits[0], bits[1], bits[2]]; + if ops::array::mul_by_u32(&mut temp, 5) == 0 { + // Multiplication succeeded without overflow, so copy result back + bits[0] = temp[0]; + bits[1] = temp[1]; + bits[2] = temp[2]; + } else { + // Multiplication by 5 overflows. The mantissa should be divided + // by 2, and therefore will lose significant digits. + exponent10 += 1; + + // Shift right + let hi_carry = bits[2] & 0x1 == 1; + bits[2] >>= 1; + let mid_carry = bits[1] & 0x1 == 1; + bits[1] = (bits[1] >> 1) | if hi_carry { SIGN_MASK } else { 0 }; + bits[0] = (bits[0] >> 1) | if mid_carry { SIGN_MASK } else { 0 }; + } + } + } + + // In order to divide the value by 5, it is best to multiply by 2/10. + // Therefore, exponent10 is decremented, and the mantissa should be multiplied by 2 + while exponent5 < 0 { + if bits[2] & SIGN_MASK == 0 { + // No far left bit, the mantissa can withstand a shift-left without overflowing + exponent10 -= 1; + exponent5 += 1; + ops::array::shl1_internal(bits, 0); + } else { + // The mantissa would overflow if shifted. Therefore it should be + // directly divided by 5. This will lose significant digits, unless + // by chance the mantissa happens to be divisible by 5. + exponent5 += 1; + ops::array::div_by_u32(bits, 5); + } + } + + // At this point, the mantissa has assimilated the exponent5, but + // exponent10 might not be suitable for assignment. exponent10 must be + // in the range [-MAX_PRECISION..0], so the mantissa must be scaled up or + // down appropriately. + while exponent10 > 0 { + // In order to bring exponent10 down to 0, the mantissa should be + // multiplied by 10 to compensate. If the exponent10 is too big, this + // will cause the mantissa to overflow. + if ops::array::mul_by_u32(bits, 10) == 0 { + exponent10 -= 1; + } else { + // Overflowed - return? + return None; + } + } + + // In order to bring exponent up to -MAX_PRECISION, the mantissa should + // be divided by 10 to compensate. If the exponent10 is too small, this + // will cause the mantissa to underflow and become 0. + while exponent10 < -(MAX_PRECISION_U32 as i32) { + let rem10 = ops::array::div_by_u32(bits, 10); + exponent10 += 1; + if ops::array::is_all_zero(bits) { + // Underflow, unable to keep dividing + exponent10 = 0; + } else if rem10 >= 5 { + ops::array::add_one_internal(bits); + } + } + + if remove_excess_bits { + // This step is required in order to remove excess bits of precision from the + // end of the bit representation, down to the precision guaranteed by the + // floating point number (see IEEE-754). + if is64 { + // Guaranteed to approx 15/16 dp + while exponent10 < 0 && (bits[2] != 0 || (bits[1] & 0xFFF0_0000) != 0) { + let rem10 = ops::array::div_by_u32(bits, 10); + exponent10 += 1; + if rem10 >= 5 { + ops::array::add_one_internal(bits); + } + } + } else { + // Guaranteed to about 7/8 dp + while exponent10 < 0 && ((bits[0] & 0xFF00_0000) != 0 || bits[1] != 0 || bits[2] != 0) { + let rem10 = ops::array::div_by_u32(bits, 10); + exponent10 += 1; + if rem10 >= 5 { + ops::array::add_one_internal(bits); + } + } + } + + // Remove multiples of 10 from the representation + while exponent10 < 0 { + let mut temp = [bits[0], bits[1], bits[2]]; + let remainder = ops::array::div_by_u32(&mut temp, 10); + if remainder == 0 { + exponent10 += 1; + bits[0] = temp[0]; + bits[1] = temp[1]; + bits[2] = temp[2]; + } else { + break; + } + } + } + + Some(Decimal { + lo: bits[0], + mid: bits[1], + hi: bits[2], + flags: flags(!positive, -exponent10 as u32), + }) +} + +impl ToPrimitive for Decimal { + fn to_i64(&self) -> Option<i64> { + let d = self.trunc(); + // If it is in the hi bit then it is a clear overflow. + if d.hi != 0 { + // Overflow + return None; + } + let negative = self.is_sign_negative(); + + // A bit more convoluted in terms of checking when it comes to the hi bit due to twos-complement + if d.mid & 0x8000_0000 > 0 { + if negative && d.mid == 0x8000_0000 && d.lo == 0 { + // We do this because below we try to convert the i64 to a positive first - of which + // doesn't fit into an i64. + return Some(i64::MIN); + } + return None; + } + + let raw: i64 = (i64::from(d.mid) << 32) | i64::from(d.lo); + if negative { + Some(raw.neg()) + } else { + Some(raw) + } + } + + fn to_i128(&self) -> Option<i128> { + let d = self.trunc(); + let raw: i128 = ((i128::from(d.hi) << 64) | i128::from(d.mid) << 32) | i128::from(d.lo); + if self.is_sign_negative() { + Some(-raw) + } else { + Some(raw) + } + } + + fn to_u64(&self) -> Option<u64> { + if self.is_sign_negative() { + return None; + } + + let d = self.trunc(); + if d.hi != 0 { + // Overflow + return None; + } + + Some((u64::from(d.mid) << 32) | u64::from(d.lo)) + } + + fn to_u128(&self) -> Option<u128> { + if self.is_sign_negative() { + return None; + } + + let d = self.trunc(); + Some((u128::from(d.hi) << 64) | (u128::from(d.mid) << 32) | u128::from(d.lo)) + } + + fn to_f64(&self) -> Option<f64> { + if self.scale() == 0 { + // If scale is zero, we are storing a 96-bit integer value, that would + // always fit into i128, which in turn is always representable as f64, + // albeit with loss of precision for values outside of -2^53..2^53 range. + let integer = self.to_i128(); + integer.map(|i| i as f64) + } else { + let sign: f64 = if self.is_sign_negative() { -1.0 } else { 1.0 }; + let mut mantissa: u128 = self.lo.into(); + mantissa |= (self.mid as u128) << 32; + mantissa |= (self.hi as u128) << 64; + // scale is at most 28, so this fits comfortably into a u128. + let scale = self.scale(); + let precision: u128 = 10_u128.pow(scale); + let integral_part = mantissa / precision; + let frac_part = mantissa % precision; + let frac_f64 = (frac_part as f64) / (precision as f64); + let value = sign * ((integral_part as f64) + frac_f64); + let round_to = 10f64.powi(self.scale() as i32); + Some((value * round_to).round() / round_to) + } + } +} + +impl fmt::Display for Decimal { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let (rep, additional) = crate::str::to_str_internal(self, false, f.precision()); + if let Some(additional) = additional { + let value = [rep.as_str(), "0".repeat(additional).as_str()].concat(); + f.pad_integral(self.is_sign_positive(), "", value.as_str()) + } else { + f.pad_integral(self.is_sign_positive(), "", rep.as_str()) + } + } +} + +impl fmt::Debug for Decimal { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerExp for Decimal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + crate::str::fmt_scientific_notation(self, "e", f) + } +} + +impl fmt::UpperExp for Decimal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + crate::str::fmt_scientific_notation(self, "E", f) + } +} + +impl Neg for Decimal { + type Output = Decimal; + + fn neg(self) -> Decimal { + let mut copy = self; + copy.set_sign_negative(self.is_sign_positive()); + copy + } +} + +impl<'a> Neg for &'a Decimal { + type Output = Decimal; + + fn neg(self) -> Decimal { + Decimal { + flags: flags(!self.is_sign_negative(), self.scale()), + hi: self.hi, + lo: self.lo, + mid: self.mid, + } + } +} + +impl AddAssign for Decimal { + fn add_assign(&mut self, other: Decimal) { + let result = self.add(other); + self.lo = result.lo; + self.mid = result.mid; + self.hi = result.hi; + self.flags = result.flags; + } +} + +impl<'a> AddAssign<&'a Decimal> for Decimal { + fn add_assign(&mut self, other: &'a Decimal) { + Decimal::add_assign(self, *other) + } +} + +impl<'a> AddAssign<Decimal> for &'a mut Decimal { + fn add_assign(&mut self, other: Decimal) { + Decimal::add_assign(*self, other) + } +} + +impl<'a> AddAssign<&'a Decimal> for &'a mut Decimal { + fn add_assign(&mut self, other: &'a Decimal) { + Decimal::add_assign(*self, *other) + } +} + +impl SubAssign for Decimal { + fn sub_assign(&mut self, other: Decimal) { + let result = self.sub(other); + self.lo = result.lo; + self.mid = result.mid; + self.hi = result.hi; + self.flags = result.flags; + } +} + +impl<'a> SubAssign<&'a Decimal> for Decimal { + fn sub_assign(&mut self, other: &'a Decimal) { + Decimal::sub_assign(self, *other) + } +} + +impl<'a> SubAssign<Decimal> for &'a mut Decimal { + fn sub_assign(&mut self, other: Decimal) { + Decimal::sub_assign(*self, other) + } +} + +impl<'a> SubAssign<&'a Decimal> for &'a mut Decimal { + fn sub_assign(&mut self, other: &'a Decimal) { + Decimal::sub_assign(*self, *other) + } +} + +impl MulAssign for Decimal { + fn mul_assign(&mut self, other: Decimal) { + let result = self.mul(other); + self.lo = result.lo; + self.mid = result.mid; + self.hi = result.hi; + self.flags = result.flags; + } +} + +impl<'a> MulAssign<&'a Decimal> for Decimal { + fn mul_assign(&mut self, other: &'a Decimal) { + Decimal::mul_assign(self, *other) + } +} + +impl<'a> MulAssign<Decimal> for &'a mut Decimal { + fn mul_assign(&mut self, other: Decimal) { + Decimal::mul_assign(*self, other) + } +} + +impl<'a> MulAssign<&'a Decimal> for &'a mut Decimal { + fn mul_assign(&mut self, other: &'a Decimal) { + Decimal::mul_assign(*self, *other) + } +} + +impl DivAssign for Decimal { + fn div_assign(&mut self, other: Decimal) { + let result = self.div(other); + self.lo = result.lo; + self.mid = result.mid; + self.hi = result.hi; + self.flags = result.flags; + } +} + +impl<'a> DivAssign<&'a Decimal> for Decimal { + fn div_assign(&mut self, other: &'a Decimal) { + Decimal::div_assign(self, *other) + } +} + +impl<'a> DivAssign<Decimal> for &'a mut Decimal { + fn div_assign(&mut self, other: Decimal) { + Decimal::div_assign(*self, other) + } +} + +impl<'a> DivAssign<&'a Decimal> for &'a mut Decimal { + fn div_assign(&mut self, other: &'a Decimal) { + Decimal::div_assign(*self, *other) + } +} + +impl RemAssign for Decimal { + fn rem_assign(&mut self, other: Decimal) { + let result = self.rem(other); + self.lo = result.lo; + self.mid = result.mid; + self.hi = result.hi; + self.flags = result.flags; + } +} + +impl<'a> RemAssign<&'a Decimal> for Decimal { + fn rem_assign(&mut self, other: &'a Decimal) { + Decimal::rem_assign(self, *other) + } +} + +impl<'a> RemAssign<Decimal> for &'a mut Decimal { + fn rem_assign(&mut self, other: Decimal) { + Decimal::rem_assign(*self, other) + } +} + +impl<'a> RemAssign<&'a Decimal> for &'a mut Decimal { + fn rem_assign(&mut self, other: &'a Decimal) { + Decimal::rem_assign(*self, *other) + } +} + +impl PartialEq for Decimal { + #[inline] + fn eq(&self, other: &Decimal) -> bool { + self.cmp(other) == Equal + } +} + +impl Eq for Decimal {} + +impl Hash for Decimal { + fn hash<H: Hasher>(&self, state: &mut H) { + let n = self.normalize(); + n.lo.hash(state); + n.mid.hash(state); + n.hi.hash(state); + n.flags.hash(state); + } +} + +impl PartialOrd for Decimal { + #[inline] + fn partial_cmp(&self, other: &Decimal) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Decimal { + fn cmp(&self, other: &Decimal) -> Ordering { + ops::cmp_impl(self, other) + } +} + +impl Product for Decimal { + /// Panics if out-of-bounds + fn product<I: Iterator<Item = Decimal>>(iter: I) -> Self { + let mut product = ONE; + for i in iter { + product *= i; + } + product + } +} + +impl<'a> Product<&'a Decimal> for Decimal { + /// Panics if out-of-bounds + fn product<I: Iterator<Item = &'a Decimal>>(iter: I) -> Self { + let mut product = ONE; + for i in iter { + product *= i; + } + product + } +} + +impl Sum for Decimal { + fn sum<I: Iterator<Item = Decimal>>(iter: I) -> Self { + let mut sum = ZERO; + for i in iter { + sum += i; + } + sum + } +} + +impl<'a> Sum<&'a Decimal> for Decimal { + fn sum<I: Iterator<Item = &'a Decimal>>(iter: I) -> Self { + let mut sum = ZERO; + for i in iter { + sum += i; + } + sum + } +} diff --git a/third_party/rust/rust_decimal/src/error.rs b/third_party/rust/rust_decimal/src/error.rs new file mode 100644 index 0000000000..5e5969e42b --- /dev/null +++ b/third_party/rust/rust_decimal/src/error.rs @@ -0,0 +1,69 @@ +use crate::{constants::MAX_PRECISION_U32, Decimal}; +use alloc::string::String; +use core::fmt; + +/// Error type for the library. +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + /// A generic error from Rust Decimal with the `String` containing more information as to what + /// went wrong. + /// + /// This is a legacy/deprecated error type retained for backwards compatibility. + ErrorString(String), + /// The value provided exceeds `Decimal::MAX`. + ExceedsMaximumPossibleValue, + /// The value provided is less than `Decimal::MIN`. + LessThanMinimumPossibleValue, + /// An underflow is when there are more fractional digits than can be represented within `Decimal`. + Underflow, + /// The scale provided exceeds the maximum scale that `Decimal` can represent. + ScaleExceedsMaximumPrecision(u32), + /// Represents a failure to convert to/from `Decimal` to the specified type. This is typically + /// due to type constraints (e.g. `Decimal::MAX` cannot be converted into `i32`). + ConversionTo(String), +} + +impl<S> From<S> for Error +where + S: Into<String>, +{ + #[inline] + fn from(from: S) -> Self { + Self::ErrorString(from.into()) + } +} + +#[cold] +pub(crate) fn tail_error(from: &'static str) -> Result<Decimal, Error> { + Err(from.into()) +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::ErrorString(ref err) => f.pad(err), + Self::ExceedsMaximumPossibleValue => { + write!(f, "Number exceeds maximum value that can be represented.") + } + Self::LessThanMinimumPossibleValue => { + write!(f, "Number less than minimum value that can be represented.") + } + Self::Underflow => { + write!(f, "Number has a high precision that can not be represented.") + } + Self::ScaleExceedsMaximumPrecision(ref scale) => { + write!( + f, + "Scale exceeds the maximum precision allowed: {} > {}", + scale, MAX_PRECISION_U32 + ) + } + Self::ConversionTo(ref type_name) => { + write!(f, "Error while converting to {}", type_name) + } + } + } +} diff --git a/third_party/rust/rust_decimal/src/fuzz.rs b/third_party/rust/rust_decimal/src/fuzz.rs new file mode 100644 index 0000000000..14f2e6004e --- /dev/null +++ b/third_party/rust/rust_decimal/src/fuzz.rs @@ -0,0 +1,14 @@ +use crate::Decimal; + +use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured}; + +impl Arbitrary<'_> for crate::Decimal { + fn arbitrary(u: &mut Unstructured<'_>) -> ArbitraryResult<Self> { + let lo = u32::arbitrary(u)?; + let mid = u32::arbitrary(u)?; + let hi = u32::arbitrary(u)?; + let negative = bool::arbitrary(u)?; + let scale = u32::arbitrary(u)?; + Ok(Decimal::from_parts(lo, mid, hi, negative, scale)) + } +} diff --git a/third_party/rust/rust_decimal/src/lib.rs b/third_party/rust/rust_decimal/src/lib.rs new file mode 100644 index 0000000000..84239fd6a8 --- /dev/null +++ b/third_party/rust/rust_decimal/src/lib.rs @@ -0,0 +1,79 @@ +#![doc = include_str!(concat!(env!("OUT_DIR"), "/README-lib.md"))] +#![forbid(unsafe_code)] +#![deny(clippy::print_stdout, clippy::print_stderr)] +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +mod constants; +mod decimal; +mod error; +mod ops; +mod str; + +// We purposely place this here for documentation ordering +mod arithmetic_impls; + +#[cfg(feature = "rust-fuzz")] +mod fuzz; +#[cfg(feature = "maths")] +mod maths; +#[cfg(any(feature = "db-diesel1-mysql", feature = "db-diesel2-mysql"))] +mod mysql; +#[cfg(any( + feature = "db-tokio-postgres", + feature = "db-postgres", + feature = "db-diesel1-postgres", + feature = "db-diesel2-postgres", +))] +mod postgres; +#[cfg(feature = "rand")] +mod rand; +#[cfg(feature = "rocket-traits")] +mod rocket; +#[cfg(all( + feature = "serde", + not(any( + feature = "serde-with-str", + feature = "serde-with-float", + feature = "serde-with-arbitrary-precision" + )) +))] +mod serde; +/// Serde specific functionality to customize how a decimal is serialized/deserialized (`serde_with`) +#[cfg(all( + feature = "serde", + any( + feature = "serde-with-str", + feature = "serde-with-float", + feature = "serde-with-arbitrary-precision" + ) +))] +pub mod serde; + +pub use decimal::{Decimal, RoundingStrategy}; +pub use error::Error; +#[cfg(feature = "maths")] +pub use maths::MathematicalOps; + +/// A convenience module appropriate for glob imports (`use rust_decimal::prelude::*;`). +pub mod prelude { + #[cfg(feature = "maths")] + pub use crate::maths::MathematicalOps; + pub use crate::{Decimal, RoundingStrategy}; + pub use core::str::FromStr; + pub use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero}; +} + +#[cfg(all(feature = "diesel1", not(feature = "diesel2")))] +#[macro_use] +extern crate diesel1 as diesel; + +#[cfg(feature = "diesel2")] +extern crate diesel2 as diesel; + +/// Shortcut for `core::result::Result<T, rust_decimal::Error>`. Useful to distinguish +/// between `rust_decimal` and `std` types. +pub type Result<T> = core::result::Result<T, Error>; + +// #[cfg(feature = "legacy-ops")] +// compiler_error!("legacy-ops has been removed as 1.x"); diff --git a/third_party/rust/rust_decimal/src/maths.rs b/third_party/rust/rust_decimal/src/maths.rs new file mode 100644 index 0000000000..c402453002 --- /dev/null +++ b/third_party/rust/rust_decimal/src/maths.rs @@ -0,0 +1,785 @@ +use crate::prelude::*; +use num_traits::pow::Pow; + +// Tolerance for inaccuracies when calculating exp +const EXP_TOLERANCE: Decimal = Decimal::from_parts(2, 0, 0, false, 7); +// Approximation of 1/ln(10) = 0.4342944819032518276511289189 +const LN10_INVERSE: Decimal = Decimal::from_parts_raw(1763037029, 1670682625, 235431510, 1835008); +// Total iterations of taylor series for Trig. +const TRIG_SERIES_UPPER_BOUND: usize = 6; +// PI / 8 +const EIGHTH_PI: Decimal = Decimal::from_parts_raw(2822163429, 3244459792, 212882598, 1835008); + +// Table representing {index}! +const FACTORIAL: [Decimal; 28] = [ + Decimal::from_parts(1, 0, 0, false, 0), + Decimal::from_parts(1, 0, 0, false, 0), + Decimal::from_parts(2, 0, 0, false, 0), + Decimal::from_parts(6, 0, 0, false, 0), + Decimal::from_parts(24, 0, 0, false, 0), + // 5! + Decimal::from_parts(120, 0, 0, false, 0), + Decimal::from_parts(720, 0, 0, false, 0), + Decimal::from_parts(5040, 0, 0, false, 0), + Decimal::from_parts(40320, 0, 0, false, 0), + Decimal::from_parts(362880, 0, 0, false, 0), + // 10! + Decimal::from_parts(3628800, 0, 0, false, 0), + Decimal::from_parts(39916800, 0, 0, false, 0), + Decimal::from_parts(479001600, 0, 0, false, 0), + Decimal::from_parts(1932053504, 1, 0, false, 0), + Decimal::from_parts(1278945280, 20, 0, false, 0), + // 15! + Decimal::from_parts(2004310016, 304, 0, false, 0), + Decimal::from_parts(2004189184, 4871, 0, false, 0), + Decimal::from_parts(4006445056, 82814, 0, false, 0), + Decimal::from_parts(3396534272, 1490668, 0, false, 0), + Decimal::from_parts(109641728, 28322707, 0, false, 0), + // 20! + Decimal::from_parts(2192834560, 566454140, 0, false, 0), + Decimal::from_parts(3099852800, 3305602358, 2, false, 0), + Decimal::from_parts(3772252160, 4003775155, 60, false, 0), + Decimal::from_parts(862453760, 1892515369, 1401, false, 0), + Decimal::from_parts(3519021056, 2470695900, 33634, false, 0), + // 25! + Decimal::from_parts(2076180480, 1637855376, 840864, false, 0), + Decimal::from_parts(2441084928, 3929534124, 21862473, false, 0), + Decimal::from_parts(1484783616, 3018206259, 590286795, false, 0), +]; + +/// Trait exposing various mathematical operations that can be applied using a Decimal. This is only +/// present when the `maths` feature has been enabled. +pub trait MathematicalOps { + /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within + /// tolerance of roughly `0.0000002`. + fn exp(&self) -> Decimal; + + /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within + /// tolerance of roughly `0.0000002`. Returns `None` on overflow. + fn checked_exp(&self) -> Option<Decimal>; + + /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint + /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating + /// sooner at the potential cost of a slightly less accurate result. + fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal; + + /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint + /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating + /// sooner at the potential cost of a slightly less accurate result. + /// Returns `None` on overflow. + fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal>; + + /// Raise self to the given integer exponent: x<sup>y</sup> + fn powi(&self, exp: i64) -> Decimal; + + /// Raise self to the given integer exponent x<sup>y</sup> returning `None` on overflow. + fn checked_powi(&self, exp: i64) -> Option<Decimal>; + + /// Raise self to the given unsigned integer exponent: x<sup>y</sup> + fn powu(&self, exp: u64) -> Decimal; + + /// Raise self to the given unsigned integer exponent x<sup>y</sup> returning `None` on overflow. + fn checked_powu(&self, exp: u64) -> Option<Decimal>; + + /// Raise self to the given floating point exponent: x<sup>y</sup> + fn powf(&self, exp: f64) -> Decimal; + + /// Raise self to the given floating point exponent x<sup>y</sup> returning `None` on overflow. + fn checked_powf(&self, exp: f64) -> Option<Decimal>; + + /// Raise self to the given Decimal exponent: x<sup>y</sup>. If `exp` is not whole then the approximation + /// e<sup>y*ln(x)</sup> is used. + fn powd(&self, exp: Decimal) -> Decimal; + + /// Raise self to the given Decimal exponent x<sup>y</sup> returning `None` on overflow. + /// If `exp` is not whole then the approximation e<sup>y*ln(x)</sup> is used. + fn checked_powd(&self, exp: Decimal) -> Option<Decimal>; + + /// The square root of a Decimal. Uses a standard Babylonian method. + fn sqrt(&self) -> Option<Decimal>; + + /// Calculates the natural logarithm for a Decimal calculated using Taylor's series. + fn ln(&self) -> Decimal; + + /// Calculates the checked natural logarithm for a Decimal calculated using Taylor's series. + /// Returns `None` for negative numbers or zero. + fn checked_ln(&self) -> Option<Decimal>; + + /// Calculates the base 10 logarithm of a specified Decimal number. + fn log10(&self) -> Decimal; + + /// Calculates the checked base 10 logarithm of a specified Decimal number. + /// Returns `None` for negative numbers or zero. + fn checked_log10(&self) -> Option<Decimal>; + + /// Abramowitz Approximation of Error Function from [wikipedia](https://en.wikipedia.org/wiki/Error_function#Numerical_approximations) + fn erf(&self) -> Decimal; + + /// The Cumulative distribution function for a Normal distribution + fn norm_cdf(&self) -> Decimal; + + /// The Probability density function for a Normal distribution. + fn norm_pdf(&self) -> Decimal; + + /// The Probability density function for a Normal distribution returning `None` on overflow. + fn checked_norm_pdf(&self) -> Option<Decimal>; + + /// Computes the sine of a number (in radians). + /// Panics upon overflow. + fn sin(&self) -> Decimal; + + /// Computes the checked sine of a number (in radians). + fn checked_sin(&self) -> Option<Decimal>; + + /// Computes the cosine of a number (in radians). + /// Panics upon overflow. + fn cos(&self) -> Decimal; + + /// Computes the checked cosine of a number (in radians). + fn checked_cos(&self) -> Option<Decimal>; + + /// Computes the tangent of a number (in radians). + /// Panics upon overflow or upon approaching a limit. + fn tan(&self) -> Decimal; + + /// Computes the checked tangent of a number (in radians). + /// Returns None on limit. + fn checked_tan(&self) -> Option<Decimal>; +} + +impl MathematicalOps for Decimal { + fn exp(&self) -> Decimal { + self.exp_with_tolerance(EXP_TOLERANCE) + } + + fn checked_exp(&self) -> Option<Decimal> { + self.checked_exp_with_tolerance(EXP_TOLERANCE) + } + + fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal { + match self.checked_exp_with_tolerance(tolerance) { + Some(d) => d, + None => { + if self.is_sign_negative() { + panic!("Exp underflowed") + } else { + panic!("Exp overflowed") + } + } + } + } + + fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal> { + if self.is_zero() { + return Some(Decimal::ONE); + } + if self.is_sign_negative() { + let mut flipped = *self; + flipped.set_sign_positive(true); + let exp = flipped.checked_exp_with_tolerance(tolerance)?; + return Decimal::ONE.checked_div(exp); + } + + let mut term = *self; + let mut result = self.checked_add(Decimal::ONE)?; + + for factorial in FACTORIAL.iter().skip(2) { + term = self.checked_mul(term)?; + let next = result + (term / factorial); + let diff = (next - result).abs(); + result = next; + if diff <= tolerance { + break; + } + } + + Some(result) + } + + fn powi(&self, exp: i64) -> Decimal { + match self.checked_powi(exp) { + Some(result) => result, + None => panic!("Pow overflowed"), + } + } + + fn checked_powi(&self, exp: i64) -> Option<Decimal> { + // For negative exponents we change x^-y into 1 / x^y. + // Otherwise, we calculate a standard unsigned exponent + if exp >= 0 { + return self.checked_powu(exp as u64); + } + + // Get the unsigned exponent + let exp = exp.unsigned_abs(); + let pow = match self.checked_powu(exp) { + Some(v) => v, + None => return None, + }; + Decimal::ONE.checked_div(pow) + } + + fn powu(&self, exp: u64) -> Decimal { + match self.checked_powu(exp) { + Some(result) => result, + None => panic!("Pow overflowed"), + } + } + + fn checked_powu(&self, exp: u64) -> Option<Decimal> { + match exp { + 0 => Some(Decimal::ONE), + 1 => Some(*self), + 2 => self.checked_mul(*self), + _ => { + // Get the squared value + let squared = match self.checked_mul(*self) { + Some(s) => s, + None => return None, + }; + // Square self once and make an infinite sized iterator of the square. + let iter = core::iter::repeat(squared); + + // We then take half of the exponent to create a finite iterator and then multiply those together. + let mut product = Decimal::ONE; + for x in iter.take((exp >> 1) as usize) { + match product.checked_mul(x) { + Some(r) => product = r, + None => return None, + }; + } + + // If the exponent is odd we still need to multiply once more + if exp & 0x1 > 0 { + match self.checked_mul(product) { + Some(p) => product = p, + None => return None, + } + } + product.normalize_assign(); + Some(product) + } + } + } + + fn powf(&self, exp: f64) -> Decimal { + match self.checked_powf(exp) { + Some(result) => result, + None => panic!("Pow overflowed"), + } + } + + fn checked_powf(&self, exp: f64) -> Option<Decimal> { + let exp = match Decimal::from_f64(exp) { + Some(f) => f, + None => return None, + }; + self.checked_powd(exp) + } + + fn powd(&self, exp: Decimal) -> Decimal { + match self.checked_powd(exp) { + Some(result) => result, + None => panic!("Pow overflowed"), + } + } + + fn checked_powd(&self, exp: Decimal) -> Option<Decimal> { + if exp.is_zero() { + return Some(Decimal::ONE); + } + if self.is_zero() { + return Some(Decimal::ZERO); + } + if self.is_one() { + return Some(Decimal::ONE); + } + if exp.is_one() { + return Some(*self); + } + + // If the scale is 0 then it's a trivial calculation + let exp = exp.normalize(); + if exp.scale() == 0 { + if exp.mid() != 0 || exp.hi() != 0 { + // Exponent way too big + return None; + } + + return if exp.is_sign_negative() { + self.checked_powi(-(exp.lo() as i64)) + } else { + self.checked_powu(exp.lo() as u64) + }; + } + + // We do some approximations since we've got a decimal exponent. + // For positive bases: a^b = exp(b*ln(a)) + let negative = self.is_sign_negative(); + let e = match self.abs().ln().checked_mul(exp) { + Some(e) => e, + None => return None, + }; + let mut result = e.checked_exp()?; + result.set_sign_negative(negative); + Some(result) + } + + fn sqrt(&self) -> Option<Decimal> { + if self.is_sign_negative() { + return None; + } + + if self.is_zero() { + return Some(Decimal::ZERO); + } + + // Start with an arbitrary number as the first guess + let mut result = self / Decimal::TWO; + // Too small to represent, so we start with self + // Future iterations could actually avoid using a decimal altogether and use a buffered + // vector, only combining back into a decimal on return + if result.is_zero() { + result = *self; + } + let mut last = result + Decimal::ONE; + + // Keep going while the difference is larger than the tolerance + let mut circuit_breaker = 0; + while last != result { + circuit_breaker += 1; + assert!(circuit_breaker < 1000, "geo mean circuit breaker"); + + last = result; + result = (result + self / result) / Decimal::TWO; + } + + Some(result) + } + + #[cfg(feature = "maths-nopanic")] + fn ln(&self) -> Decimal { + match self.checked_ln() { + Some(result) => result, + None => Decimal::ZERO, + } + } + + #[cfg(not(feature = "maths-nopanic"))] + fn ln(&self) -> Decimal { + match self.checked_ln() { + Some(result) => result, + None => { + if self.is_sign_negative() { + panic!("Unable to calculate ln for negative numbers") + } else if self.is_zero() { + panic!("Unable to calculate ln for zero") + } else { + panic!("Calculation of ln failed for unknown reasons") + } + } + } + } + + fn checked_ln(&self) -> Option<Decimal> { + if self.is_sign_negative() || self.is_zero() { + return None; + } + if self.is_one() { + return Some(Decimal::ZERO); + } + + // Approximate using Taylor Series + let mut x = *self; + let mut count = 0; + while x >= Decimal::ONE { + x *= Decimal::E_INVERSE; + count += 1; + } + while x <= Decimal::E_INVERSE { + x *= Decimal::E; + count -= 1; + } + x -= Decimal::ONE; + if x.is_zero() { + return Some(Decimal::new(count, 0)); + } + let mut result = Decimal::ZERO; + let mut iteration = 0; + let mut y = Decimal::ONE; + let mut last = Decimal::ONE; + while last != result && iteration < 100 { + iteration += 1; + last = result; + y *= -x; + result += y / Decimal::new(iteration, 0); + } + Some(Decimal::new(count, 0) - result) + } + + #[cfg(feature = "maths-nopanic")] + fn log10(&self) -> Decimal { + match self.checked_log10() { + Some(result) => result, + None => Decimal::ZERO, + } + } + + #[cfg(not(feature = "maths-nopanic"))] + fn log10(&self) -> Decimal { + match self.checked_log10() { + Some(result) => result, + None => { + if self.is_sign_negative() { + panic!("Unable to calculate log10 for negative numbers") + } else if self.is_zero() { + panic!("Unable to calculate log10 for zero") + } else { + panic!("Calculation of log10 failed for unknown reasons") + } + } + } + } + + fn checked_log10(&self) -> Option<Decimal> { + use crate::ops::array::{div_by_u32, is_all_zero}; + // Early exits + if self.is_sign_negative() || self.is_zero() { + return None; + } + if self.is_one() { + return Some(Decimal::ZERO); + } + + // This uses a very basic method for calculating log10. We know the following is true: + // log10(n) = ln(n) / ln(10) + // From this we can perform some small optimizations: + // 1. ln(10) is a constant + // 2. Multiplication is faster than division, so we can pre-calculate the constant 1/ln(10) + // This allows us to then simplify log10(n) to: + // log10(n) = C * ln(n) + + // Before doing all of this however, we see if there are simple calculations to be made. + let scale = self.scale(); + let mut working = self.mantissa_array3(); + + // Check for scales less than 1 as an early exit + if scale > 0 && working[2] == 0 && working[1] == 0 && working[0] == 1 { + return Some(Decimal::from_parts(scale, 0, 0, true, 0)); + } + + // Loop for detecting bordering base 10 values + let mut result = 0; + let mut base10 = true; + while !is_all_zero(&working) { + let remainder = div_by_u32(&mut working, 10u32); + if remainder != 0 { + base10 = false; + break; + } + result += 1; + if working[2] == 0 && working[1] == 0 && working[0] == 1 { + break; + } + } + if base10 { + return Some((result - scale as i32).into()); + } + + self.checked_ln().map(|result| LN10_INVERSE * result) + } + + fn erf(&self) -> Decimal { + if self.is_sign_positive() { + let one = &Decimal::ONE; + + let xa1 = self * Decimal::from_parts(705230784, 0, 0, false, 10); + let xa2 = self.powi(2) * Decimal::from_parts(422820123, 0, 0, false, 10); + let xa3 = self.powi(3) * Decimal::from_parts(92705272, 0, 0, false, 10); + let xa4 = self.powi(4) * Decimal::from_parts(1520143, 0, 0, false, 10); + let xa5 = self.powi(5) * Decimal::from_parts(2765672, 0, 0, false, 10); + let xa6 = self.powi(6) * Decimal::from_parts(430638, 0, 0, false, 10); + + let sum = one + xa1 + xa2 + xa3 + xa4 + xa5 + xa6; + one - (one / sum.powi(16)) + } else { + -self.abs().erf() + } + } + + fn norm_cdf(&self) -> Decimal { + (Decimal::ONE + (self / Decimal::from_parts(2318911239, 3292722, 0, false, 16)).erf()) / Decimal::TWO + } + + fn norm_pdf(&self) -> Decimal { + match self.checked_norm_pdf() { + Some(d) => d, + None => panic!("Norm Pdf overflowed"), + } + } + + fn checked_norm_pdf(&self) -> Option<Decimal> { + let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008); + let factor = -self.checked_powi(2)?; + let factor = factor.checked_div(Decimal::TWO)?; + factor.checked_exp()?.checked_div(sqrt2pi) + } + + fn sin(&self) -> Decimal { + match self.checked_sin() { + Some(x) => x, + None => panic!("Sin overflowed"), + } + } + + fn checked_sin(&self) -> Option<Decimal> { + if self.is_zero() { + return Some(Decimal::ZERO); + } + if self.is_sign_negative() { + // -Sin(-x) + return (-self).checked_sin().map(|x| -x); + } + if self >= &Decimal::TWO_PI { + // Reduce large numbers early - we can do this using rem to constrain to a range + let adjusted = self.checked_rem(Decimal::TWO_PI)?; + return adjusted.checked_sin(); + } + if self >= &Decimal::PI { + // -Sin(x-Ï€) + return (self - Decimal::PI).checked_sin().map(|x| -x); + } + if self >= &Decimal::QUARTER_PI { + // Cos(Ï€2-x) + return (Decimal::HALF_PI - self).checked_cos(); + } + + // Taylor series: + // ∑(n=0 to ∞) : ((−1)^n / (2n + 1)!) * x^(2n + 1) , x∈R + // First few expansions: + // x^1/1! - x^3/3! + x^5/5! - x^7/7! + x^9/9! + let mut result = Decimal::ZERO; + for n in 0..TRIG_SERIES_UPPER_BOUND { + let x = 2 * n + 1; + let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?; + if n & 0x1 == 0 { + result += element; + } else { + result -= element; + } + } + Some(result) + } + + fn cos(&self) -> Decimal { + match self.checked_cos() { + Some(x) => x, + None => panic!("Cos overflowed"), + } + } + + fn checked_cos(&self) -> Option<Decimal> { + if self.is_zero() { + return Some(Decimal::ONE); + } + if self.is_sign_negative() { + // Cos(-x) + return (-self).checked_cos(); + } + if self >= &Decimal::TWO_PI { + // Reduce large numbers early - we can do this using rem to constrain to a range + let adjusted = self.checked_rem(Decimal::TWO_PI)?; + return adjusted.checked_cos(); + } + if self >= &Decimal::PI { + // -Cos(x-Ï€) + return (self - Decimal::PI).checked_cos().map(|x| -x); + } + if self >= &Decimal::QUARTER_PI { + // Sin(Ï€2-x) + return (Decimal::HALF_PI - self).checked_sin(); + } + + // Taylor series: + // ∑(n=0 to ∞) : ((−1)^n / (2n)!) * x^(2n) , x∈R + // First few expansions: + // x^0/0! - x^2/2! + x^4/4! - x^6/6! + x^8/8! + let mut result = Decimal::ZERO; + for n in 0..TRIG_SERIES_UPPER_BOUND { + let x = 2 * n; + let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?; + if n & 0x1 == 0 { + result += element; + } else { + result -= element; + } + } + Some(result) + } + + fn tan(&self) -> Decimal { + match self.checked_tan() { + Some(x) => x, + None => panic!("Tan overflowed"), + } + } + + fn checked_tan(&self) -> Option<Decimal> { + if self.is_zero() { + return Some(Decimal::ZERO); + } + if self.is_sign_negative() { + // -Tan(-x) + return (-self).checked_tan().map(|x| -x); + } + if self >= &Decimal::TWO_PI { + // Reduce large numbers early - we can do this using rem to constrain to a range + let adjusted = self.checked_rem(Decimal::TWO_PI)?; + return adjusted.checked_tan(); + } + // Reduce to 0 <= x <= PI + if self >= &Decimal::PI { + // Tan(x-Ï€) + return (self - Decimal::PI).checked_tan(); + } + // Reduce to 0 <= x <= PI/2 + if self > &Decimal::HALF_PI { + // We can use the symmetrical function inside the first quadrant + // e.g. tan(x) = -tan((PI/2 - x) + PI/2) + return ((Decimal::HALF_PI - self) + Decimal::HALF_PI).checked_tan().map(|x| -x); + } + + // It has now been reduced to 0 <= x <= PI/2. If it is >= PI/4 we can make it even smaller + // by calculating tan(PI/2 - x) and taking the reciprocal + if self > &Decimal::QUARTER_PI { + return match (Decimal::HALF_PI - self).checked_tan() { + Some(x) => Decimal::ONE.checked_div(x), + None => None, + }; + } + + // Due the way that tan(x) sharply tends towards infinity, we try to optimize + // the resulting accuracy by using Trigonometric identity when > PI/8. We do this by + // replacing the angle with one that is half as big. + if self > &EIGHTH_PI { + // Work out tan(x/2) + let tan_half = (self / Decimal::TWO).checked_tan()?; + // Work out the dividend i.e. 2tan(x/2) + let dividend = Decimal::TWO.checked_mul(tan_half)?; + + // Work out the divisor i.e. 1 - tan^2(x/2) + let squared = tan_half.checked_mul(tan_half)?; + let divisor = Decimal::ONE - squared; + // Treat this as infinity + if divisor.is_zero() { + return None; + } + return dividend.checked_div(divisor); + } + + // Do a polynomial approximation based upon the Maclaurin series. + // This can be simplified to something like: + // + // ∑(n=1,3,5,7,9)(f(n)(0)/n!)x^n + // + // First few expansions (which we leverage): + // (f'(0)/1!)x^1 + (f'''(0)/3!)x^3 + (f'''''(0)/5!)x^5 + (f'''''''/7!)x^7 + // + // x + (1/3)x^3 + (2/15)x^5 + (17/315)x^7 + (62/2835)x^9 + (1382/155925)x^11 + // + // (Generated by https://www.wolframalpha.com/widgets/view.jsp?id=fe1ad8d4f5dbb3cb866d0c89beb527a6) + // The more terms, the better the accuracy. This generates accuracy within approx 10^-8 for angles + // less than PI/8. + const SERIES: [(Decimal, u64); 6] = [ + // 1 / 3 + (Decimal::from_parts_raw(89478485, 347537611, 180700362, 1835008), 3), + // 2 / 15 + (Decimal::from_parts_raw(894784853, 3574988881, 72280144, 1835008), 5), + // 17 / 315 + (Decimal::from_parts_raw(905437054, 3907911371, 2925624, 1769472), 7), + // 62 / 2835 + (Decimal::from_parts_raw(3191872741, 2108928381, 11855473, 1835008), 9), + // 1382 / 155925 + (Decimal::from_parts_raw(3482645539, 2612995122, 4804769, 1835008), 11), + // 21844 / 6081075 + (Decimal::from_parts_raw(4189029078, 2192791200, 1947296, 1835008), 13), + ]; + let mut result = *self; + for (fraction, pow) in SERIES { + result += fraction * self.powu(pow); + } + Some(result) + } +} + +impl Pow<Decimal> for Decimal { + type Output = Decimal; + + fn pow(self, rhs: Decimal) -> Self::Output { + MathematicalOps::powd(&self, rhs) + } +} + +impl Pow<u64> for Decimal { + type Output = Decimal; + + fn pow(self, rhs: u64) -> Self::Output { + MathematicalOps::powu(&self, rhs) + } +} + +impl Pow<i64> for Decimal { + type Output = Decimal; + + fn pow(self, rhs: i64) -> Self::Output { + MathematicalOps::powi(&self, rhs) + } +} + +impl Pow<f64> for Decimal { + type Output = Decimal; + + fn pow(self, rhs: f64) -> Self::Output { + MathematicalOps::powf(&self, rhs) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(not(feature = "std"))] + use alloc::string::ToString; + + #[test] + fn test_factorials() { + assert_eq!("1", FACTORIAL[0].to_string(), "0!"); + assert_eq!("1", FACTORIAL[1].to_string(), "1!"); + assert_eq!("2", FACTORIAL[2].to_string(), "2!"); + assert_eq!("6", FACTORIAL[3].to_string(), "3!"); + assert_eq!("24", FACTORIAL[4].to_string(), "4!"); + assert_eq!("120", FACTORIAL[5].to_string(), "5!"); + assert_eq!("720", FACTORIAL[6].to_string(), "6!"); + assert_eq!("5040", FACTORIAL[7].to_string(), "7!"); + assert_eq!("40320", FACTORIAL[8].to_string(), "8!"); + assert_eq!("362880", FACTORIAL[9].to_string(), "9!"); + assert_eq!("3628800", FACTORIAL[10].to_string(), "10!"); + assert_eq!("39916800", FACTORIAL[11].to_string(), "11!"); + assert_eq!("479001600", FACTORIAL[12].to_string(), "12!"); + assert_eq!("6227020800", FACTORIAL[13].to_string(), "13!"); + assert_eq!("87178291200", FACTORIAL[14].to_string(), "14!"); + assert_eq!("1307674368000", FACTORIAL[15].to_string(), "15!"); + assert_eq!("20922789888000", FACTORIAL[16].to_string(), "16!"); + assert_eq!("355687428096000", FACTORIAL[17].to_string(), "17!"); + assert_eq!("6402373705728000", FACTORIAL[18].to_string(), "18!"); + assert_eq!("121645100408832000", FACTORIAL[19].to_string(), "19!"); + assert_eq!("2432902008176640000", FACTORIAL[20].to_string(), "20!"); + assert_eq!("51090942171709440000", FACTORIAL[21].to_string(), "21!"); + assert_eq!("1124000727777607680000", FACTORIAL[22].to_string(), "22!"); + assert_eq!("25852016738884976640000", FACTORIAL[23].to_string(), "23!"); + assert_eq!("620448401733239439360000", FACTORIAL[24].to_string(), "24!"); + assert_eq!("15511210043330985984000000", FACTORIAL[25].to_string(), "25!"); + assert_eq!("403291461126605635584000000", FACTORIAL[26].to_string(), "26!"); + assert_eq!("10888869450418352160768000000", FACTORIAL[27].to_string(), "27!"); + } +} diff --git a/third_party/rust/rust_decimal/src/mysql.rs b/third_party/rust/rust_decimal/src/mysql.rs new file mode 100644 index 0000000000..6dc0db253d --- /dev/null +++ b/third_party/rust/rust_decimal/src/mysql.rs @@ -0,0 +1,241 @@ +use crate::Decimal; +use diesel::{ + deserialize::{self, FromSql}, + mysql::Mysql, + serialize::{self, IsNull, Output, ToSql}, + sql_types::Numeric, +}; +use std::io::Write; +use std::str::FromStr; + +#[cfg(all(feature = "diesel1", not(feature = "diesel2")))] +impl ToSql<Numeric, Mysql> for Decimal { + fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result { + write!(out, "{}", *self).map(|_| IsNull::No).map_err(|e| e.into()) + } +} + +#[cfg(feature = "diesel2")] +impl ToSql<Numeric, Mysql> for Decimal { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + write!(out, "{}", *self).map(|_| IsNull::No).map_err(|e| e.into()) + } +} + +#[cfg(all(feature = "diesel1", not(feature = "diesel2")))] +impl FromSql<Numeric, Mysql> for Decimal { + fn from_sql(numeric: Option<&[u8]>) -> deserialize::Result<Self> { + // From what I can ascertain, MySQL simply reads from a string format for the Decimal type. + // Explicitly, it looks like it is length followed by the string. Regardless, we can leverage + // internal types. + let bytes = numeric.ok_or("Invalid decimal")?; + let s = std::str::from_utf8(bytes)?; + Decimal::from_str(s).map_err(|e| e.into()) + } +} + +#[cfg(feature = "diesel2")] +impl FromSql<Numeric, Mysql> for Decimal { + fn from_sql(numeric: diesel::mysql::MysqlValue) -> deserialize::Result<Self> { + // From what I can ascertain, MySQL simply reads from a string format for the Decimal type. + // Explicitly, it looks like it is length followed by the string. Regardless, we can leverage + // internal types. + let s = std::str::from_utf8(numeric.as_bytes())?; + Decimal::from_str(s).map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use diesel::deserialize::QueryableByName; + use diesel::prelude::*; + use diesel::row::NamedRow; + use diesel::sql_query; + use diesel::sql_types::Text; + + struct Test { + value: Decimal, + } + + struct NullableTest { + value: Option<Decimal>, + } + + pub static TEST_DECIMALS: &[(u32, u32, &str, &str)] = &[ + // precision, scale, sent, expected + (1, 0, "1", "1"), + (6, 2, "1", "1.00"), + (6, 2, "9999.99", "9999.99"), + (35, 6, "3950.123456", "3950.123456"), + (10, 2, "3950.123456", "3950.12"), + (35, 6, "3950", "3950.000000"), + (4, 0, "3950", "3950"), + (35, 6, "0.1", "0.100000"), + (35, 6, "0.01", "0.010000"), + (35, 6, "0.001", "0.001000"), + (35, 6, "0.0001", "0.000100"), + (35, 6, "0.00001", "0.000010"), + (35, 6, "0.000001", "0.000001"), + (35, 6, "1", "1.000000"), + (35, 6, "-100", "-100.000000"), + (35, 6, "-123.456", "-123.456000"), + (35, 6, "119996.25", "119996.250000"), + (35, 6, "1000000", "1000000.000000"), + (35, 6, "9999999.99999", "9999999.999990"), + (35, 6, "12340.56789", "12340.567890"), + ]; + + /// Gets the URL for connecting to MySQL for testing. Set the MYSQL_URL + /// environment variable to change from the default of "mysql://root@localhost/mysql". + fn get_mysql_url() -> String { + if let Ok(url) = std::env::var("MYSQL_URL") { + return url; + } + "mysql://root@127.0.0.1/mysql".to_string() + } + + #[cfg(all(feature = "diesel1", not(feature = "diesel2")))] + mod diesel1 { + use super::*; + + impl QueryableByName<Mysql> for Test { + fn build<R: NamedRow<Mysql>>(row: &R) -> deserialize::Result<Self> { + let value = row.get("value")?; + Ok(Test { value }) + } + } + + impl QueryableByName<Mysql> for NullableTest { + fn build<R: NamedRow<Mysql>>(row: &R) -> deserialize::Result<Self> { + let value = row.get("value")?; + Ok(NullableTest { value }) + } + } + + #[test] + fn test_null() { + let connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + + // Test NULL + let items: Vec<NullableTest> = sql_query("SELECT CAST(NULL AS DECIMAL) AS value") + .load(&connection) + .expect("Unable to query value"); + let result = items.first().unwrap().value; + assert_eq!(None, result); + } + + #[test] + fn read_numeric_type() { + let connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let items: Vec<Test> = sql_query(format!( + "SELECT CAST('{}' AS DECIMAL({}, {})) AS value", + sent, precision, scale + )) + .load(&connection) + .expect("Unable to query value"); + assert_eq!( + expected, + items.first().unwrap().value.to_string(), + "DECIMAL({}, {}) sent: {}", + precision, + scale, + sent + ); + } + } + + #[test] + fn write_numeric_type() { + let connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let items: Vec<Test> = + sql_query(format!("SELECT CAST(? AS DECIMAL({}, {})) AS value", precision, scale)) + .bind::<Text, _>(sent) + .load(&connection) + .expect("Unable to query value"); + assert_eq!( + expected, + items.first().unwrap().value.to_string(), + "DECIMAL({}, {}) sent: {}", + precision, + scale, + sent + ); + } + } + } + + #[cfg(feature = "diesel2")] + mod diesel2 { + use super::*; + + impl QueryableByName<Mysql> for Test { + fn build<'a>(row: &impl NamedRow<'a, Mysql>) -> deserialize::Result<Self> { + let value = NamedRow::get(row, "value")?; + Ok(Test { value }) + } + } + + impl QueryableByName<Mysql> for NullableTest { + fn build<'a>(row: &impl NamedRow<'a, Mysql>) -> deserialize::Result<Self> { + let value = NamedRow::get(row, "value")?; + Ok(NullableTest { value }) + } + } + + #[test] + fn test_null() { + let mut connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + + // Test NULL + let items: Vec<NullableTest> = sql_query("SELECT CAST(NULL AS DECIMAL) AS value") + .load(&mut connection) + .expect("Unable to query value"); + let result = items.first().unwrap().value; + assert_eq!(None, result); + } + + #[test] + fn read_numeric_type() { + let mut connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let items: Vec<Test> = sql_query(format!( + "SELECT CAST('{}' AS DECIMAL({}, {})) AS value", + sent, precision, scale + )) + .load(&mut connection) + .expect("Unable to query value"); + assert_eq!( + expected, + items.first().unwrap().value.to_string(), + "DECIMAL({}, {}) sent: {}", + precision, + scale, + sent + ); + } + } + + #[test] + fn write_numeric_type() { + let mut connection = diesel::MysqlConnection::establish(&get_mysql_url()).expect("Establish connection"); + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let items: Vec<Test> = + sql_query(format!("SELECT CAST(? AS DECIMAL({}, {})) AS value", precision, scale)) + .bind::<Text, _>(sent) + .load(&mut connection) + .expect("Unable to query value"); + assert_eq!( + expected, + items.first().unwrap().value.to_string(), + "DECIMAL({}, {}) sent: {}", + precision, + scale, + sent + ); + } + } + } +} diff --git a/third_party/rust/rust_decimal/src/ops.rs b/third_party/rust/rust_decimal/src/ops.rs new file mode 100644 index 0000000000..6bda140e89 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops.rs @@ -0,0 +1,34 @@ +// This code (in fact, this library) is heavily inspired by the dotnet Decimal number library +// implementation. Consequently, a huge thank you for to all the contributors to that project +// whose work has also inspired the solutions found here. + +pub(crate) mod array; + +#[cfg(feature = "legacy-ops")] +mod legacy; +#[cfg(feature = "legacy-ops")] +pub(crate) use legacy::{add_impl, cmp_impl, div_impl, mul_impl, rem_impl, sub_impl}; + +#[cfg(not(feature = "legacy-ops"))] +mod add; +#[cfg(not(feature = "legacy-ops"))] +mod cmp; +#[cfg(not(feature = "legacy-ops"))] +pub(in crate::ops) mod common; +#[cfg(not(feature = "legacy-ops"))] +mod div; +#[cfg(not(feature = "legacy-ops"))] +mod mul; +#[cfg(not(feature = "legacy-ops"))] +mod rem; + +#[cfg(not(feature = "legacy-ops"))] +pub(crate) use add::{add_impl, sub_impl}; +#[cfg(not(feature = "legacy-ops"))] +pub(crate) use cmp::cmp_impl; +#[cfg(not(feature = "legacy-ops"))] +pub(crate) use div::div_impl; +#[cfg(not(feature = "legacy-ops"))] +pub(crate) use mul::mul_impl; +#[cfg(not(feature = "legacy-ops"))] +pub(crate) use rem::rem_impl; diff --git a/third_party/rust/rust_decimal/src/ops/add.rs b/third_party/rust/rust_decimal/src/ops/add.rs new file mode 100644 index 0000000000..52ba675f23 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/add.rs @@ -0,0 +1,382 @@ +use crate::constants::{MAX_I32_SCALE, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, U32_MASK, U32_MAX}; +use crate::decimal::{CalculationResult, Decimal}; +use crate::ops::common::{Buf24, Dec64}; + +pub(crate) fn add_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + add_sub_internal(d1, d2, false) +} + +pub(crate) fn sub_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + add_sub_internal(d1, d2, true) +} + +#[inline] +fn add_sub_internal(d1: &Decimal, d2: &Decimal, subtract: bool) -> CalculationResult { + if d1.is_zero() { + // 0 - x or 0 + x + let mut result = *d2; + if subtract && !d2.is_zero() { + result.set_sign_negative(d2.is_sign_positive()); + } + return CalculationResult::Ok(result); + } + if d2.is_zero() { + // x - 0 or x + 0 + return CalculationResult::Ok(*d1); + } + + // Work out whether we need to rescale and/or if it's a subtract still given the signs of the + // numbers. + let flags = d1.flags() ^ d2.flags(); + let subtract = subtract ^ ((flags & SIGN_MASK) != 0); + let rescale = (flags & SCALE_MASK) > 0; + + // We optimize towards using 32 bit logic as much as possible. It's noticeably faster at + // scale, even on 64 bit machines + if d1.mid() | d1.hi() == 0 && d2.mid() | d2.hi() == 0 { + // We'll try to rescale, however we may end up with 64 bit (or more) numbers + // If we do, we'll choose a different flow than fast_add + if rescale { + // This is less optimized if we scale to a 64 bit integer. We can add some further logic + // here later on. + let rescale_factor = ((d2.flags() & SCALE_MASK) as i32 - (d1.flags() & SCALE_MASK) as i32) >> SCALE_SHIFT; + if rescale_factor < 0 { + // We try to rescale the rhs + if let Some(rescaled) = rescale32(d2.lo(), -rescale_factor) { + return fast_add(d1.lo(), rescaled, d1.flags(), subtract); + } + } else { + // We try to rescale the lhs + if let Some(rescaled) = rescale32(d1.lo(), rescale_factor) { + return fast_add( + rescaled, + d2.lo(), + (d2.flags() & SCALE_MASK) | (d1.flags() & SIGN_MASK), + subtract, + ); + } + } + } else { + return fast_add(d1.lo(), d2.lo(), d1.flags(), subtract); + } + } + + // Continue on with the slower 64 bit method + let d1 = Dec64::new(d1); + let d2 = Dec64::new(d2); + + // If we're not the same scale then make sure we're there first before starting addition + if rescale { + let rescale_factor = d2.scale as i32 - d1.scale as i32; + if rescale_factor < 0 { + let negative = subtract ^ d1.negative; + let scale = d1.scale; + unaligned_add(d2, d1, negative, scale, -rescale_factor, subtract) + } else { + let negative = d1.negative; + let scale = d2.scale; + unaligned_add(d1, d2, negative, scale, rescale_factor, subtract) + } + } else { + let neg = d1.negative; + let scale = d1.scale; + aligned_add(d1, d2, neg, scale, subtract) + } +} + +#[inline(always)] +fn rescale32(num: u32, rescale_factor: i32) -> Option<u32> { + if rescale_factor > MAX_I32_SCALE { + return None; + } + num.checked_mul(POWERS_10[rescale_factor as usize]) +} + +fn fast_add(lo1: u32, lo2: u32, flags: u32, subtract: bool) -> CalculationResult { + if subtract { + // Sub can't overflow because we're ensuring the bigger number always subtracts the smaller number + if lo1 < lo2 { + return CalculationResult::Ok(Decimal::from_parts_raw(lo2 - lo1, 0, 0, flags ^ SIGN_MASK)); + } + return CalculationResult::Ok(Decimal::from_parts_raw(lo1 - lo2, 0, 0, flags)); + } + // Add can overflow however, so we check for that explicitly + let lo = lo1.wrapping_add(lo2); + let mid = if lo < lo1 { 1 } else { 0 }; + CalculationResult::Ok(Decimal::from_parts_raw(lo, mid, 0, flags)) +} + +fn aligned_add(lhs: Dec64, rhs: Dec64, negative: bool, scale: u32, subtract: bool) -> CalculationResult { + if subtract { + // Signs differ, so subtract + let mut result = Dec64 { + negative, + scale, + low64: lhs.low64.wrapping_sub(rhs.low64), + hi: lhs.hi.wrapping_sub(rhs.hi), + }; + + // Check for carry + if result.low64 > lhs.low64 { + result.hi = result.hi.wrapping_sub(1); + if result.hi >= lhs.hi { + flip_sign(&mut result); + } + } else if result.hi > lhs.hi { + flip_sign(&mut result); + } + CalculationResult::Ok(result.to_decimal()) + } else { + // Signs are the same, so add + let mut result = Dec64 { + negative, + scale, + low64: lhs.low64.wrapping_add(rhs.low64), + hi: lhs.hi.wrapping_add(rhs.hi), + }; + + // Check for carry + if result.low64 < lhs.low64 { + result.hi = result.hi.wrapping_add(1); + if result.hi <= lhs.hi { + if result.scale == 0 { + return CalculationResult::Overflow; + } + reduce_scale(&mut result); + } + } else if result.hi < lhs.hi { + if result.scale == 0 { + return CalculationResult::Overflow; + } + reduce_scale(&mut result); + } + CalculationResult::Ok(result.to_decimal()) + } +} + +fn flip_sign(result: &mut Dec64) { + // Bitwise not the high portion + result.hi = !result.hi; + let low64 = ((result.low64 as i64).wrapping_neg()) as u64; + if low64 == 0 { + result.hi += 1; + } + result.low64 = low64; + result.negative = !result.negative; +} + +fn reduce_scale(result: &mut Dec64) { + let mut low64 = result.low64; + let mut hi = result.hi; + + let mut num = (hi as u64) + (1u64 << 32); + hi = (num / 10u64) as u32; + num = ((num - (hi as u64) * 10u64) << 32) + (low64 >> 32); + let mut div = (num / 10) as u32; + num = ((num - (div as u64) * 10u64) << 32) + (low64 & U32_MASK); + low64 = (div as u64) << 32; + div = (num / 10u64) as u32; + low64 = low64.wrapping_add(div as u64); + let remainder = (num as u32).wrapping_sub(div.wrapping_mul(10)); + + // Finally, round. This is optimizing slightly toward non-rounded numbers + if remainder >= 5 && (remainder > 5 || (low64 & 1) > 0) { + low64 = low64.wrapping_add(1); + if low64 == 0 { + hi += 1; + } + } + + result.low64 = low64; + result.hi = hi; + result.scale -= 1; +} + +// Assumption going into this function is that the LHS is the larger number and will "absorb" the +// smaller number. +fn unaligned_add( + lhs: Dec64, + rhs: Dec64, + negative: bool, + scale: u32, + rescale_factor: i32, + subtract: bool, +) -> CalculationResult { + let mut lhs = lhs; + let mut low64 = lhs.low64; + let mut high = lhs.hi; + let mut rescale_factor = rescale_factor; + + // First off, we see if we can get away with scaling small amounts (or none at all) + if high == 0 { + if low64 <= U32_MAX { + // We know it's not zero, so we start scaling. + // Start with reducing the scale down for the low portion + while low64 <= U32_MAX { + if rescale_factor <= MAX_I32_SCALE { + low64 *= POWERS_10[rescale_factor as usize] as u64; + lhs.low64 = low64; + return aligned_add(lhs, rhs, negative, scale, subtract); + } + rescale_factor -= MAX_I32_SCALE; + low64 *= POWERS_10[9] as u64; + } + } + + // Reduce the scale for the high portion + while high == 0 { + let power = if rescale_factor <= MAX_I32_SCALE { + POWERS_10[rescale_factor as usize] as u64 + } else { + POWERS_10[9] as u64 + }; + + let tmp_low = (low64 & U32_MASK) * power; + let tmp_hi = (low64 >> 32) * power + (tmp_low >> 32); + low64 = (tmp_low & U32_MASK) + (tmp_hi << 32); + high = (tmp_hi >> 32) as u32; + rescale_factor -= MAX_I32_SCALE; + if rescale_factor <= 0 { + lhs.low64 = low64; + lhs.hi = high; + return aligned_add(lhs, rhs, negative, scale, subtract); + } + } + } + + // See if we can get away with keeping it in the 96 bits. Otherwise, we need a buffer + let mut tmp64: u64; + loop { + let power = if rescale_factor <= MAX_I32_SCALE { + POWERS_10[rescale_factor as usize] as u64 + } else { + POWERS_10[9] as u64 + }; + + let tmp_low = (low64 & U32_MASK) * power; + tmp64 = (low64 >> 32) * power + (tmp_low >> 32); + low64 = (tmp_low & U32_MASK) + (tmp64 << 32); + tmp64 >>= 32; + tmp64 += (high as u64) * power; + + rescale_factor -= MAX_I32_SCALE; + + if tmp64 > U32_MAX { + break; + } else { + high = tmp64 as u32; + if rescale_factor <= 0 { + lhs.low64 = low64; + lhs.hi = high; + return aligned_add(lhs, rhs, negative, scale, subtract); + } + } + } + + let mut buffer = Buf24::zero(); + buffer.set_low64(low64); + buffer.set_mid64(tmp64); + + let mut upper_word = buffer.upper_word(); + while rescale_factor > 0 { + let power = if rescale_factor <= MAX_I32_SCALE { + POWERS_10[rescale_factor as usize] as u64 + } else { + POWERS_10[9] as u64 + }; + tmp64 = 0; + for (index, part) in buffer.data.iter_mut().enumerate() { + tmp64 = tmp64.wrapping_add((*part as u64) * power); + *part = tmp64 as u32; + tmp64 >>= 32; + if index + 1 > upper_word { + break; + } + } + + if tmp64 & U32_MASK > 0 { + // Extend the result + upper_word += 1; + buffer.data[upper_word] = tmp64 as u32; + } + + rescale_factor -= MAX_I32_SCALE; + } + + // Do the add + tmp64 = buffer.low64(); + low64 = rhs.low64; + let tmp_hi = buffer.data[2]; + high = rhs.hi; + + if subtract { + low64 = tmp64.wrapping_sub(low64); + high = tmp_hi.wrapping_sub(high); + + // Check for carry + let carry = if low64 > tmp64 { + high = high.wrapping_sub(1); + high >= tmp_hi + } else { + high > tmp_hi + }; + + if carry { + for part in buffer.data.iter_mut().skip(3) { + *part = part.wrapping_sub(1); + if *part > 0 { + break; + } + } + + if buffer.data[upper_word] == 0 && upper_word < 3 { + return CalculationResult::Ok(Decimal::from_parts( + low64 as u32, + (low64 >> 32) as u32, + high, + negative, + scale, + )); + } + } + } else { + low64 = low64.wrapping_add(tmp64); + high = high.wrapping_add(tmp_hi); + + // Check for carry + let carry = if low64 < tmp64 { + high = high.wrapping_add(1); + high <= tmp_hi + } else { + high < tmp_hi + }; + + if carry { + for (index, part) in buffer.data.iter_mut().enumerate().skip(3) { + if upper_word < index { + *part = 1; + upper_word = index; + break; + } + *part = part.wrapping_add(1); + if *part > 0 { + break; + } + } + } + } + + buffer.set_low64(low64); + buffer.data[2] = high; + if let Some(scale) = buffer.rescale(upper_word, scale) { + CalculationResult::Ok(Decimal::from_parts( + buffer.data[0], + buffer.data[1], + buffer.data[2], + negative, + scale, + )) + } else { + CalculationResult::Overflow + } +} diff --git a/third_party/rust/rust_decimal/src/ops/array.rs b/third_party/rust/rust_decimal/src/ops/array.rs new file mode 100644 index 0000000000..2ab58b4dfb --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/array.rs @@ -0,0 +1,381 @@ +use crate::constants::{MAX_PRECISION_U32, POWERS_10, U32_MASK}; + +/// Rescales the given decimal to new scale. +/// e.g. with 1.23 and new scale 3 rescale the value to 1.230 +#[inline(always)] +pub(crate) fn rescale_internal(value: &mut [u32; 3], value_scale: &mut u32, new_scale: u32) { + if *value_scale == new_scale { + // Nothing to do + return; + } + + if is_all_zero(value) { + *value_scale = new_scale.min(MAX_PRECISION_U32); + return; + } + + if *value_scale > new_scale { + let mut diff = value_scale.wrapping_sub(new_scale); + // Scaling further isn't possible since we got an overflow + // In this case we need to reduce the accuracy of the "side to keep" + + // Now do the necessary rounding + let mut remainder = 0; + while let Some(diff_minus_one) = diff.checked_sub(1) { + if is_all_zero(value) { + *value_scale = new_scale; + return; + } + + diff = diff_minus_one; + + // Any remainder is discarded if diff > 0 still (i.e. lost precision) + remainder = div_by_u32(value, 10); + } + if remainder >= 5 { + for part in value.iter_mut() { + let digit = u64::from(*part) + 1u64; + remainder = if digit > U32_MASK { 1 } else { 0 }; + *part = (digit & U32_MASK) as u32; + if remainder == 0 { + break; + } + } + } + *value_scale = new_scale; + } else { + let mut diff = new_scale.wrapping_sub(*value_scale); + let mut working = [value[0], value[1], value[2]]; + while let Some(diff_minus_one) = diff.checked_sub(1) { + if mul_by_10(&mut working) == 0 { + value.copy_from_slice(&working); + diff = diff_minus_one; + } else { + break; + } + } + *value_scale = new_scale.wrapping_sub(diff); + } +} + +#[cfg(feature = "legacy-ops")] +pub(crate) fn add_by_internal(value: &mut [u32], by: &[u32]) -> u32 { + let mut carry: u64 = 0; + let vl = value.len(); + let bl = by.len(); + if vl >= bl { + let mut sum: u64; + for i in 0..bl { + sum = u64::from(value[i]) + u64::from(by[i]) + carry; + value[i] = (sum & U32_MASK) as u32; + carry = sum >> 32; + } + if vl > bl && carry > 0 { + for i in value.iter_mut().skip(bl) { + sum = u64::from(*i) + carry; + *i = (sum & U32_MASK) as u32; + carry = sum >> 32; + if carry == 0 { + break; + } + } + } + } else if vl + 1 == bl { + // Overflow, by default, is anything in the high portion of by + let mut sum: u64; + for i in 0..vl { + sum = u64::from(value[i]) + u64::from(by[i]) + carry; + value[i] = (sum & U32_MASK) as u32; + carry = sum >> 32; + } + if by[vl] > 0 { + carry += u64::from(by[vl]); + } + } else { + panic!("Internal error: add using incompatible length arrays. {} <- {}", vl, bl); + } + carry as u32 +} + +pub(crate) fn add_by_internal_flattened(value: &mut [u32; 3], by: u32) -> u32 { + manage_add_by_internal(by, value) +} + +#[inline] +pub(crate) fn add_one_internal(value: &mut [u32; 3]) -> u32 { + manage_add_by_internal(1, value) +} + +// `u64 as u32` are safe because of widening and 32bits shifts +#[inline] +pub(crate) fn manage_add_by_internal<const N: usize>(initial_carry: u32, value: &mut [u32; N]) -> u32 { + let mut carry = u64::from(initial_carry); + let mut iter = 0..value.len(); + let mut sum = 0; + + let mut sum_fn = |local_carry: &mut u64, idx| { + sum = u64::from(value[idx]).wrapping_add(*local_carry); + value[idx] = (sum & U32_MASK) as u32; + *local_carry = sum.wrapping_shr(32); + }; + + if let Some(idx) = iter.next() { + sum_fn(&mut carry, idx); + } + + for idx in iter { + if carry > 0 { + sum_fn(&mut carry, idx); + } + } + + carry as u32 +} + +pub(crate) fn sub_by_internal(value: &mut [u32], by: &[u32]) -> u32 { + // The way this works is similar to long subtraction + // Let's assume we're working with bytes for simplicity in an example: + // 257 - 8 = 249 + // 0000_0001 0000_0001 - 0000_0000 0000_1000 = 0000_0000 1111_1001 + // We start by doing the first byte... + // Overflow = 0 + // Left = 0000_0001 (1) + // Right = 0000_1000 (8) + // Firstly, we make sure the left and right are scaled up to twice the size + // Left = 0000_0000 0000_0001 + // Right = 0000_0000 0000_1000 + // We then subtract right from left + // Result = Left - Right = 1111_1111 1111_1001 + // We subtract the overflow, which in this case is 0. + // Because left < right (1 < 8) we invert the high part. + // Lo = 1111_1001 + // Hi = 1111_1111 -> 0000_0001 + // Lo is the field, hi is the overflow. + // We do the same for the second byte... + // Overflow = 1 + // Left = 0000_0001 + // Right = 0000_0000 + // Result = Left - Right = 0000_0000 0000_0001 + // We subtract the overflow... + // Result = 0000_0000 0000_0001 - 1 = 0 + // And we invert the high, just because (invert 0 = 0). + // So our result is: + // 0000_0000 1111_1001 + let mut overflow = 0; + let vl = value.len(); + let bl = by.len(); + for i in 0..vl { + if i >= bl { + break; + } + let (lo, hi) = sub_part(value[i], by[i], overflow); + value[i] = lo; + overflow = hi; + } + overflow +} + +fn sub_part(left: u32, right: u32, overflow: u32) -> (u32, u32) { + let part = 0x1_0000_0000u64 + u64::from(left) - (u64::from(right) + u64::from(overflow)); + let lo = part as u32; + let hi = 1 - ((part >> 32) as u32); + (lo, hi) +} + +// Returns overflow +#[inline] +pub(crate) fn mul_by_10(bits: &mut [u32; 3]) -> u32 { + let mut overflow = 0u64; + for b in bits.iter_mut() { + let result = u64::from(*b) * 10u64 + overflow; + let hi = (result >> 32) & U32_MASK; + let lo = (result & U32_MASK) as u32; + *b = lo; + overflow = hi; + } + + overflow as u32 +} + +// Returns overflow +pub(crate) fn mul_by_u32(bits: &mut [u32], m: u32) -> u32 { + let mut overflow = 0; + for b in bits.iter_mut() { + let (lo, hi) = mul_part(*b, m, overflow); + *b = lo; + overflow = hi; + } + overflow +} + +pub(crate) fn mul_part(left: u32, right: u32, high: u32) -> (u32, u32) { + let result = u64::from(left) * u64::from(right) + u64::from(high); + let hi = ((result >> 32) & U32_MASK) as u32; + let lo = (result & U32_MASK) as u32; + (lo, hi) +} + +// Returns remainder +pub(crate) fn div_by_u32<const N: usize>(bits: &mut [u32; N], divisor: u32) -> u32 { + if divisor == 0 { + // Divide by zero + panic!("Internal error: divide by zero"); + } else if divisor == 1 { + // dividend remains unchanged + 0 + } else { + let mut remainder = 0u32; + let divisor = u64::from(divisor); + for part in bits.iter_mut().rev() { + let temp = (u64::from(remainder) << 32) + u64::from(*part); + remainder = (temp % divisor) as u32; + *part = (temp / divisor) as u32; + } + + remainder + } +} + +pub(crate) fn div_by_1x(bits: &mut [u32; 3], power: usize) -> u32 { + let mut remainder = 0u32; + let divisor = POWERS_10[power] as u64; + let temp = ((remainder as u64) << 32) + (bits[2] as u64); + remainder = (temp % divisor) as u32; + bits[2] = (temp / divisor) as u32; + let temp = ((remainder as u64) << 32) + (bits[1] as u64); + remainder = (temp % divisor) as u32; + bits[1] = (temp / divisor) as u32; + let temp = ((remainder as u64) << 32) + (bits[0] as u64); + remainder = (temp % divisor) as u32; + bits[0] = (temp / divisor) as u32; + remainder +} + +#[inline] +pub(crate) fn shl1_internal(bits: &mut [u32], carry: u32) -> u32 { + let mut carry = carry; + for part in bits.iter_mut() { + let b = *part >> 31; + *part = (*part << 1) | carry; + carry = b; + } + carry +} + +#[inline] +pub(crate) fn cmp_internal(left: &[u32; 3], right: &[u32; 3]) -> core::cmp::Ordering { + let left_hi: u32 = left[2]; + let right_hi: u32 = right[2]; + let left_lo: u64 = u64::from(left[1]) << 32 | u64::from(left[0]); + let right_lo: u64 = u64::from(right[1]) << 32 | u64::from(right[0]); + if left_hi < right_hi || (left_hi <= right_hi && left_lo < right_lo) { + core::cmp::Ordering::Less + } else if left_hi == right_hi && left_lo == right_lo { + core::cmp::Ordering::Equal + } else { + core::cmp::Ordering::Greater + } +} + +#[inline] +pub(crate) fn is_all_zero<const N: usize>(bits: &[u32; N]) -> bool { + bits.iter().all(|b| *b == 0) +} + +#[cfg(test)] +mod test { + // Tests on private methods. + // + // All public tests should go under `tests/`. + + use super::*; + use crate::prelude::*; + + #[test] + fn it_can_rescale_internal() { + fn extract(value: &str) -> ([u32; 3], u32) { + let v = Decimal::from_str(value).unwrap(); + (v.mantissa_array3(), v.scale()) + } + + let tests = &[ + ("1", 0, "1", 0), + ("1", 1, "1.0", 1), + ("1", 5, "1.00000", 5), + ("1", 10, "1.0000000000", 10), + ("1", 20, "1.00000000000000000000", 20), + ( + "0.6386554621848739495798319328", + 27, + "0.638655462184873949579831933", + 27, + ), + ( + "843.65000000", // Scale 8 + 25, + "843.6500000000000000000000000", + 25, + ), + ( + "843.65000000", // Scale 8 + 30, + "843.6500000000000000000000000", + 25, // Only fits 25 + ), + ("0", 130, "0.000000000000000000000000000000", 28), + ]; + + for &(value_raw, new_scale, expected_value, expected_scale) in tests { + let (expected_value, _) = extract(expected_value); + let (mut value, mut value_scale) = extract(value_raw); + rescale_internal(&mut value, &mut value_scale, new_scale); + assert_eq!(value, expected_value); + assert_eq!( + value_scale, expected_scale, + "value: {}, requested scale: {}", + value_raw, new_scale + ); + } + } + + #[test] + fn test_shl1_internal() { + struct TestCase { + // One thing to be cautious of is that the structure of a number here for shifting left is + // the reverse of how you may conceive this mentally. i.e. a[2] contains the higher order + // bits: a[2] a[1] a[0] + given: [u32; 3], + given_carry: u32, + expected: [u32; 3], + expected_carry: u32, + } + let tests = [ + TestCase { + given: [1, 0, 0], + given_carry: 0, + expected: [2, 0, 0], + expected_carry: 0, + }, + TestCase { + given: [1, 0, 2147483648], + given_carry: 1, + expected: [3, 0, 0], + expected_carry: 1, + }, + ]; + for case in &tests { + let mut test = [case.given[0], case.given[1], case.given[2]]; + let carry = shl1_internal(&mut test, case.given_carry); + assert_eq!( + test, case.expected, + "Bits: {:?} << 1 | {}", + case.given, case.given_carry + ); + assert_eq!( + carry, case.expected_carry, + "Carry: {:?} << 1 | {}", + case.given, case.given_carry + ) + } + } +} diff --git a/third_party/rust/rust_decimal/src/ops/cmp.rs b/third_party/rust/rust_decimal/src/ops/cmp.rs new file mode 100644 index 0000000000..636085bff5 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/cmp.rs @@ -0,0 +1,101 @@ +use crate::constants::{MAX_I32_SCALE, POWERS_10, U32_MASK, U32_MAX}; +use crate::decimal::Decimal; +use crate::ops::common::Dec64; + +use core::cmp::Ordering; + +pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering { + if d2.is_zero() { + return if d1.is_zero() { + return Ordering::Equal; + } else if d1.is_sign_negative() { + Ordering::Less + } else { + Ordering::Greater + }; + } + if d1.is_zero() { + return if d2.is_sign_negative() { + Ordering::Greater + } else { + Ordering::Less + }; + } + // If the sign is different, then it's an easy answer + if d1.is_sign_negative() != d2.is_sign_negative() { + return if d1.is_sign_negative() { + Ordering::Less + } else { + Ordering::Greater + }; + } + + // Otherwise, do a deep comparison + let d1 = Dec64::new(d1); + let d2 = Dec64::new(d2); + // We know both signs are the same here so flip it here. + // Negative is handled differently. i.e. 0.5 > 0.01 however -0.5 < -0.01 + if d1.negative { + cmp_internal(&d2, &d1) + } else { + cmp_internal(&d1, &d2) + } +} + +pub(in crate::ops) fn cmp_internal(d1: &Dec64, d2: &Dec64) -> Ordering { + // This function ignores sign + let mut d1_low = d1.low64; + let mut d1_high = d1.hi; + let mut d2_low = d2.low64; + let mut d2_high = d2.hi; + + // If the scale factors aren't equal then + if d1.scale != d2.scale { + let mut diff = d2.scale as i32 - d1.scale as i32; + if diff < 0 { + diff = -diff; + if !rescale(&mut d2_low, &mut d2_high, diff as u32) { + return Ordering::Less; + } + } else if !rescale(&mut d1_low, &mut d1_high, diff as u32) { + return Ordering::Greater; + } + } + + // They're the same scale, do a standard bitwise comparison + let hi_order = d1_high.cmp(&d2_high); + if hi_order != Ordering::Equal { + return hi_order; + } + d1_low.cmp(&d2_low) +} + +fn rescale(low64: &mut u64, high: &mut u32, diff: u32) -> bool { + let mut diff = diff as i32; + // We need to modify d1 by 10^diff to get it to the same scale as d2 + loop { + let power = if diff >= MAX_I32_SCALE { + POWERS_10[9] + } else { + POWERS_10[diff as usize] + } as u64; + let tmp_lo_32 = (*low64 & U32_MASK) * power; + let mut tmp = (*low64 >> 32) * power + (tmp_lo_32 >> 32); + *low64 = (tmp_lo_32 & U32_MASK) + (tmp << 32); + tmp >>= 32; + tmp = tmp.wrapping_add((*high as u64) * power); + // Indicates > 96 bits + if tmp > U32_MAX { + return false; + } + *high = tmp as u32; + + // Keep scaling if there is more to go + diff -= MAX_I32_SCALE; + if diff <= 0 { + break; + } + } + + true +} diff --git a/third_party/rust/rust_decimal/src/ops/common.rs b/third_party/rust/rust_decimal/src/ops/common.rs new file mode 100644 index 0000000000..c29362d824 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/common.rs @@ -0,0 +1,455 @@ +use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; +use crate::Decimal; + +#[derive(Debug)] +pub struct Buf12 { + pub data: [u32; 3], +} + +impl Buf12 { + pub(super) const fn from_dec64(value: &Dec64) -> Self { + Buf12 { + data: [value.low64 as u32, (value.low64 >> 32) as u32, value.hi], + } + } + + pub(super) const fn from_decimal(value: &Decimal) -> Self { + Buf12 { + data: value.mantissa_array3(), + } + } + + #[inline(always)] + pub const fn lo(&self) -> u32 { + self.data[0] + } + #[inline(always)] + pub const fn mid(&self) -> u32 { + self.data[1] + } + #[inline(always)] + pub const fn hi(&self) -> u32 { + self.data[2] + } + #[inline(always)] + pub fn set_lo(&mut self, value: u32) { + self.data[0] = value; + } + #[inline(always)] + pub fn set_mid(&mut self, value: u32) { + self.data[1] = value; + } + #[inline(always)] + pub fn set_hi(&mut self, value: u32) { + self.data[2] = value; + } + + #[inline(always)] + pub const fn low64(&self) -> u64 { + ((self.data[1] as u64) << 32) | (self.data[0] as u64) + } + + #[inline(always)] + pub fn set_low64(&mut self, value: u64) { + self.data[1] = (value >> 32) as u32; + self.data[0] = value as u32; + } + + #[inline(always)] + pub const fn high64(&self) -> u64 { + ((self.data[2] as u64) << 32) | (self.data[1] as u64) + } + + #[inline(always)] + pub fn set_high64(&mut self, value: u64) { + self.data[2] = (value >> 32) as u32; + self.data[1] = value as u32; + } + + // Determine the maximum value of x that ensures that the quotient when scaled up by 10^x + // still fits in 96 bits. Ultimately, we want to make scale positive - if we can't then + // we're going to overflow. Because x is ultimately used to lookup inside the POWERS array, it + // must be a valid value 0 <= x <= 9 + pub fn find_scale(&self, scale: i32) -> Option<usize> { + const OVERFLOW_MAX_9_HI: u32 = 4; + const OVERFLOW_MAX_8_HI: u32 = 42; + const OVERFLOW_MAX_7_HI: u32 = 429; + const OVERFLOW_MAX_6_HI: u32 = 4294; + const OVERFLOW_MAX_5_HI: u32 = 42949; + const OVERFLOW_MAX_4_HI: u32 = 429496; + const OVERFLOW_MAX_3_HI: u32 = 4294967; + const OVERFLOW_MAX_2_HI: u32 = 42949672; + const OVERFLOW_MAX_1_HI: u32 = 429496729; + const OVERFLOW_MAX_9_LOW64: u64 = 5441186219426131129; + + let hi = self.data[2]; + let low64 = self.low64(); + let mut x = 0usize; + + // Quick check to stop us from trying to scale any more. + // + if hi > OVERFLOW_MAX_1_HI { + // If it's less than 0, which it probably is - overflow. We can't do anything. + if scale < 0 { + return None; + } + return Some(x); + } + + if scale > MAX_PRECISION_I32 - 9 { + // We can't scale by 10^9 without exceeding the max scale factor. + // Instead, we'll try to scale by the most that we can and see if that works. + // This is safe to do due to the check above. e.g. scale > 19 in the above, so it will + // evaluate to 9 or less below. + x = (MAX_PRECISION_I32 - scale) as usize; + if hi < POWER_OVERFLOW_VALUES[x - 1].data[2] { + if x as i32 + scale < 0 { + // We still overflow + return None; + } + return Some(x); + } + } else if hi < OVERFLOW_MAX_9_HI || hi == OVERFLOW_MAX_9_HI && low64 <= OVERFLOW_MAX_9_LOW64 { + return Some(9); + } + + // Do a binary search to find a power to scale by that is less than 9 + x = if hi > OVERFLOW_MAX_5_HI { + if hi > OVERFLOW_MAX_3_HI { + if hi > OVERFLOW_MAX_2_HI { + 1 + } else { + 2 + } + } else if hi > OVERFLOW_MAX_4_HI { + 3 + } else { + 4 + } + } else if hi > OVERFLOW_MAX_7_HI { + if hi > OVERFLOW_MAX_6_HI { + 5 + } else { + 6 + } + } else if hi > OVERFLOW_MAX_8_HI { + 7 + } else { + 8 + }; + + // Double check what we've found won't overflow. Otherwise, we go one below. + if hi == POWER_OVERFLOW_VALUES[x - 1].data[2] && low64 > POWER_OVERFLOW_VALUES[x - 1].low64() { + x -= 1; + } + + // Confirm we've actually resolved things + if x as i32 + scale < 0 { + None + } else { + Some(x) + } + } +} + +// This is a table of the largest values that will not overflow when multiplied +// by a given power as represented by the index. +static POWER_OVERFLOW_VALUES: [Buf12; 8] = [ + Buf12 { + data: [2576980377, 2576980377, 429496729], + }, + Buf12 { + data: [687194767, 4123168604, 42949672], + }, + Buf12 { + data: [2645699854, 1271310319, 4294967], + }, + Buf12 { + data: [694066715, 3133608139, 429496], + }, + Buf12 { + data: [2216890319, 2890341191, 42949], + }, + Buf12 { + data: [2369172679, 4154504685, 4294], + }, + Buf12 { + data: [4102387834, 2133437386, 429], + }, + Buf12 { + data: [410238783, 4078814305, 42], + }, +]; + +pub(super) struct Dec64 { + pub negative: bool, + pub scale: u32, + pub hi: u32, + pub low64: u64, +} + +impl Dec64 { + pub(super) const fn new(d: &Decimal) -> Dec64 { + let m = d.mantissa_array3(); + if m[1] == 0 { + Dec64 { + negative: d.is_sign_negative(), + scale: d.scale(), + hi: m[2], + low64: m[0] as u64, + } + } else { + Dec64 { + negative: d.is_sign_negative(), + scale: d.scale(), + hi: m[2], + low64: ((m[1] as u64) << 32) | (m[0] as u64), + } + } + } + + #[inline(always)] + pub(super) const fn lo(&self) -> u32 { + self.low64 as u32 + } + #[inline(always)] + pub(super) const fn mid(&self) -> u32 { + (self.low64 >> 32) as u32 + } + + #[inline(always)] + pub(super) const fn high64(&self) -> u64 { + (self.low64 >> 32) | ((self.hi as u64) << 32) + } + + pub(super) const fn to_decimal(&self) -> Decimal { + Decimal::from_parts( + self.low64 as u32, + (self.low64 >> 32) as u32, + self.hi, + self.negative, + self.scale, + ) + } +} + +pub struct Buf16 { + pub data: [u32; 4], +} + +impl Buf16 { + pub const fn zero() -> Self { + Buf16 { data: [0, 0, 0, 0] } + } + + pub const fn low64(&self) -> u64 { + ((self.data[1] as u64) << 32) | (self.data[0] as u64) + } + + pub fn set_low64(&mut self, value: u64) { + self.data[1] = (value >> 32) as u32; + self.data[0] = value as u32; + } + + pub const fn mid64(&self) -> u64 { + ((self.data[2] as u64) << 32) | (self.data[1] as u64) + } + + pub fn set_mid64(&mut self, value: u64) { + self.data[2] = (value >> 32) as u32; + self.data[1] = value as u32; + } + + pub const fn high64(&self) -> u64 { + ((self.data[3] as u64) << 32) | (self.data[2] as u64) + } + + pub fn set_high64(&mut self, value: u64) { + self.data[3] = (value >> 32) as u32; + self.data[2] = value as u32; + } +} + +#[derive(Debug)] +pub struct Buf24 { + pub data: [u32; 6], +} + +impl Buf24 { + pub const fn zero() -> Self { + Buf24 { + data: [0, 0, 0, 0, 0, 0], + } + } + + pub const fn low64(&self) -> u64 { + ((self.data[1] as u64) << 32) | (self.data[0] as u64) + } + + pub fn set_low64(&mut self, value: u64) { + self.data[1] = (value >> 32) as u32; + self.data[0] = value as u32; + } + + #[allow(dead_code)] + pub const fn mid64(&self) -> u64 { + ((self.data[3] as u64) << 32) | (self.data[2] as u64) + } + + pub fn set_mid64(&mut self, value: u64) { + self.data[3] = (value >> 32) as u32; + self.data[2] = value as u32; + } + + #[allow(dead_code)] + pub const fn high64(&self) -> u64 { + ((self.data[5] as u64) << 32) | (self.data[4] as u64) + } + + pub fn set_high64(&mut self, value: u64) { + self.data[5] = (value >> 32) as u32; + self.data[4] = value as u32; + } + + pub const fn upper_word(&self) -> usize { + if self.data[5] > 0 { + return 5; + } + if self.data[4] > 0 { + return 4; + } + if self.data[3] > 0 { + return 3; + } + if self.data[2] > 0 { + return 2; + } + if self.data[1] > 0 { + return 1; + } + 0 + } + + // Attempt to rescale the number into 96 bits. If successful, the scale is returned wrapped + // in an Option. If it failed due to overflow, we return None. + // * `upper` - Index of last non-zero value in self. + // * `scale` - Current scale factor for this value. + pub fn rescale(&mut self, upper: usize, scale: u32) -> Option<u32> { + let mut scale = scale as i32; + let mut upper = upper; + + // Determine a rescale target to start with + let mut rescale_target = 0i32; + if upper > 2 { + rescale_target = upper as i32 * 32 - 64 - 1; + rescale_target -= self.data[upper].leading_zeros() as i32; + rescale_target = ((rescale_target * 77) >> 8) + 1; + if rescale_target > scale { + return None; + } + } + + // Make sure we scale enough to bring it into a valid range + if rescale_target < scale - MAX_PRECISION_I32 { + rescale_target = scale - MAX_PRECISION_I32; + } + + if rescale_target > 0 { + // We're going to keep reducing by powers of 10. So, start by reducing the scale by + // that amount. + scale -= rescale_target; + let mut sticky = 0; + let mut remainder = 0; + loop { + sticky |= remainder; + let mut power = if rescale_target > 8 { + POWERS_10[9] + } else { + POWERS_10[rescale_target as usize] + }; + + let high = self.data[upper]; + let high_quotient = high / power; + remainder = high - high_quotient * power; + + for item in self.data.iter_mut().rev().skip(6 - upper) { + let num = (*item as u64).wrapping_add((remainder as u64) << 32); + *item = (num / power as u64) as u32; + remainder = (num as u32).wrapping_sub(item.wrapping_mul(power)); + } + + self.data[upper] = high_quotient; + + // If the high quotient was zero then decrease the upper bound + if high_quotient == 0 && upper > 0 { + upper -= 1; + } + if rescale_target > MAX_I32_SCALE { + // Scale some more + rescale_target -= MAX_I32_SCALE; + continue; + } + + // If we fit into 96 bits then we've scaled enough. Otherwise, scale once more. + if upper > 2 { + if scale == 0 { + return None; + } + // Equivalent to scaling down by 10 + rescale_target = 1; + scale -= 1; + continue; + } + + // Round the final result. + power >>= 1; + let carried = if power <= remainder { + // If we're less than half then we're fine. Otherwise, we round if odd or if the + // sticky bit is set. + if power < remainder || ((self.data[0] & 1) | sticky) != 0 { + // Round up + self.data[0] = self.data[0].wrapping_add(1); + // Check if we carried + self.data[0] == 0 + } else { + false + } + } else { + false + }; + + // If we carried then propagate through the portions + if carried { + let mut pos = 0; + for (index, value) in self.data.iter_mut().enumerate().skip(1) { + pos = index; + *value = value.wrapping_add(1); + if *value != 0 { + break; + } + } + + // If we ended up rounding over the 96 bits then we'll try to rescale down (again) + if pos > 2 { + // Nothing to scale down from will cause overflow + if scale == 0 { + return None; + } + + // Loop back around using scale of 10. + // Reset the sticky bit and remainder before looping. + upper = pos; + sticky = 0; + remainder = 0; + rescale_target = 1; + scale -= 1; + continue; + } + } + break; + } + } + + Some(scale as u32) + } +} diff --git a/third_party/rust/rust_decimal/src/ops/div.rs b/third_party/rust/rust_decimal/src/ops/div.rs new file mode 100644 index 0000000000..3b5ec577b2 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/div.rs @@ -0,0 +1,658 @@ +use crate::constants::{MAX_PRECISION_I32, POWERS_10}; +use crate::decimal::{CalculationResult, Decimal}; +use crate::ops::common::{Buf12, Buf16, Dec64}; + +use core::cmp::Ordering; +use core::ops::BitXor; + +impl Buf12 { + // Returns true if successful, else false for an overflow + fn add32(&mut self, value: u32) -> Result<(), DivError> { + let value = value as u64; + let new = self.low64().wrapping_add(value); + self.set_low64(new); + if new < value { + self.data[2] = self.data[2].wrapping_add(1); + if self.data[2] == 0 { + return Err(DivError::Overflow); + } + } + Ok(()) + } + + // Divide a Decimal union by a 32 bit divisor. + // Self is overwritten with the quotient. + // Return value is a 32 bit remainder. + fn div32(&mut self, divisor: u32) -> u32 { + let divisor64 = divisor as u64; + // See if we can get by using a simple u64 division + if self.data[2] != 0 { + let mut temp = self.high64(); + let q64 = temp / divisor64; + self.set_high64(q64); + + // Calculate the "remainder" + temp = ((temp - q64 * divisor64) << 32) | (self.data[0] as u64); + if temp == 0 { + return 0; + } + let q32 = (temp / divisor64) as u32; + self.data[0] = q32; + ((temp as u32).wrapping_sub(q32.wrapping_mul(divisor))) as u32 + } else { + // Super easy divisor + let low64 = self.low64(); + if low64 == 0 { + // Nothing to do + return 0; + } + // Do the calc + let quotient = low64 / divisor64; + self.set_low64(quotient); + // Remainder is the leftover that wasn't used + (low64.wrapping_sub(quotient.wrapping_mul(divisor64))) as u32 + } + } + + // Divide the number by a power constant + // Returns true if division was successful + fn div32_const(&mut self, pow: u32) -> bool { + let pow64 = pow as u64; + let high64 = self.high64(); + let lo = self.data[0] as u64; + let div64: u64 = high64 / pow64; + let div = ((((high64 - div64 * pow64) << 32) + lo) / pow64) as u32; + if self.data[0] == div.wrapping_mul(pow) { + self.set_high64(div64); + self.data[0] = div; + true + } else { + false + } + } +} + +impl Buf16 { + // Does a partial divide with a 64 bit divisor. The divisor in this case must be 64 bits + // otherwise various assumptions fail (e.g. 32 bit quotient). + // To assist, the upper 64 bits must be greater than the divisor for this to succeed. + // Consequently, it will return the quotient as a 32 bit number and overwrite self with the + // 64 bit remainder. + pub(super) fn partial_divide_64(&mut self, divisor: u64) -> u32 { + // We make this assertion here, however below we pivot based on the data + debug_assert!(divisor > self.mid64()); + + // If we have an empty high bit, then divisor must be greater than the dividend due to + // the assumption that the divisor REQUIRES 64 bits. + if self.data[2] == 0 { + let low64 = self.low64(); + if low64 < divisor { + // We can't divide at at all so result is 0. The dividend remains untouched since + // the full amount is the remainder. + return 0; + } + + let quotient = low64 / divisor; + self.set_low64(low64 - (quotient * divisor)); + return quotient as u32; + } + + // Do a simple check to see if the hi portion of the dividend is greater than the hi + // portion of the divisor. + let divisor_hi32 = (divisor >> 32) as u32; + if self.data[2] >= divisor_hi32 { + // We know that the divisor goes into this at MOST u32::max times. + // So we kick things off, with that assumption + let mut low64 = self.low64(); + low64 = low64.wrapping_sub(divisor << 32).wrapping_add(divisor); + let mut quotient = u32::MAX; + + // If we went negative then keep adding it back in + loop { + if low64 < divisor { + break; + } + quotient = quotient.wrapping_sub(1); + low64 = low64.wrapping_add(divisor); + } + self.set_low64(low64); + return quotient; + } + + let mid64 = self.mid64(); + let divisor_hi32_64 = divisor_hi32 as u64; + if mid64 < divisor_hi32_64 as u64 { + // similar situation as above where we've got nothing left to divide + return 0; + } + + let mut quotient = mid64 / divisor_hi32_64; + let mut remainder = self.data[0] as u64 | ((mid64 - quotient * divisor_hi32_64) << 32); + + // Do quotient * lo divisor + let product = quotient * (divisor & 0xFFFF_FFFF); + remainder = remainder.wrapping_sub(product); + + // Check if we've gone negative. If so, add it back + if remainder > product.bitxor(u64::MAX) { + loop { + quotient = quotient.wrapping_sub(1); + remainder = remainder.wrapping_add(divisor); + if remainder < divisor { + break; + } + } + } + + self.set_low64(remainder); + quotient as u32 + } + + // Does a partial divide with a 96 bit divisor. The divisor in this case must require 96 bits + // otherwise various assumptions fail (e.g. 32 bit quotient). + pub(super) fn partial_divide_96(&mut self, divisor: &Buf12) -> u32 { + let dividend = self.high64(); + let divisor_hi = divisor.data[2]; + if dividend < divisor_hi as u64 { + // Dividend is too small - entire number is remainder + return 0; + } + + let mut quo = (dividend / divisor_hi as u64) as u32; + let mut remainder = (dividend as u32).wrapping_sub(quo.wrapping_mul(divisor_hi)); + + // Compute full remainder + let mut prod1 = quo as u64 * divisor.data[0] as u64; + let mut prod2 = quo as u64 * divisor.data[1] as u64; + prod2 += prod1 >> 32; + prod1 = (prod1 & 0xFFFF_FFFF) | (prod2 << 32); + prod2 >>= 32; + + let mut num = self.low64(); + num = num.wrapping_sub(prod1); + remainder = remainder.wrapping_sub(prod2 as u32); + + // If there are carries make sure they are propagated + if num > prod1.bitxor(u64::MAX) { + remainder = remainder.wrapping_sub(1); + if remainder < (prod2 as u32).bitxor(u32::MAX) { + self.set_low64(num); + self.data[2] = remainder; + return quo; + } + } else if remainder <= (prod2 as u32).bitxor(u32::MAX) { + self.set_low64(num); + self.data[2] = remainder; + return quo; + } + + // Remainder went negative, add divisor back until it's positive + prod1 = divisor.low64(); + loop { + quo = quo.wrapping_sub(1); + num = num.wrapping_add(prod1); + remainder = remainder.wrapping_add(divisor_hi); + + if num < prod1 { + // Detected carry. + let tmp = remainder; + remainder = remainder.wrapping_add(1); + if tmp < divisor_hi { + break; + } + } + if remainder < divisor_hi { + break; // detected carry + } + } + + self.set_low64(num); + self.data[2] = remainder; + quo + } +} + +enum DivError { + Overflow, +} + +pub(crate) fn div_impl(dividend: &Decimal, divisor: &Decimal) -> CalculationResult { + if divisor.is_zero() { + return CalculationResult::DivByZero; + } + if dividend.is_zero() { + return CalculationResult::Ok(Decimal::ZERO); + } + let dividend = Dec64::new(dividend); + let divisor = Dec64::new(divisor); + + // Pre calculate the scale and the sign + let mut scale = (dividend.scale as i32) - (divisor.scale as i32); + let sign_negative = dividend.negative ^ divisor.negative; + + // Set up some variables for modification throughout + let mut require_unscale = false; + let mut quotient = Buf12::from_dec64(÷nd); + let divisor = Buf12::from_dec64(&divisor); + + // Branch depending on the complexity of the divisor + if divisor.data[2] | divisor.data[1] == 0 { + // We have a simple(r) divisor (32 bit) + let divisor32 = divisor.data[0]; + + // Remainder can only be 32 bits since the divisor is 32 bits. + let mut remainder = quotient.div32(divisor32); + let mut power_scale = 0; + + // Figure out how to apply the remainder (i.e. we may have performed something like 10/3 or 8/5) + loop { + // Remainder is 0 so we have a simple situation + if remainder == 0 { + // If the scale is positive then we're actually done + if scale >= 0 { + break; + } + power_scale = 9usize.min((-scale) as usize); + } else { + // We may need to normalize later, so set the flag appropriately + require_unscale = true; + + // We have a remainder so we effectively want to try to adjust the quotient and add + // the remainder into the quotient. We do this below, however first of all we want + // to try to avoid overflowing so we do that check first. + let will_overflow = if scale == MAX_PRECISION_I32 { + true + } else { + // Figure out how much we can scale by + if let Some(s) = quotient.find_scale(scale) { + power_scale = s; + } else { + return CalculationResult::Overflow; + } + // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since + // we're doing nothing. + power_scale == 0 + }; + if will_overflow { + // No more scaling can be done, but remainder is non-zero so we round if necessary. + let tmp = remainder << 1; + let round = if tmp < remainder { + // We round if we wrapped around + true + } else if tmp >= divisor32 { + // If we're greater than the divisor (i.e. underflow) + // or if there is a lo bit set, we round + tmp > divisor32 || (quotient.data[0] & 0x1) > 0 + } else { + false + }; + + // If we need to round, try to do so. + if round { + if let Ok(new_scale) = round_up(&mut quotient, scale) { + scale = new_scale; + } else { + // Overflowed + return CalculationResult::Overflow; + } + } + break; + } + } + + // Do some scaling + let power = POWERS_10[power_scale]; + scale += power_scale as i32; + // Increase the quotient by the power that was looked up + let overflow = increase_scale(&mut quotient, power as u64); + if overflow > 0 { + return CalculationResult::Overflow; + } + + let remainder_scaled = (remainder as u64) * (power as u64); + let remainder_quotient = (remainder_scaled / (divisor32 as u64)) as u32; + remainder = (remainder_scaled - remainder_quotient as u64 * divisor32 as u64) as u32; + if let Err(DivError::Overflow) = quotient.add32(remainder_quotient) { + if let Ok(adj) = unscale_from_overflow(&mut quotient, scale, remainder != 0) { + scale = adj; + } else { + // Still overflowing + return CalculationResult::Overflow; + } + break; + } + } + } else { + // We have a divisor greater than 32 bits. Both of these share some quick calculation wins + // so we'll do those before branching into separate logic. + // The win we can do is shifting the bits to the left as much as possible. We do this to both + // the dividend and the divisor to ensure the quotient is not changed. + // As a simple contrived example: if we have 4 / 2 then we could bit shift all the way to the + // left meaning that the lo portion would have nothing inside of it. Of course, shifting these + // left one has the same result (8/4) etc. + // The advantage is that we may be able to write off lower portions of the number making things + // easier. + let mut power_scale = if divisor.data[2] == 0 { + divisor.data[1].leading_zeros() + } else { + divisor.data[2].leading_zeros() + } as usize; + let mut remainder = Buf16::zero(); + remainder.set_low64(quotient.low64() << power_scale); + let tmp_high = ((quotient.data[1] as u64) + ((quotient.data[2] as u64) << 32)) >> (32 - power_scale); + remainder.set_high64(tmp_high); + + // Work out the divisor after it's shifted + let divisor64 = divisor.low64() << power_scale; + // Check if the divisor is 64 bit or the full 96 bits + if divisor.data[2] == 0 { + // It's 64 bits + quotient.data[2] = 0; + + // Calc mid/lo by shifting accordingly + let rem_lo = remainder.data[0]; + remainder.data[0] = remainder.data[1]; + remainder.data[1] = remainder.data[2]; + remainder.data[2] = remainder.data[3]; + quotient.data[1] = remainder.partial_divide_64(divisor64); + + remainder.data[2] = remainder.data[1]; + remainder.data[1] = remainder.data[0]; + remainder.data[0] = rem_lo; + quotient.data[0] = remainder.partial_divide_64(divisor64); + + loop { + let rem_low64 = remainder.low64(); + if rem_low64 == 0 { + // If the scale is positive then we're actually done + if scale >= 0 { + break; + } + power_scale = 9usize.min((-scale) as usize); + } else { + // We may need to normalize later, so set the flag appropriately + require_unscale = true; + + // We have a remainder so we effectively want to try to adjust the quotient and add + // the remainder into the quotient. We do this below, however first of all we want + // to try to avoid overflowing so we do that check first. + let will_overflow = if scale == MAX_PRECISION_I32 { + true + } else { + // Figure out how much we can scale by + if let Some(s) = quotient.find_scale(scale) { + power_scale = s; + } else { + return CalculationResult::Overflow; + } + // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since + // we're doing nothing. + power_scale == 0 + }; + if will_overflow { + // No more scaling can be done, but remainder is non-zero so we round if necessary. + let mut tmp = remainder.low64(); + let round = if (tmp as i64) < 0 { + // We round if we wrapped around + true + } else { + tmp <<= 1; + if tmp > divisor64 { + true + } else { + tmp == divisor64 && quotient.data[0] & 0x1 != 0 + } + }; + + // If we need to round, try to do so. + if round { + if let Ok(new_scale) = round_up(&mut quotient, scale) { + scale = new_scale; + } else { + // Overflowed + return CalculationResult::Overflow; + } + } + break; + } + } + + // Do some scaling + let power = POWERS_10[power_scale]; + scale += power_scale as i32; + + // Increase the quotient by the power that was looked up + let overflow = increase_scale(&mut quotient, power as u64); + if overflow > 0 { + return CalculationResult::Overflow; + } + increase_scale64(&mut remainder, power as u64); + + let tmp = remainder.partial_divide_64(divisor64); + if let Err(DivError::Overflow) = quotient.add32(tmp) { + if let Ok(adj) = unscale_from_overflow(&mut quotient, scale, remainder.low64() != 0) { + scale = adj; + } else { + // Still overflowing + return CalculationResult::Overflow; + } + break; + } + } + } else { + // It's 96 bits + // Start by finishing the shift left + let divisor_mid = divisor.data[1]; + let divisor_hi = divisor.data[2]; + let mut divisor = divisor; + divisor.set_low64(divisor64); + divisor.data[2] = ((divisor_mid as u64 + ((divisor_hi as u64) << 32)) >> (32 - power_scale)) as u32; + + let quo = remainder.partial_divide_96(&divisor); + quotient.set_low64(quo as u64); + quotient.data[2] = 0; + + loop { + let mut rem_low64 = remainder.low64(); + if rem_low64 == 0 && remainder.data[2] == 0 { + // If the scale is positive then we're actually done + if scale >= 0 { + break; + } + power_scale = 9usize.min((-scale) as usize); + } else { + // We may need to normalize later, so set the flag appropriately + require_unscale = true; + + // We have a remainder so we effectively want to try to adjust the quotient and add + // the remainder into the quotient. We do this below, however first of all we want + // to try to avoid overflowing so we do that check first. + let will_overflow = if scale == MAX_PRECISION_I32 { + true + } else { + // Figure out how much we can scale by + if let Some(s) = quotient.find_scale(scale) { + power_scale = s; + } else { + return CalculationResult::Overflow; + } + // If it comes back as 0 (i.e. 10^0 = 1) then we're going to overflow since + // we're doing nothing. + power_scale == 0 + }; + if will_overflow { + // No more scaling can be done, but remainder is non-zero so we round if necessary. + let round = if (remainder.data[2] as i32) < 0 { + // We round if we wrapped around + true + } else { + let tmp = remainder.data[1] >> 31; + rem_low64 <<= 1; + remainder.set_low64(rem_low64); + remainder.data[2] = (&remainder.data[2] << 1) + tmp; + + match remainder.data[2].cmp(&divisor.data[2]) { + Ordering::Less => false, + Ordering::Equal => { + let divisor_low64 = divisor.low64(); + if rem_low64 > divisor_low64 { + true + } else { + rem_low64 == divisor_low64 && (quotient.data[0] & 1) != 0 + } + } + Ordering::Greater => true, + } + }; + + // If we need to round, try to do so. + if round { + if let Ok(new_scale) = round_up(&mut quotient, scale) { + scale = new_scale; + } else { + // Overflowed + return CalculationResult::Overflow; + } + } + break; + } + } + + // Do some scaling + let power = POWERS_10[power_scale]; + scale += power_scale as i32; + + // Increase the quotient by the power that was looked up + let overflow = increase_scale(&mut quotient, power as u64); + if overflow > 0 { + return CalculationResult::Overflow; + } + let mut tmp_remainder = Buf12 { + data: [remainder.data[0], remainder.data[1], remainder.data[2]], + }; + let overflow = increase_scale(&mut tmp_remainder, power as u64); + remainder.data[0] = tmp_remainder.data[0]; + remainder.data[1] = tmp_remainder.data[1]; + remainder.data[2] = tmp_remainder.data[2]; + remainder.data[3] = overflow; + + let tmp = remainder.partial_divide_96(&divisor); + if let Err(DivError::Overflow) = quotient.add32(tmp) { + if let Ok(adj) = + unscale_from_overflow(&mut quotient, scale, (remainder.low64() | remainder.high64()) != 0) + { + scale = adj; + } else { + // Still overflowing + return CalculationResult::Overflow; + } + break; + } + } + } + } + if require_unscale { + scale = unscale(&mut quotient, scale); + } + CalculationResult::Ok(Decimal::from_parts( + quotient.data[0], + quotient.data[1], + quotient.data[2], + sign_negative, + scale as u32, + )) +} + +// Multiply num by power (multiple of 10). Power must be 32 bits. +// Returns the overflow, if any +fn increase_scale(num: &mut Buf12, power: u64) -> u32 { + let mut tmp = (num.data[0] as u64) * power; + num.data[0] = tmp as u32; + tmp >>= 32; + tmp += (num.data[1] as u64) * power; + num.data[1] = tmp as u32; + tmp >>= 32; + tmp += (num.data[2] as u64) * power; + num.data[2] = tmp as u32; + (tmp >> 32) as u32 +} + +// Multiply num by power (multiple of 10). Power must be 32 bits. +fn increase_scale64(num: &mut Buf16, power: u64) { + let mut tmp = (num.data[0] as u64) * power; + num.data[0] = tmp as u32; + tmp >>= 32; + tmp += (num.data[1] as u64) * power; + num.set_mid64(tmp) +} + +// Adjust the number to deal with an overflow. This function follows being scaled up (i.e. multiplied +// by 10, so this effectively tries to reverse that by dividing by 10 then feeding in the high bit +// to undo the overflow and rounding instead. +// Returns the updated scale. +fn unscale_from_overflow(num: &mut Buf12, scale: i32, sticky: bool) -> Result<i32, DivError> { + let scale = scale - 1; + if scale < 0 { + return Err(DivError::Overflow); + } + + // This function is called when the hi portion has "overflowed" upon adding one and has wrapped + // back around to 0. Consequently, we need to "feed" that back in, but also rescaling down + // to reverse out the overflow. + const HIGH_BIT: u64 = 0x1_0000_0000; + num.data[2] = (HIGH_BIT / 10) as u32; + + // Calc the mid + let mut tmp = ((HIGH_BIT % 10) << 32) + (num.data[1] as u64); + let mut val = (tmp / 10) as u32; + num.data[1] = val; + + // Calc the lo using a similar method + tmp = ((tmp - (val as u64) * 10) << 32) + (num.data[0] as u64); + val = (tmp / 10) as u32; + num.data[0] = val; + + // Work out the remainder, and round if we have one (since it doesn't fit) + let remainder = (tmp - (val as u64) * 10) as u32; + if remainder > 5 || (remainder == 5 && (sticky || num.data[0] & 0x1 > 0)) { + let _ = num.add32(1); + } + Ok(scale) +} + +#[inline] +fn round_up(num: &mut Buf12, scale: i32) -> Result<i32, DivError> { + let low64 = num.low64().wrapping_add(1); + num.set_low64(low64); + if low64 != 0 { + return Ok(scale); + } + let hi = num.data[2].wrapping_add(1); + num.data[2] = hi; + if hi != 0 { + return Ok(scale); + } + unscale_from_overflow(num, scale, true) +} + +fn unscale(num: &mut Buf12, scale: i32) -> i32 { + // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 we can extract. + // We use this as a quick test on whether to try a given power. + let mut scale = scale; + while num.data[0] == 0 && scale >= 8 && num.div32_const(100000000) { + scale -= 8; + } + + if (num.data[0] & 0xF) == 0 && scale >= 4 && num.div32_const(10000) { + scale -= 4; + } + + if (num.data[0] & 0x3) == 0 && scale >= 2 && num.div32_const(100) { + scale -= 2; + } + + if (num.data[0] & 0x1) == 0 && scale >= 1 && num.div32_const(10) { + scale -= 1; + } + scale +} diff --git a/third_party/rust/rust_decimal/src/ops/legacy.rs b/third_party/rust/rust_decimal/src/ops/legacy.rs new file mode 100644 index 0000000000..49f39814f8 --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/legacy.rs @@ -0,0 +1,843 @@ +use crate::{ + constants::{MAX_PRECISION_U32, POWERS_10, U32_MASK}, + decimal::{CalculationResult, Decimal}, + ops::array::{ + add_by_internal, cmp_internal, div_by_u32, is_all_zero, mul_by_u32, mul_part, rescale_internal, shl1_internal, + }, +}; + +use core::cmp::Ordering; +use num_traits::Zero; + +pub(crate) fn add_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + // Convert to the same scale + let mut my = d1.mantissa_array3(); + let mut my_scale = d1.scale(); + let mut ot = d2.mantissa_array3(); + let mut other_scale = d2.scale(); + rescale_to_maximum_scale(&mut my, &mut my_scale, &mut ot, &mut other_scale); + let mut final_scale = my_scale.max(other_scale); + + // Add the items together + let my_negative = d1.is_sign_negative(); + let other_negative = d2.is_sign_negative(); + let mut negative = false; + let carry; + if !(my_negative ^ other_negative) { + negative = my_negative; + carry = add_by_internal3(&mut my, &ot); + } else { + let cmp = cmp_internal(&my, &ot); + // -x + y + // if x > y then it's negative (i.e. -2 + 1) + match cmp { + Ordering::Less => { + negative = other_negative; + sub_by_internal3(&mut ot, &my); + my[0] = ot[0]; + my[1] = ot[1]; + my[2] = ot[2]; + } + Ordering::Greater => { + negative = my_negative; + sub_by_internal3(&mut my, &ot); + } + Ordering::Equal => { + // -2 + 2 + my[0] = 0; + my[1] = 0; + my[2] = 0; + } + } + carry = 0; + } + + // If we have a carry we underflowed. + // We need to lose some significant digits (if possible) + if carry > 0 { + if final_scale == 0 { + return CalculationResult::Overflow; + } + + // Copy it over to a temp array for modification + let mut temp = [my[0], my[1], my[2], carry]; + while final_scale > 0 && temp[3] != 0 { + div_by_u32(&mut temp, 10); + final_scale -= 1; + } + + // If we still have a carry bit then we overflowed + if temp[3] > 0 { + return CalculationResult::Overflow; + } + + // Copy it back - we're done + my[0] = temp[0]; + my[1] = temp[1]; + my[2] = temp[2]; + } + + CalculationResult::Ok(Decimal::from_parts(my[0], my[1], my[2], negative, final_scale)) +} + +pub(crate) fn sub_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + add_impl(d1, &(-*d2)) +} + +pub(crate) fn div_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + if d2.is_zero() { + return CalculationResult::DivByZero; + } + if d1.is_zero() { + return CalculationResult::Ok(Decimal::zero()); + } + + let dividend = d1.mantissa_array3(); + let divisor = d2.mantissa_array3(); + let mut quotient = [0u32, 0u32, 0u32]; + let mut quotient_scale: i32 = d1.scale() as i32 - d2.scale() as i32; + + // We supply an extra overflow word for each of the dividend and the remainder + let mut working_quotient = [dividend[0], dividend[1], dividend[2], 0u32]; + let mut working_remainder = [0u32, 0u32, 0u32, 0u32]; + let mut working_scale = quotient_scale; + let mut remainder_scale = quotient_scale; + let mut underflow; + + loop { + div_internal(&mut working_quotient, &mut working_remainder, &divisor); + underflow = add_with_scale_internal( + &mut quotient, + &mut quotient_scale, + &mut working_quotient, + &mut working_scale, + ); + + // Multiply the remainder by 10 + let mut overflow = 0; + for part in working_remainder.iter_mut() { + let (lo, hi) = mul_part(*part, 10, overflow); + *part = lo; + overflow = hi; + } + // Copy temp remainder into the temp quotient section + working_quotient.copy_from_slice(&working_remainder); + + remainder_scale += 1; + working_scale = remainder_scale; + + if underflow || is_all_zero(&working_remainder) { + break; + } + } + + // If we have a really big number try to adjust the scale to 0 + while quotient_scale < 0 { + copy_array_diff_lengths(&mut working_quotient, "ient); + working_quotient[3] = 0; + working_remainder.iter_mut().for_each(|x| *x = 0); + + // Mul 10 + let mut overflow = 0; + for part in &mut working_quotient { + let (lo, hi) = mul_part(*part, 10, overflow); + *part = lo; + overflow = hi; + } + for part in &mut working_remainder { + let (lo, hi) = mul_part(*part, 10, overflow); + *part = lo; + overflow = hi; + } + if working_quotient[3] == 0 && is_all_zero(&working_remainder) { + quotient_scale += 1; + quotient[0] = working_quotient[0]; + quotient[1] = working_quotient[1]; + quotient[2] = working_quotient[2]; + } else { + // Overflow + return CalculationResult::Overflow; + } + } + + if quotient_scale > 255 { + quotient[0] = 0; + quotient[1] = 0; + quotient[2] = 0; + quotient_scale = 0; + } + + let mut quotient_negative = d1.is_sign_negative() ^ d2.is_sign_negative(); + + // Check for underflow + let mut final_scale: u32 = quotient_scale as u32; + if final_scale > MAX_PRECISION_U32 { + let mut remainder = 0; + + // Division underflowed. We must remove some significant digits over using + // an invalid scale. + while final_scale > MAX_PRECISION_U32 && !is_all_zero("ient) { + remainder = div_by_u32(&mut quotient, 10); + final_scale -= 1; + } + if final_scale > MAX_PRECISION_U32 { + // Result underflowed so set to zero + final_scale = 0; + quotient_negative = false; + } else if remainder >= 5 { + for part in &mut quotient { + if remainder == 0 { + break; + } + let digit: u64 = u64::from(*part) + 1; + remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 }; + *part = (digit & 0xFFFF_FFFF) as u32; + } + } + } + + CalculationResult::Ok(Decimal::from_parts( + quotient[0], + quotient[1], + quotient[2], + quotient_negative, + final_scale, + )) +} + +pub(crate) fn mul_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + // Early exit if either is zero + if d1.is_zero() || d2.is_zero() { + return CalculationResult::Ok(Decimal::zero()); + } + + // We are only resulting in a negative if we have mismatched signs + let negative = d1.is_sign_negative() ^ d2.is_sign_negative(); + + // We get the scale of the result by adding the operands. This may be too big, however + // we'll correct later + let mut final_scale = d1.scale() + d2.scale(); + + // First of all, if ONLY the lo parts of both numbers is filled + // then we can simply do a standard 64 bit calculation. It's a minor + // optimization however prevents the need for long form multiplication + let my = d1.mantissa_array3(); + let ot = d2.mantissa_array3(); + if my[1] == 0 && my[2] == 0 && ot[1] == 0 && ot[2] == 0 { + // Simply multiplication + let mut u64_result = u64_to_array(u64::from(my[0]) * u64::from(ot[0])); + + // If we're above max precision then this is a very small number + if final_scale > MAX_PRECISION_U32 { + final_scale -= MAX_PRECISION_U32; + + // If the number is above 19 then this will equate to zero. + // This is because the max value in 64 bits is 1.84E19 + if final_scale > 19 { + return CalculationResult::Ok(Decimal::zero()); + } + + let mut rem_lo = 0; + let mut power; + if final_scale > 9 { + // Since 10^10 doesn't fit into u32, we divide by 10^10/4 + // and multiply the next divisor by 4. + rem_lo = div_by_u32(&mut u64_result, 2_500_000_000); + power = POWERS_10[final_scale as usize - 10] << 2; + } else { + power = POWERS_10[final_scale as usize]; + } + + // Divide fits in 32 bits + let rem_hi = div_by_u32(&mut u64_result, power); + + // Round the result. Since the divisor is a power of 10 + // we check to see if the remainder is >= 1/2 divisor + power >>= 1; + if rem_hi >= power && (rem_hi > power || (rem_lo | (u64_result[0] & 0x1)) != 0) { + u64_result[0] += 1; + } + + final_scale = MAX_PRECISION_U32; + } + return CalculationResult::Ok(Decimal::from_parts( + u64_result[0], + u64_result[1], + 0, + negative, + final_scale, + )); + } + + // We're using some of the high bits, so we essentially perform + // long form multiplication. We compute the 9 partial products + // into a 192 bit result array. + // + // [my-h][my-m][my-l] + // x [ot-h][ot-m][ot-l] + // -------------------------------------- + // 1. [r-hi][r-lo] my-l * ot-l [0, 0] + // 2. [r-hi][r-lo] my-l * ot-m [0, 1] + // 3. [r-hi][r-lo] my-m * ot-l [1, 0] + // 4. [r-hi][r-lo] my-m * ot-m [1, 1] + // 5. [r-hi][r-lo] my-l * ot-h [0, 2] + // 6. [r-hi][r-lo] my-h * ot-l [2, 0] + // 7. [r-hi][r-lo] my-m * ot-h [1, 2] + // 8. [r-hi][r-lo] my-h * ot-m [2, 1] + // 9.[r-hi][r-lo] my-h * ot-h [2, 2] + let mut product = [0u32, 0u32, 0u32, 0u32, 0u32, 0u32]; + + // We can perform a minor short circuit here. If the + // high portions are both 0 then we can skip portions 5-9 + let to = if my[2] == 0 && ot[2] == 0 { 2 } else { 3 }; + + for (my_index, my_item) in my.iter().enumerate().take(to) { + for (ot_index, ot_item) in ot.iter().enumerate().take(to) { + let (mut rlo, mut rhi) = mul_part(*my_item, *ot_item, 0); + + // Get the index for the lo portion of the product + for prod in product.iter_mut().skip(my_index + ot_index) { + let (res, overflow) = add_part(rlo, *prod); + *prod = res; + + // If we have something in rhi from before then promote that + if rhi > 0 { + // If we overflowed in the last add, add that with rhi + if overflow > 0 { + let (nlo, nhi) = add_part(rhi, overflow); + rlo = nlo; + rhi = nhi; + } else { + rlo = rhi; + rhi = 0; + } + } else if overflow > 0 { + rlo = overflow; + rhi = 0; + } else { + break; + } + + // If nothing to do next round then break out + if rlo == 0 { + break; + } + } + } + } + + // If our result has used up the high portion of the product + // then we either have an overflow or an underflow situation + // Overflow will occur if we can't scale it back, whereas underflow + // with kick in rounding + let mut remainder = 0; + while final_scale > 0 && (product[3] != 0 || product[4] != 0 || product[5] != 0) { + remainder = div_by_u32(&mut product, 10u32); + final_scale -= 1; + } + + // Round up the carry if we need to + if remainder >= 5 { + for part in product.iter_mut() { + if remainder == 0 { + break; + } + let digit: u64 = u64::from(*part) + 1; + remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 }; + *part = (digit & 0xFFFF_FFFF) as u32; + } + } + + // If we're still above max precision then we'll try again to + // reduce precision - we may be dealing with a limit of "0" + if final_scale > MAX_PRECISION_U32 { + // We're in an underflow situation + // The easiest way to remove precision is to divide off the result + while final_scale > MAX_PRECISION_U32 && !is_all_zero(&product) { + div_by_u32(&mut product, 10); + final_scale -= 1; + } + // If we're still at limit then we can't represent any + // significant decimal digits and will return an integer only + // Can also be invoked while representing 0. + if final_scale > MAX_PRECISION_U32 { + final_scale = 0; + } + } else if !(product[3] == 0 && product[4] == 0 && product[5] == 0) { + // We're in an overflow situation - we're within our precision bounds + // but still have bits in overflow + return CalculationResult::Overflow; + } + + CalculationResult::Ok(Decimal::from_parts( + product[0], + product[1], + product[2], + negative, + final_scale, + )) +} + +pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + if d2.is_zero() { + return CalculationResult::DivByZero; + } + if d1.is_zero() { + return CalculationResult::Ok(Decimal::zero()); + } + + // Rescale so comparable + let initial_scale = d1.scale(); + let mut quotient = d1.mantissa_array3(); + let mut quotient_scale = initial_scale; + let mut divisor = d2.mantissa_array3(); + let mut divisor_scale = d2.scale(); + rescale_to_maximum_scale(&mut quotient, &mut quotient_scale, &mut divisor, &mut divisor_scale); + + // Working is the remainder + the quotient + // We use an aligned array since we'll be using it a lot. + let mut working_quotient = [quotient[0], quotient[1], quotient[2], 0u32]; + let mut working_remainder = [0u32, 0u32, 0u32, 0u32]; + div_internal(&mut working_quotient, &mut working_remainder, &divisor); + + // Round if necessary. This is for semantic correctness, but could feasibly be removed for + // performance improvements. + if quotient_scale > initial_scale { + let mut working = [ + working_remainder[0], + working_remainder[1], + working_remainder[2], + working_remainder[3], + ]; + while quotient_scale > initial_scale { + if div_by_u32(&mut working, 10) > 0 { + break; + } + quotient_scale -= 1; + working_remainder.copy_from_slice(&working); + } + } + + CalculationResult::Ok(Decimal::from_parts( + working_remainder[0], + working_remainder[1], + working_remainder[2], + d1.is_sign_negative(), + quotient_scale, + )) +} + +pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering { + // Quick exit if major differences + if d1.is_zero() && d2.is_zero() { + return Ordering::Equal; + } + let self_negative = d1.is_sign_negative(); + let other_negative = d2.is_sign_negative(); + if self_negative && !other_negative { + return Ordering::Less; + } else if !self_negative && other_negative { + return Ordering::Greater; + } + + // If we have 1.23 and 1.2345 then we have + // 123 scale 2 and 12345 scale 4 + // We need to convert the first to + // 12300 scale 4 so we can compare equally + let left: &Decimal; + let right: &Decimal; + if self_negative && other_negative { + // Both are negative, so reverse cmp + left = d2; + right = d1; + } else { + left = d1; + right = d2; + } + let mut left_scale = left.scale(); + let mut right_scale = right.scale(); + let mut left_raw = left.mantissa_array3(); + let mut right_raw = right.mantissa_array3(); + + if left_scale == right_scale { + // Fast path for same scale + if left_raw[2] != right_raw[2] { + return left_raw[2].cmp(&right_raw[2]); + } + if left_raw[1] != right_raw[1] { + return left_raw[1].cmp(&right_raw[1]); + } + return left_raw[0].cmp(&right_raw[0]); + } + + // Rescale and compare + rescale_to_maximum_scale(&mut left_raw, &mut left_scale, &mut right_raw, &mut right_scale); + cmp_internal(&left_raw, &right_raw) +} + +#[inline] +fn add_part(left: u32, right: u32) -> (u32, u32) { + let added = u64::from(left) + u64::from(right); + ((added & U32_MASK) as u32, (added >> 32 & U32_MASK) as u32) +} + +#[inline(always)] +fn sub_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) { + let mut overflow = 0; + let vl = value.len(); + for i in 0..vl { + let part = (0x1_0000_0000u64 + u64::from(value[i])) - (u64::from(by[i]) + overflow); + value[i] = part as u32; + overflow = 1 - (part >> 32); + } +} + +fn div_internal(quotient: &mut [u32; 4], remainder: &mut [u32; 4], divisor: &[u32; 3]) { + // There are a couple of ways to do division on binary numbers: + // 1. Using long division + // 2. Using the complement method + // ref: http://paulmason.me/dividing-binary-numbers-part-2/ + // The complement method basically keeps trying to subtract the + // divisor until it can't anymore and placing the rest in remainder. + let mut complement = [ + divisor[0] ^ 0xFFFF_FFFF, + divisor[1] ^ 0xFFFF_FFFF, + divisor[2] ^ 0xFFFF_FFFF, + 0xFFFF_FFFF, + ]; + + // Add one onto the complement + add_one_internal4(&mut complement); + + // Make sure the remainder is 0 + remainder.iter_mut().for_each(|x| *x = 0); + + // If we have nothing in our hi+ block then shift over till we do + let mut blocks_to_process = 0; + while blocks_to_process < 4 && quotient[3] == 0 { + // memcpy would be useful here + quotient[3] = quotient[2]; + quotient[2] = quotient[1]; + quotient[1] = quotient[0]; + quotient[0] = 0; + + // Increment the counter + blocks_to_process += 1; + } + + // Let's try and do the addition... + let mut block = blocks_to_process << 5; + let mut working = [0u32, 0u32, 0u32, 0u32]; + while block < 128 { + // << 1 for quotient AND remainder. Moving the carry from the quotient to the bottom of the + // remainder. + let carry = shl1_internal(quotient, 0); + shl1_internal(remainder, carry); + + // Copy the remainder of working into sub + working.copy_from_slice(remainder); + + // Add the remainder with the complement + add_by_internal(&mut working, &complement); + + // Check for the significant bit - move over to the quotient + // as necessary + if (working[3] & 0x8000_0000) == 0 { + remainder.copy_from_slice(&working); + quotient[0] |= 1; + } + + // Increment our pointer + block += 1; + } +} + +#[inline] +fn copy_array_diff_lengths(into: &mut [u32], from: &[u32]) { + for i in 0..into.len() { + if i >= from.len() { + break; + } + into[i] = from[i]; + } +} + +#[inline] +fn add_one_internal4(value: &mut [u32; 4]) -> u32 { + let mut carry: u64 = 1; // Start with one, since adding one + let mut sum: u64; + for i in value.iter_mut() { + sum = (*i as u64) + carry; + *i = (sum & U32_MASK) as u32; + carry = sum >> 32; + } + + carry as u32 +} + +#[inline] +fn add_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) -> u32 { + let mut carry: u32 = 0; + let bl = by.len(); + for i in 0..bl { + let res1 = value[i].overflowing_add(by[i]); + let res2 = res1.0.overflowing_add(carry); + value[i] = res2.0; + carry = (res1.1 | res2.1) as u32; + } + carry +} + +#[inline] +const fn u64_to_array(value: u64) -> [u32; 2] { + [(value & U32_MASK) as u32, (value >> 32 & U32_MASK) as u32] +} + +fn add_with_scale_internal( + quotient: &mut [u32; 3], + quotient_scale: &mut i32, + working_quotient: &mut [u32; 4], + working_scale: &mut i32, +) -> bool { + // Add quotient and the working (i.e. quotient = quotient + working) + if is_all_zero(quotient) { + // Quotient is zero so we can just copy the working quotient in directly + // First, make sure they are both 96 bit. + while working_quotient[3] != 0 { + div_by_u32(working_quotient, 10); + *working_scale -= 1; + } + copy_array_diff_lengths(quotient, working_quotient); + *quotient_scale = *working_scale; + return false; + } + + if is_all_zero(working_quotient) { + return false; + } + + // We have ensured that our working is not zero so we should do the addition + + // If our two quotients are different then + // try to scale down the one with the bigger scale + let mut temp3 = [0u32, 0u32, 0u32]; + let mut temp4 = [0u32, 0u32, 0u32, 0u32]; + if *quotient_scale != *working_scale { + // TODO: Remove necessity for temp (without performance impact) + fn div_by_10<const N: usize>(target: &mut [u32], temp: &mut [u32; N], scale: &mut i32, target_scale: i32) { + // Copy to the temp array + temp.copy_from_slice(target); + // divide by 10 until target scale is reached + while *scale > target_scale { + let remainder = div_by_u32(temp, 10); + if remainder == 0 { + *scale -= 1; + target.copy_from_slice(temp); + } else { + break; + } + } + } + + if *quotient_scale < *working_scale { + div_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale); + } else { + div_by_10(quotient, &mut temp3, quotient_scale, *working_scale); + } + } + + // If our two quotients are still different then + // try to scale up the smaller scale + if *quotient_scale != *working_scale { + // TODO: Remove necessity for temp (without performance impact) + fn mul_by_10(target: &mut [u32], temp: &mut [u32], scale: &mut i32, target_scale: i32) { + temp.copy_from_slice(target); + let mut overflow = 0; + // Multiply by 10 until target scale reached or overflow + while *scale < target_scale && overflow == 0 { + overflow = mul_by_u32(temp, 10); + if overflow == 0 { + // Still no overflow + *scale += 1; + target.copy_from_slice(temp); + } + } + } + + if *quotient_scale > *working_scale { + mul_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale); + } else { + mul_by_10(quotient, &mut temp3, quotient_scale, *working_scale); + } + } + + // If our two quotients are still different then + // try to scale down the one with the bigger scale + // (ultimately losing significant digits) + if *quotient_scale != *working_scale { + // TODO: Remove necessity for temp (without performance impact) + fn div_by_10_lossy<const N: usize>( + target: &mut [u32], + temp: &mut [u32; N], + scale: &mut i32, + target_scale: i32, + ) { + temp.copy_from_slice(target); + // divide by 10 until target scale is reached + while *scale > target_scale { + div_by_u32(temp, 10); + *scale -= 1; + target.copy_from_slice(temp); + } + } + if *quotient_scale < *working_scale { + div_by_10_lossy(working_quotient, &mut temp4, working_scale, *quotient_scale); + } else { + div_by_10_lossy(quotient, &mut temp3, quotient_scale, *working_scale); + } + } + + // If quotient or working are zero we have an underflow condition + if is_all_zero(quotient) || is_all_zero(working_quotient) { + // Underflow + return true; + } else { + // Both numbers have the same scale and can be added. + // We just need to know whether we can fit them in + let mut underflow = false; + let mut temp = [0u32, 0u32, 0u32]; + while !underflow { + temp.copy_from_slice(quotient); + + // Add the working quotient + let overflow = add_by_internal(&mut temp, working_quotient); + if overflow == 0 { + // addition was successful + quotient.copy_from_slice(&temp); + break; + } else { + // addition overflowed - remove significant digits and try again + div_by_u32(quotient, 10); + *quotient_scale -= 1; + div_by_u32(working_quotient, 10); + *working_scale -= 1; + // Check for underflow + underflow = is_all_zero(quotient) || is_all_zero(working_quotient); + } + } + if underflow { + return true; + } + } + false +} + +/// Rescales the given decimals to equivalent scales. +/// It will firstly try to scale both the left and the right side to +/// the maximum scale of left/right. If it is unable to do that it +/// will try to reduce the accuracy of the other argument. +/// e.g. with 1.23 and 2.345 it'll rescale the first arg to 1.230 +#[inline(always)] +fn rescale_to_maximum_scale(left: &mut [u32; 3], left_scale: &mut u32, right: &mut [u32; 3], right_scale: &mut u32) { + if left_scale == right_scale { + // Nothing to do + return; + } + + if is_all_zero(left) { + *left_scale = *right_scale; + return; + } else if is_all_zero(right) { + *right_scale = *left_scale; + return; + } + + if left_scale > right_scale { + rescale_internal(right, right_scale, *left_scale); + if right_scale != left_scale { + rescale_internal(left, left_scale, *right_scale); + } + } else { + rescale_internal(left, left_scale, *right_scale); + if right_scale != left_scale { + rescale_internal(right, right_scale, *left_scale); + } + } +} + +#[cfg(test)] +mod test { + // Tests on private methods. + // + // All public tests should go under `tests/`. + + use super::*; + use crate::prelude::*; + + #[test] + fn it_can_rescale_to_maximum_scale() { + fn extract(value: &str) -> ([u32; 3], u32) { + let v = Decimal::from_str(value).unwrap(); + (v.mantissa_array3(), v.scale()) + } + + let tests = &[ + ("1", "1", "1", "1"), + ("1", "1.0", "1.0", "1.0"), + ("1", "1.00000", "1.00000", "1.00000"), + ("1", "1.0000000000", "1.0000000000", "1.0000000000"), + ( + "1", + "1.00000000000000000000", + "1.00000000000000000000", + "1.00000000000000000000", + ), + ("1.1", "1.1", "1.1", "1.1"), + ("1.1", "1.10000", "1.10000", "1.10000"), + ("1.1", "1.1000000000", "1.1000000000", "1.1000000000"), + ( + "1.1", + "1.10000000000000000000", + "1.10000000000000000000", + "1.10000000000000000000", + ), + ( + "0.6386554621848739495798319328", + "11.815126050420168067226890757", + "0.638655462184873949579831933", + "11.815126050420168067226890757", + ), + ( + "0.0872727272727272727272727272", // Scale 28 + "843.65000000", // Scale 8 + "0.0872727272727272727272727", // 25 + "843.6500000000000000000000000", // 25 + ), + ]; + + for &(left_raw, right_raw, expected_left, expected_right) in tests { + // Left = the value to rescale + // Right = the new scale we're scaling to + // Expected = the expected left value after rescale + let (expected_left, expected_lscale) = extract(expected_left); + let (expected_right, expected_rscale) = extract(expected_right); + + let (mut left, mut left_scale) = extract(left_raw); + let (mut right, mut right_scale) = extract(right_raw); + rescale_to_maximum_scale(&mut left, &mut left_scale, &mut right, &mut right_scale); + assert_eq!(left, expected_left); + assert_eq!(left_scale, expected_lscale); + assert_eq!(right, expected_right); + assert_eq!(right_scale, expected_rscale); + + // Also test the transitive case + let (mut left, mut left_scale) = extract(left_raw); + let (mut right, mut right_scale) = extract(right_raw); + rescale_to_maximum_scale(&mut right, &mut right_scale, &mut left, &mut left_scale); + assert_eq!(left, expected_left); + assert_eq!(left_scale, expected_lscale); + assert_eq!(right, expected_right); + assert_eq!(right_scale, expected_rscale); + } + } +} diff --git a/third_party/rust/rust_decimal/src/ops/mul.rs b/third_party/rust/rust_decimal/src/ops/mul.rs new file mode 100644 index 0000000000..b36729599d --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/mul.rs @@ -0,0 +1,168 @@ +use crate::constants::{BIG_POWERS_10, MAX_I64_SCALE, MAX_PRECISION_U32, U32_MAX}; +use crate::decimal::{CalculationResult, Decimal}; +use crate::ops::common::Buf24; + +pub(crate) fn mul_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + if d1.is_zero() || d2.is_zero() { + // We should think about this - does zero need to maintain precision? This treats it like + // an absolute which I think is ok, especially since we have is_zero() functions etc. + return CalculationResult::Ok(Decimal::ZERO); + } + + let mut scale = d1.scale() + d2.scale(); + let negative = d1.is_sign_negative() ^ d2.is_sign_negative(); + let mut product = Buf24::zero(); + + // See if we can optimize this calculation depending on whether the hi bits are set + if d1.hi() | d1.mid() == 0 { + if d2.hi() | d2.mid() == 0 { + // We're multiplying two 32 bit integers, so we can take some liberties to optimize this. + let mut low64 = d1.lo() as u64 * d2.lo() as u64; + if scale > MAX_PRECISION_U32 { + // We've exceeded maximum scale so we need to start reducing the precision (aka + // rounding) until we have something that fits. + // If we're too big then we effectively round to zero. + if scale > MAX_PRECISION_U32 + MAX_I64_SCALE { + return CalculationResult::Ok(Decimal::ZERO); + } + + scale -= MAX_PRECISION_U32 + 1; + let mut power = BIG_POWERS_10[scale as usize]; + + let tmp = low64 / power; + let remainder = low64 - tmp * power; + low64 = tmp; + + // Round the result. Since the divisor was a power of 10, it's always even. + power >>= 1; + if remainder >= power && (remainder > power || (low64 as u32 & 1) > 0) { + low64 += 1; + } + + scale = MAX_PRECISION_U32; + } + + // Early exit + return CalculationResult::Ok(Decimal::from_parts( + low64 as u32, + (low64 >> 32) as u32, + 0, + negative, + scale, + )); + } + + // We know that the left hand side is just 32 bits but the right hand side is either + // 64 or 96 bits. + mul_by_32bit_lhs(d1.lo() as u64, d2, &mut product); + } else if d2.mid() | d2.hi() == 0 { + // We know that the right hand side is just 32 bits. + mul_by_32bit_lhs(d2.lo() as u64, d1, &mut product); + } else { + // We know we're not dealing with simple 32 bit operands on either side. + // We compute and accumulate the 9 partial products using long multiplication + + // 1: ll * rl + let mut tmp = d1.lo() as u64 * d2.lo() as u64; + product.data[0] = tmp as u32; + + // 2: ll * rm + let mut tmp2 = (d1.lo() as u64 * d2.mid() as u64).wrapping_add(tmp >> 32); + + // 3: lm * rl + tmp = d1.mid() as u64 * d2.lo() as u64; + tmp = tmp.wrapping_add(tmp2); + product.data[1] = tmp as u32; + + // Detect if carry happened from the wrapping add + if tmp < tmp2 { + tmp2 = (tmp >> 32) | (1u64 << 32); + } else { + tmp2 = tmp >> 32; + } + + // 4: lm * rm + tmp = (d1.mid() as u64 * d2.mid() as u64) + tmp2; + + // If the high bit isn't set then we can stop here. Otherwise, we need to continue calculating + // using the high bits. + if (d1.hi() | d2.hi()) > 0 { + // 5. ll * rh + tmp2 = d1.lo() as u64 * d2.hi() as u64; + tmp = tmp.wrapping_add(tmp2); + // Detect if we carried + let mut tmp3 = if tmp < tmp2 { 1 } else { 0 }; + + // 6. lh * rl + tmp2 = d1.hi() as u64 * d2.lo() as u64; + tmp = tmp.wrapping_add(tmp2); + product.data[2] = tmp as u32; + // Detect if we carried + if tmp < tmp2 { + tmp3 += 1; + } + tmp2 = (tmp3 << 32) | (tmp >> 32); + + // 7. lm * rh + tmp = d1.mid() as u64 * d2.hi() as u64; + tmp = tmp.wrapping_add(tmp2); + // Check for carry + tmp3 = if tmp < tmp2 { 1 } else { 0 }; + + // 8. lh * rm + tmp2 = d1.hi() as u64 * d2.mid() as u64; + tmp = tmp.wrapping_add(tmp2); + product.data[3] = tmp as u32; + // Check for carry + if tmp < tmp2 { + tmp3 += 1; + } + tmp = (tmp3 << 32) | (tmp >> 32); + + // 9. lh * rh + product.set_high64(d1.hi() as u64 * d2.hi() as u64 + tmp); + } else { + product.set_mid64(tmp); + } + } + + // We may want to "rescale". This is the case if the mantissa is > 96 bits or if the scale + // exceeds the maximum precision. + let upper_word = product.upper_word(); + if upper_word > 2 || scale > MAX_PRECISION_U32 { + scale = if let Some(new_scale) = product.rescale(upper_word, scale) { + new_scale + } else { + return CalculationResult::Overflow; + } + } + + CalculationResult::Ok(Decimal::from_parts( + product.data[0], + product.data[1], + product.data[2], + negative, + scale, + )) +} + +#[inline(always)] +fn mul_by_32bit_lhs(d1: u64, d2: &Decimal, product: &mut Buf24) { + let mut tmp = d1 * d2.lo() as u64; + product.data[0] = tmp as u32; + tmp = (d1 * d2.mid() as u64).wrapping_add(tmp >> 32); + product.data[1] = tmp as u32; + tmp >>= 32; + + // If we're multiplying by a 96 bit integer then continue the calculation + if d2.hi() > 0 { + tmp = tmp.wrapping_add(d1 * d2.hi() as u64); + if tmp > U32_MAX { + product.set_mid64(tmp); + } else { + product.data[2] = tmp as u32; + } + } else { + product.data[2] = tmp as u32; + } +} diff --git a/third_party/rust/rust_decimal/src/ops/rem.rs b/third_party/rust/rust_decimal/src/ops/rem.rs new file mode 100644 index 0000000000..a79334e04b --- /dev/null +++ b/third_party/rust/rust_decimal/src/ops/rem.rs @@ -0,0 +1,285 @@ +use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; +use crate::decimal::{CalculationResult, Decimal}; +use crate::ops::common::{Buf12, Buf16, Buf24, Dec64}; + +pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { + if d2.is_zero() { + return CalculationResult::DivByZero; + } + if d1.is_zero() { + return CalculationResult::Ok(Decimal::ZERO); + } + + // We handle the structs a bit different here. Firstly, we ignore both the sign/scale of d2. + // This is because during a remainder operation we do not care about the sign of the divisor + // and only concern ourselves with that of the dividend. + let mut d1 = Dec64::new(d1); + let d2_scale = d2.scale(); + let mut d2 = Buf12::from_decimal(d2); + + let cmp = crate::ops::cmp::cmp_internal( + &d1, + &Dec64 { + negative: d1.negative, + scale: d2_scale, + hi: d2.hi(), + low64: d2.low64(), + }, + ); + match cmp { + core::cmp::Ordering::Equal => { + // Same numbers meaning that remainder is zero + return CalculationResult::Ok(Decimal::ZERO); + } + core::cmp::Ordering::Less => { + // d1 < d2, e.g. 1/2. This means that the result is the value of d1 + return CalculationResult::Ok(d1.to_decimal()); + } + core::cmp::Ordering::Greater => {} + } + + // At this point we know that the dividend > divisor and that they are both non-zero. + let mut scale = d1.scale as i32 - d2_scale as i32; + if scale > 0 { + // Scale up the divisor + loop { + let power = if scale >= MAX_I32_SCALE { + POWERS_10[9] + } else { + POWERS_10[scale as usize] + } as u64; + + let mut tmp = d2.lo() as u64 * power; + d2.set_lo(tmp as u32); + tmp >>= 32; + tmp = tmp.wrapping_add((d2.mid() as u64 + ((d2.hi() as u64) << 32)) * power); + d2.set_mid(tmp as u32); + d2.set_hi((tmp >> 32) as u32); + + // Keep scaling if there is more to go + scale -= MAX_I32_SCALE; + if scale <= 0 { + break; + } + } + scale = 0; + } + + loop { + // If the dividend is smaller than the divisor then try to scale that up first + if scale < 0 { + let mut quotient = Buf12 { + data: [d1.lo(), d1.mid(), d1.hi], + }; + loop { + // Figure out how much we can scale by + let power_scale; + if let Some(u) = quotient.find_scale(MAX_PRECISION_I32 + scale) { + if u >= POWERS_10.len() { + power_scale = 9; + } else { + power_scale = u; + } + } else { + return CalculationResult::Overflow; + }; + if power_scale == 0 { + break; + } + let power = POWERS_10[power_scale] as u64; + scale += power_scale as i32; + + let mut tmp = quotient.data[0] as u64 * power; + quotient.data[0] = tmp as u32; + tmp >>= 32; + quotient.set_high64(tmp.wrapping_add(quotient.high64().wrapping_mul(power))); + if power_scale != 9 { + break; + } + if scale >= 0 { + break; + } + } + d1.low64 = quotient.low64(); + d1.hi = quotient.data[2]; + d1.scale = d2_scale; + } + + // if the high portion is empty then return the modulus of the bottom portion + if d1.hi == 0 { + d1.low64 %= d2.low64(); + return CalculationResult::Ok(d1.to_decimal()); + } else if (d2.mid() | d2.hi()) == 0 { + let mut tmp = d1.high64(); + tmp = ((tmp % d2.lo() as u64) << 32) | (d1.lo() as u64); + d1.low64 = tmp % d2.lo() as u64; + d1.hi = 0; + } else { + // Divisor is > 32 bits + return rem_full(&d1, &d2, scale); + } + + if scale >= 0 { + break; + } + } + + CalculationResult::Ok(d1.to_decimal()) +} + +fn rem_full(d1: &Dec64, d2: &Buf12, scale: i32) -> CalculationResult { + let mut scale = scale; + + // First normalize the divisor + let shift = if d2.hi() == 0 { + d2.mid().leading_zeros() + } else { + d2.hi().leading_zeros() + }; + + let mut buffer = Buf24::zero(); + let mut overflow = 0u32; + buffer.set_low64(d1.low64 << shift); + buffer.set_mid64(((d1.mid() as u64).wrapping_add((d1.hi as u64) << 32)) >> (32 - shift)); + let mut upper = 3; // We start at 3 due to bit shifting + + while scale < 0 { + let power = if -scale >= MAX_I32_SCALE { + POWERS_10[9] + } else { + POWERS_10[-scale as usize] + } as u64; + let mut tmp64 = buffer.data[0] as u64 * power; + buffer.data[0] = tmp64 as u32; + + for (index, part) in buffer.data.iter_mut().enumerate().skip(1) { + if index > upper { + break; + } + tmp64 >>= 32; + tmp64 = tmp64.wrapping_add((*part as u64).wrapping_mul(power)); + *part = tmp64 as u32; + } + // If we have overflow then also process that + if upper == 6 { + tmp64 >>= 32; + tmp64 = tmp64.wrapping_add((overflow as u64).wrapping_mul(power)); + overflow = tmp64 as u32; + } + + // Make sure the high bit is not set + if tmp64 > 0x7FFF_FFFF { + upper += 1; + if upper > 5 { + overflow = (tmp64 >> 32) as u32; + } else { + buffer.data[upper] = (tmp64 >> 32) as u32; + } + } + scale += MAX_I32_SCALE; + } + + // TODO: Optimize slice logic + + let mut tmp = Buf16::zero(); + let divisor = d2.low64() << shift; + if d2.hi() == 0 { + // Do some division + if upper == 6 { + upper -= 1; + + tmp.data = [buffer.data[4], buffer.data[5], overflow, 0]; + tmp.partial_divide_64(divisor); + buffer.data[4] = tmp.data[0]; + buffer.data[5] = tmp.data[1]; + } + if upper == 5 { + upper -= 1; + tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], 0]; + tmp.partial_divide_64(divisor); + buffer.data[3] = tmp.data[0]; + buffer.data[4] = tmp.data[1]; + buffer.data[5] = tmp.data[2]; + } + if upper == 4 { + tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], 0]; + tmp.partial_divide_64(divisor); + buffer.data[2] = tmp.data[0]; + buffer.data[3] = tmp.data[1]; + buffer.data[4] = tmp.data[2]; + } + + tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], 0]; + tmp.partial_divide_64(divisor); + buffer.data[1] = tmp.data[0]; + buffer.data[2] = tmp.data[1]; + buffer.data[3] = tmp.data[2]; + + tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], 0]; + tmp.partial_divide_64(divisor); + buffer.data[0] = tmp.data[0]; + buffer.data[1] = tmp.data[1]; + buffer.data[2] = tmp.data[2]; + + let low64 = buffer.low64() >> shift; + CalculationResult::Ok(Decimal::from_parts( + low64 as u32, + (low64 >> 32) as u32, + 0, + d1.negative, + d1.scale, + )) + } else { + let divisor_low64 = divisor; + let divisor = Buf12 { + data: [ + divisor_low64 as u32, + (divisor_low64 >> 32) as u32, + (((d2.mid() as u64) + ((d2.hi() as u64) << 32)) >> (32 - shift)) as u32, + ], + }; + + // Do some division + if upper == 6 { + upper -= 1; + tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], overflow]; + tmp.partial_divide_96(&divisor); + buffer.data[3] = tmp.data[0]; + buffer.data[4] = tmp.data[1]; + buffer.data[5] = tmp.data[2]; + } + if upper == 5 { + upper -= 1; + tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], buffer.data[5]]; + tmp.partial_divide_96(&divisor); + buffer.data[2] = tmp.data[0]; + buffer.data[3] = tmp.data[1]; + buffer.data[4] = tmp.data[2]; + buffer.data[5] = tmp.data[3]; + } + if upper == 4 { + tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], buffer.data[4]]; + tmp.partial_divide_96(&divisor); + buffer.data[1] = tmp.data[0]; + buffer.data[2] = tmp.data[1]; + buffer.data[3] = tmp.data[2]; + buffer.data[4] = tmp.data[3]; + } + + tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], buffer.data[3]]; + tmp.partial_divide_96(&divisor); + buffer.data[0] = tmp.data[0]; + buffer.data[1] = tmp.data[1]; + buffer.data[2] = tmp.data[2]; + buffer.data[3] = tmp.data[3]; + + let low64 = (buffer.low64() >> shift) + ((buffer.data[2] as u64) << (32 - shift) << 32); + CalculationResult::Ok(Decimal::from_parts( + low64 as u32, + (low64 >> 32) as u32, + buffer.data[2] >> shift, + d1.negative, + d1.scale, + )) + } +} diff --git a/third_party/rust/rust_decimal/src/postgres.rs b/third_party/rust/rust_decimal/src/postgres.rs new file mode 100644 index 0000000000..0930e592ab --- /dev/null +++ b/third_party/rust/rust_decimal/src/postgres.rs @@ -0,0 +1,8 @@ +// Shared +mod common; + +#[cfg(any(feature = "db-diesel1-postgres", feature = "db-diesel2-postgres"))] +mod diesel; + +#[cfg(any(feature = "db-postgres", feature = "db-tokio-postgres"))] +mod driver; diff --git a/third_party/rust/rust_decimal/src/postgres/common.rs b/third_party/rust/rust_decimal/src/postgres/common.rs new file mode 100644 index 0000000000..b821b1edaa --- /dev/null +++ b/third_party/rust/rust_decimal/src/postgres/common.rs @@ -0,0 +1,140 @@ +use crate::constants::MAX_PRECISION_U32; +use crate::{ + ops::array::{div_by_u32, is_all_zero, mul_by_u32}, + Decimal, +}; +use core::fmt; +use std::error; + +#[derive(Debug, Clone)] +pub struct InvalidDecimal { + inner: Option<String>, +} + +impl fmt::Display for InvalidDecimal { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if let Some(ref msg) = self.inner { + fmt.write_fmt(format_args!("Invalid Decimal: {}", msg)) + } else { + fmt.write_str("Invalid Decimal") + } + } +} + +impl error::Error for InvalidDecimal {} + +pub(in crate::postgres) struct PostgresDecimal<D> { + pub neg: bool, + pub weight: i16, + pub scale: u16, + pub digits: D, +} + +impl Decimal { + pub(in crate::postgres) fn from_postgres<D: ExactSizeIterator<Item = u16>>( + PostgresDecimal { + neg, + scale, + digits, + weight, + }: PostgresDecimal<D>, + ) -> Self { + let mut digits = digits.into_iter().collect::<Vec<_>>(); + + let fractionals_part_count = digits.len() as i32 + (-weight as i32) - 1; + let integers_part_count = weight as i32 + 1; + + let mut result = Decimal::ZERO; + // adding integer part + if integers_part_count > 0 { + let (start_integers, last) = if integers_part_count > digits.len() as i32 { + (integers_part_count - digits.len() as i32, digits.len() as i32) + } else { + (0, integers_part_count) + }; + let integers: Vec<_> = digits.drain(..last as usize).collect(); + for digit in integers { + result *= Decimal::from_i128_with_scale(10i128.pow(4), 0); + result += Decimal::new(digit as i64, 0); + } + result *= Decimal::from_i128_with_scale(10i128.pow(4 * start_integers as u32), 0); + } + // adding fractional part + if fractionals_part_count > 0 { + let start_fractionals = if weight < 0 { (-weight as u32) - 1 } else { 0 }; + for (i, digit) in digits.into_iter().enumerate() { + let fract_pow = 4 * (i as u32 + 1 + start_fractionals); + if fract_pow <= MAX_PRECISION_U32 { + result += Decimal::new(digit as i64, 0) / Decimal::from_i128_with_scale(10i128.pow(fract_pow), 0); + } else if fract_pow == MAX_PRECISION_U32 + 4 { + // rounding last digit + if digit >= 5000 { + result += + Decimal::new(1_i64, 0) / Decimal::from_i128_with_scale(10i128.pow(MAX_PRECISION_U32), 0); + } + } + } + } + + result.set_sign_negative(neg); + // Rescale to the postgres value, automatically rounding as needed. + result.rescale(scale as u32); + result + } + + pub(in crate::postgres) fn to_postgres(self) -> PostgresDecimal<Vec<i16>> { + if self.is_zero() { + return PostgresDecimal { + neg: false, + weight: 0, + scale: 0, + digits: vec![0], + }; + } + let scale = self.scale() as u16; + + let groups_diff = scale & 0x3; // groups_diff = scale % 4 + + let mut mantissa = self.mantissa_array4(); + + if groups_diff > 0 { + let remainder = 4 - groups_diff; + let power = 10u32.pow(u32::from(remainder)); + mul_by_u32(&mut mantissa, power); + } + + // array to store max mantissa of Decimal in Postgres decimal format + const MAX_GROUP_COUNT: usize = 8; + let mut digits = Vec::with_capacity(MAX_GROUP_COUNT); + + while !is_all_zero(&mantissa) { + let digit = div_by_u32(&mut mantissa, 10000) as u16; + digits.push(digit.try_into().unwrap()); + } + digits.reverse(); + let digits_after_decimal = (scale + 3) as u16 / 4; + let weight = digits.len() as i16 - digits_after_decimal as i16 - 1; + + let unnecessary_zeroes = if weight >= 0 { + let index_of_decimal = (weight + 1) as usize; + digits + .get(index_of_decimal..) + .expect("enough digits exist") + .iter() + .rev() + .take_while(|i| **i == 0) + .count() + } else { + 0 + }; + let relevant_digits = digits.len() - unnecessary_zeroes; + digits.truncate(relevant_digits); + + PostgresDecimal { + neg: self.is_sign_negative(), + digits, + scale, + weight, + } + } +} diff --git a/third_party/rust/rust_decimal/src/postgres/diesel.rs b/third_party/rust/rust_decimal/src/postgres/diesel.rs new file mode 100644 index 0000000000..26cd3b33be --- /dev/null +++ b/third_party/rust/rust_decimal/src/postgres/diesel.rs @@ -0,0 +1,333 @@ +use crate::postgres::common::*; +use crate::Decimal; +use diesel::{ + deserialize::{self, FromSql}, + pg::data_types::PgNumeric, + pg::Pg, + serialize::{self, Output, ToSql}, + sql_types::Numeric, +}; +use std::error; + +impl<'a> TryFrom<&'a PgNumeric> for Decimal { + type Error = Box<dyn error::Error + Send + Sync>; + + fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> { + let (neg, weight, scale, digits) = match *numeric { + PgNumeric::Positive { + weight, + scale, + ref digits, + } => (false, weight, scale, digits), + PgNumeric::Negative { + weight, + scale, + ref digits, + } => (true, weight, scale, digits), + PgNumeric::NaN => return Err(Box::from("NaN is not supported in Decimal")), + }; + + Ok(Self::from_postgres(PostgresDecimal { + neg, + weight, + scale, + digits: digits.iter().copied().map(|v| v.try_into().unwrap()), + })) + } +} + +impl TryFrom<PgNumeric> for Decimal { + type Error = Box<dyn error::Error + Send + Sync>; + + fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> { + (&numeric).try_into() + } +} + +impl<'a> From<&'a Decimal> for PgNumeric { + fn from(decimal: &'a Decimal) -> Self { + let PostgresDecimal { + neg, + weight, + scale, + digits, + } = decimal.to_postgres(); + + if neg { + PgNumeric::Negative { digits, scale, weight } + } else { + PgNumeric::Positive { digits, scale, weight } + } + } +} + +impl From<Decimal> for PgNumeric { + fn from(decimal: Decimal) -> Self { + (&decimal).into() + } +} + +#[cfg(all(feature = "diesel1", not(feature = "diesel2")))] +impl ToSql<Numeric, Pg> for Decimal { + fn to_sql<W: std::io::Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result { + let numeric = PgNumeric::from(self); + ToSql::<Numeric, Pg>::to_sql(&numeric, out) + } +} + +#[cfg(feature = "diesel2")] +impl ToSql<Numeric, Pg> for Decimal { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let numeric = PgNumeric::from(self); + ToSql::<Numeric, Pg>::to_sql(&numeric, &mut out.reborrow()) + } +} + +#[cfg(all(feature = "diesel1", not(feature = "diesel2")))] +impl FromSql<Numeric, Pg> for Decimal { + fn from_sql(numeric: Option<&[u8]>) -> deserialize::Result<Self> { + PgNumeric::from_sql(numeric)?.try_into() + } +} + +#[cfg(feature = "diesel2")] +impl FromSql<Numeric, Pg> for Decimal { + fn from_sql(numeric: diesel::pg::PgValue) -> deserialize::Result<Self> { + PgNumeric::from_sql(numeric)?.try_into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::str::FromStr; + + #[test] + fn test_unnecessary_zeroes() { + fn extract(value: &str) -> Decimal { + Decimal::from_str(value).unwrap() + } + + let tests = &[ + ("0.000001660"), + ("41.120255926293000"), + ("0.5538973300"), + ("08883.55986854293100"), + ("0.0000_0000_0016_6000_00"), + ("0.00000166650000"), + ("1666500000000"), + ("1666500000000.0000054500"), + ("8944.000000000000"), + ]; + + for &value in tests { + let value = extract(value); + let pg = PgNumeric::from(value); + let dec = Decimal::try_from(pg).unwrap(); + assert_eq!(dec, value); + } + } + + #[test] + fn decimal_to_pgnumeric_converts_digits_to_base_10000() { + let decimal = Decimal::from_str("1").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 0, + digits: vec![1], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("10").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 0, + digits: vec![10], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("10000").unwrap(); + let expected = PgNumeric::Positive { + weight: 1, + scale: 0, + digits: vec![1, 0], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("10001").unwrap(); + let expected = PgNumeric::Positive { + weight: 1, + scale: 0, + digits: vec![1, 1], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("100000000").unwrap(); + let expected = PgNumeric::Positive { + weight: 2, + scale: 0, + digits: vec![1, 0, 0], + }; + assert_eq!(expected, decimal.into()); + } + + #[test] + fn decimal_to_pg_numeric_properly_adjusts_scale() { + let decimal = Decimal::from_str("1").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 0, + digits: vec![1], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("1.0").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 1, + digits: vec![1], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("1.1").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 1, + digits: vec![1, 1000], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("1.10").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 2, + digits: vec![1, 1000], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("100000000.0001").unwrap(); + let expected = PgNumeric::Positive { + weight: 2, + scale: 4, + digits: vec![1, 0, 0, 1], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("0.1").unwrap(); + let expected = PgNumeric::Positive { + weight: -1, + scale: 1, + digits: vec![1000], + }; + assert_eq!(expected, decimal.into()); + } + + #[test] + fn decimal_to_pg_numeric_retains_sign() { + let decimal = Decimal::from_str("123.456").unwrap(); + let expected = PgNumeric::Positive { + weight: 0, + scale: 3, + digits: vec![123, 4560], + }; + assert_eq!(expected, decimal.into()); + + let decimal = Decimal::from_str("-123.456").unwrap(); + let expected = PgNumeric::Negative { + weight: 0, + scale: 3, + digits: vec![123, 4560], + }; + assert_eq!(expected, decimal.into()); + } + + #[test] + fn pg_numeric_to_decimal_works() { + let expected = Decimal::from_str("50").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 0, + digits: vec![50], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res, expected); + let expected = Decimal::from_str("123.456").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 3, + digits: vec![123, 4560], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res, expected); + + let expected = Decimal::from_str("-56.78").unwrap(); + let pg_numeric = PgNumeric::Negative { + weight: 0, + scale: 2, + digits: vec![56, 7800], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res, expected); + + // Verify no trailing zeroes are lost. + + let expected = Decimal::from_str("1.100").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 3, + digits: vec![1, 1000], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + + // To represent 5.00, Postgres can return either [5, 0] as the list of digits. + let expected = Decimal::from_str("5.00").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 2, + + digits: vec![5, 0], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + + // To represent 5.00, Postgres can return [5] as the list of digits. + let expected = Decimal::from_str("5.00").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 2, + digits: vec![5], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + + let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 30, + digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800], + }; + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + + let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 34, + digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800], + }; + + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + + let expected = Decimal::from_str("1.2345678901234567890123456790").unwrap(); + let pg_numeric = PgNumeric::Positive { + weight: 0, + scale: 34, + digits: vec![1, 2345, 6789, 0123, 4567, 8901, 2345, 6789, 5000, 0], + }; + + let res: Decimal = pg_numeric.try_into().unwrap(); + assert_eq!(res.to_string(), expected.to_string()); + } +} diff --git a/third_party/rust/rust_decimal/src/postgres/driver.rs b/third_party/rust/rust_decimal/src/postgres/driver.rs new file mode 100644 index 0000000000..7d185e3bab --- /dev/null +++ b/third_party/rust/rust_decimal/src/postgres/driver.rs @@ -0,0 +1,383 @@ +use crate::postgres::common::*; +use crate::Decimal; +use byteorder::{BigEndian, ReadBytesExt}; +use bytes::{BufMut, BytesMut}; +use postgres::types::{to_sql_checked, FromSql, IsNull, ToSql, Type}; +use std::io::Cursor; + +impl<'a> FromSql<'a> for Decimal { + // Decimals are represented as follows: + // Header: + // u16 numGroups + // i16 weightFirstGroup (10000^weight) + // u16 sign (0x0000 = positive, 0x4000 = negative, 0xC000 = NaN) + // i16 dscale. Number of digits (in base 10) to print after decimal separator + // + // Pseudo code : + // const Decimals [ + // 0.0000000000000000000000000001, + // 0.000000000000000000000001, + // 0.00000000000000000001, + // 0.0000000000000001, + // 0.000000000001, + // 0.00000001, + // 0.0001, + // 1, + // 10000, + // 100000000, + // 1000000000000, + // 10000000000000000, + // 100000000000000000000, + // 1000000000000000000000000, + // 10000000000000000000000000000 + // ] + // overflow = false + // result = 0 + // for i = 0, weight = weightFirstGroup + 7; i < numGroups; i++, weight-- + // group = read.u16 + // if weight < 0 or weight > MaxNum + // overflow = true + // else + // result += Decimals[weight] * group + // sign == 0x4000 ? -result : result + + // So if we were to take the number: 3950.123456 + // + // Stored on Disk: + // 00 03 00 00 00 00 00 06 0F 6E 04 D2 15 E0 + // + // Number of groups: 00 03 + // Weight of first group: 00 00 + // Sign: 00 00 + // DScale: 00 06 + // + // 0F 6E = 3950 + // result = result + 3950 * 1; + // 04 D2 = 1234 + // result = result + 1234 * 0.0001; + // 15 E0 = 5600 + // result = result + 5600 * 0.00000001; + // + + fn from_sql(_: &Type, raw: &[u8]) -> Result<Decimal, Box<dyn std::error::Error + 'static + Sync + Send>> { + let mut raw = Cursor::new(raw); + let num_groups = raw.read_u16::<BigEndian>()?; + let weight = raw.read_i16::<BigEndian>()?; // 10000^weight + // Sign: 0x0000 = positive, 0x4000 = negative, 0xC000 = NaN + let sign = raw.read_u16::<BigEndian>()?; + // Number of digits (in base 10) to print after decimal separator + let scale = raw.read_u16::<BigEndian>()?; + + // Read all of the groups + let mut groups = Vec::new(); + for _ in 0..num_groups as usize { + groups.push(raw.read_u16::<BigEndian>()?); + } + + Ok(Self::from_postgres(PostgresDecimal { + neg: sign == 0x4000, + weight, + scale, + digits: groups.into_iter(), + })) + } + + fn accepts(ty: &Type) -> bool { + matches!(*ty, Type::NUMERIC) + } +} + +impl ToSql for Decimal { + fn to_sql( + &self, + _: &Type, + out: &mut BytesMut, + ) -> Result<IsNull, Box<dyn std::error::Error + 'static + Sync + Send>> { + let PostgresDecimal { + neg, + weight, + scale, + digits, + } = self.to_postgres(); + + let num_digits = digits.len(); + + // Reserve bytes + out.reserve(8 + num_digits * 2); + + // Number of groups + out.put_u16(num_digits.try_into().unwrap()); + // Weight of first group + out.put_i16(weight); + // Sign + out.put_u16(if neg { 0x4000 } else { 0x0000 }); + // DScale + out.put_u16(scale); + // Now process the number + for digit in digits[0..num_digits].iter() { + out.put_i16(*digit); + } + + Ok(IsNull::No) + } + + fn accepts(ty: &Type) -> bool { + matches!(*ty, Type::NUMERIC) + } + + to_sql_checked!(); +} + +#[cfg(test)] +mod test { + use super::*; + use ::postgres::{Client, NoTls}; + use core::str::FromStr; + + /// Gets the URL for connecting to PostgreSQL for testing. Set the POSTGRES_URL + /// environment variable to change from the default of "postgres://postgres@localhost". + fn get_postgres_url() -> String { + if let Ok(url) = std::env::var("POSTGRES_URL") { + return url; + } + "postgres://postgres@localhost".to_string() + } + + pub static TEST_DECIMALS: &[(u32, u32, &str, &str)] = &[ + // precision, scale, sent, expected + (35, 6, "3950.123456", "3950.123456"), + (35, 2, "3950.123456", "3950.12"), + (35, 2, "3950.1256", "3950.13"), + (10, 2, "3950.123456", "3950.12"), + (35, 6, "3950", "3950.000000"), + (4, 0, "3950", "3950"), + (35, 6, "0.1", "0.100000"), + (35, 6, "0.01", "0.010000"), + (35, 6, "0.001", "0.001000"), + (35, 6, "0.0001", "0.000100"), + (35, 6, "0.00001", "0.000010"), + (35, 6, "0.000001", "0.000001"), + (35, 6, "1", "1.000000"), + (35, 6, "-100", "-100.000000"), + (35, 6, "-123.456", "-123.456000"), + (35, 6, "119996.25", "119996.250000"), + (35, 6, "1000000", "1000000.000000"), + (35, 6, "9999999.99999", "9999999.999990"), + (35, 6, "12340.56789", "12340.567890"), + // Scale is only 28 since that is the maximum we can represent. + (65, 30, "1.2", "1.2000000000000000000000000000"), + // Pi - rounded at scale 28 + ( + 65, + 30, + "3.141592653589793238462643383279", + "3.1415926535897932384626433833", + ), + ( + 65, + 34, + "3.1415926535897932384626433832795028", + "3.1415926535897932384626433833", + ), + // Unrounded number + ( + 65, + 34, + "1.234567890123456789012345678950000", + "1.2345678901234567890123456790", + ), + ( + 65, + 34, // No rounding due to 49999 after significant digits + "1.234567890123456789012345678949999", + "1.2345678901234567890123456789", + ), + // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF (96 bit) + (35, 0, "79228162514264337593543950335", "79228162514264337593543950335"), + // 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF (95 bit) + (35, 1, "4951760157141521099596496895", "4951760157141521099596496895.0"), + // 0x1000_0000_0000_0000_0000_0000 + (35, 1, "4951760157141521099596496896", "4951760157141521099596496896.0"), + (35, 6, "18446744073709551615", "18446744073709551615.000000"), + (35, 6, "-18446744073709551615", "-18446744073709551615.000000"), + (35, 6, "0.10001", "0.100010"), + (35, 6, "0.12345", "0.123450"), + ]; + + #[test] + fn test_null() { + let mut client = match Client::connect(&get_postgres_url(), NoTls) { + Ok(x) => x, + Err(err) => panic!("{:#?}", err), + }; + + // Test NULL + let result: Option<Decimal> = match client.query("SELECT NULL::numeric", &[]) { + Ok(x) => x.iter().next().unwrap().get(0), + Err(err) => panic!("{:#?}", err), + }; + assert_eq!(None, result); + } + + #[tokio::test] + #[cfg(feature = "tokio-pg")] + async fn async_test_null() { + use futures::future::FutureExt; + use tokio_postgres::connect; + + let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); + let connection = connection.map(|e| e.unwrap()); + tokio::spawn(connection); + + let statement = client.prepare(&"SELECT NULL::numeric").await.unwrap(); + let rows = client.query(&statement, &[]).await.unwrap(); + let result: Option<Decimal> = rows.iter().next().unwrap().get(0); + + assert_eq!(None, result); + } + + #[test] + fn read_very_small_numeric_type() { + let mut client = match Client::connect(&get_postgres_url(), NoTls) { + Ok(x) => x, + Err(err) => panic!("{:#?}", err), + }; + let result: Decimal = match client.query("SELECT 1e-130::NUMERIC(130, 0)", &[]) { + Ok(x) => x.iter().next().unwrap().get(0), + Err(err) => panic!("error - {:#?}", err), + }; + // We compare this to zero since it is so small that it is effectively zero + assert_eq!(Decimal::ZERO, result); + } + + #[test] + fn read_numeric_type() { + let mut client = match Client::connect(&get_postgres_url(), NoTls) { + Ok(x) => x, + Err(err) => panic!("{:#?}", err), + }; + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let result: Decimal = + match client.query(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale), &[]) { + Ok(x) => x.iter().next().unwrap().get(0), + Err(err) => panic!("SELECT {}::NUMERIC({}, {}), error - {:#?}", sent, precision, scale, err), + }; + assert_eq!( + expected, + result.to_string(), + "NUMERIC({}, {}) sent: {}", + precision, + scale, + sent + ); + } + } + + #[tokio::test] + #[cfg(feature = "tokio-pg")] + async fn async_read_numeric_type() { + use futures::future::FutureExt; + use tokio_postgres::connect; + + let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); + let connection = connection.map(|e| e.unwrap()); + tokio::spawn(connection); + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let statement = client + .prepare(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale)) + .await + .unwrap(); + let rows = client.query(&statement, &[]).await.unwrap(); + let result: Decimal = rows.iter().next().unwrap().get(0); + + assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); + } + } + + #[test] + fn write_numeric_type() { + let mut client = match Client::connect(&get_postgres_url(), NoTls) { + Ok(x) => x, + Err(err) => panic!("{:#?}", err), + }; + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let number = Decimal::from_str(sent).unwrap(); + let result: Decimal = + match client.query(&*format!("SELECT $1::NUMERIC({}, {})", precision, scale), &[&number]) { + Ok(x) => x.iter().next().unwrap().get(0), + Err(err) => panic!("{:#?}", err), + }; + assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); + } + } + + #[tokio::test] + #[cfg(feature = "tokio-pg")] + async fn async_write_numeric_type() { + use futures::future::FutureExt; + use tokio_postgres::connect; + + let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); + let connection = connection.map(|e| e.unwrap()); + tokio::spawn(connection); + + for &(precision, scale, sent, expected) in TEST_DECIMALS.iter() { + let statement = client + .prepare(&*format!("SELECT $1::NUMERIC({}, {})", precision, scale)) + .await + .unwrap(); + let number = Decimal::from_str(sent).unwrap(); + let rows = client.query(&statement, &[&number]).await.unwrap(); + let result: Decimal = rows.iter().next().unwrap().get(0); + + assert_eq!(expected, result.to_string(), "NUMERIC({}, {})", precision, scale); + } + } + + #[test] + fn numeric_overflow() { + let tests = [(4, 4, "3950.1234")]; + let mut client = match Client::connect(&get_postgres_url(), NoTls) { + Ok(x) => x, + Err(err) => panic!("{:#?}", err), + }; + for &(precision, scale, sent) in tests.iter() { + match client.query(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale), &[]) { + Ok(_) => panic!( + "Expected numeric overflow for {}::NUMERIC({}, {})", + sent, precision, scale + ), + Err(err) => { + assert_eq!("22003", err.code().unwrap().code(), "Unexpected error code"); + } + }; + } + } + + #[tokio::test] + #[cfg(feature = "tokio-pg")] + async fn async_numeric_overflow() { + use futures::future::FutureExt; + use tokio_postgres::connect; + + let tests = [(4, 4, "3950.1234")]; + let (client, connection) = connect(&get_postgres_url(), NoTls).await.unwrap(); + let connection = connection.map(|e| e.unwrap()); + tokio::spawn(connection); + + for &(precision, scale, sent) in tests.iter() { + let statement = client + .prepare(&*format!("SELECT {}::NUMERIC({}, {})", sent, precision, scale)) + .await + .unwrap(); + + match client.query(&statement, &[]).await { + Ok(_) => panic!( + "Expected numeric overflow for {}::NUMERIC({}, {})", + sent, precision, scale + ), + Err(err) => assert_eq!("22003", err.code().unwrap().code(), "Unexpected error code"), + } + } + } +} diff --git a/third_party/rust/rust_decimal/src/rand.rs b/third_party/rust/rust_decimal/src/rand.rs new file mode 100644 index 0000000000..afd8a64361 --- /dev/null +++ b/third_party/rust/rust_decimal/src/rand.rs @@ -0,0 +1,176 @@ +use crate::Decimal; +use rand::{ + distributions::{ + uniform::{SampleBorrow, SampleUniform, UniformInt, UniformSampler}, + Distribution, Standard, + }, + Rng, +}; + +impl Distribution<Decimal> for Standard { + fn sample<R>(&self, rng: &mut R) -> Decimal + where + R: Rng + ?Sized, + { + Decimal::from_parts( + rng.next_u32(), + rng.next_u32(), + rng.next_u32(), + rng.gen(), + rng.next_u32(), + ) + } +} + +impl SampleUniform for Decimal { + type Sampler = DecimalSampler; +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct DecimalSampler { + mantissa_sampler: UniformInt<i128>, + scale: u32, +} + +impl UniformSampler for DecimalSampler { + type X = Decimal; + + /// Creates a new sampler that will yield random decimal objects between `low` and `high`. + /// + /// The sampler will always provide decimals at the same scale as the inputs; if the inputs + /// have different scales, the higher scale is used. + /// + /// # Example + /// + /// ``` + /// # use rand::Rng; + /// # use rust_decimal_macros::dec; + /// let mut rng = rand::rngs::OsRng; + /// let random = rng.gen_range(dec!(1.00)..dec!(2.00)); + /// assert!(random >= dec!(1.00)); + /// assert!(random < dec!(2.00)); + /// assert_eq!(random.scale(), 2); + /// ``` + #[inline] + fn new<B1, B2>(low: B1, high: B2) -> Self + where + B1: SampleBorrow<Self::X> + Sized, + B2: SampleBorrow<Self::X> + Sized, + { + let (low, high) = sync_scales(*low.borrow(), *high.borrow()); + let high = Decimal::from_i128_with_scale(high.mantissa() - 1, high.scale()); + UniformSampler::new_inclusive(low, high) + } + + /// Creates a new sampler that will yield random decimal objects between `low` and `high`. + /// + /// The sampler will always provide decimals at the same scale as the inputs; if the inputs + /// have different scales, the higher scale is used. + /// + /// # Example + /// + /// ``` + /// # use rand::Rng; + /// # use rust_decimal_macros::dec; + /// let mut rng = rand::rngs::OsRng; + /// let random = rng.gen_range(dec!(1.00)..=dec!(2.00)); + /// assert!(random >= dec!(1.00)); + /// assert!(random <= dec!(2.00)); + /// assert_eq!(random.scale(), 2); + /// ``` + #[inline] + fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self + where + B1: SampleBorrow<Self::X> + Sized, + B2: SampleBorrow<Self::X> + Sized, + { + let (low, high) = sync_scales(*low.borrow(), *high.borrow()); + + // Return our sampler, which contains an underlying i128 sampler so we + // outsource the actual randomness implementation. + Self { + mantissa_sampler: UniformInt::new_inclusive(low.mantissa(), high.mantissa()), + scale: low.scale(), + } + } + + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X { + let mantissa = self.mantissa_sampler.sample(rng); + Decimal::from_i128_with_scale(mantissa, self.scale) + } +} + +/// Return equivalent Decimal objects with the same scale as one another. +#[inline] +fn sync_scales(mut a: Decimal, mut b: Decimal) -> (Decimal, Decimal) { + if a.scale() == b.scale() { + return (a, b); + } + + // Set scales to match one another, because we are relying on mantissas' + // being comparable in order outsource the actual sampling implementation. + a.rescale(a.scale().max(b.scale())); + b.rescale(a.scale().max(b.scale())); + + // Edge case: If the values have _wildly_ different scales, the values may not have rescaled far enough to match one another. + // + // In this case, we accept some precision loss because the randomization approach we are using assumes that the scales will necessarily match. + if a.scale() != b.scale() { + a.rescale(a.scale().min(b.scale())); + b.rescale(a.scale().min(b.scale())); + } + + (a, b) +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::*; + + macro_rules! dec { + ($e:expr) => { + Decimal::from_str_exact(stringify!($e)).unwrap() + }; + } + + #[test] + fn has_random_decimal_instances() { + let mut rng = rand::rngs::OsRng; + let random: [Decimal; 32] = rng.gen(); + assert!(random.windows(2).any(|slice| { slice[0] != slice[1] })); + } + + #[test] + fn generates_within_range() { + let mut rng = rand::rngs::OsRng; + for _ in 0..128 { + let random = rng.gen_range(dec!(1.00)..dec!(1.05)); + assert!(random < dec!(1.05)); + assert!(random >= dec!(1.00)); + } + } + + #[test] + fn generates_within_inclusive_range() { + let mut rng = rand::rngs::OsRng; + let mut values: HashSet<Decimal> = HashSet::new(); + for _ in 0..256 { + let random = rng.gen_range(dec!(1.00)..=dec!(1.01)); + // The scale is 2, so 1.00 and 1.01 are the only two valid choices. + assert!(random == dec!(1.00) || random == dec!(1.01)); + values.insert(random); + } + // Somewhat flaky, will fail 1 out of every 2^255 times this is run. + // Probably acceptable in the real world. + assert_eq!(values.len(), 2); + } + + #[test] + fn test_edge_case_scales_match() { + let (low, high) = sync_scales(dec!(1.000_000_000_000_000_000_01), dec!(100_000_000_000_000_000_001)); + assert_eq!(low.scale(), high.scale()); + } +} diff --git a/third_party/rust/rust_decimal/src/rocket.rs b/third_party/rust/rust_decimal/src/rocket.rs new file mode 100644 index 0000000000..6c8938ebec --- /dev/null +++ b/third_party/rust/rust_decimal/src/rocket.rs @@ -0,0 +1,12 @@ +use crate::Decimal; +use rocket::form::{self, FromFormField, ValueField}; +use std::str::FromStr; + +impl<'v> FromFormField<'v> for Decimal { + fn default() -> Option<Self> { + None + } + fn from_value(field: ValueField<'v>) -> form::Result<'v, Self> { + Decimal::from_str(field.value).map_err(|_| form::Error::validation("not a valid number").into()) + } +} diff --git a/third_party/rust/rust_decimal/src/serde.rs b/third_party/rust/rust_decimal/src/serde.rs new file mode 100644 index 0000000000..ce876309f9 --- /dev/null +++ b/third_party/rust/rust_decimal/src/serde.rs @@ -0,0 +1,899 @@ +use crate::Decimal; +use alloc::string::ToString; +use core::{fmt, str::FromStr}; +use num_traits::FromPrimitive; +use serde::{self, de::Unexpected}; + +/// Serialize/deserialize Decimals as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct ArbitraryExample { +/// #[serde(with = "rust_decimal::serde::arbitrary_precision")] +/// value: Decimal, +/// } +/// +/// let value = ArbitraryExample { value: Decimal::from_str("123.400").unwrap() }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":123.400}"# +/// ); +/// ``` +#[cfg(feature = "serde-with-arbitrary-precision")] +pub mod arbitrary_precision { + use super::*; + use serde::Serialize; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_any(DecimalVisitor) + } + + pub fn serialize<S>(value: &Decimal, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + serde_json::Number::from_str(&value.to_string()) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) + } +} + +/// Serialize/deserialize optional Decimals as arbitrary precision numbers in JSON using the `arbitrary_precision` feature within `serde_json`. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct ArbitraryExample { +/// #[serde(with = "rust_decimal::serde::arbitrary_precision_option")] +/// value: Option<Decimal>, +/// } +/// +/// let value = ArbitraryExample { value: Some(Decimal::from_str("123.400").unwrap()) }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":123.400}"# +/// ); +/// +/// let value = ArbitraryExample { value: None }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":null}"# +/// ); +/// ``` +#[cfg(feature = "serde-with-arbitrary-precision")] +pub mod arbitrary_precision_option { + use super::*; + use serde::Serialize; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_option(OptionDecimalVisitor) + } + + pub fn serialize<S>(value: &Option<Decimal>, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + match *value { + Some(ref decimal) => serde_json::Number::from_str(&decimal.to_string()) + .map_err(serde::ser::Error::custom)? + .serialize(serializer), + None => serializer.serialize_none(), + } + } +} + +/// Serialize/deserialize Decimals as floats. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct FloatExample { +/// #[serde(with = "rust_decimal::serde::float")] +/// value: Decimal, +/// } +/// +/// let value = FloatExample { value: Decimal::from_str("123.400").unwrap() }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":123.4}"# +/// ); +/// ``` +#[cfg(feature = "serde-with-float")] +pub mod float { + use super::*; + use serde::Serialize; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_any(DecimalVisitor) + } + + pub fn serialize<S>(value: &Decimal, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + use num_traits::ToPrimitive; + value.to_f64().unwrap().serialize(serializer) + } +} + +/// Serialize/deserialize optional Decimals as floats. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct FloatExample { +/// #[serde(with = "rust_decimal::serde::float_option")] +/// value: Option<Decimal>, +/// } +/// +/// let value = FloatExample { value: Some(Decimal::from_str("123.400").unwrap()) }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":123.4}"# +/// ); +/// +/// let value = FloatExample { value: None }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":null}"# +/// ); +/// ``` +#[cfg(feature = "serde-with-float")] +pub mod float_option { + use super::*; + use serde::Serialize; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_option(OptionDecimalVisitor) + } + + pub fn serialize<S>(value: &Option<Decimal>, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + match *value { + Some(ref decimal) => { + use num_traits::ToPrimitive; + decimal.to_f64().unwrap().serialize(serializer) + } + None => serializer.serialize_none(), + } + } +} + +/// Serialize/deserialize Decimals as strings. This is particularly useful when using binary encoding formats. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct StringExample { +/// #[serde(with = "rust_decimal::serde::str")] +/// value: Decimal, +/// } +/// +/// let value = StringExample { value: Decimal::from_str("123.400").unwrap() }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":"123.400"}"# +/// ); +/// +/// ``` +#[cfg(feature = "serde-with-str")] +pub mod str { + use super::*; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_str(DecimalVisitor) + } + + pub fn serialize<S>(value: &Decimal, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + let value = crate::str::to_str_internal(value, true, None); + serializer.serialize_str(value.0.as_ref()) + } +} + +/// Serialize/deserialize optional Decimals as strings. This is particularly useful when using binary encoding formats. +/// +/// ``` +/// # use serde::{Serialize, Deserialize}; +/// # use rust_decimal::Decimal; +/// # use std::str::FromStr; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct StringExample { +/// #[serde(with = "rust_decimal::serde::str_option")] +/// value: Option<Decimal>, +/// } +/// +/// let value = StringExample { value: Some(Decimal::from_str("123.400").unwrap()) }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":"123.400"}"# +/// ); +/// +/// let value = StringExample { value: None }; +/// assert_eq!( +/// &serde_json::to_string(&value).unwrap(), +/// r#"{"value":null}"# +/// ); +/// ``` +#[cfg(feature = "serde-with-str")] +pub mod str_option { + use super::*; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_option(OptionDecimalStrVisitor) + } + + pub fn serialize<S>(value: &Option<Decimal>, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + match *value { + Some(ref decimal) => { + let decimal = crate::str::to_str_internal(decimal, true, None); + serializer.serialize_some(decimal.0.as_ref()) + } + None => serializer.serialize_none(), + } + } +} + +#[cfg(not(feature = "serde-str"))] +impl<'de> serde::Deserialize<'de> for Decimal { + fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_any(DecimalVisitor) + } +} + +#[cfg(all(feature = "serde-str", not(feature = "serde-float")))] +impl<'de> serde::Deserialize<'de> for Decimal { + fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_str(DecimalVisitor) + } +} + +#[cfg(all(feature = "serde-str", feature = "serde-float"))] +impl<'de> serde::Deserialize<'de> for Decimal { + fn deserialize<D>(deserializer: D) -> Result<Decimal, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_f64(DecimalVisitor) + } +} + +// It's a shame this needs to be redefined for this feature and not able to be referenced directly +#[cfg(feature = "serde-with-arbitrary-precision")] +const DECIMAL_KEY_TOKEN: &str = "$serde_json::private::Number"; + +struct DecimalVisitor; + +impl<'de> serde::de::Visitor<'de> for DecimalVisitor { + type Value = Decimal; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a Decimal type representing a fixed-point number") + } + + fn visit_i64<E>(self, value: i64) -> Result<Decimal, E> + where + E: serde::de::Error, + { + match Decimal::from_i64(value) { + Some(s) => Ok(s), + None => Err(E::invalid_value(Unexpected::Signed(value), &self)), + } + } + + fn visit_u64<E>(self, value: u64) -> Result<Decimal, E> + where + E: serde::de::Error, + { + match Decimal::from_u64(value) { + Some(s) => Ok(s), + None => Err(E::invalid_value(Unexpected::Unsigned(value), &self)), + } + } + + fn visit_f64<E>(self, value: f64) -> Result<Decimal, E> + where + E: serde::de::Error, + { + Decimal::from_str(&value.to_string()).map_err(|_| E::invalid_value(Unexpected::Float(value), &self)) + } + + fn visit_str<E>(self, value: &str) -> Result<Decimal, E> + where + E: serde::de::Error, + { + Decimal::from_str(value) + .or_else(|_| Decimal::from_scientific(value)) + .map_err(|_| E::invalid_value(Unexpected::Str(value), &self)) + } + + #[cfg(feature = "serde-with-arbitrary-precision")] + fn visit_map<A>(self, map: A) -> Result<Decimal, A::Error> + where + A: serde::de::MapAccess<'de>, + { + let mut map = map; + let value = map.next_key::<DecimalKey>()?; + if value.is_none() { + return Err(serde::de::Error::invalid_type(Unexpected::Map, &self)); + } + let v: DecimalFromString = map.next_value()?; + Ok(v.value) + } +} + +struct OptionDecimalVisitor; + +impl<'de> serde::de::Visitor<'de> for OptionDecimalVisitor { + type Value = Option<Decimal>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a Decimal type representing a fixed-point number") + } + + fn visit_none<E>(self) -> Result<Option<Decimal>, E> + where + E: serde::de::Error, + { + Ok(None) + } + + #[cfg(all(feature = "serde-str", feature = "serde-float"))] + fn visit_some<D>(self, d: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + // We've got multiple types that we may see so we need to use any + d.deserialize_any(DecimalVisitor).map(Some) + } + + #[cfg(not(all(feature = "serde-str", feature = "serde-float")))] + fn visit_some<D>(self, d: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + <Decimal as serde::Deserialize>::deserialize(d).map(Some) + } +} + +#[cfg(feature = "serde-with-str")] +struct OptionDecimalStrVisitor; + +#[cfg(feature = "serde-with-str")] +impl<'de> serde::de::Visitor<'de> for OptionDecimalStrVisitor { + type Value = Option<Decimal>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a Decimal type representing a fixed-point number") + } + + fn visit_none<E>(self) -> Result<Option<Decimal>, E> + where + E: serde::de::Error, + { + Ok(None) + } + + fn visit_some<D>(self, d: D) -> Result<Option<Decimal>, D::Error> + where + D: serde::de::Deserializer<'de>, + { + d.deserialize_str(DecimalVisitor).map(Some) + } +} + +#[cfg(feature = "serde-with-arbitrary-precision")] +struct DecimalKey; + +#[cfg(feature = "serde-with-arbitrary-precision")] +impl<'de> serde::de::Deserialize<'de> for DecimalKey { + fn deserialize<D>(deserializer: D) -> Result<DecimalKey, D::Error> + where + D: serde::de::Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid decimal field") + } + + fn visit_str<E>(self, s: &str) -> Result<(), E> + where + E: serde::de::Error, + { + if s == DECIMAL_KEY_TOKEN { + Ok(()) + } else { + Err(serde::de::Error::custom("expected field with custom name")) + } + } + } + + deserializer.deserialize_identifier(FieldVisitor)?; + Ok(DecimalKey) + } +} + +#[cfg(feature = "serde-with-arbitrary-precision")] +pub struct DecimalFromString { + pub value: Decimal, +} + +#[cfg(feature = "serde-with-arbitrary-precision")] +impl<'de> serde::de::Deserialize<'de> for DecimalFromString { + fn deserialize<D>(deserializer: D) -> Result<DecimalFromString, D::Error> + where + D: serde::de::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = DecimalFromString; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string containing a decimal") + } + + fn visit_str<E>(self, value: &str) -> Result<DecimalFromString, E> + where + E: serde::de::Error, + { + let d = Decimal::from_str(value) + .or_else(|_| Decimal::from_scientific(value)) + .map_err(serde::de::Error::custom)?; + Ok(DecimalFromString { value: d }) + } + } + + deserializer.deserialize_str(Visitor) + } +} + +#[cfg(not(feature = "serde-float"))] +impl serde::Serialize for Decimal { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + let value = crate::str::to_str_internal(self, true, None); + serializer.serialize_str(value.0.as_ref()) + } +} + +#[cfg(all(feature = "serde-float", not(feature = "serde-arbitrary-precision")))] +impl serde::Serialize for Decimal { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + use num_traits::ToPrimitive; + serializer.serialize_f64(self.to_f64().unwrap()) + } +} + +#[cfg(all(feature = "serde-float", feature = "serde-arbitrary-precision"))] +impl serde::Serialize for Decimal { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + serde_json::Number::from_str(&self.to_string()) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) + } +} + +#[cfg(test)] +mod test { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize, Debug)] + struct Record { + amount: Decimal, + } + + #[test] + #[cfg(not(feature = "serde-str"))] + fn deserialize_valid_decimal() { + let data = [ + ("{\"amount\":\"1.234\"}", "1.234"), + ("{\"amount\":1234}", "1234"), + ("{\"amount\":1234.56}", "1234.56"), + ("{\"amount\":\"1.23456e3\"}", "1234.56"), + ]; + for &(serialized, value) in data.iter() { + let result = serde_json::from_str(serialized); + assert_eq!( + true, + result.is_ok(), + "expected successful deserialization for {}. Error: {:?}", + serialized, + result.err().unwrap() + ); + let record: Record = result.unwrap(); + assert_eq!( + value, + record.amount.to_string(), + "expected: {}, actual: {}", + value, + record.amount.to_string() + ); + } + } + + #[test] + #[cfg(feature = "serde-arbitrary-precision")] + fn deserialize_basic_decimal() { + let d: Decimal = serde_json::from_str("1.1234127836128763").unwrap(); + // Typically, this would not work without this feature enabled due to rounding + assert_eq!(d.to_string(), "1.1234127836128763"); + } + + #[test] + #[should_panic] + fn deserialize_invalid_decimal() { + let serialized = "{\"amount\":\"foo\"}"; + let _: Record = serde_json::from_str(serialized).unwrap(); + } + + #[test] + #[cfg(not(feature = "serde-float"))] + fn serialize_decimal() { + let record = Record { + amount: Decimal::new(1234, 3), + }; + let serialized = serde_json::to_string(&record).unwrap(); + assert_eq!("{\"amount\":\"1.234\"}", serialized); + } + + #[test] + #[cfg(not(feature = "serde-float"))] + fn serialize_negative_zero() { + let record = Record { amount: -Decimal::ZERO }; + let serialized = serde_json::to_string(&record).unwrap(); + assert_eq!("{\"amount\":\"-0\"}", serialized); + } + + #[test] + #[cfg(feature = "serde-float")] + fn serialize_decimal() { + let record = Record { + amount: Decimal::new(1234, 3), + }; + let serialized = serde_json::to_string(&record).unwrap(); + assert_eq!("{\"amount\":1.234}", serialized); + } + + #[test] + #[cfg(all(feature = "serde-float", feature = "serde-arbitrary-precision"))] + fn serialize_decimal_roundtrip() { + let record = Record { + // 4.81 is intentionally chosen as it is unrepresentable as a floating point number, meaning this test + // would fail if the `serde-arbitrary-precision` was not activated. + amount: Decimal::new(481, 2), + }; + let serialized = serde_json::to_string(&record).unwrap(); + assert_eq!("{\"amount\":4.81}", serialized); + let deserialized: Record = serde_json::from_str(&serialized).unwrap(); + assert_eq!(record.amount, deserialized.amount); + } + + #[test] + #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] + fn serialize_decimal_roundtrip() { + let record = Record { + amount: Decimal::new(481, 2), + }; + let serialized = serde_json::to_string(&record).unwrap(); + assert_eq!("{\"amount\":\"4.81\"}", serialized); + let deserialized: Record = serde_json::from_str(&serialized).unwrap(); + assert_eq!(record.amount, deserialized.amount); + } + + #[test] + #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] + fn bincode_serialization() { + use bincode::{deserialize, serialize}; + + let data = [ + "0", + "0.00", + "3.14159", + "-3.14159", + "1234567890123.4567890", + "-1234567890123.4567890", + "5233.9008808150288439427720175", + "-5233.9008808150288439427720175", + ]; + for &raw in data.iter() { + let value = Decimal::from_str(raw).unwrap(); + let encoded = serialize(&value).unwrap(); + let decoded: Decimal = deserialize(&encoded[..]).unwrap(); + assert_eq!(value, decoded); + assert_eq!(8usize + raw.len(), encoded.len()); + } + } + + #[test] + #[cfg(all(feature = "serde-str", feature = "serde-float"))] + fn bincode_serialization() { + use bincode::{deserialize, serialize}; + + let data = [ + ("0", "0"), + ("0.00", "0.00"), + ("3.14159", "3.14159"), + ("-3.14159", "-3.14159"), + ("1234567890123.4567890", "1234567890123.4568"), + ("-1234567890123.4567890", "-1234567890123.4568"), + ]; + for &(value, expected) in data.iter() { + let value = Decimal::from_str(value).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + let encoded = serialize(&value).unwrap(); + let decoded: Decimal = deserialize(&encoded[..]).unwrap(); + assert_eq!(expected, decoded); + assert_eq!(8usize, encoded.len()); + } + } + + #[test] + #[cfg(all(feature = "serde-str", not(feature = "serde-float")))] + fn bincode_nested_serialization() { + // Issue #361 + #[derive(Deserialize, Serialize, Debug)] + pub struct Foo { + value: Decimal, + } + + let s = Foo { + value: Decimal::new(-1, 3).round_dp(0), + }; + let ser = bincode::serialize(&s).unwrap(); + let des: Foo = bincode::deserialize(&ser).unwrap(); + assert_eq!(des.value, s.value); + } + + #[test] + #[cfg(feature = "serde-with-arbitrary-precision")] + fn with_arbitrary_precision() { + #[derive(Serialize, Deserialize)] + pub struct ArbitraryExample { + #[serde(with = "crate::serde::arbitrary_precision")] + value: Decimal, + } + + let value = ArbitraryExample { + value: Decimal::from_str("123.400").unwrap(), + }; + assert_eq!(&serde_json::to_string(&value).unwrap(), r#"{"value":123.400}"#); + } + + #[test] + #[cfg(feature = "serde-with-arbitrary-precision")] + fn with_arbitrary_precision_from_string() { + #[derive(Serialize, Deserialize)] + pub struct ArbitraryExample { + #[serde(with = "crate::serde::arbitrary_precision")] + value: Decimal, + } + + let value: ArbitraryExample = serde_json::from_str(r#"{"value":"1.1234127836128763"}"#).unwrap(); + assert_eq!(value.value.to_string(), "1.1234127836128763"); + } + + #[test] + #[cfg(feature = "serde-with-float")] + fn with_float() { + #[derive(Serialize, Deserialize)] + pub struct FloatExample { + #[serde(with = "crate::serde::float")] + value: Decimal, + } + + let value = FloatExample { + value: Decimal::from_str("123.400").unwrap(), + }; + assert_eq!(&serde_json::to_string(&value).unwrap(), r#"{"value":123.4}"#); + } + + #[test] + #[cfg(feature = "serde-with-str")] + fn with_str() { + #[derive(Serialize, Deserialize)] + pub struct StringExample { + #[serde(with = "crate::serde::str")] + value: Decimal, + } + + let value = StringExample { + value: Decimal::from_str("123.400").unwrap(), + }; + assert_eq!(&serde_json::to_string(&value).unwrap(), r#"{"value":"123.400"}"#); + } + + #[test] + #[cfg(feature = "serde-with-str")] + fn with_str_bincode() { + use bincode::{deserialize, serialize}; + + #[derive(Serialize, Deserialize)] + struct BincodeExample { + #[serde(with = "crate::serde::str")] + value: Decimal, + } + + let data = [ + ("0", "0"), + ("0.00", "0.00"), + ("1.234", "1.234"), + ("3.14159", "3.14159"), + ("-3.14159", "-3.14159"), + ("1234567890123.4567890", "1234567890123.4567890"), + ("-1234567890123.4567890", "-1234567890123.4567890"), + ]; + for &(value, expected) in data.iter() { + let value = Decimal::from_str(value).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + let input = BincodeExample { value }; + + let encoded = serialize(&input).unwrap(); + let decoded: BincodeExample = deserialize(&encoded[..]).unwrap(); + assert_eq!(expected, decoded.value); + } + } + + #[test] + #[cfg(feature = "serde-with-str")] + fn with_str_bincode_optional() { + use bincode::{deserialize, serialize}; + + #[derive(Serialize, Deserialize)] + struct BincodeExample { + #[serde(with = "crate::serde::str_option")] + value: Option<Decimal>, + } + + // Some(value) + let value = Some(Decimal::new(1234, 3)); + let input = BincodeExample { value }; + let encoded = serialize(&input).unwrap(); + let decoded: BincodeExample = deserialize(&encoded[..]).unwrap(); + assert_eq!(value, decoded.value, "Some(value)"); + + // None + let input = BincodeExample { value: None }; + let encoded = serialize(&input).unwrap(); + let decoded: BincodeExample = deserialize(&encoded[..]).unwrap(); + assert_eq!(None, decoded.value, "None"); + } + + #[test] + #[cfg(feature = "serde-with-str")] + fn with_str_optional() { + #[derive(Serialize, Deserialize)] + pub struct StringExample { + #[serde(with = "crate::serde::str_option")] + value: Option<Decimal>, + } + + let original = StringExample { + value: Some(Decimal::from_str("123.400").unwrap()), + }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":"123.400"}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":"123.400"}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_some()); + assert_eq!(deserialized.value.unwrap().unpack(), original.value.unwrap().unpack()); + + // Null tests + let original = StringExample { value: None }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":null}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":null}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_none()); + } + + #[test] + #[cfg(feature = "serde-with-float")] + fn with_float_optional() { + #[derive(Serialize, Deserialize)] + pub struct StringExample { + #[serde(with = "crate::serde::float_option")] + value: Option<Decimal>, + } + + let original = StringExample { + value: Some(Decimal::from_str("123.400").unwrap()), + }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":123.4}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":123.4}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_some()); // Scale is different! + + // Null tests + let original = StringExample { value: None }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":null}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":null}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_none()); + } + + #[test] + #[cfg(feature = "serde-with-arbitrary-precision")] + fn with_arbitrary_precision_optional() { + #[derive(Serialize, Deserialize)] + pub struct StringExample { + #[serde(with = "crate::serde::arbitrary_precision_option")] + value: Option<Decimal>, + } + + let original = StringExample { + value: Some(Decimal::from_str("123.400").unwrap()), + }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":123.400}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":123.400}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_some()); + assert_eq!(deserialized.value.unwrap().unpack(), original.value.unwrap().unpack()); + + // Null tests + let original = StringExample { value: None }; + assert_eq!(&serde_json::to_string(&original).unwrap(), r#"{"value":null}"#); + let deserialized: StringExample = serde_json::from_str(r#"{"value":null}"#).unwrap(); + assert_eq!(deserialized.value, original.value); + assert!(deserialized.value.is_none()); + } +} diff --git a/third_party/rust/rust_decimal/src/str.rs b/third_party/rust/rust_decimal/src/str.rs new file mode 100644 index 0000000000..f3b89d31e0 --- /dev/null +++ b/third_party/rust/rust_decimal/src/str.rs @@ -0,0 +1,993 @@ +use crate::{ + constants::{BYTES_TO_OVERFLOW_U64, MAX_PRECISION, MAX_STR_BUFFER_SIZE, OVERFLOW_U96, WILL_OVERFLOW_U64}, + error::{tail_error, Error}, + ops::array::{add_by_internal_flattened, add_one_internal, div_by_u32, is_all_zero, mul_by_u32}, + Decimal, +}; + +use arrayvec::{ArrayString, ArrayVec}; + +use alloc::{string::String, vec::Vec}; +use core::fmt; + +// impl that doesn't allocate for serialization purposes. +pub(crate) fn to_str_internal( + value: &Decimal, + append_sign: bool, + precision: Option<usize>, +) -> (ArrayString<MAX_STR_BUFFER_SIZE>, Option<usize>) { + // Get the scale - where we need to put the decimal point + let scale = value.scale() as usize; + + // Convert to a string and manipulate that (neg at front, inject decimal) + let mut chars = ArrayVec::<_, MAX_STR_BUFFER_SIZE>::new(); + let mut working = value.mantissa_array3(); + while !is_all_zero(&working) { + let remainder = div_by_u32(&mut working, 10u32); + chars.push(char::from(b'0' + remainder as u8)); + } + while scale > chars.len() { + chars.push('0'); + } + + let (prec, additional) = match precision { + Some(prec) => { + let max: usize = MAX_PRECISION.into(); + if prec > max { + (max, Some(prec - max)) + } else { + (prec, None) + } + } + None => (scale, None), + }; + + let len = chars.len(); + let whole_len = len - scale; + let mut rep = ArrayString::new(); + // Append the negative sign if necessary while also keeping track of the length of an "empty" string representation + let empty_len = if append_sign && value.is_sign_negative() { + rep.push('-'); + 1 + } else { + 0 + }; + for i in 0..whole_len + prec { + if i == len - scale { + if i == 0 { + rep.push('0'); + } + rep.push('.'); + } + + if i >= len { + rep.push('0'); + } else { + let c = chars[len - i - 1]; + rep.push(c); + } + } + + // corner case for when we truncated everything in a low fractional + if rep.len() == empty_len { + rep.push('0'); + } + + (rep, additional) +} + +pub(crate) fn fmt_scientific_notation( + value: &Decimal, + exponent_symbol: &str, + f: &mut fmt::Formatter<'_>, +) -> fmt::Result { + #[cfg(not(feature = "std"))] + use alloc::string::ToString; + + // Get the scale - this is the e value. With multiples of 10 this may get bigger. + let mut exponent = -(value.scale() as isize); + + // Convert the integral to a string + let mut chars = Vec::new(); + let mut working = value.mantissa_array3(); + while !is_all_zero(&working) { + let remainder = div_by_u32(&mut working, 10u32); + chars.push(char::from(b'0' + remainder as u8)); + } + + // First of all, apply scientific notation rules. That is: + // 1. If non-zero digit comes first, move decimal point left so that e is a positive integer + // 2. If decimal point comes first, move decimal point right until after the first non-zero digit + // Since decimal notation naturally lends itself this way, we just need to inject the decimal + // point in the right place and adjust the exponent accordingly. + + let len = chars.len(); + let mut rep; + // We either are operating with a precision specified, or on defaults. Defaults will perform "smart" + // reduction of precision. + if let Some(precision) = f.precision() { + if len > 1 { + // If we're zero precision AND it's trailing zeros then strip them + if precision == 0 && chars.iter().take(len - 1).all(|c| *c == '0') { + rep = chars.iter().skip(len - 1).collect::<String>(); + } else { + // We may still be zero precision, however we aren't trailing zeros + if precision > 0 { + chars.insert(len - 1, '.'); + } + rep = chars + .iter() + .rev() + // Add on extra zeros according to the precision. At least one, since we added a decimal place. + .chain(core::iter::repeat(&'0')) + .take(if precision == 0 { 1 } else { 2 + precision }) + .collect::<String>(); + } + exponent += (len - 1) as isize; + } else if precision > 0 { + // We have precision that we want to add + chars.push('.'); + rep = chars + .iter() + .chain(core::iter::repeat(&'0')) + .take(2 + precision) + .collect::<String>(); + } else { + rep = chars.iter().collect::<String>(); + } + } else if len > 1 { + // If the number is just trailing zeros then we treat it like 0 precision + if chars.iter().take(len - 1).all(|c| *c == '0') { + rep = chars.iter().skip(len - 1).collect::<String>(); + } else { + // Otherwise, we need to insert a decimal place and make it a scientific number + chars.insert(len - 1, '.'); + rep = chars.iter().rev().collect::<String>(); + } + exponent += (len - 1) as isize; + } else { + rep = chars.iter().collect::<String>(); + } + + rep.push_str(exponent_symbol); + rep.push_str(&exponent.to_string()); + f.pad_integral(value.is_sign_positive(), "", &rep) +} + +// dedicated implementation for the most common case. +#[inline] +pub(crate) fn parse_str_radix_10(str: &str) -> Result<Decimal, Error> { + let bytes = str.as_bytes(); + if bytes.len() < BYTES_TO_OVERFLOW_U64 { + parse_str_radix_10_dispatch::<false, true>(bytes) + } else { + parse_str_radix_10_dispatch::<true, true>(bytes) + } +} + +#[inline] +pub(crate) fn parse_str_radix_10_exact(str: &str) -> Result<Decimal, Error> { + let bytes = str.as_bytes(); + if bytes.len() < BYTES_TO_OVERFLOW_U64 { + parse_str_radix_10_dispatch::<false, false>(bytes) + } else { + parse_str_radix_10_dispatch::<true, false>(bytes) + } +} + +#[inline] +fn parse_str_radix_10_dispatch<const BIG: bool, const ROUND: bool>(bytes: &[u8]) -> Result<Decimal, Error> { + match bytes { + [b, rest @ ..] => byte_dispatch_u64::<false, false, false, BIG, true, ROUND>(rest, 0, 0, *b), + [] => tail_error("Invalid decimal: empty"), + } +} + +#[inline] +fn overflow_64(val: u64) -> bool { + val >= WILL_OVERFLOW_U64 +} + +#[inline] +pub fn overflow_128(val: u128) -> bool { + val >= OVERFLOW_U96 +} + +/// Dispatch the next byte: +/// +/// * POINT - a decimal point has been seen +/// * NEG - we've encountered a `-` and the number is negative +/// * HAS - a digit has been encountered (when HAS is false it's invalid) +/// * BIG - a number that uses 96 bits instead of only 64 bits +/// * FIRST - true if it is the first byte in the string +#[inline] +fn dispatch_next<const POINT: bool, const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>( + bytes: &[u8], + data64: u64, + scale: u8, +) -> Result<Decimal, Error> { + if let Some((next, bytes)) = bytes.split_first() { + byte_dispatch_u64::<POINT, NEG, HAS, BIG, false, ROUND>(bytes, data64, scale, *next) + } else { + handle_data::<NEG, HAS>(data64 as u128, scale) + } +} + +#[inline(never)] +fn non_digit_dispatch_u64< + const POINT: bool, + const NEG: bool, + const HAS: bool, + const BIG: bool, + const FIRST: bool, + const ROUND: bool, +>( + bytes: &[u8], + data64: u64, + scale: u8, + b: u8, +) -> Result<Decimal, Error> { + match b { + b'-' if FIRST && !HAS => dispatch_next::<false, true, false, BIG, ROUND>(bytes, data64, scale), + b'+' if FIRST && !HAS => dispatch_next::<false, false, false, BIG, ROUND>(bytes, data64, scale), + b'_' if HAS => handle_separator::<POINT, NEG, BIG, ROUND>(bytes, data64, scale), + b => tail_invalid_digit(b), + } +} + +#[inline] +fn byte_dispatch_u64< + const POINT: bool, + const NEG: bool, + const HAS: bool, + const BIG: bool, + const FIRST: bool, + const ROUND: bool, +>( + bytes: &[u8], + data64: u64, + scale: u8, + b: u8, +) -> Result<Decimal, Error> { + match b { + b'0'..=b'9' => handle_digit_64::<POINT, NEG, BIG, ROUND>(bytes, data64, scale, b - b'0'), + b'.' if !POINT => handle_point::<NEG, HAS, BIG, ROUND>(bytes, data64, scale), + b => non_digit_dispatch_u64::<POINT, NEG, HAS, BIG, FIRST, ROUND>(bytes, data64, scale, b), + } +} + +#[inline(never)] +fn handle_digit_64<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>( + bytes: &[u8], + data64: u64, + scale: u8, + digit: u8, +) -> Result<Decimal, Error> { + // we have already validated that we cannot overflow + let data64 = data64 * 10 + digit as u64; + let scale = if POINT { scale + 1 } else { 0 }; + + if let Some((next, bytes)) = bytes.split_first() { + let next = *next; + if POINT && BIG && scale >= 28 { + if ROUND { + maybe_round(data64 as u128, next, scale, POINT, NEG) + } else { + Err(Error::Underflow) + } + } else if BIG && overflow_64(data64) { + handle_full_128::<POINT, NEG, ROUND>(data64 as u128, bytes, scale, next) + } else { + byte_dispatch_u64::<POINT, NEG, true, BIG, false, ROUND>(bytes, data64, scale, next) + } + } else { + let data: u128 = data64 as u128; + + handle_data::<NEG, true>(data, scale) + } +} + +#[inline(never)] +fn handle_point<const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>( + bytes: &[u8], + data64: u64, + scale: u8, +) -> Result<Decimal, Error> { + dispatch_next::<true, NEG, HAS, BIG, ROUND>(bytes, data64, scale) +} + +#[inline(never)] +fn handle_separator<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>( + bytes: &[u8], + data64: u64, + scale: u8, +) -> Result<Decimal, Error> { + dispatch_next::<POINT, NEG, true, BIG, ROUND>(bytes, data64, scale) +} + +#[inline(never)] +#[cold] +fn tail_invalid_digit(digit: u8) -> Result<Decimal, Error> { + match digit { + b'.' => tail_error("Invalid decimal: two decimal points"), + b'_' => tail_error("Invalid decimal: must start lead with a number"), + _ => tail_error("Invalid decimal: unknown character"), + } +} + +#[inline(never)] +#[cold] +fn handle_full_128<const POINT: bool, const NEG: bool, const ROUND: bool>( + mut data: u128, + bytes: &[u8], + scale: u8, + next_byte: u8, +) -> Result<Decimal, Error> { + let b = next_byte; + match b { + b'0'..=b'9' => { + let digit = u32::from(b - b'0'); + + // If the data is going to overflow then we should go into recovery mode + let next = (data * 10) + digit as u128; + if overflow_128(next) { + if !POINT { + return tail_error("Invalid decimal: overflow from too many digits"); + } + + if ROUND { + maybe_round(data, next_byte, scale, POINT, NEG) + } else { + Err(Error::Underflow) + } + } else { + data = next; + let scale = scale + POINT as u8; + if let Some((next, bytes)) = bytes.split_first() { + let next = *next; + if POINT && scale >= 28 { + if ROUND { + maybe_round(data, next, scale, POINT, NEG) + } else { + Err(Error::Underflow) + } + } else { + handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, next) + } + } else { + handle_data::<NEG, true>(data, scale) + } + } + } + b'.' if !POINT => { + // This call won't tail? + if let Some((next, bytes)) = bytes.split_first() { + handle_full_128::<true, NEG, ROUND>(data, bytes, scale, *next) + } else { + handle_data::<NEG, true>(data, scale) + } + } + b'_' => { + if let Some((next, bytes)) = bytes.split_first() { + handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, *next) + } else { + handle_data::<NEG, true>(data, scale) + } + } + b => tail_invalid_digit(b), + } +} + +#[inline(never)] +#[cold] +fn maybe_round( + mut data: u128, + next_byte: u8, + mut scale: u8, + point: bool, + negative: bool, +) -> Result<Decimal, crate::Error> { + let digit = match next_byte { + b'0'..=b'9' => u32::from(next_byte - b'0'), + b'_' => 0, // this should be an invalid string? + b'.' if point => 0, + b => return tail_invalid_digit(b), + }; + + // Round at midpoint + if digit >= 5 { + data += 1; + + // If the mantissa is now overflowing, round to the next + // next least significant digit and discard precision + if overflow_128(data) { + if scale == 0 { + return tail_error("Invalid decimal: overflow from mantissa after rounding"); + } + data += 4; + data /= 10; + scale -= 1; + } + } + + if negative { + handle_data::<true, true>(data, scale) + } else { + handle_data::<false, true>(data, scale) + } +} + +#[inline(never)] +fn tail_no_has() -> Result<Decimal, Error> { + tail_error("Invalid decimal: no digits found") +} + +#[inline] +fn handle_data<const NEG: bool, const HAS: bool>(data: u128, scale: u8) -> Result<Decimal, Error> { + debug_assert_eq!(data >> 96, 0); + if !HAS { + tail_no_has() + } else { + Ok(Decimal::from_parts( + data as u32, + (data >> 32) as u32, + (data >> 64) as u32, + NEG, + scale as u32, + )) + } +} + +pub(crate) fn parse_str_radix_n(str: &str, radix: u32) -> Result<Decimal, Error> { + if str.is_empty() { + return Err(Error::from("Invalid decimal: empty")); + } + if radix < 2 { + return Err(Error::from("Unsupported radix < 2")); + } + if radix > 36 { + // As per trait documentation + return Err(Error::from("Unsupported radix > 36")); + } + + let mut offset = 0; + let mut len = str.len(); + let bytes = str.as_bytes(); + let mut negative = false; // assume positive + + // handle the sign + if bytes[offset] == b'-' { + negative = true; // leading minus means negative + offset += 1; + len -= 1; + } else if bytes[offset] == b'+' { + // leading + allowed + offset += 1; + len -= 1; + } + + // should now be at numeric part of the significand + let mut digits_before_dot: i32 = -1; // digits before '.', -1 if no '.' + let mut coeff = ArrayVec::<_, 96>::new(); // integer significand array + + // Supporting different radix + let (max_n, max_alpha_lower, max_alpha_upper) = if radix <= 10 { + (b'0' + (radix - 1) as u8, 0, 0) + } else { + let adj = (radix - 11) as u8; + (b'9', adj + b'a', adj + b'A') + }; + + // Estimate the max precision. All in all, it needs to fit into 96 bits. + // Rather than try to estimate, I've included the constants directly in here. We could, + // perhaps, replace this with a formula if it's faster - though it does appear to be log2. + let estimated_max_precision = match radix { + 2 => 96, + 3 => 61, + 4 => 48, + 5 => 42, + 6 => 38, + 7 => 35, + 8 => 32, + 9 => 31, + 10 => 28, + 11 => 28, + 12 => 27, + 13 => 26, + 14 => 26, + 15 => 25, + 16 => 24, + 17 => 24, + 18 => 24, + 19 => 23, + 20 => 23, + 21 => 22, + 22 => 22, + 23 => 22, + 24 => 21, + 25 => 21, + 26 => 21, + 27 => 21, + 28 => 20, + 29 => 20, + 30 => 20, + 31 => 20, + 32 => 20, + 33 => 20, + 34 => 19, + 35 => 19, + 36 => 19, + _ => return Err(Error::from("Unsupported radix")), + }; + + let mut maybe_round = false; + while len > 0 { + let b = bytes[offset]; + match b { + b'0'..=b'9' => { + if b > max_n { + return Err(Error::from("Invalid decimal: invalid character")); + } + coeff.push(u32::from(b - b'0')); + offset += 1; + len -= 1; + + // If the coefficient is longer than the max, exit early + if coeff.len() as u32 > estimated_max_precision { + maybe_round = true; + break; + } + } + b'a'..=b'z' => { + if b > max_alpha_lower { + return Err(Error::from("Invalid decimal: invalid character")); + } + coeff.push(u32::from(b - b'a') + 10); + offset += 1; + len -= 1; + + if coeff.len() as u32 > estimated_max_precision { + maybe_round = true; + break; + } + } + b'A'..=b'Z' => { + if b > max_alpha_upper { + return Err(Error::from("Invalid decimal: invalid character")); + } + coeff.push(u32::from(b - b'A') + 10); + offset += 1; + len -= 1; + + if coeff.len() as u32 > estimated_max_precision { + maybe_round = true; + break; + } + } + b'.' => { + if digits_before_dot >= 0 { + return Err(Error::from("Invalid decimal: two decimal points")); + } + digits_before_dot = coeff.len() as i32; + offset += 1; + len -= 1; + } + b'_' => { + // Must start with a number... + if coeff.is_empty() { + return Err(Error::from("Invalid decimal: must start lead with a number")); + } + offset += 1; + len -= 1; + } + _ => return Err(Error::from("Invalid decimal: unknown character")), + } + } + + // If we exited before the end of the string then do some rounding if necessary + if maybe_round && offset < bytes.len() { + let next_byte = bytes[offset]; + let digit = match next_byte { + b'0'..=b'9' => { + if next_byte > max_n { + return Err(Error::from("Invalid decimal: invalid character")); + } + u32::from(next_byte - b'0') + } + b'a'..=b'z' => { + if next_byte > max_alpha_lower { + return Err(Error::from("Invalid decimal: invalid character")); + } + u32::from(next_byte - b'a') + 10 + } + b'A'..=b'Z' => { + if next_byte > max_alpha_upper { + return Err(Error::from("Invalid decimal: invalid character")); + } + u32::from(next_byte - b'A') + 10 + } + b'_' => 0, + b'.' => { + // Still an error if we have a second dp + if digits_before_dot >= 0 { + return Err(Error::from("Invalid decimal: two decimal points")); + } + 0 + } + _ => return Err(Error::from("Invalid decimal: unknown character")), + }; + + // Round at midpoint + let midpoint = if radix & 0x1 == 1 { radix / 2 } else { (radix + 1) / 2 }; + if digit >= midpoint { + let mut index = coeff.len() - 1; + loop { + let new_digit = coeff[index] + 1; + if new_digit <= 9 { + coeff[index] = new_digit; + break; + } else { + coeff[index] = 0; + if index == 0 { + coeff.insert(0, 1u32); + digits_before_dot += 1; + coeff.pop(); + break; + } + } + index -= 1; + } + } + } + + // here when no characters left + if coeff.is_empty() { + return Err(Error::from("Invalid decimal: no digits found")); + } + + let mut scale = if digits_before_dot >= 0 { + // we had a decimal place so set the scale + (coeff.len() as u32) - (digits_before_dot as u32) + } else { + 0 + }; + + // Parse this using specified radix + let mut data = [0u32, 0u32, 0u32]; + let mut tmp = [0u32, 0u32, 0u32]; + let len = coeff.len(); + for (i, digit) in coeff.iter().enumerate() { + // If the data is going to overflow then we should go into recovery mode + tmp[0] = data[0]; + tmp[1] = data[1]; + tmp[2] = data[2]; + let overflow = mul_by_u32(&mut tmp, radix); + if overflow > 0 { + // This means that we have more data to process, that we're not sure what to do with. + // This may or may not be an issue - depending on whether we're past a decimal point + // or not. + if (i as i32) < digits_before_dot && i + 1 < len { + return Err(Error::from("Invalid decimal: overflow from too many digits")); + } + + if *digit >= 5 { + let carry = add_one_internal(&mut data); + if carry > 0 { + // Highly unlikely scenario which is more indicative of a bug + return Err(Error::from("Invalid decimal: overflow when rounding")); + } + } + // We're also one less digit so reduce the scale + let diff = (len - i) as u32; + if diff > scale { + return Err(Error::from("Invalid decimal: overflow from scale mismatch")); + } + scale -= diff; + break; + } else { + data[0] = tmp[0]; + data[1] = tmp[1]; + data[2] = tmp[2]; + let carry = add_by_internal_flattened(&mut data, *digit); + if carry > 0 { + // Highly unlikely scenario which is more indicative of a bug + return Err(Error::from("Invalid decimal: overflow from carry")); + } + } + } + + Ok(Decimal::from_parts(data[0], data[1], data[2], negative, scale)) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Decimal; + use arrayvec::ArrayString; + use core::{fmt::Write, str::FromStr}; + + #[test] + fn display_does_not_overflow_max_capacity() { + let num = Decimal::from_str("1.2").unwrap(); + let mut buffer = ArrayString::<64>::new(); + let _ = buffer.write_fmt(format_args!("{:.31}", num)).unwrap(); + assert_eq!("1.2000000000000000000000000000000", buffer.as_str()); + } + + #[test] + fn from_str_rounding_0() { + assert_eq!( + parse_str_radix_10("1.234").unwrap().unpack(), + Decimal::new(1234, 3).unpack() + ); + } + + #[test] + fn from_str_rounding_1() { + assert_eq!( + parse_str_radix_10("11111_11111_11111.11111_11111_11111") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_111, 14).unpack() + ); + } + + #[test] + fn from_str_rounding_2() { + assert_eq!( + parse_str_radix_10("11111_11111_11111.11111_11111_11115") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_112, 14).unpack() + ); + } + + #[test] + fn from_str_rounding_3() { + assert_eq!( + parse_str_radix_10("11111_11111_11111.11111_11111_11195") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(1_111_111_111_111_111_111_111_111_1120, 14).unpack() // was Decimal::from_i128_with_scale(1_111_111_111_111_111_111_111_111_112, 13) + ); + } + + #[test] + fn from_str_rounding_4() { + assert_eq!( + parse_str_radix_10("99999_99999_99999.99999_99999_99995") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 13).unpack() // was Decimal::from_i128_with_scale(1_000_000_000_000_000_000_000_000_000, 12) + ); + } + + #[test] + fn from_str_no_rounding_0() { + assert_eq!( + parse_str_radix_10_exact("1.234").unwrap().unpack(), + Decimal::new(1234, 3).unpack() + ); + } + + #[test] + fn from_str_no_rounding_1() { + assert_eq!( + parse_str_radix_10_exact("11111_11111_11111.11111_11111_11111"), + Err(Error::Underflow) + ); + } + + #[test] + fn from_str_no_rounding_2() { + assert_eq!( + parse_str_radix_10_exact("11111_11111_11111.11111_11111_11115"), + Err(Error::Underflow) + ); + } + + #[test] + fn from_str_no_rounding_3() { + assert_eq!( + parse_str_radix_10_exact("11111_11111_11111.11111_11111_11195"), + Err(Error::Underflow) + ); + } + + #[test] + fn from_str_no_rounding_4() { + assert_eq!( + parse_str_radix_10_exact("99999_99999_99999.99999_99999_99995"), + Err(Error::Underflow) + ); + } + + #[test] + fn from_str_many_pointless_chars() { + assert_eq!( + parse_str_radix_10("00________________________________________________________________001.1") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(11, 1).unpack() + ); + } + + #[test] + fn from_str_leading_0s_1() { + assert_eq!( + parse_str_radix_10("00001.1").unwrap().unpack(), + Decimal::from_i128_with_scale(11, 1).unpack() + ); + } + + #[test] + fn from_str_leading_0s_2() { + assert_eq!( + parse_str_radix_10("00000_00000_00000_00000_00001.00001") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(100001, 5).unpack() + ); + } + + #[test] + fn from_str_leading_0s_3() { + assert_eq!( + parse_str_radix_10("0.00000_00000_00000_00000_00000_00100") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(1, 28).unpack() + ); + } + + #[test] + fn from_str_trailing_0s_1() { + assert_eq!( + parse_str_radix_10("0.00001_00000_00000").unwrap().unpack(), + Decimal::from_i128_with_scale(10_000_000_000, 15).unpack() + ); + } + + #[test] + fn from_str_trailing_0s_2() { + assert_eq!( + parse_str_radix_10("0.00001_00000_00000_00000_00000_00000") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(100_000_000_000_000_000_000_000, 28).unpack() + ); + } + + #[test] + fn from_str_overflow_1() { + assert_eq!( + parse_str_radix_10("99999_99999_99999_99999_99999_99999.99999"), + // The original implementation returned + // Ok(10000_00000_00000_00000_00000_0000) + // Which is a bug! + Err(Error::from("Invalid decimal: overflow from too many digits")) + ); + } + + #[test] + fn from_str_overflow_2() { + assert!( + parse_str_radix_10("99999_99999_99999_99999_99999_11111.11111").is_err(), + // The original implementation is 'overflow from scale mismatch' + // but we got rid of that now + ); + } + + #[test] + fn from_str_overflow_3() { + assert!( + parse_str_radix_10("99999_99999_99999_99999_99999_99994").is_err() // We could not get into 'overflow when rounding' or 'overflow from carry' + // in the original implementation because the rounding logic before prevented it + ); + } + + #[test] + fn from_str_overflow_4() { + assert_eq!( + // This does not overflow, moving the decimal point 1 more step would result in + // 'overflow from too many digits' + parse_str_radix_10("99999_99999_99999_99999_99999_999.99") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 0).unpack() + ); + } + + #[test] + fn from_str_mantissa_overflow_1() { + // reminder: + assert_eq!(OVERFLOW_U96, 79_228_162_514_264_337_593_543_950_336); + assert_eq!( + parse_str_radix_10("79_228_162_514_264_337_593_543_950_33.56") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 0).unpack() + ); + // This is a mantissa of OVERFLOW_U96 - 1 just before reaching the last digit. + // Previously, this would return Err("overflow from mantissa after rounding") + // instead of successfully rounding. + } + + #[test] + fn from_str_mantissa_overflow_2() { + assert_eq!( + parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.6"), + Err(Error::from("Invalid decimal: overflow from mantissa after rounding")) + ); + // this case wants to round to 79_228_162_514_264_337_593_543_950_340. + // (79_228_162_514_264_337_593_543_950_336 is OVERFLOW_U96 and too large + // to fit in 96 bits) which is also too large for the mantissa so fails. + } + + #[test] + fn from_str_mantissa_overflow_3() { + // this hits the other avoidable overflow case in maybe_round + assert_eq!( + parse_str_radix_10("7.92281625142643375935439503356").unwrap().unpack(), + Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack() + ); + } + + #[ignore] + #[test] + fn from_str_mantissa_overflow_4() { + // Same test as above, however with underscores. This causes issues. + assert_eq!( + parse_str_radix_10("7.9_228_162_514_264_337_593_543_950_335_6") + .unwrap() + .unpack(), + Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack() + ); + } + + #[test] + fn from_str_edge_cases_1() { + assert_eq!(parse_str_radix_10(""), Err(Error::from("Invalid decimal: empty"))); + } + + #[test] + fn from_str_edge_cases_2() { + assert_eq!( + parse_str_radix_10("0.1."), + Err(Error::from("Invalid decimal: two decimal points")) + ); + } + + #[test] + fn from_str_edge_cases_3() { + assert_eq!( + parse_str_radix_10("_"), + Err(Error::from("Invalid decimal: must start lead with a number")) + ); + } + + #[test] + fn from_str_edge_cases_4() { + assert_eq!( + parse_str_radix_10("1?2"), + Err(Error::from("Invalid decimal: unknown character")) + ); + } + + #[test] + fn from_str_edge_cases_5() { + assert_eq!( + parse_str_radix_10("."), + Err(Error::from("Invalid decimal: no digits found")) + ); + } + + #[test] + fn from_str_edge_cases_6() { + // Decimal::MAX + 0.99999 + assert_eq!( + parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.99999"), + Err(Error::from("Invalid decimal: overflow from mantissa after rounding")) + ); + } +} diff --git a/third_party/rust/rust_decimal/tests/decimal_tests.rs b/third_party/rust/rust_decimal/tests/decimal_tests.rs new file mode 100644 index 0000000000..71dcf00856 --- /dev/null +++ b/third_party/rust/rust_decimal/tests/decimal_tests.rs @@ -0,0 +1,4595 @@ +mod macros; + +use core::{cmp::Ordering::*, str::FromStr}; +use num_traits::{Inv, Signed, ToPrimitive}; +use rust_decimal::{Decimal, Error, RoundingStrategy}; + +#[test] +#[cfg(feature = "c-repr")] +fn layout_is_correct() { + assert_eq!(std::mem::size_of::<Decimal>(), std::mem::size_of::<u128>()); +} + +#[test] +fn it_can_extract_the_mantissa() { + let tests = [ + ("1", 1i128, 0), + ("1.123456", 1123456i128, 6), + ("-0.123456", -123456i128, 6), + ]; + for &(input, mantissa, scale) in &tests { + let num = Decimal::from_str(input).unwrap(); + assert_eq!(num.mantissa(), mantissa, "Mantissa for {}", input); + assert_eq!(num.scale(), scale, "Scale for {}", input); + } +} + +// Parsing + +#[test] +fn it_creates_a_new_negative_decimal() { + let a = Decimal::new(-100, 2); + assert_eq!(a.is_sign_negative(), true); + assert_eq!(a.scale(), 2); + assert_eq!("-1.00", a.to_string()); +} + +#[test] +fn it_creates_a_new_decimal_using_numeric_boundaries() { + let a = Decimal::new(i64::MAX, 2); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 2); + assert_eq!("92233720368547758.07", a.to_string()); + + let b = Decimal::new(i64::MIN, 2); + assert_eq!(b.is_sign_negative(), true); + assert_eq!(b.scale(), 2); + assert_eq!("-92233720368547758.08", b.to_string()); +} + +#[test] +fn it_parses_empty_string() { + assert!(Decimal::from_str("").is_err()); + assert!(Decimal::from_str(" ").is_err()); +} + +#[test] +fn it_parses_positive_int_string() { + let a = Decimal::from_str("233").unwrap(); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 0); + assert_eq!("233", a.to_string()); +} + +#[test] +fn it_parses_negative_int_string() { + let a = Decimal::from_str("-233").unwrap(); + assert_eq!(a.is_sign_negative(), true); + assert_eq!(a.scale(), 0); + assert_eq!("-233", a.to_string()); +} + +#[test] +fn it_parses_positive_float_string() { + let a = Decimal::from_str("233.323223").unwrap(); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 6); + assert_eq!("233.323223", a.to_string()); +} + +#[test] +fn it_parses_negative_float_string() { + let a = Decimal::from_str("-233.43343").unwrap(); + assert_eq!(a.is_sign_negative(), true); + assert_eq!(a.scale(), 5); + assert_eq!("-233.43343", a.to_string()); +} + +#[test] +fn it_parses_positive_tiny_float_string() { + let a = Decimal::from_str(".000001").unwrap(); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 6); + assert_eq!("0.000001", a.to_string()); +} + +#[test] +fn it_parses_negative_tiny_float_string() { + let a = Decimal::from_str("-0.000001").unwrap(); + assert_eq!(a.is_sign_negative(), true); + assert_eq!(a.scale(), 6); + assert_eq!("-0.000001", a.to_string()); +} + +#[test] +fn it_parses_big_integer_string() { + let a = Decimal::from_str("79228162514264337593543950330").unwrap(); + assert_eq!("79228162514264337593543950330", a.to_string()); +} + +#[test] +fn it_parses_big_float_string() { + let a = Decimal::from_str("79.228162514264337593543950330").unwrap(); + assert_eq!("79.228162514264337593543950330", a.to_string()); +} + +#[test] +fn it_can_serialize_deserialize() { + let tests = [ + "12.3456789", + "5233.9008808150288439427720175", + "-5233.9008808150288439427720175", + ]; + for test in &tests { + let a = Decimal::from_str(test).unwrap(); + let bytes = a.serialize(); + let b = Decimal::deserialize(bytes); + assert_eq!(test.to_string(), b.to_string()); + } +} + +#[test] +#[cfg(feature = "borsh")] +fn it_can_serialize_deserialize_borsh() { + let tests = [ + "12.3456789", + "5233.9008808150288439427720175", + "-5233.9008808150288439427720175", + ]; + for test in &tests { + let a = Decimal::from_str(test).unwrap(); + let mut bytes: Vec<u8> = Vec::new(); + borsh::BorshSerialize::serialize(&a, &mut bytes).unwrap(); + let b: Decimal = borsh::BorshDeserialize::deserialize(&mut bytes.as_slice()).unwrap(); + assert_eq!(test.to_string(), b.to_string()); + let bytes = borsh::try_to_vec_with_schema(&a); + assert!(bytes.is_ok(), "try_to_vec_with_schema.is_ok()"); + let bytes = bytes.unwrap(); + let result = borsh::try_from_slice_with_schema(&bytes); + assert!(result.is_ok(), "try_from_slice_with_schema.is_ok()"); + let b: Decimal = result.unwrap(); + assert_eq!(test.to_string(), b.to_string()); + } +} + +#[test] +#[cfg(feature = "rkyv")] +fn it_can_serialize_deserialize_rkyv() { + use rkyv::Deserialize; + let tests = [ + "12.3456789", + "5233.9008808150288439427720175", + "-5233.9008808150288439427720175", + ]; + for test in &tests { + let a = Decimal::from_str(test).unwrap(); + let bytes = rkyv::to_bytes::<_, 256>(&a).unwrap(); + + #[cfg(feature = "rkyv-safe")] + { + let archived = rkyv::check_archived_root::<Decimal>(&bytes[..]).unwrap(); + assert_eq!(archived, &a); + } + + let archived = unsafe { rkyv::archived_root::<Decimal>(&bytes[..]) }; + assert_eq!(archived, &a); + + let deserialized: Decimal = archived.deserialize(&mut rkyv::Infallible).unwrap(); + assert_eq!(deserialized, a); + } +} + +#[test] +fn it_can_deserialize_unbounded_values() { + // Mantissa for these: 19393111376951473493673267553 + let tests = [ + ( + [1u8, 0, 28, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], + // Scale 28: -1.9393111376951473493673267553 + "-1.9393111376951473493673267553", + ), + ( + [1u8, 0, 29, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], + // Scale 29: -0.19393111376951473493673267553 + "-0.1939311137695147349367326755", + ), + ( + [1u8, 0, 30, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], + // Scale 30: -0.019393111376951473493673267553 + "-0.0193931113769514734936732676", + ), + ( + [1u8, 0, 31, 206, 97, 81, 216, 182, 20, 30, 165, 78, 18, 155, 169, 62], + // Scale 31: -0.0019393111376951473493673267553 + "-0.0019393111376951473493673268", + ), + ]; + for &(bytes, expected) in &tests { + let dec = Decimal::deserialize(bytes); + let string = format!("{:.9999}", dec); + let dec2 = Decimal::from_str(&string).unwrap(); + assert_eq!(dec, dec2); + assert_eq!(dec.to_string(), expected, "dec.to_string()"); + assert_eq!(dec2.to_string(), expected, "dec2.to_string()"); + } +} + +// Formatting + +#[test] +fn it_formats() { + let a = Decimal::from_str("233.323223").unwrap(); + assert_eq!(format!("{}", a), "233.323223"); + assert_eq!(format!("{:.9}", a), "233.323223000"); + assert_eq!(format!("{:.0}", a), "233"); + assert_eq!(format!("{:.2}", a), "233.32"); + assert_eq!(format!("{:010.2}", a), "0000233.32"); + assert_eq!(format!("{:0<10.2}", a), "233.320000"); +} +#[test] +fn it_formats_neg() { + let a = Decimal::from_str("-233.323223").unwrap(); + assert_eq!(format!("{}", a), "-233.323223"); + assert_eq!(format!("{:.9}", a), "-233.323223000"); + assert_eq!(format!("{:.0}", a), "-233"); + assert_eq!(format!("{:.2}", a), "-233.32"); + assert_eq!(format!("{:010.2}", a), "-000233.32"); + assert_eq!(format!("{:0<10.2}", a), "-233.32000"); +} +#[test] +fn it_formats_small() { + let a = Decimal::from_str("0.2223").unwrap(); + assert_eq!(format!("{}", a), "0.2223"); + assert_eq!(format!("{:.9}", a), "0.222300000"); + assert_eq!(format!("{:.0}", a), "0"); + assert_eq!(format!("{:.2}", a), "0.22"); + assert_eq!(format!("{:010.2}", a), "0000000.22"); + assert_eq!(format!("{:0<10.2}", a), "0.22000000"); +} +#[test] +fn it_formats_small_leading_zeros() { + let a = Decimal::from_str("0.0023554701772169").unwrap(); + assert_eq!(format!("{}", a), "0.0023554701772169"); + assert_eq!(format!("{:.9}", a), "0.002355470"); + assert_eq!(format!("{:.0}", a), "0"); + assert_eq!(format!("{:.2}", a), "0.00"); + assert_eq!(format!("{:010.2}", a), "0000000.00"); + assert_eq!(format!("{:0<10.2}", a), "0.00000000"); +} +#[test] +fn it_formats_small_neg() { + let a = Decimal::from_str("-0.2223").unwrap(); + assert_eq!(format!("{}", a), "-0.2223"); + assert_eq!(format!("{:.9}", a), "-0.222300000"); + assert_eq!(format!("{:.0}", a), "-0"); + assert_eq!(format!("{:.2}", a), "-0.22"); + assert_eq!(format!("{:010.2}", a), "-000000.22"); + assert_eq!(format!("{:0<10.2}", a), "-0.2200000"); +} + +#[test] +fn it_formats_zero() { + let a = Decimal::from_str("0").unwrap(); + assert_eq!(format!("{}", a), "0"); + assert_eq!(format!("{:.9}", a), "0.000000000"); + assert_eq!(format!("{:.0}", a), "0"); + assert_eq!(format!("{:.2}", a), "0.00"); + assert_eq!(format!("{:010.2}", a), "0000000.00"); + assert_eq!(format!("{:0<10.2}", a), "0.00000000"); +} + +#[test] +fn it_formats_int() { + let a = Decimal::from_str("5").unwrap(); + assert_eq!(format!("{}", a), "5"); + assert_eq!(format!("{:.9}", a), "5.000000000"); + assert_eq!(format!("{:.0}", a), "5"); + assert_eq!(format!("{:.2}", a), "5.00"); + assert_eq!(format!("{:010.2}", a), "0000005.00"); + assert_eq!(format!("{:0<10.2}", a), "5.00000000"); +} + +#[test] +fn it_formats_lower_exp() { + let tests = [ + ("0.00001", "1e-5"), + ("-0.00001", "-1e-5"), + ("42.123", "4.2123e1"), + ("-42.123", "-4.2123e1"), + ("100", "1e2"), + ]; + for (value, expected) in &tests { + let a = Decimal::from_str(value).unwrap(); + assert_eq!(&format!("{:e}", a), *expected, "format!(\"{{:e}}\", {})", a); + } +} + +#[test] +fn it_formats_lower_exp_padding() { + let tests = [ + ("0.00001", "01e-5"), + ("-0.00001", "-1e-5"), + ("42.123", "4.2123e1"), + ("-42.123", "-4.2123e1"), + ("100", "001e2"), + ]; + for (value, expected) in &tests { + let a = Decimal::from_str(value).unwrap(); + assert_eq!(&format!("{:05e}", a), *expected, "format!(\"{{:05e}}\", {})", a); + } +} + +#[test] +fn it_formats_scientific_precision() { + for (num, scale, expected_no_precision, expected_precision) in [ + ( + 123456, + 10, + "1.23456e-5", + [ + "1e-5", + "1.2e-5", + "1.23e-5", + "1.234e-5", + "1.2345e-5", + "1.23456e-5", + "1.234560e-5", + "1.2345600e-5", + ], + ), + ( + 123456, + 0, + "1.23456e5", + [ + "1e5", + "1.2e5", + "1.23e5", + "1.234e5", + "1.2345e5", + "1.23456e5", + "1.234560e5", + "1.2345600e5", + ], + ), + ( + 1, + 0, + "1e0", + [ + "1e0", + "1.0e0", + "1.00e0", + "1.000e0", + "1.0000e0", + "1.00000e0", + "1.000000e0", + "1.0000000e0", + ], + ), + ( + -123456, + 10, + "-1.23456e-5", + [ + "-1e-5", + "-1.2e-5", + "-1.23e-5", + "-1.234e-5", + "-1.2345e-5", + "-1.23456e-5", + "-1.234560e-5", + "-1.2345600e-5", + ], + ), + ( + -100000, + 10, + "-1e-5", + [ + "-1e-5", + "-1.0e-5", + "-1.00e-5", + "-1.000e-5", + "-1.0000e-5", + "-1.00000e-5", + "-1.000000e-5", + "-1.0000000e-5", + ], + ), + ] { + assert_eq!(format!("{:e}", Decimal::new(num, scale)), expected_no_precision); + for i in 0..expected_precision.len() { + assert_eq!( + format!("{:.prec$e}", Decimal::new(num, scale), prec = i), + expected_precision[i] + ); + } + } +} + +// Negation +#[test] +fn it_negates_decimals() { + fn neg(a: &str, b: &str) { + let a = Decimal::from_str(a).unwrap(); + let result = -a; + assert_eq!(b, result.to_string(), "- {}", a.to_string()); + } + + let tests = &[ + ("1", "-1"), + ("2", "-2"), + ("2454495034", "-2454495034"), + ("0.1", "-0.1"), + ("11.815126050420168067226890757", "-11.815126050420168067226890757"), + ]; + + for &(a, b) in tests { + neg(a, b); + neg(b, a); + } +} + +// Addition + +#[test] +fn it_can_add_simple() { + // This is the most basic test for addition, intended largely for micro-optimization. + let two = Decimal::ONE + Decimal::ONE; + assert_eq!(two.to_u32(), Some(2)); +} + +#[test] +fn it_adds_decimals() { + fn add(a: &str, b: &str, c: &str) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + let result = a + b; + assert_eq!(c, result.to_string(), "{} + {}", a.to_string(), b.to_string()); + let result = b + a; + assert_eq!(c, result.to_string(), "{} + {}", b.to_string(), a.to_string()); + } + + let tests = &[ + ("0", "0", "0"), + ("0", "-0", "0"), + ("-0", "0", "0"), + ("-0", "-0", "0"), + ("2", "3", "5"), + ("2454495034", "3451204593", "5905699627"), + ("24544.95034", ".3451204593", "24545.2954604593"), + (".1", ".1", "0.2"), + (".10", ".1", "0.20"), + (".1", "-.1", "0.0"), + ("0", "1.001", "1.001"), + ("2", "-3", "-1"), + ("-2", "3", "1"), + ("-2", "-3", "-5"), + ("3", "-2", "1"), + ("-3", "2", "-1"), + ("1.234", "2.4567", "3.6907"), + ( + "11.815126050420168067226890757", + "0.6386554621848739495798319328", + "12.453781512605042016806722690", + ), + ( + "-11.815126050420168067226890757", + "0.6386554621848739495798319328", + "-11.176470588235294117647058824", + ), + ( + "11.815126050420168067226890757", + "-0.6386554621848739495798319328", + "11.176470588235294117647058824", + ), + ( + "-11.815126050420168067226890757", + "-0.6386554621848739495798319328", + "-12.453781512605042016806722690", + ), + ( + "11815126050420168067226890757", + "0.4386554621848739495798319328", + "11815126050420168067226890757", + ), + ( + "-11815126050420168067226890757", + "0.4386554621848739495798319328", + "-11815126050420168067226890757", + ), + ( + "11815126050420168067226890757", + "-0.4386554621848739495798319328", + "11815126050420168067226890757", + ), + ( + "-11815126050420168067226890757", + "-0.4386554621848739495798319328", + "-11815126050420168067226890757", + ), + ( + "0.0872727272727272727272727272", + "843.65000000", + "843.7372727272727272727272727", + ), + ( + "7314.6229858868828353570724702", + "1000", + // Overflow causes this to round + "8314.622985886882835357072470", + ), + ( + "108053.27500000000000000000000", + "0.00000000000000000000000", + "108053.27500000000000000000000", + ), + ( + "108053.27500000000000000000000", + // This zero value has too high precision and will be trimmed + "0.000000000000000000000000", + "108053.27500000000000000000000", + ), + ( + "108053.27500000000000000000000", + // This value has too high precision and will be rounded + "0.000000000000000000000001", + "108053.27500000000000000000000", + ), + ( + "108053.27500000000000000000000", + // This value has too high precision and will be rounded + "0.000000000000000000000005", + either!("108053.27500000000000000000000", "108053.27500000000000000000001"), + ), + ( + "8097370036018690744.2590371109596744091", + "3807285637671831400.15346897797550749555", + "11904655673690522144.412506089", + ), + ]; + for &(a, b, c) in tests { + add(a, b, c); + } +} + +#[test] +fn it_can_addassign() { + let mut a = Decimal::from_str("1.01").unwrap(); + let b = Decimal::from_str("0.99").unwrap(); + a += b; + assert_eq!("2.00", a.to_string()); + + a += &b; + assert_eq!("2.99", a.to_string()); + + let mut c = &mut a; + c += b; + assert_eq!("3.98", a.to_string()); + + let mut c = &mut a; + c += &b; + assert_eq!("4.97", a.to_string()); +} + +// Subtraction + +#[test] +fn it_subtracts_decimals() { + fn sub(a: &str, b: &str, c: &str) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + let result = a - b; + assert_eq!(c, result.to_string(), "{} - {}", a.to_string(), b.to_string()); + } + + let tests = &[ + ("0", "0", "0"), + ("0", "-0", "0"), + ("-0", "0", "0"), + ("-0", "-0", "0"), + ("2", "3", "-1"), + ("3451204593", "2323322332", "1127882261"), + ("24544.95034", ".3451204593", "24544.6052195407"), + (".1", ".1", "0.0"), + (".1", "-.1", "0.2"), + ("1.001", "0", "1.001"), + ("2", "-3", "5"), + ("-2", "3", "-5"), + ("-2", "-3", "1"), + ("3", "-2", "5"), + ("-3", "2", "-5"), + ("1.234", "2.4567", "-1.2227"), + ("844.13000000", "843.65000000", "0.48000000"), + ("79228162514264337593543950335", "79228162514264337593543950335", "0"), // 0xFFFF_FFFF_FFFF_FFFF_FFF_FFFF - 0xFFFF_FFFF_FFFF_FFFF_FFF_FFFF + ("79228162514264337593543950335", "0", "79228162514264337593543950335"), + ("79228162514264337593543950335", "79228162514264337593543950333", "2"), + ("4951760157141521099596496896", "1", "4951760157141521099596496895"), // 0x1000_0000_0000_0000_0000_0000 - 1 = 0x0FFF_FFFF_FFFF_FFFF_FFF_FFFF + ("79228162514264337593543950334", "79228162514264337593543950335", "-1"), + ("1", "4951760157141521099596496895", "-4951760157141521099596496894"), + ("18446744073709551615", "-18446744073709551615", "36893488147419103230"), // 0xFFFF_FFFF_FFFF_FFFF - -0xFFFF_FFFF_FFFF_FFFF + ]; + for &(a, b, c) in tests { + sub(a, b, c); + } +} + +#[test] +fn it_can_subassign() { + let mut a = Decimal::from_str("1.01").unwrap(); + let b = Decimal::from_str("0.51").unwrap(); + a -= b; + assert_eq!("0.50", a.to_string()); + + a -= &b; + assert_eq!("-0.01", a.to_string()); + + let mut c = &mut a; + c -= b; + assert_eq!("-0.52", a.to_string()); + + let mut c = &mut a; + c -= &b; + assert_eq!("-1.03", a.to_string()); +} + +// Multiplication + +#[test] +fn it_multiplies_decimals() { + fn mul(a: &str, b: &str, c: &str) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + let result = a * b; + assert_eq!(c, result.to_string(), "{} * {}", a.to_string(), b.to_string()); + let result = b * a; + assert_eq!(c, result.to_string(), "{} * {}", b.to_string(), a.to_string()); + } + + let tests = &[ + ("2", "3", "6"), + ("2454495034", "3451204593", "8470964534836491162"), + ("24544.95034", ".3451204593", "8470.964534836491162"), + (".1", ".1", "0.01"), + ("0", "1.001", "0"), + ("2", "-3", "-6"), + ("-2", "3", "-6"), + ("-2", "-3", "6"), + ("1", "2.01", "2.01"), + ("1.0", "2.01", "2.010"), // Scale is always additive + ( + "0.00000000000000001", + "0.00000000000000001", + "0.0000000000000000000000000000", + ), + ("0.0000000000000000000000000001", "0.0000000000000000000000000001", "0"), + ( + "0.6386554621848739495798319328", + "11.815126050420168067226890757", + "7.5457947885036367488171739292", + ), + ( + "2123456789012345678901234567.8", + "11.815126050420168067226890757", + "25088909624801327937270048761", + ), + ( + "2123456789012345678901234567.8", + "-11.815126050420168067226890757", + "-25088909624801327937270048761", + ), + ( + "2.1234567890123456789012345678", + "2.1234567890123456789012345678", + "4.5090687348026215523554336227", + ), + ( + "0.48000000", + "0.1818181818181818181818181818", + either!("0.0872727272727272727272727273", "0.0872727272727272727272727272"), + ), + ]; + for &(a, b, c) in tests { + mul(a, b, c); + } +} + +#[test] +#[should_panic(expected = "Multiplication overflowed")] +fn it_panics_when_multiply_with_overflow() { + let a = Decimal::from_str("2000000000000000000001").unwrap(); + let b = Decimal::from_str("3000000000000000000001").unwrap(); + let _ = a * b; +} + +#[test] +fn it_can_mulassign() { + let mut a = Decimal::from_str("1.25").unwrap(); + let b = Decimal::from_str("0.01").unwrap(); + + a *= b; + assert_eq!("0.0125", a.to_string()); + + a *= &b; + assert_eq!("0.000125", a.to_string()); + + let mut c = &mut a; + c *= b; + assert_eq!("0.00000125", a.to_string()); + + let mut c = &mut a; + c *= &b; + assert_eq!("0.0000000125", a.to_string()); +} + +// Division + +#[test] +fn it_divides_decimals() { + fn div(a: &str, b: &str, c: &str) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + let result = a / b; + assert_eq!(c, result.to_string(), "{} / {}", a.to_string(), b.to_string()); + } + + let tests = &[ + ("6", "3", "2"), + ("10", "2", "5"), + ("2.2", "1.1", "2"), + ("-2.2", "-1.1", "2"), + ("12.88", "5.6", "2.3"), + ( + "1023427554493", + "43432632", + either!("23563.562864276795382789603909", "23563.562864276795382789603908"), + ), + ("10000", "3", "3333.3333333333333333333333333"), + ("2", "3", "0.6666666666666666666666666667"), + ("1", "3", "0.3333333333333333333333333333"), + ("-2", "3", "-0.6666666666666666666666666667"), + ("2", "-3", "-0.6666666666666666666666666667"), + ("-2", "-3", "0.6666666666666666666666666667"), + ( + "1234.5678", + "0.1234567890123456", + either!("9999.99926999999982999953127", "9999.999269999999829999531269"), + ), + ("1234.567890123456789012345678", "1.234567890123456789012345678", "1000"), + ( + "32.91625929004387114334488", + "3.27629537", + either!("10.046792359274942644546996384", "10.046792359274942644546996383"), + ), + ("5000", "1000.26957490549", "4.9986524887277738570721416846"), + ("6142.6941216127122745222131114", "2", "3071.3470608063561372611065557"), + ( + "3071.3470608063561372611065557", + "1228.87000756", + either!("2.4993262443638869285360708423", "2.4993262443638869285360708422"), + ), + ( + "590.3274854004009467754255123", + "53.68997202826239", + "10.995116277759516850521689988", + ), + ]; + for &(a, b, c) in tests { + div(a, b, c); + } +} + +#[test] +#[should_panic(expected = "Division by zero")] +fn it_can_divide_by_zero() { + let a = Decimal::from_str("2").unwrap(); + let _ = a / Decimal::ZERO; +} + +#[test] +fn it_can_divassign() { + let mut a = Decimal::from_str("1.25").unwrap(); + let b = Decimal::from_str("0.01").unwrap(); + + a /= b; + assert_eq!("125", a.to_string()); + + a /= &b; + assert_eq!("12500", a.to_string()); + + let mut c = &mut a; + c /= b; + assert_eq!("1250000", a.to_string()); + + let mut c = &mut a; + c /= &b; + assert_eq!("125000000", a.to_string()); +} + +// Modulus and Remainder are not the same thing! +// https://math.stackexchange.com/q/801962/82277 + +#[test] +fn it_rems_decimals() { + fn rem(a: &str, b: &str, c: &str) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + // a = qb + r + let result = a % b; + assert_eq!(c, result.to_string(), "{} % {}", a.to_string(), b.to_string()); + } + + let tests = &[ + ("2", "3", "2"), + ("-2", "3", "-2"), + ("2", "-3", "2"), + ("-2", "-3", "-2"), + ("6", "3", "0"), + ("42.2", "11.9", "6.5"), + ("2.1", "3", "2.1"), + ("2", "3.1", "2"), + ("2.0", "3.1", "2.0"), + ("4", "3.1", "0.9"), + ("2", "2", "0"), + ("2", "-2", "0"), + // Legacy keeps sign from lhs operand + ("-2", "2", "0"), + ("-2", "-2", "0"), + ]; + for &(a, b, c) in tests { + rem(a, b, c); + } +} + +#[test] +fn it_can_remassign() { + let mut a = Decimal::from_str("5").unwrap(); + let b = Decimal::from_str("2").unwrap(); + + a %= b; + assert_eq!("1", a.to_string()); + + a %= &b; + assert_eq!("1", a.to_string()); + + let mut c = &mut a; + c %= b; + assert_eq!("1", a.to_string()); + + let mut c = &mut a; + c %= &b; + assert_eq!("1", a.to_string()); +} + +#[test] +fn it_eqs_decimals() { + fn eq(a: &str, b: &str, c: bool) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + assert_eq!(c, a.eq(&b), "{} == {}", a.to_string(), b.to_string()); + assert_eq!(c, b.eq(&a), "{} == {}", b.to_string(), a.to_string()); + } + + let tests = &[ + ("1", "1", true), + ("1", "-1", false), + ("1", "1.00", true), + ("1.2345000000000", "1.2345", true), + ("1.0000000000000000000000000000", "1.0000000000000000000000000000", true), + ( + "1.0000000000000000000000000001", + "1.0000000000000000000000000000", + false, + ), + ]; + for &(a, b, c) in tests { + eq(a, b, c); + } +} + +#[test] +fn it_cmps_decimals() { + fn cmp(a: &str, b: &str, c: core::cmp::Ordering) { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + assert_eq!( + c, + a.cmp(&b), + "{} {} {}", + a.to_string(), + match c { + Less => "<", + Equal => "==", + Greater => ">", + }, + b.to_string() + ); + } + + let tests = &[ + ("1", "1", Equal), + ("1", "-1", Greater), + ("1", "1.00", Equal), + ("1.2345000000000", "1.2345", Equal), + ( + "1.0000000000000000000000000001", + "1.0000000000000000000000000000", + Greater, + ), + ("1.0000000000000000000000000000", "1.0000000000000000000000000001", Less), + ("-1", "100", Less), + ("-100", "1", Less), + ("0", "0.5", Less), + ("0.5", "0", Greater), + ("100", "0.0098", Greater), + ("1000000000000000", "999000000000000.0001", Greater), + ("2.0001", "2.0001", Equal), + ( + "11.815126050420168067226890757", + "0.6386554621848739495798319328", + Greater, + ), + ("0.6386554621848739495798319328", "11.815126050420168067226890757", Less), + ("-0.5", "-0.01", Less), + ("-0.5", "-0.1", Less), + ("-0.01", "-0.5", Greater), + ("-0.1", "-0.5", Greater), + // 000 equality + ("0.00000000", "0.00000000", Equal), + // 000 000 same scale + ("0.00000000", "0.00000000", Equal), + ("-0.00000000", "0.00000000", Equal), + ("0.00000000", "-0.00000000", Equal), + ("-0.00000000", "-0.00000000", Equal), + // 000 000 different scale + ("0.000000000", "0.00000000000000000000000", Equal), + ("-0.000000000", "0.00000000000000000000000", Equal), + ("0.000000000", "-0.00000000000000000000000", Equal), + ("-0.000000000", "-0.00000000000000000000000", Equal), + // 000 100 same scale + ("0.00000000", "6.56792910", Less), + ("-0.00000000", "6.56792910", Less), + ("0.00000000", "-6.56792910", Greater), + ("-0.00000000", "-6.56792910", Greater), + // 000 100 different scale + ("0.0000000000000000000", "0.00000000000000001916236746", Less), + ("-0.0000000000000000000", "0.00000000000000001916236746", Less), + ("0.0000000000000000000", "-0.00000000000000001916236746", Greater), + ("-0.0000000000000000000", "-0.00000000000000001916236746", Greater), + // 000 010 same scale + ("0.00000000", "49037796231.72571136", Less), + ("-0.00000000", "49037796231.72571136", Less), + ("0.00000000", "-49037796231.72571136", Greater), + ("-0.00000000", "-49037796231.72571136", Greater), + // 000 010 different scale + ("0", "14459264155.12895488", Less), + ("-0", "14459264155.12895488", Less), + ("0", "-14459264155.12895488", Greater), + ("-0", "-14459264155.12895488", Greater), + // 000 110 same scale + ("0.00000000", "38675108055.09052783", Less), + ("-0.00000000", "38675108055.09052783", Less), + ("0.00000000", "-38675108055.09052783", Greater), + ("-0.00000000", "-38675108055.09052783", Greater), + // 000 110 different scale + ("0.00", "1495767034080324868", Less), + ("-0.00", "1495767034080324868", Less), + ("0.00", "-1495767034080324868", Greater), + ("-0.00", "-1495767034080324868", Greater), + // 000 001 same scale + ("0.00000000", "359299289270893106016.81305600", Less), + ("-0.00000000", "359299289270893106016.81305600", Less), + ("0.00000000", "-359299289270893106016.81305600", Greater), + ("-0.00000000", "-359299289270893106016.81305600", Greater), + // 000 001 different scale + ("0.00000000000000000000000000", "261631091689.9518486763536384", Less), + ("-0.00000000000000000000000000", "261631091689.9518486763536384", Less), + ( + "0.00000000000000000000000000", + "-261631091689.9518486763536384", + Greater, + ), + ( + "-0.00000000000000000000000000", + "-261631091689.9518486763536384", + Greater, + ), + // 000 101 same scale + ("0.00000000", "184137107696737410476.63166815", Less), + ("-0.00000000", "184137107696737410476.63166815", Less), + ("0.00000000", "-184137107696737410476.63166815", Greater), + ("-0.00000000", "-184137107696737410476.63166815", Greater), + // 000 101 different scale + ("0.000000000", "2286857871088.7514840434334478", Less), + ("-0.000000000", "2286857871088.7514840434334478", Less), + ("0.000000000", "-2286857871088.7514840434334478", Greater), + ("-0.000000000", "-2286857871088.7514840434334478", Greater), + // 000 011 same scale + ("0.00000000", "169194696640288819908.07715840", Less), + ("-0.00000000", "169194696640288819908.07715840", Less), + ("0.00000000", "-169194696640288819908.07715840", Greater), + ("-0.00000000", "-169194696640288819908.07715840", Greater), + // 000 011 different scale + ("0.00000000", "2757550691.7650076909569048576", Less), + ("-0.00000000", "2757550691.7650076909569048576", Less), + ("0.00000000", "-2757550691.7650076909569048576", Greater), + ("-0.00000000", "-2757550691.7650076909569048576", Greater), + // 000 111 same scale + ("0.00000000", "133610725292915899001.10059212", Less), + ("-0.00000000", "133610725292915899001.10059212", Less), + ("0.00000000", "-133610725292915899001.10059212", Greater), + ("-0.00000000", "-133610725292915899001.10059212", Greater), + // 000 111 different scale + ("0.00000000000000000000", "86.25432767926620368822165265", Less), + ("-0.00000000000000000000", "86.25432767926620368822165265", Less), + ("0.00000000000000000000", "-86.25432767926620368822165265", Greater), + ("-0.00000000000000000000", "-86.25432767926620368822165265", Greater), + // 100 equality + ("0.0000000000598992228", "0.0000000000598992228", Equal), + // 100 000 same scale + ("0.0000000000598992228", "0.0000000000000000000", Greater), + ("-0.0000000000598992228", "0.0000000000000000000", Less), + ("0.0000000000598992228", "-0.0000000000000000000", Greater), + ("-0.0000000000598992228", "-0.0000000000000000000", Less), + // 100 000 different scale + ("0.1797407597", "0.0000000000000000000", Greater), + ("-0.1797407597", "0.0000000000000000000", Less), + ("0.1797407597", "-0.0000000000000000000", Greater), + ("-0.1797407597", "-0.0000000000000000000", Less), + // 100 100 same scale + ("0.0000000000598992228", "0.0000000000064510789", Greater), + ("-0.0000000000598992228", "0.0000000000064510789", Less), + ("0.0000000000598992228", "-0.0000000000064510789", Greater), + ("-0.0000000000598992228", "-0.0000000000064510789", Less), + // 100 100 different scale + ("0.000000000000011217354", "0.0000000000217735186", Less), + ("-0.000000000000011217354", "0.0000000000217735186", Less), + ("0.000000000000011217354", "-0.0000000000217735186", Greater), + ("-0.000000000000011217354", "-0.0000000000217735186", Greater), + // 100 010 same scale + ("0.0000000000598992228", "0.0659116848159129600", Less), + ("-0.0000000000598992228", "0.0659116848159129600", Less), + ("0.0000000000598992228", "-0.0659116848159129600", Greater), + ("-0.0000000000598992228", "-0.0659116848159129600", Greater), + // 100 010 different scale + ("0.00042035421", "0.004709460588143575040", Less), + ("-0.00042035421", "0.004709460588143575040", Less), + ("0.00042035421", "-0.004709460588143575040", Greater), + ("-0.00042035421", "-0.004709460588143575040", Greater), + // 100 110 same scale + ("0.0000000000598992228", "0.0755686585127091375", Less), + ("-0.0000000000598992228", "0.0755686585127091375", Less), + ("0.0000000000598992228", "-0.0755686585127091375", Greater), + ("-0.0000000000598992228", "-0.0755686585127091375", Greater), + // 100 110 different scale + ("14872.94465", "1284707831905.854085", Less), + ("-14872.94465", "1284707831905.854085", Less), + ("14872.94465", "-1284707831905.854085", Greater), + ("-14872.94465", "-1284707831905.854085", Greater), + // 100 001 same scale + ("0.0000000000598992228", "888767595.6145376468836286464", Less), + ("-0.0000000000598992228", "888767595.6145376468836286464", Less), + ("0.0000000000598992228", "-888767595.6145376468836286464", Greater), + ("-0.0000000000598992228", "-888767595.6145376468836286464", Greater), + // 100 001 different scale + ("0.0002108155975", "36.07555527243968476014968832", Less), + ("-0.0002108155975", "36.07555527243968476014968832", Less), + ("0.0002108155975", "-36.07555527243968476014968832", Greater), + ("-0.0002108155975", "-36.07555527243968476014968832", Greater), + // 100 101 same scale + ("0.0000000000598992228", "125730345.7412344676309569911", Less), + ("-0.0000000000598992228", "125730345.7412344676309569911", Less), + ("0.0000000000598992228", "-125730345.7412344676309569911", Greater), + ("-0.0000000000598992228", "-125730345.7412344676309569911", Greater), + // 100 101 different scale + ("0.0000000000871576741", "1.7283925558865766140690239662", Less), + ("-0.0000000000871576741", "1.7283925558865766140690239662", Less), + ("0.0000000000871576741", "-1.7283925558865766140690239662", Greater), + ("-0.0000000000871576741", "-1.7283925558865766140690239662", Greater), + // 100 011 same scale + ("0.0000000000598992228", "645513262.9193254090737451008", Less), + ("-0.0000000000598992228", "645513262.9193254090737451008", Less), + ("0.0000000000598992228", "-645513262.9193254090737451008", Greater), + ("-0.0000000000598992228", "-645513262.9193254090737451008", Greater), + // 100 011 different scale + ("0.000000000000000760885021", "3718370638.2004059326675681280", Less), + ("-0.000000000000000760885021", "3718370638.2004059326675681280", Less), + ("0.000000000000000760885021", "-3718370638.2004059326675681280", Greater), + ( + "-0.000000000000000760885021", + "-3718370638.2004059326675681280", + Greater, + ), + // 100 111 same scale + ("0.0000000000598992228", "422482675.5515775479939306437", Less), + ("-0.0000000000598992228", "422482675.5515775479939306437", Less), + ("0.0000000000598992228", "-422482675.5515775479939306437", Greater), + ("-0.0000000000598992228", "-422482675.5515775479939306437", Greater), + // 100 111 different scale + ("0.000000044182898", "25.452953262109919998605674045", Less), + ("-0.000000044182898", "25.452953262109919998605674045", Less), + ("0.000000044182898", "-25.452953262109919998605674045", Greater), + ("-0.000000044182898", "-25.452953262109919998605674045", Greater), + // 010 equality + ("423.1744746042687488", "423.1744746042687488", Equal), + // 010 000 same scale + ("423.1744746042687488", "0.0000000000000000", Greater), + ("-423.1744746042687488", "0.0000000000000000", Less), + ("423.1744746042687488", "-0.0000000000000000", Greater), + ("-423.1744746042687488", "-0.0000000000000000", Less), + // 010 000 different scale + ("9002354991.192604672", "0.00000000000000000000000000", Greater), + ("-9002354991.192604672", "0.00000000000000000000000000", Less), + ("9002354991.192604672", "-0.00000000000000000000000000", Greater), + ("-9002354991.192604672", "-0.00000000000000000000000000", Less), + // 010 100 same scale + ("423.1744746042687488", "0.0000000981820809", Greater), + ("-423.1744746042687488", "0.0000000981820809", Less), + ("423.1744746042687488", "-0.0000000981820809", Greater), + ("-423.1744746042687488", "-0.0000000981820809", Less), + // 010 100 different scale + ("4327019125101559.808", "0.00484846050", Greater), + ("-4327019125101559.808", "0.00484846050", Less), + ("4327019125101559.808", "-0.00484846050", Greater), + ("-4327019125101559.808", "-0.00484846050", Less), + // 010 010 same scale + ("423.1744746042687488", "786.9082854590775296", Less), + ("-423.1744746042687488", "786.9082854590775296", Less), + ("423.1744746042687488", "-786.9082854590775296", Greater), + ("-423.1744746042687488", "-786.9082854590775296", Greater), + // 010 010 different scale + ("793.0067125291450368", "0.0001587297248335626240", Greater), + ("-793.0067125291450368", "0.0001587297248335626240", Less), + ("793.0067125291450368", "-0.0001587297248335626240", Greater), + ("-793.0067125291450368", "-0.0001587297248335626240", Less), + // 010 110 same scale + ("423.1744746042687488", "300.0541360230572049", Greater), + ("-423.1744746042687488", "300.0541360230572049", Less), + ("423.1744746042687488", "-300.0541360230572049", Greater), + ("-423.1744746042687488", "-300.0541360230572049", Less), + // 010 110 different scale + ("90627.2042582540288", "4472414566654924.741", Less), + ("-90627.2042582540288", "4472414566654924.741", Less), + ("90627.2042582540288", "-4472414566654924.741", Greater), + ("-90627.2042582540288", "-4472414566654924.741", Greater), + // 010 001 same scale + ("423.1744746042687488", "3960577151543.5796707636412416", Less), + ("-423.1744746042687488", "3960577151543.5796707636412416", Less), + ("423.1744746042687488", "-3960577151543.5796707636412416", Greater), + ("-423.1744746042687488", "-3960577151543.5796707636412416", Greater), + // 010 001 different scale + ("0.000008867461591822499840", "185286.04378228713249986052096", Less), + ("-0.000008867461591822499840", "185286.04378228713249986052096", Less), + ("0.000008867461591822499840", "-185286.04378228713249986052096", Greater), + ( + "-0.000008867461591822499840", + "-185286.04378228713249986052096", + Greater, + ), + // 010 101 same scale + ("423.1744746042687488", "2825958416017.6213507229869501", Less), + ("-423.1744746042687488", "2825958416017.6213507229869501", Less), + ("423.1744746042687488", "-2825958416017.6213507229869501", Greater), + ("-423.1744746042687488", "-2825958416017.6213507229869501", Greater), + // 010 101 different scale + ("0.01901870767742648320", "37662082383021542232.529651212", Less), + ("-0.01901870767742648320", "37662082383021542232.529651212", Less), + ("0.01901870767742648320", "-37662082383021542232.529651212", Greater), + ("-0.01901870767742648320", "-37662082383021542232.529651212", Greater), + // 010 011 same scale + ("423.1744746042687488", "3628063966991.6759417059016704", Less), + ("-423.1744746042687488", "3628063966991.6759417059016704", Less), + ("423.1744746042687488", "-3628063966991.6759417059016704", Greater), + ("-423.1744746042687488", "-3628063966991.6759417059016704", Greater), + // 010 011 different scale + ("45359904.32470925312", "2.4203452488052342918570049536", Greater), + ("-45359904.32470925312", "2.4203452488052342918570049536", Less), + ("45359904.32470925312", "-2.4203452488052342918570049536", Greater), + ("-45359904.32470925312", "-2.4203452488052342918570049536", Less), + // 010 111 same scale + ("423.1744746042687488", "2629665172331.9610693820109120", Less), + ("-423.1744746042687488", "2629665172331.9610693820109120", Less), + ("423.1744746042687488", "-2629665172331.9610693820109120", Greater), + ("-423.1744746042687488", "-2629665172331.9610693820109120", Greater), + // 010 111 different scale + ("0.0006420803252266205184", "8172.032417576265900588945489", Less), + ("-0.0006420803252266205184", "8172.032417576265900588945489", Less), + ("0.0006420803252266205184", "-8172.032417576265900588945489", Greater), + ("-0.0006420803252266205184", "-8172.032417576265900588945489", Greater), + // 110 equality + ("844.5530620517286511", "844.5530620517286511", Equal), + // 110 000 same scale + ("844.5530620517286511", "0.0000000000000000", Greater), + ("-844.5530620517286511", "0.0000000000000000", Less), + ("844.5530620517286511", "-0.0000000000000000", Greater), + ("-844.5530620517286511", "-0.0000000000000000", Less), + // 110 000 different scale + ("3285.530033386074797", "0.00000000000000000000", Greater), + ("-3285.530033386074797", "0.00000000000000000000", Less), + ("3285.530033386074797", "-0.00000000000000000000", Greater), + ("-3285.530033386074797", "-0.00000000000000000000", Less), + // 110 100 same scale + ("844.5530620517286511", "0.0000001953470063", Greater), + ("-844.5530620517286511", "0.0000001953470063", Less), + ("844.5530620517286511", "-0.0000001953470063", Greater), + ("-844.5530620517286511", "-0.0000001953470063", Less), + // 110 100 different scale + ("371284.0210972493371", "0.000000000000001307794657", Greater), + ("-371284.0210972493371", "0.000000000000001307794657", Less), + ("371284.0210972493371", "-0.000000000000001307794657", Greater), + ("-371284.0210972493371", "-0.000000000000001307794657", Less), + // 110 010 same scale + ("844.5530620517286511", "612.1542773033140224", Greater), + ("-844.5530620517286511", "612.1542773033140224", Less), + ("844.5530620517286511", "-612.1542773033140224", Greater), + ("-844.5530620517286511", "-612.1542773033140224", Less), + // 110 010 different scale + ("0.00004869219159821525572", "23341676485159.15776", Less), + ("-0.00004869219159821525572", "23341676485159.15776", Less), + ("0.00004869219159821525572", "-23341676485159.15776", Greater), + ("-0.00004869219159821525572", "-23341676485159.15776", Greater), + // 110 110 same scale + ("844.5530620517286511", "326.6132818317622015", Greater), + ("-844.5530620517286511", "326.6132818317622015", Less), + ("844.5530620517286511", "-326.6132818317622015", Greater), + ("-844.5530620517286511", "-326.6132818317622015", Less), + // 110 110 different scale + ("4752139369958.820619", "1330851022882027.972", Less), + ("-4752139369958.820619", "1330851022882027.972", Less), + ("4752139369958.820619", "-1330851022882027.972", Greater), + ("-4752139369958.820619", "-1330851022882027.972", Greater), + // 110 001 same scale + ("844.5530620517286511", "3585610241942.1922435648192512", Less), + ("-844.5530620517286511", "3585610241942.1922435648192512", Less), + ("844.5530620517286511", "-3585610241942.1922435648192512", Greater), + ("-844.5530620517286511", "-3585610241942.1922435648192512", Greater), + // 110 001 different scale + ("539313715923.4424678", "12410950080603997079634706432", Less), + ("-539313715923.4424678", "12410950080603997079634706432", Less), + ("539313715923.4424678", "-12410950080603997079634706432", Greater), + ("-539313715923.4424678", "-12410950080603997079634706432", Greater), + // 110 101 same scale + ("844.5530620517286511", "1947825396031.6933708908343230", Less), + ("-844.5530620517286511", "1947825396031.6933708908343230", Less), + ("844.5530620517286511", "-1947825396031.6933708908343230", Greater), + ("-844.5530620517286511", "-1947825396031.6933708908343230", Greater), + // 110 101 different scale + ("0.2301405445512525317", "245433629587.71206426704897154", Less), + ("-0.2301405445512525317", "245433629587.71206426704897154", Less), + ("0.2301405445512525317", "-245433629587.71206426704897154", Greater), + ("-0.2301405445512525317", "-245433629587.71206426704897154", Greater), + // 110 011 same scale + ("844.5530620517286511", "3637850451015.0843464291450880", Less), + ("-844.5530620517286511", "3637850451015.0843464291450880", Less), + ("844.5530620517286511", "-3637850451015.0843464291450880", Greater), + ("-844.5530620517286511", "-3637850451015.0843464291450880", Greater), + // 110 011 different scale + ("0.00000000717944802566514691", "18143443615.480512395717115904", Less), + ("-0.00000000717944802566514691", "18143443615.480512395717115904", Less), + ( + "0.00000000717944802566514691", + "-18143443615.480512395717115904", + Greater, + ), + ( + "-0.00000000717944802566514691", + "-18143443615.480512395717115904", + Greater, + ), + // 110 111 same scale + ("844.5530620517286511", "2738424264600.4917875777303163", Less), + ("-844.5530620517286511", "2738424264600.4917875777303163", Less), + ("844.5530620517286511", "-2738424264600.4917875777303163", Greater), + ("-844.5530620517286511", "-2738424264600.4917875777303163", Greater), + // 110 111 different scale + ("0.0000000007762706076409491335", "2489879185787497651.6458518595", Less), + ( + "-0.0000000007762706076409491335", + "2489879185787497651.6458518595", + Less, + ), + ( + "0.0000000007762706076409491335", + "-2489879185787497651.6458518595", + Greater, + ), + ( + "-0.0000000007762706076409491335", + "-2489879185787497651.6458518595", + Greater, + ), + // 001 equality + ("316007568232.9263258873102336", "316007568232.9263258873102336", Equal), + // 001 000 same scale + ("316007568232.9263258873102336", "0.0000000000000000", Greater), + ("-316007568232.9263258873102336", "0.0000000000000000", Less), + ("316007568232.9263258873102336", "-0.0000000000000000", Greater), + ("-316007568232.9263258873102336", "-0.0000000000000000", Less), + // 001 000 different scale + ("3522055990024364385815547084.8", "0.000000000000", Greater), + ("-3522055990024364385815547084.8", "0.000000000000", Less), + ("3522055990024364385815547084.8", "-0.000000000000", Greater), + ("-3522055990024364385815547084.8", "-0.000000000000", Less), + // 001 100 same scale + ("316007568232.9263258873102336", "0.0000001073412971", Greater), + ("-316007568232.9263258873102336", "0.0000001073412971", Less), + ("316007568232.9263258873102336", "-0.0000001073412971", Greater), + ("-316007568232.9263258873102336", "-0.0000001073412971", Less), + // 001 100 different scale + ("1319006.0491408208640440532992", "0.00000000000000611866432", Greater), + ("-1319006.0491408208640440532992", "0.00000000000000611866432", Less), + ("1319006.0491408208640440532992", "-0.00000000000000611866432", Greater), + ("-1319006.0491408208640440532992", "-0.00000000000000611866432", Less), + // 001 010 same scale + ("316007568232.9263258873102336", "159.1054215143227392", Greater), + ("-316007568232.9263258873102336", "159.1054215143227392", Less), + ("316007568232.9263258873102336", "-159.1054215143227392", Greater), + ("-316007568232.9263258873102336", "-159.1054215143227392", Less), + // 001 010 different scale + ("2470144.7146711063666704252928", "211.0186916505714688", Greater), + ("-2470144.7146711063666704252928", "211.0186916505714688", Less), + ("2470144.7146711063666704252928", "-211.0186916505714688", Greater), + ("-2470144.7146711063666704252928", "-211.0186916505714688", Less), + // 001 110 same scale + ("316007568232.9263258873102336", "15.1186658969096112", Greater), + ("-316007568232.9263258873102336", "15.1186658969096112", Less), + ("316007568232.9263258873102336", "-15.1186658969096112", Greater), + ("-316007568232.9263258873102336", "-15.1186658969096112", Less), + // 001 110 different scale + ("3840504199004148630832.3360768", "7581138850996748864", Greater), + ("-3840504199004148630832.3360768", "7581138850996748864", Less), + ("3840504199004148630832.3360768", "-7581138850996748864", Greater), + ("-3840504199004148630832.3360768", "-7581138850996748864", Less), + // 001 001 same scale + ("316007568232.9263258873102336", "810157633226.6053390856880128", Less), + ("-316007568232.9263258873102336", "810157633226.6053390856880128", Less), + ( + "316007568232.9263258873102336", + "-810157633226.6053390856880128", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-810157633226.6053390856880128", + Greater, + ), + // 001 001 different scale + ("1951046382014.4037956952260608", "3626102772868740412010083.1232", Less), + ( + "-1951046382014.4037956952260608", + "3626102772868740412010083.1232", + Less, + ), + ( + "1951046382014.4037956952260608", + "-3626102772868740412010083.1232", + Greater, + ), + ( + "-1951046382014.4037956952260608", + "-3626102772868740412010083.1232", + Greater, + ), + // 001 101 same scale + ("316007568232.9263258873102336", "3258394380359.1965879291312453", Less), + ("-316007568232.9263258873102336", "3258394380359.1965879291312453", Less), + ( + "316007568232.9263258873102336", + "-3258394380359.1965879291312453", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-3258394380359.1965879291312453", + Greater, + ), + // 001 101 different scale + ( + "17580513970289834.943527780352", + "3.7977957031395371036126086595", + Greater, + ), + ( + "-17580513970289834.943527780352", + "3.7977957031395371036126086595", + Less, + ), + ( + "17580513970289834.943527780352", + "-3.7977957031395371036126086595", + Greater, + ), + ( + "-17580513970289834.943527780352", + "-3.7977957031395371036126086595", + Less, + ), + // 001 011 same scale + ("316007568232.9263258873102336", "1154574080460.9867510617997312", Less), + ("-316007568232.9263258873102336", "1154574080460.9867510617997312", Less), + ( + "316007568232.9263258873102336", + "-1154574080460.9867510617997312", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-1154574080460.9867510617997312", + Greater, + ), + // 001 011 different scale + ("2008379587.5525351789031325696", "32824109460554.341800487157760", Less), + ( + "-2008379587.5525351789031325696", + "32824109460554.341800487157760", + Less, + ), + ( + "2008379587.5525351789031325696", + "-32824109460554.341800487157760", + Greater, + ), + ( + "-2008379587.5525351789031325696", + "-32824109460554.341800487157760", + Greater, + ), + // 001 111 same scale + ("316007568232.9263258873102336", "2816795479724.6069787794805311", Less), + ("-316007568232.9263258873102336", "2816795479724.6069787794805311", Less), + ( + "316007568232.9263258873102336", + "-2816795479724.6069787794805311", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-2816795479724.6069787794805311", + Greater, + ), + // 001 111 different scale + ( + "3536806574745420013541890.4576", + "9793146.81730411145833529126", + Greater, + ), + ("-3536806574745420013541890.4576", "9793146.81730411145833529126", Less), + ( + "3536806574745420013541890.4576", + "-9793146.81730411145833529126", + Greater, + ), + ("-3536806574745420013541890.4576", "-9793146.81730411145833529126", Less), + // 101 equality + ( + "254208186622762823842.71629992", + "254208186622762823842.71629992", + Equal, + ), + // 101 000 same scale + ("254208186622762823842.71629992", "0.00000000", Greater), + ("-254208186622762823842.71629992", "0.00000000", Less), + ("254208186622762823842.71629992", "-0.00000000", Greater), + ("-254208186622762823842.71629992", "-0.00000000", Less), + // 101 000 different scale + ("975421950664039.3614304091804", "0.0000000000000000", Greater), + ("-975421950664039.3614304091804", "0.0000000000000000", Less), + ("975421950664039.3614304091804", "-0.0000000000000000", Greater), + ("-975421950664039.3614304091804", "-0.0000000000000000", Less), + // 101 100 same scale + ("254208186622762823842.71629992", "10.74141379", Greater), + ("-254208186622762823842.71629992", "10.74141379", Less), + ("254208186622762823842.71629992", "-10.74141379", Greater), + ("-254208186622762823842.71629992", "-10.74141379", Less), + // 101 100 different scale + ("2552221405032275.8358630506229", "0.000000000000000592656995", Greater), + ("-2552221405032275.8358630506229", "0.000000000000000592656995", Less), + ("2552221405032275.8358630506229", "-0.000000000000000592656995", Greater), + ("-2552221405032275.8358630506229", "-0.000000000000000592656995", Less), + // 101 010 same scale + ("254208186622762823842.71629992", "62767493748.48499712", Greater), + ("-254208186622762823842.71629992", "62767493748.48499712", Less), + ("254208186622762823842.71629992", "-62767493748.48499712", Greater), + ("-254208186622762823842.71629992", "-62767493748.48499712", Less), + // 101 010 different scale + ("197346074219.25327589999174264", "0.7623493575178715136", Greater), + ("-197346074219.25327589999174264", "0.7623493575178715136", Less), + ("197346074219.25327589999174264", "-0.7623493575178715136", Greater), + ("-197346074219.25327589999174264", "-0.7623493575178715136", Less), + // 101 110 same scale + ("254208186622762823842.71629992", "76597126194.51389094", Greater), + ("-254208186622762823842.71629992", "76597126194.51389094", Less), + ("254208186622762823842.71629992", "-76597126194.51389094", Greater), + ("-254208186622762823842.71629992", "-76597126194.51389094", Less), + // 101 110 different scale + ("25899773651648.380071130043467", "61306258142804903.38", Less), + ("-25899773651648.380071130043467", "61306258142804903.38", Less), + ("25899773651648.380071130043467", "-61306258142804903.38", Greater), + ("-25899773651648.380071130043467", "-61306258142804903.38", Greater), + // 101 001 same scale + ( + "254208186622762823842.71629992", + "83547191565151621967.28086528", + Greater, + ), + ("-254208186622762823842.71629992", "83547191565151621967.28086528", Less), + ( + "254208186622762823842.71629992", + "-83547191565151621967.28086528", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-83547191565151621967.28086528", + Less, + ), + // 101 001 different scale + ("244762.90302171293318719286219", "9964072.600255221193011888128", Less), + ("-244762.90302171293318719286219", "9964072.600255221193011888128", Less), + ( + "244762.90302171293318719286219", + "-9964072.600255221193011888128", + Greater, + ), + ( + "-244762.90302171293318719286219", + "-9964072.600255221193011888128", + Greater, + ), + // 101 101 same scale + ( + "254208186622762823842.71629992", + "106541875981662806348.63716235", + Greater, + ), + ( + "-254208186622762823842.71629992", + "106541875981662806348.63716235", + Less, + ), + ( + "254208186622762823842.71629992", + "-106541875981662806348.63716235", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-106541875981662806348.63716235", + Less, + ), + // 101 101 different scale + ("362319.18250030256385507568342", "3619454249020577423546109236", Less), + ("-362319.18250030256385507568342", "3619454249020577423546109236", Less), + ( + "362319.18250030256385507568342", + "-3619454249020577423546109236", + Greater, + ), + ( + "-362319.18250030256385507568342", + "-3619454249020577423546109236", + Greater, + ), + // 101 011 same scale + ( + "254208186622762823842.71629992", + "156781478378762688050.06557184", + Greater, + ), + ( + "-254208186622762823842.71629992", + "156781478378762688050.06557184", + Less, + ), + ( + "254208186622762823842.71629992", + "-156781478378762688050.06557184", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-156781478378762688050.06557184", + Less, + ), + // 101 011 different scale + ("2486073465266.0337130589931876", "153874906950888902858.19691008", Less), + ( + "-2486073465266.0337130589931876", + "153874906950888902858.19691008", + Less, + ), + ( + "2486073465266.0337130589931876", + "-153874906950888902858.19691008", + Greater, + ), + ( + "-2486073465266.0337130589931876", + "-153874906950888902858.19691008", + Greater, + ), + // 101 111 same scale + ( + "254208186622762823842.71629992", + "10101645723744656462.08148676", + Greater, + ), + ("-254208186622762823842.71629992", "10101645723744656462.08148676", Less), + ( + "254208186622762823842.71629992", + "-10101645723744656462.08148676", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-10101645723744656462.08148676", + Less, + ), + // 101 111 different scale + ("14107601965.909635434653634526", "111238758546502973973477.99454", Less), + ( + "-14107601965.909635434653634526", + "111238758546502973973477.99454", + Less, + ), + ( + "14107601965.909635434653634526", + "-111238758546502973973477.99454", + Greater, + ), + ( + "-14107601965.909635434653634526", + "-111238758546502973973477.99454", + Greater, + ), + // 011 equality + ( + "272322.48219624218537039495168", + "272322.48219624218537039495168", + Equal, + ), + // 011 000 same scale + ("272322.48219624218537039495168", "0.00000000000000000000000", Greater), + ("-272322.48219624218537039495168", "0.00000000000000000000000", Less), + ("272322.48219624218537039495168", "-0.00000000000000000000000", Greater), + ("-272322.48219624218537039495168", "-0.00000000000000000000000", Less), + // 011 000 different scale + ("3214885516.0787854158246969344", "0.00000000000000000", Greater), + ("-3214885516.0787854158246969344", "0.00000000000000000", Less), + ("3214885516.0787854158246969344", "-0.00000000000000000", Greater), + ("-3214885516.0787854158246969344", "-0.00000000000000000", Less), + // 011 100 same scale + ("272322.48219624218537039495168", "0.00000000000000379487994", Greater), + ("-272322.48219624218537039495168", "0.00000000000000379487994", Less), + ("272322.48219624218537039495168", "-0.00000000000000379487994", Greater), + ("-272322.48219624218537039495168", "-0.00000000000000379487994", Less), + // 011 100 different scale + ("388166715906.19371912596291584", "0.000000000000001996700736", Greater), + ("-388166715906.19371912596291584", "0.000000000000001996700736", Less), + ("388166715906.19371912596291584", "-0.000000000000001996700736", Greater), + ("-388166715906.19371912596291584", "-0.000000000000001996700736", Less), + // 011 010 same scale + ("272322.48219624218537039495168", "0.00000175873997328613376", Greater), + ("-272322.48219624218537039495168", "0.00000175873997328613376", Less), + ("272322.48219624218537039495168", "-0.00000175873997328613376", Greater), + ("-272322.48219624218537039495168", "-0.00000175873997328613376", Less), + // 011 010 different scale + ( + "17963864.0946434527121637376", + "0.0000000001112699367808040960", + Greater, + ), + ("-17963864.0946434527121637376", "0.0000000001112699367808040960", Less), + ( + "17963864.0946434527121637376", + "-0.0000000001112699367808040960", + Greater, + ), + ("-17963864.0946434527121637376", "-0.0000000001112699367808040960", Less), + // 011 110 same scale + ("272322.48219624218537039495168", "0.00009168252278596474115", Greater), + ("-272322.48219624218537039495168", "0.00009168252278596474115", Less), + ("272322.48219624218537039495168", "-0.00009168252278596474115", Greater), + ("-272322.48219624218537039495168", "-0.00009168252278596474115", Less), + // 011 110 different scale + ("3.1530040332824324172729548800", "0.05636139104712652784", Greater), + ("-3.1530040332824324172729548800", "0.05636139104712652784", Less), + ("3.1530040332824324172729548800", "-0.05636139104712652784", Greater), + ("-3.1530040332824324172729548800", "-0.05636139104712652784", Less), + // 011 001 same scale + ( + "272322.48219624218537039495168", + "78956.15667671475190631497728", + Greater, + ), + ("-272322.48219624218537039495168", "78956.15667671475190631497728", Less), + ( + "272322.48219624218537039495168", + "-78956.15667671475190631497728", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-78956.15667671475190631497728", + Less, + ), + // 011 001 different scale + ( + "247159638929.90774677497446400", + "18573048.37697991870462820352", + Greater, + ), + ("-247159638929.90774677497446400", "18573048.37697991870462820352", Less), + ( + "247159638929.90774677497446400", + "-18573048.37697991870462820352", + Greater, + ), + ( + "-247159638929.90774677497446400", + "-18573048.37697991870462820352", + Less, + ), + // 011 101 same scale + ("272322.48219624218537039495168", "311953.28803357654172947915942", Less), + ( + "-272322.48219624218537039495168", + "311953.28803357654172947915942", + Less, + ), + ( + "272322.48219624218537039495168", + "-311953.28803357654172947915942", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-311953.28803357654172947915942", + Greater, + ), + // 011 101 different scale + ("2922696937234119470.0273745920", "22441101906503785827606686629", Less), + ("-2922696937234119470.0273745920", "22441101906503785827606686629", Less), + ( + "2922696937234119470.0273745920", + "-22441101906503785827606686629", + Greater, + ), + ( + "-2922696937234119470.0273745920", + "-22441101906503785827606686629", + Greater, + ), + // 011 011 same scale + ("272322.48219624218537039495168", "348316.28306497394164006649856", Less), + ( + "-272322.48219624218537039495168", + "348316.28306497394164006649856", + Less, + ), + ( + "272322.48219624218537039495168", + "-348316.28306497394164006649856", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-348316.28306497394164006649856", + Greater, + ), + // 011 011 different scale + ("178190346.76624086261395619840", "62208030746.22038852927225856", Less), + ("-178190346.76624086261395619840", "62208030746.22038852927225856", Less), + ( + "178190346.76624086261395619840", + "-62208030746.22038852927225856", + Greater, + ), + ( + "-178190346.76624086261395619840", + "-62208030746.22038852927225856", + Greater, + ), + // 011 111 same scale + ( + "272322.48219624218537039495168", + "41534.52021391898039335355927", + Greater, + ), + ("-272322.48219624218537039495168", "41534.52021391898039335355927", Less), + ( + "272322.48219624218537039495168", + "-41534.52021391898039335355927", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-41534.52021391898039335355927", + Less, + ), + // 011 111 different scale + ("11.959910518519677083499626496", "2844684364802261541879551.2259", Less), + ( + "-11.959910518519677083499626496", + "2844684364802261541879551.2259", + Less, + ), + ( + "11.959910518519677083499626496", + "-2844684364802261541879551.2259", + Greater, + ), + ( + "-11.959910518519677083499626496", + "-2844684364802261541879551.2259", + Greater, + ), + // 111 equality + ( + "3836286746260530032892706.6174", + "3836286746260530032892706.6174", + Equal, + ), + // 111 000 same scale + ("3836286746260530032892706.6174", "0.0000", Greater), + ("-3836286746260530032892706.6174", "0.0000", Less), + ("3836286746260530032892706.6174", "-0.0000", Greater), + ("-3836286746260530032892706.6174", "-0.0000", Less), + // 111 000 different scale + ("4401861854803552.033657814547", "0.0000000", Greater), + ("-4401861854803552.033657814547", "0.0000000", Less), + ("4401861854803552.033657814547", "-0.0000000", Greater), + ("-4401861854803552.033657814547", "-0.0000000", Less), + // 111 100 same scale + ("3836286746260530032892706.6174", "68758.6561", Greater), + ("-3836286746260530032892706.6174", "68758.6561", Less), + ("3836286746260530032892706.6174", "-68758.6561", Greater), + ("-3836286746260530032892706.6174", "-68758.6561", Less), + // 111 100 different scale + ( + "18794337354296131695536777.153", + "0.000000000000000001563875977", + Greater, + ), + ("-18794337354296131695536777.153", "0.000000000000000001563875977", Less), + ( + "18794337354296131695536777.153", + "-0.000000000000000001563875977", + Greater, + ), + ( + "-18794337354296131695536777.153", + "-0.000000000000000001563875977", + Less, + ), + // 111 010 same scale + ("3836286746260530032892706.6174", "439097665563236.7616", Greater), + ("-3836286746260530032892706.6174", "439097665563236.7616", Less), + ("3836286746260530032892706.6174", "-439097665563236.7616", Greater), + ("-3836286746260530032892706.6174", "-439097665563236.7616", Less), + // 111 010 different scale + ("219364497389.57405761662363679", "0.6569644274462228480", Greater), + ("-219364497389.57405761662363679", "0.6569644274462228480", Less), + ("219364497389.57405761662363679", "-0.6569644274462228480", Greater), + ("-219364497389.57405761662363679", "-0.6569644274462228480", Less), + // 111 110 same scale + ("3836286746260530032892706.6174", "100013274294974.8269", Greater), + ("-3836286746260530032892706.6174", "100013274294974.8269", Less), + ("3836286746260530032892706.6174", "-100013274294974.8269", Greater), + ("-3836286746260530032892706.6174", "-100013274294974.8269", Less), + // 111 110 different scale + ("76072704083682.85472479207171", "50.52437989651117182", Greater), + ("-76072704083682.85472479207171", "50.52437989651117182", Less), + ("76072704083682.85472479207171", "-50.52437989651117182", Greater), + ("-76072704083682.85472479207171", "-50.52437989651117182", Less), + // 111 001 same scale + ( + "3836286746260530032892706.6174", + "2766133872545894272402142.0032", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "2766133872545894272402142.0032", + Less, + ), + ( + "3836286746260530032892706.6174", + "-2766133872545894272402142.0032", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-2766133872545894272402142.0032", + Less, + ), + // 111 001 different scale + ( + "38199979438250010.80610984395", + "31104752430710.408848162684928", + Greater, + ), + ("-38199979438250010.80610984395", "31104752430710.408848162684928", Less), + ( + "38199979438250010.80610984395", + "-31104752430710.408848162684928", + Greater, + ), + ( + "-38199979438250010.80610984395", + "-31104752430710.408848162684928", + Less, + ), + // 111 101 same scale + ( + "3836286746260530032892706.6174", + "441847458119168110406908.6115", + Greater, + ), + ("-3836286746260530032892706.6174", "441847458119168110406908.6115", Less), + ( + "3836286746260530032892706.6174", + "-441847458119168110406908.6115", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-441847458119168110406908.6115", + Less, + ), + // 111 101 different scale + ("255945012905633524.15746865235", "1005021647855597114428453997.8", Less), + ( + "-255945012905633524.15746865235", + "1005021647855597114428453997.8", + Less, + ), + ( + "255945012905633524.15746865235", + "-1005021647855597114428453997.8", + Greater, + ), + ( + "-255945012905633524.15746865235", + "-1005021647855597114428453997.8", + Greater, + ), + // 111 011 same scale + ( + "3836286746260530032892706.6174", + "1111481055212557787730018.3040", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "1111481055212557787730018.3040", + Less, + ), + ( + "3836286746260530032892706.6174", + "-1111481055212557787730018.3040", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-1111481055212557787730018.3040", + Less, + ), + // 111 011 different scale + ( + "79710135995301690627798250.27", + "4613684285077.479267304996864", + Greater, + ), + ("-79710135995301690627798250.27", "4613684285077.479267304996864", Less), + ( + "79710135995301690627798250.27", + "-4613684285077.479267304996864", + Greater, + ), + ("-79710135995301690627798250.27", "-4613684285077.479267304996864", Less), + // 111 111 same scale + ( + "3836286746260530032892706.6174", + "1881105048659612897896770.8539", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "1881105048659612897896770.8539", + Less, + ), + ( + "3836286746260530032892706.6174", + "-1881105048659612897896770.8539", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-1881105048659612897896770.8539", + Less, + ), + // 111 111 different scale + ( + "3879592276836332218003.2886500", + "35612499407667292.686490959658", + Greater, + ), + ( + "-3879592276836332218003.2886500", + "35612499407667292.686490959658", + Less, + ), + ( + "3879592276836332218003.2886500", + "-35612499407667292.686490959658", + Greater, + ), + ( + "-3879592276836332218003.2886500", + "-35612499407667292.686490959658", + Less, + ), + ]; + for &(a, b, c) in tests { + cmp(a, b, c); + } +} + +#[test] +fn it_floors_decimals() { + let tests = &[ + ("1", "1"), + ("1.00", "1"), + ("1.2345", "1"), + ("-1", "-1"), + ("-1.00", "-1"), + ("-1.2345", "-2"), + ]; + for &(a, expected) in tests { + let a = Decimal::from_str(a).unwrap(); + assert_eq!(expected, a.floor().to_string(), "Failed flooring {}", a); + } +} + +#[test] +fn it_ceils_decimals() { + let tests = &[ + ("1", "1"), + ("1.00", "1"), + ("1.2345", "2"), + ("-1", "-1"), + ("-1.00", "-1"), + ("-1.2345", "-1"), + ]; + for &(a, expected) in tests { + let a = Decimal::from_str(a).unwrap(); + assert_eq!(expected, a.ceil().to_string(), "Failed ceiling {}", a); + } +} + +#[test] +fn it_finds_max_of_two() { + let tests = &[("1", "1", "1"), ("2", "1", "2"), ("1", "2", "2")]; + for &(a, b, expected) in tests { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + assert_eq!(expected, a.max(b).to_string()); + } +} + +#[test] +fn it_finds_min_of_two() { + let tests = &[("1", "1", "1"), ("2", "1", "1"), ("1", "2", "1")]; + for &(a, b, expected) in tests { + let a = Decimal::from_str(a).unwrap(); + let b = Decimal::from_str(b).unwrap(); + assert_eq!(expected, a.min(b).to_string()); + } +} + +#[test] +fn test_max_compares() { + let x = "225.33543601344182".parse::<Decimal>().unwrap(); + let y = Decimal::MAX; + assert!(x < y); + assert!(y > x); + assert_ne!(y, x); +} + +#[test] +fn test_min_compares() { + let x = "225.33543601344182".parse::<Decimal>().unwrap(); + let y = Decimal::MIN; + assert!(x > y); + assert!(y < x); + assert_ne!(y, x); +} + +#[test] +fn it_can_parse_from_i32() { + use num_traits::FromPrimitive; + + let tests = &[ + (0i32, "0"), + (1i32, "1"), + (-1i32, "-1"), + (i32::MAX, "2147483647"), + (i32::MIN, "-2147483648"), + ]; + for &(input, expected) in tests { + let parsed = Decimal::from_i32(input).unwrap(); + assert_eq!( + expected, + parsed.to_string(), + "expected {} does not match parsed {}", + expected, + parsed + ); + assert_eq!( + input.to_string(), + parsed.to_string(), + "i32 to_string {} does not match parsed {}", + input, + parsed + ); + } +} + +#[test] +fn it_can_parse_from_i64() { + use num_traits::FromPrimitive; + + let tests = &[ + (0i64, "0"), + (1i64, "1"), + (-1i64, "-1"), + (i64::MAX, "9223372036854775807"), + (i64::MIN, "-9223372036854775808"), + ]; + for &(input, expected) in tests { + let parsed = Decimal::from_i64(input).unwrap(); + assert_eq!( + expected, + parsed.to_string(), + "expected {} does not match parsed {}", + expected, + parsed + ); + assert_eq!( + input.to_string(), + parsed.to_string(), + "i64 to_string {} does not match parsed {}", + input, + parsed + ); + } +} + +#[test] +fn it_can_round_to_2dp() { + let a = Decimal::from_str("6.12345").unwrap(); + let b = (Decimal::from_str("100").unwrap() * a).round() / Decimal::from_str("100").unwrap(); + assert_eq!("6.12", b.to_string()); +} + +#[test] +fn it_can_round_using_basic_midpoint_rules() { + let tests = &[ + ("3.5", RoundingStrategy::MidpointAwayFromZero, "4"), + ("2.8", RoundingStrategy::MidpointAwayFromZero, "3"), + ("2.5", RoundingStrategy::MidpointAwayFromZero, "3"), + ("2.1", RoundingStrategy::MidpointAwayFromZero, "2"), + ("-2.1", RoundingStrategy::MidpointAwayFromZero, "-2"), + ("-2.5", RoundingStrategy::MidpointAwayFromZero, "-3"), + ("-2.8", RoundingStrategy::MidpointAwayFromZero, "-3"), + ("-3.5", RoundingStrategy::MidpointAwayFromZero, "-4"), + ("3.5", RoundingStrategy::MidpointNearestEven, "4"), + ("2.8", RoundingStrategy::MidpointNearestEven, "3"), + ("2.5", RoundingStrategy::MidpointNearestEven, "2"), + ("2.1", RoundingStrategy::MidpointNearestEven, "2"), + ("-2.1", RoundingStrategy::MidpointNearestEven, "-2"), + ("-2.5", RoundingStrategy::MidpointNearestEven, "-2"), + ("-2.8", RoundingStrategy::MidpointNearestEven, "-3"), + ("-3.5", RoundingStrategy::MidpointNearestEven, "-4"), + ("3.5", RoundingStrategy::MidpointTowardZero, "3"), + ("2.8", RoundingStrategy::MidpointTowardZero, "3"), + ("2.5", RoundingStrategy::MidpointTowardZero, "2"), + ("2.1", RoundingStrategy::MidpointTowardZero, "2"), + ("-2.1", RoundingStrategy::MidpointTowardZero, "-2"), + ("-2.5", RoundingStrategy::MidpointTowardZero, "-2"), + ("-2.8", RoundingStrategy::MidpointTowardZero, "-3"), + ("-3.5", RoundingStrategy::MidpointTowardZero, "-3"), + ("2.8", RoundingStrategy::ToNegativeInfinity, "2"), + ("2.5", RoundingStrategy::ToNegativeInfinity, "2"), + ("2.1", RoundingStrategy::ToNegativeInfinity, "2"), + ("-2.1", RoundingStrategy::ToNegativeInfinity, "-3"), + ("-2.5", RoundingStrategy::ToNegativeInfinity, "-3"), + ("-2.8", RoundingStrategy::ToNegativeInfinity, "-3"), + ("2.8", RoundingStrategy::ToPositiveInfinity, "3"), + ("2.5", RoundingStrategy::ToPositiveInfinity, "3"), + ("2.1", RoundingStrategy::ToPositiveInfinity, "3"), + ("-2.1", RoundingStrategy::ToPositiveInfinity, "-2"), + ("-2.5", RoundingStrategy::ToPositiveInfinity, "-2"), + ("-2.8", RoundingStrategy::ToPositiveInfinity, "-2"), + ("2.8", RoundingStrategy::ToZero, "2"), + ("2.5", RoundingStrategy::ToZero, "2"), + ("2.1", RoundingStrategy::ToZero, "2"), + ("-2.1", RoundingStrategy::ToZero, "-2"), + ("-2.5", RoundingStrategy::ToZero, "-2"), + ("-2.8", RoundingStrategy::ToZero, "-2"), + ("2.8", RoundingStrategy::AwayFromZero, "3"), + ("2.5", RoundingStrategy::AwayFromZero, "3"), + ("2.1", RoundingStrategy::AwayFromZero, "3"), + ("-2.1", RoundingStrategy::AwayFromZero, "-3"), + ("-2.5", RoundingStrategy::AwayFromZero, "-3"), + ("-2.8", RoundingStrategy::AwayFromZero, "-3"), + ]; + + for &(input, strategy, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + let b = a.round_dp_with_strategy(0, strategy); + assert_eq!(expected, b.to_string(), "{} > {} for {:?}", input, expected, strategy); + } +} + +#[test] +fn it_can_round_using_bankers_rounding() { + let tests = &[ + ("6.12345", 2, "6.12"), + ("6.126", 2, "6.13"), + ("-6.126", 2, "-6.13"), + ("6.5", 0, "6"), + ("7.5", 0, "8"), + ("1.2250", 2, "1.22"), + ("1.2252", 2, "1.23"), + ("1.2249", 2, "1.22"), + ("6.1", 2, "6.1"), + ("0.0000", 2, "0.00"), + ("0.6666666666666666666666666666", 2, "0.67"), + ("1.40", 0, "1"), + ("2.60", 0, "3"), + ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), + ]; + for &(input, dp, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + #[allow(deprecated)] + let b = a.round_dp_with_strategy(dp, RoundingStrategy::BankersRounding); + assert_eq!(expected, b.to_string(), "BankersRounding"); + + // Recommended replacement + let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointNearestEven); + assert_eq!(expected, b.to_string(), "MidpointNearestEven"); + } +} + +#[test] +fn it_can_round_complex_numbers_using_bankers_rounding() { + // Issue #71 + let rate = Decimal::new(19, 2); // 0.19 + let one = Decimal::new(1, 0); // 1 + let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 + + #[allow(deprecated)] + let part = part.round_dp_with_strategy(2, RoundingStrategy::BankersRounding); // 0.16 + assert_eq!("0.16", part.to_string(), "BankersRounding"); + + // Recommended replacement + let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointNearestEven); // 0.16 + assert_eq!("0.16", part.to_string(), "MidpointNearestEven"); +} + +#[test] +fn it_can_round_using_round_half_up() { + let tests = &[ + ("0", 0, "0"), + ("1.234", 3, "1.234"), + ("1.12", 5, "1.12"), + ("6.34567", 2, "6.35"), + ("6.5", 0, "7"), + ("12.49", 0, "12"), + ("0.6666666666666666666666666666", 2, "0.67"), + ("1.40", 0, "1"), + ("2.60", 0, "3"), + ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), + ]; + for &(input, dp, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + #[allow(deprecated)] + let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundHalfUp); + assert_eq!(expected, b.to_string(), "RoundHalfUp"); + + // Recommended replacement + let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointAwayFromZero); + assert_eq!(expected, b.to_string(), "MidpointAwayFromZero"); + } +} + +#[test] +fn it_can_round_complex_numbers_using_round_half_up() { + // Issue #71 + let rate = Decimal::new(19, 2); // 0.19 + let one = Decimal::new(1, 0); // 1 + let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 + #[allow(deprecated)] + let part = part.round_dp_with_strategy(2, RoundingStrategy::RoundHalfUp); // 0.16 + assert_eq!("0.16", part.to_string(), "RoundHalfUp"); + + // Recommended replacement + let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointAwayFromZero); // 0.16 + assert_eq!("0.16", part.to_string(), "MidpointAwayFromZero"); +} + +#[test] +fn it_can_round_using_round_half_down() { + let tests = &[ + ("0", 0, "0"), + ("1.234", 3, "1.234"), + ("1.12", 5, "1.12"), + ("6.34567", 2, "6.35"), + ("6.51", 0, "7"), + ("12.5", 0, "12"), + ("0.6666666666666666666666666666", 2, "0.67"), + ("1.40", 0, "1"), + ("2.60", 0, "3"), + ("2.1234567890123456789012345678", 27, "2.123456789012345678901234568"), + ]; + for &(input, dp, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + #[allow(deprecated)] + let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundHalfDown); + assert_eq!(expected, b.to_string(), "RoundHalfDown"); + + // Recommended replacement + let b = a.round_dp_with_strategy(dp, RoundingStrategy::MidpointTowardZero); + assert_eq!(expected, b.to_string(), "MidpointTowardZero"); + } +} + +#[test] +fn it_can_round_complex_numbers_using_round_half_down() { + // Issue #71 + let rate = Decimal::new(19, 2); // 0.19 + let one = Decimal::new(1, 0); // 1 + let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 + + #[allow(deprecated)] + let part = part.round_dp_with_strategy(2, RoundingStrategy::RoundHalfDown); // 0.16 + assert_eq!("0.16", part.to_string(), "RoundHalfDown"); + + // Recommended replacement + let part = part.round_dp_with_strategy(2, RoundingStrategy::MidpointTowardZero); // 0.16 + assert_eq!("0.16", part.to_string(), "RoundHalfDown"); +} + +#[test] +fn it_can_round_to_2dp_using_explicit_function() { + let a = Decimal::from_str("6.12345").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("6.12", b.to_string()); +} + +#[test] +fn it_can_round_up_to_2dp_using_explicit_function() { + let a = Decimal::from_str("6.126").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("6.13", b.to_string()); +} + +#[test] +fn it_can_round_down_to_2dp_using_explicit_function() { + let a = Decimal::from_str("-6.126").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("-6.13", b.to_string()); +} + +#[test] +fn it_can_round_down_using_bankers_rounding() { + let a = Decimal::from_str("6.5").unwrap(); + let b = a.round_dp(0u32); + assert_eq!("6", b.to_string()); +} + +#[test] +fn it_can_round_up_using_bankers_rounding() { + let a = Decimal::from_str("7.5").unwrap(); + let b = a.round_dp(0u32); + assert_eq!("8", b.to_string()); +} + +#[test] +fn it_can_round_correctly_using_bankers_rounding_1() { + let a = Decimal::from_str("1.2250").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("1.22", b.to_string()); +} + +#[test] +fn it_can_round_correctly_using_bankers_rounding_2() { + let a = Decimal::from_str("1.2251").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("1.23", b.to_string()); +} + +#[test] +fn it_can_round_down_when_required() { + let a = Decimal::from_str("1.2249").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("1.22", b.to_string()); +} + +#[test] +fn it_can_round_to_2dp_using_explicit_function_without_changing_value() { + let a = Decimal::from_str("6.1").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("6.1", b.to_string()); +} + +#[test] +fn it_can_round_zero() { + let a = Decimal::from_str("0.0000").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("0.00", b.to_string()); +} + +#[test] +fn it_can_round_large_decimals() { + let a = Decimal::from_str("0.6666666666666666666666666666").unwrap(); + let b = a.round_dp(2u32); + assert_eq!("0.67", b.to_string()); +} + +#[test] +fn it_can_round_simple_numbers_down() { + let a = Decimal::from_str("1.40").unwrap(); + let b = a.round_dp(0u32); + assert_eq!("1", b.to_string()); +} + +#[test] +fn it_can_round_simple_numbers_up() { + let a = Decimal::from_str("2.60").unwrap(); + let b = a.round_dp(0u32); + assert_eq!("3", b.to_string()); +} + +#[test] +fn it_can_round_simple_numbers_with_high_precision() { + let a = Decimal::from_str("2.1234567890123456789012345678").unwrap(); + let b = a.round_dp(27u32); + assert_eq!("2.123456789012345678901234568", b.to_string()); +} + +#[test] +fn it_can_round_complex_numbers() { + // Issue #71 + let rate = Decimal::new(19, 2); // 0.19 + let one = Decimal::new(1, 0); // 1 + let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 + let part = part.round_dp(2); // 0.16 + assert_eq!("0.16", part.to_string()); +} + +#[test] +fn it_can_round_down() { + let tests = &[ + ("0.470", 1, "0.4"), + ("-0.470", 1, "-0.4"), // Toward zero + ("0.400", 1, "0.4"), + ("-0.400", 1, "-0.4"), + ]; + for &(input, dp, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + #[allow(deprecated)] + let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundDown); + assert_eq!(expected, b.to_string(), "RoundDown"); + + // Recommended replacement + let b = a.round_dp_with_strategy(dp, RoundingStrategy::ToZero); + assert_eq!(expected, b.to_string(), "ToZero"); + } +} + +#[test] +fn it_can_round_up() { + let tests = &[ + ("2.8", 0, "3"), + ("2.5", 0, "3"), + ("2.1", 0, "3"), + ("-2.1", 0, "-3"), + ("-2.5", 0, "-3"), + ("-2.8", 0, "-3"), + ("0.320", 1, "0.4"), + ("-0.320", 1, "-0.4"), + ("0.300", 1, "0.3"), + ("-0.300", 1, "-0.3"), + ]; + + for &(input, dp, expected) in tests { + let a = Decimal::from_str(input).unwrap(); + #[allow(deprecated)] + let b = a.round_dp_with_strategy(dp, RoundingStrategy::RoundUp); + assert_eq!(expected, b.to_string(), "RoundUp"); + + // Recommended replacement + let b = a.round_dp_with_strategy(dp, RoundingStrategy::AwayFromZero); + assert_eq!(expected, b.to_string(), "AwayFromZero"); + } +} + +#[test] +fn it_can_round_significant_figures() { + let tests = &[ + ("305.459", 0u32, Some("0")), + ("305.459", 1, Some("300")), + ("305.459", 2, Some("310")), + ("305.459", 3, Some("305")), + ("305.459", 4, Some("305.5")), + ("305.459", 5, Some("305.46")), + ("305.459", 6, Some("305.459")), + ("305.459", 7, Some("305.4590")), + ("305.459", 10, Some("305.4590000")), + ("-305.459", 3, Some("-305")), + ("-305.459", 2, Some("-310")), // We ignore the negative + ("-305.459", 5, Some("-305.46")), + ( + "79228162514264337593543950335", + 29, + Some("79228162514264337593543950335"), + ), + ("79228162514264337593543950335", 1, None), + ( + "79228162514264337593543950335", + 2, + Some("79000000000000000000000000000"), + ), + ( + "79228162514264337593543950335", + 30, + Some("79228162514264337593543950335"), + ), + ( + "79228162514264337593543950335", + u32::MAX, + Some("79228162514264337593543950335"), + ), + ]; + for &(input, sf, expected) in tests { + let input = Decimal::from_str(input).unwrap(); + let result = input.round_sf(sf); + if let Some(expected) = expected { + assert!(result.is_some(), "Expected result for {}.round_sf({})", input, sf); + assert_eq!(expected, result.unwrap().to_string(), "{}.round_sf({})", input, sf); + } else { + assert!(result.is_none(), "Unexpected result for {}.round_sf({})", input, sf); + } + } +} + +#[test] +fn it_can_round_significant_figures_with_strategy() { + let tests = &[ + ("12301", 3u32, RoundingStrategy::AwayFromZero, Some("12400")), + ("123.01", 3u32, RoundingStrategy::AwayFromZero, Some("124")), + ("1.2301", 3u32, RoundingStrategy::AwayFromZero, Some("1.24")), + ("0.12301", 3u32, RoundingStrategy::AwayFromZero, Some("0.124")), + ("0.012301", 3u32, RoundingStrategy::AwayFromZero, Some("0.0124")), + ("0.0000012301", 3u32, RoundingStrategy::AwayFromZero, Some("0.00000124")), + ("1.012301", 3u32, RoundingStrategy::AwayFromZero, Some("1.02")), + ]; + for &(input, sf, strategy, expected) in tests { + let input = Decimal::from_str(input).unwrap(); + let result = input.round_sf_with_strategy(sf, strategy); + if let Some(expected) = expected { + assert!( + result.is_some(), + "Expected result for {}.round_sf_with_strategy({}, {:?})", + input, + sf, + strategy + ); + assert_eq!( + expected, + result.unwrap().to_string(), + "{}.round_sf_with_strategy({}, {:?})", + input, + sf, + strategy + ); + } else { + assert!( + result.is_none(), + "Unexpected result for {}.round_sf_with_strategy({}, {:?})", + input, + sf, + strategy + ); + } + } +} + +#[test] +fn it_can_trunc() { + let tests = &[("1.00000000000000000000", "1"), ("1.000000000000000000000001", "1")]; + + for &(value, expected) in tests { + let value = Decimal::from_str(value).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + let trunc = value.trunc(); + assert_eq!(expected.to_string(), trunc.to_string()); + } +} + +#[test] +fn it_can_fract() { + let tests = &[ + ("1.00000000000000000000", "0.00000000000000000000"), + ("1.000000000000000000000001", "0.000000000000000000000001"), + ]; + + for &(value, expected) in tests { + let value = Decimal::from_str(value).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + let fract = value.fract(); + assert_eq!(expected.to_string(), fract.to_string()); + } +} + +#[test] +fn it_can_normalize() { + let tests = &[ + ("1.00000000000000000000", "1"), + ("1.10000000000000000000000", "1.1"), + ("1.00010000000000000000000", "1.0001"), + ("1", "1"), + ("1.1", "1.1"), + ("1.0001", "1.0001"), + ("-0", "0"), + ("-0.0", "0"), + ("-0.010", "-0.01"), + ("0.0", "0"), + ]; + + for &(value, expected) in tests { + let value = Decimal::from_str(value).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + let normalized = value.normalize(); + assert_eq!(expected.to_string(), normalized.to_string()); + } +} + +#[test] +fn it_can_return_the_max_value() { + assert_eq!("79228162514264337593543950335", Decimal::MAX.to_string()); +} + +#[test] +fn it_can_return_the_min_value() { + assert_eq!("-79228162514264337593543950335", Decimal::MIN.to_string()); +} + +#[test] +fn it_can_go_from_and_into() { + let d = Decimal::from_str("5").unwrap(); + let di8: Decimal = 5u8.into(); + let di32: Decimal = 5i32.into(); + let disize: Decimal = 5isize.into(); + let di64: Decimal = 5i64.into(); + let du8: Decimal = 5u8.into(); + let du32: Decimal = 5u32.into(); + let dusize: Decimal = 5usize.into(); + let du64: Decimal = 5u64.into(); + + assert_eq!(d, di8); + assert_eq!(di8, di32); + assert_eq!(di32, disize); + assert_eq!(disize, di64); + assert_eq!(di64, du8); + assert_eq!(du8, du32); + assert_eq!(du32, dusize); + assert_eq!(dusize, du64); +} + +#[test] +fn it_converts_to_f64() { + let tests = &[ + ("5", Some(5f64)), + ("-5", Some(-5f64)), + ("0.1", Some(0.1f64)), + ("0.0", Some(0f64)), + ("-0.0", Some(0f64)), + ("0.0000000000025", Some(0.25e-11f64)), + ("1000000.0000000000025", Some(1e6f64)), + ("0.000000000000000000000000025", Some(0.25e-25_f64)), + ( + "2.1234567890123456789012345678", + Some(2.1234567890123456789012345678_f64), + ), + ("21234567890123456789012345678", Some(21234567890123458000000000000_f64)), + ( + "-21234567890123456789012345678", + Some(-21234567890123458000000000000_f64), + ), + ("1.59283191", Some(1.59283191_f64)), + ("2.2238", Some(2.2238_f64)), + ("2.2238123", Some(2.2238123_f64)), + ("22238", Some(22238_f64)), + ]; + for &(value, expected) in tests { + let value = Decimal::from_str(value).unwrap().to_f64(); + assert_eq!(expected, value); + } +} + +#[test] +fn it_converts_to_f64_try() { + let tests = &[ + ("5", Some(5f64)), + ("-5", Some(-5f64)), + ("0.1", Some(0.1f64)), + ("0.0", Some(0f64)), + ("-0.0", Some(0f64)), + ("0.0000000000025", Some(0.25e-11f64)), + ("1000000.0000000000025", Some(1e6f64)), + ("0.000000000000000000000000025", Some(0.25e-25_f64)), + ( + "2.1234567890123456789012345678", + Some(2.1234567890123456789012345678_f64), + ), + ("21234567890123456789012345678", Some(21234567890123458000000000000_f64)), + ( + "-21234567890123456789012345678", + Some(-21234567890123458000000000000_f64), + ), + ("1.59283191", Some(1.59283191_f64)), + ]; + for &(value, expected) in tests { + let value = Decimal::from_str(value).unwrap().try_into().ok(); + assert_eq!(expected, value); + } +} + +#[test] +fn it_converts_to_i64() { + let tests = [ + ("5", Some(5_i64)), + ("-5", Some(-5_i64)), + ("5.12345", Some(5_i64)), + ("-5.12345", Some(-5_i64)), + ("-9223372036854775808", Some(-9223372036854775808_i64)), + ("-9223372036854775808", Some(i64::MIN)), + ("9223372036854775807", Some(9223372036854775807_i64)), + ("9223372036854775807", Some(i64::MAX)), + ("-9223372036854775809", None), // i64::MIN - 1 + ("9223372036854775808", None), // i64::MAX + 1 + // Clear overflows in hi bit + ("-92233720368547758089", None), + ("92233720368547758088", None), + ]; + for (input, expected) in tests { + let input = Decimal::from_str(input).unwrap(); + let actual = input.to_i64(); + assert_eq!(expected, actual, "Input: {}", input); + } +} + +#[test] +fn it_converts_to_u64() { + assert_eq!(5u64, Decimal::from_str("5").unwrap().to_u64().unwrap()); + assert_eq!(None, Decimal::from_str("-5").unwrap().to_u64()); + assert_eq!(5u64, Decimal::from_str("5.12345").unwrap().to_u64().unwrap()); + assert_eq!( + 0xFFFF_FFFF_FFFF_FFFF, + Decimal::from_str("18446744073709551615").unwrap().to_u64().unwrap() + ); + assert_eq!(None, Decimal::from_str("18446744073709551616").unwrap().to_u64()); +} + +#[test] +fn it_converts_to_i128() { + let tests = &[ + ("5", Some(5i128)), + ("-5", Some(-5i128)), + ("5.12345", Some(5i128)), + ("-5.12345", Some(-5i128)), + ("9223372036854775807", Some(0x7FFF_FFFF_FFFF_FFFF)), + ("92233720368547758089", Some(92233720368547758089i128)), + ]; + for (dec, expected) in tests { + assert_eq!(Decimal::from_str(dec).unwrap().to_i128(), *expected); + } + + assert_eq!( + 79_228_162_514_264_337_593_543_950_335_i128, + Decimal::MAX.to_i128().unwrap() + ); +} + +#[test] +fn it_converts_to_u128() { + let tests = &[ + ("5", Some(5u128)), + ("-5", None), + ("5.12345", Some(5u128)), + ("-5.12345", None), + ("18446744073709551615", Some(0xFFFF_FFFF_FFFF_FFFF)), + ("18446744073709551616", Some(18446744073709551616u128)), + ]; + for (dec, expected) in tests { + assert_eq!(Decimal::from_str(dec).unwrap().to_u128(), *expected); + } + assert_eq!( + 79_228_162_514_264_337_593_543_950_335_u128, + Decimal::MAX.to_u128().unwrap() + ); +} + +#[test] +fn it_converts_from_i128() { + let tests: &[(i128, Option<&str>)] = &[ + (5, Some("5")), + (-5, Some("-5")), + (0x7FFF_FFFF_FFFF_FFFF, Some("9223372036854775807")), + (92233720368547758089, Some("92233720368547758089")), + (0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF, Some("79228162514264337593543950335")), + (0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, None), + ]; + for (value, expected) in tests { + if let Some(expected_value) = expected { + let decimal = Decimal::from_str(expected_value).unwrap(); + assert_eq!(num_traits::FromPrimitive::from_i128(*value), Some(decimal)); + } + } +} + +#[test] +fn it_converts_from_u128() { + let tests: &[(u128, Option<&str>)] = &[ + (5, Some("5")), + (0xFFFF_FFFF_FFFF_FFFF, Some("18446744073709551615")), + (0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF, Some("79228162514264337593543950335")), + (0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, None), + ]; + for (value, expected) in tests { + if let Some(expected_value) = expected { + let decimal = Decimal::from_str(expected_value).unwrap(); + assert_eq!(num_traits::FromPrimitive::from_u128(*value), Some(decimal)); + } + } +} + +#[test] +fn it_converts_from_str() { + assert_eq!(Decimal::try_from("1").unwrap(), Decimal::ONE); + assert_eq!(Decimal::try_from("10").unwrap(), Decimal::TEN); +} + +#[test] +fn it_converts_from_f32() { + use num_traits::FromPrimitive; + + let tests = [ + (0.1_f32, "0.1"), + (1_f32, "1"), + (0_f32, "0"), + (0.12345_f32, "0.12345"), + (0.1234567800123456789012345678_f32, "0.12345678"), + (0.12345678901234567890123456789_f32, "0.12345679"), + (0.00000000000000000000000000001_f32, "0"), + (5.1_f32, "5.1"), + ]; + + for &(input, expected) in &tests { + assert_eq!( + expected, + Decimal::from_f32(input).unwrap().to_string(), + "from_f32({})", + input + ); + assert_eq!( + expected, + Decimal::try_from(input).unwrap().to_string(), + "try_from({})", + input + ); + } +} + +#[test] +fn it_converts_from_f32_limits() { + use num_traits::FromPrimitive; + + assert!(Decimal::from_f32(f32::NAN).is_none(), "from_f32(f32::NAN)"); + assert!(Decimal::from_f32(f32::INFINITY).is_none(), "from_f32(f32::INFINITY)"); + assert!(Decimal::try_from(f32::NAN).is_err(), "try_from(f32::NAN)"); + assert!(Decimal::try_from(f32::INFINITY).is_err(), "try_from(f32::INFINITY)"); + + // These overflow + assert!(Decimal::from_f32(f32::MAX).is_none(), "from_f32(f32::MAX)"); + assert!(Decimal::from_f32(f32::MIN).is_none(), "from_f32(f32::MIN)"); + assert!(Decimal::try_from(f32::MAX).is_err(), "try_from(f32::MAX)"); + assert!(Decimal::try_from(f32::MIN).is_err(), "try_from(f32::MIN)"); +} + +#[test] +fn it_converts_from_f32_retaining_bits() { + let tests = [ + (0.1_f32, "0.100000001490116119384765625"), + (2_f32, "2"), + (4.000_f32, "4"), + (5.1_f32, "5.099999904632568359375"), + ]; + + for &(input, expected) in &tests { + assert_eq!( + expected, + Decimal::from_f32_retain(input).unwrap().to_string(), + "from_f32_retain({})", + input + ); + } +} + +#[test] +fn it_converts_from_f64() { + use num_traits::FromPrimitive; + + let tests = [ + (0.1_f64, "0.1"), + (1_f64, "1"), + (0_f64, "0"), + (0.12345_f64, "0.12345"), + (0.1234567890123456089012345678_f64, "0.1234567890123456"), + (0.12345678901234567890123456789_f64, "0.1234567890123457"), + (0.00000000000000000000000000001_f64, "0"), + (0.6927_f64, "0.6927"), + (0.00006927_f64, "0.00006927"), + (0.000000006927_f64, "0.000000006927"), + (5.1_f64, "5.1"), + ]; + + for &(input, expected) in &tests { + assert_eq!( + expected, + Decimal::from_f64(input).unwrap().to_string(), + "from_f64({})", + input + ); + assert_eq!( + expected, + Decimal::try_from(input).unwrap().to_string(), + "try_from({})", + input + ); + } +} + +#[test] +fn it_converts_from_f64_limits() { + use num_traits::FromPrimitive; + + assert!(Decimal::from_f64(f64::NAN).is_none(), "from_f64(f64::NAN)"); + assert!(Decimal::from_f64(f64::INFINITY).is_none(), "from_f64(f64::INFINITY)"); + assert!(Decimal::try_from(f64::NAN).is_err(), "try_from(f64::NAN)"); + assert!(Decimal::try_from(f64::INFINITY).is_err(), "try_from(f64::INFINITY)"); + + // These overflow + assert!(Decimal::from_f64(f64::MAX).is_none(), "from_f64(f64::MAX)"); + assert!(Decimal::from_f64(f64::MIN).is_none(), "from_f64(f64::MIN)"); + assert!(Decimal::try_from(f64::MAX).is_err(), "try_from(f64::MIN)"); + assert!(Decimal::try_from(f64::MIN).is_err(), "try_from(f64::MAX)"); +} + +#[test] +fn it_converts_from_f64_retaining_bits() { + let tests = [ + (0.1_f64, "0.1000000000000000055511151231"), + (2_f64, "2"), + (4.000_f64, "4"), + (5.1_f64, "5.0999999999999996447286321175"), + ]; + + for &(input, expected) in &tests { + assert_eq!( + expected, + Decimal::from_f64_retain(input).unwrap().to_string(), + "from_f64_retain({})", + input + ); + } +} + +#[test] +fn it_converts_to_integers() { + assert_eq!(i64::try_from(Decimal::ONE), Ok(1)); + assert_eq!(i64::try_from(Decimal::MAX), Err(Error::ConversionTo("i64".to_string()))); + assert_eq!(u128::try_from(Decimal::ONE_HUNDRED), Ok(100)); +} + +#[test] +fn it_handles_simple_underflow() { + // Issue #71 + let rate = Decimal::new(19, 2); // 0.19 + let one = Decimal::new(1, 0); // 1 + let part = rate / (rate + one); // 0.19 / (0.19 + 1) = 0.1596638655462184873949579832 + let result = one * part; + assert_eq!("0.1596638655462184873949579832", result.to_string()); + + // 169 * 0.1596638655462184873949579832 = 26.983193277310924 + let result = part * Decimal::new(169, 0); + assert_eq!("26.983193277310924369747899161", result.to_string()); + let result = Decimal::new(169, 0) * part; + assert_eq!("26.983193277310924369747899161", result.to_string()); +} + +#[test] +fn it_can_parse_highly_significant_numbers() { + let tests = &[ + ("11.111111111111111111111111111", "11.111111111111111111111111111"), + ("11.11111111111111111111111111111", "11.111111111111111111111111111"), + ("11.1111111111111111111111111115", "11.111111111111111111111111112"), + ("115.111111111111111111111111111", "115.11111111111111111111111111"), + ("1115.11111111111111111111111111", "1115.1111111111111111111111111"), + ("11.1111111111111111111111111195", "11.111111111111111111111111120"), + ("99.9999999999999999999999999995", "100.00000000000000000000000000"), + ("-11.1111111111111111111111111195", "-11.111111111111111111111111120"), + ("-99.9999999999999999999999999995", "-100.00000000000000000000000000"), + ("3.1415926535897932384626433832", "3.1415926535897932384626433832"), + ( + "8808257419827262908.5944405087133154018", + "8808257419827262908.594440509", + ), + ( + "8097370036018690744.2590371109596744091", + "8097370036018690744.259037111", + ), + ( + "8097370036018690744.2590371149596744091", + "8097370036018690744.259037115", + ), + ( + "8097370036018690744.2590371159596744091", + "8097370036018690744.259037116", + ), + ("1.234567890123456789012345678949999", "1.2345678901234567890123456789"), + (".00000000000000000000000000001", "0.0000000000000000000000000000"), + (".10000000000000000000000000000", "0.1000000000000000000000000000"), + ]; + for &(value, expected) in tests { + assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); + } +} + +#[test] +fn it_can_parse_exact_highly_significant_numbers() { + use rust_decimal::Error; + + let tests = &[ + ( + "11.111111111111111111111111111", + Ok("11.111111111111111111111111111".to_string()), + ), + ("11.11111111111111111111111111111", Err(Error::Underflow)), + ("11.1111111111111111111111111115", Err(Error::Underflow)), + ("115.111111111111111111111111111", Err(Error::Underflow)), + ("1115.11111111111111111111111111", Err(Error::Underflow)), + ("11.1111111111111111111111111195", Err(Error::Underflow)), + ("99.9999999999999999999999999995", Err(Error::Underflow)), + ("-11.1111111111111111111111111195", Err(Error::Underflow)), + ("-99.9999999999999999999999999995", Err(Error::Underflow)), + ( + "3.1415926535897932384626433832", + Ok("3.1415926535897932384626433832".to_string()), + ), + ("8808257419827262908.5944405087133154018", Err(Error::Underflow)), + ("8097370036018690744.2590371109596744091", Err(Error::Underflow)), + ("8097370036018690744.2590371149596744091", Err(Error::Underflow)), + ("8097370036018690744.2590371159596744091", Err(Error::Underflow)), + ("1.234567890123456789012345678949999", Err(Error::Underflow)), + (".00000000000000000000000000001", Err(Error::Underflow)), + (".10000000000000000000000000000", Err(Error::Underflow)), + ]; + for &(value, ref expected) in tests.into_iter() { + let actual = Decimal::from_str_exact(value).map(|d| d.to_string()); + assert_eq!(*expected, actual); + } +} + +#[test] +fn it_can_parse_alternative_formats() { + let tests = &[ + ("1_000", "1000"), + ("1_000_000", "1000000"), + ("10_000_000", "10000000"), + ("100_000", "100000"), + // At the moment, we'll accept this + ("1_____________0", "10"), + ]; + for &(value, expected) in tests { + assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); + } +} + +#[test] +fn it_can_parse_fractional_numbers_with_underscore_separators() { + let a = Decimal::from_str("0.1_23_456").unwrap(); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 6); + assert_eq!("0.123456", a.to_string()); +} + +#[test] +fn it_can_parse_numbers_with_underscore_separators_before_decimal_point() { + let a = Decimal::from_str("1_234.56").unwrap(); + assert_eq!(a.is_sign_negative(), false); + assert_eq!(a.scale(), 2); + assert_eq!("1234.56", a.to_string()); +} + +#[test] +fn it_can_parse_numbers_and_round_correctly_with_underscore_separators_before_decimal_point() { + let tests = &[ + ( + "8_097_370_036_018_690_744.2590371159596744091", + "8097370036018690744.259037116", + ), + ( + "8097370036018690744.259_037_115_959_674_409_1", + "8097370036018690744.259037116", + ), + ( + "8_097_370_036_018_690_744.259_037_115_959_674_409_1", + "8097370036018690744.259037116", + ), + ]; + for &(value, expected) in tests { + assert_eq!(expected, Decimal::from_str(value).unwrap().to_string()); + } +} + +#[test] +fn it_can_reject_invalid_formats() { + let tests = &["_1", "1.0.0", "10_00.0_00.0"]; + for &value in tests { + assert!( + Decimal::from_str(value).is_err(), + "This succeeded unexpectedly: {}", + value + ); + } +} + +#[test] +fn it_can_reject_large_numbers_with_panic() { + let tests = &[ + // The maximum number supported is 79,228,162,514,264,337,593,543,950,335 + "79228162514264337593543950336", + "79228162514264337593543950337", + "79228162514264337593543950338", + "79228162514264337593543950339", + "79228162514264337593543950340", + ]; + for &value in tests { + if let Ok(out) = Decimal::from_str(value) { + panic!("Unexpectedly parsed {} into {}", value, out) + } + } +} + +#[test] +fn it_can_parse_individual_parts() { + let pi = Decimal::from_parts(1102470952, 185874565, 1703060790, false, 28); + assert_eq!(pi.to_string(), "3.1415926535897932384626433832"); +} + +#[test] +fn it_can_parse_scientific_notation() { + let tests = &[ + ("9.7e-7", "0.00000097"), + ("9e-7", "0.0000009"), + ("1.2e10", "12000000000"), + ("1.2e+10", "12000000000"), + ("12e10", "120000000000"), + ("9.7E-7", "0.00000097"), + ("1.2345E-24", "0.0000000000000000000000012345"), + ("12345E-28", "0.0000000000000000000000012345"), + ("1.2345E0", "1.2345"), + ("1E28", "10000000000000000000000000000"), + ]; + + for &(value, expected) in tests { + assert_eq!(expected, Decimal::from_scientific(value).unwrap().to_string()); + } +} + +#[test] +fn it_errors_parsing_large_scientific_notation() { + let result = Decimal::from_scientific("1.2345E-28"); + assert!(result.is_err()); + assert_eq!( + result.err(), + Some(Error::ScaleExceedsMaximumPrecision(32)) // 4 + 28 + ); + + let result = Decimal::from_scientific("12345E29"); + assert!(result.is_err()); + assert_eq!(result.err(), Some(Error::ScaleExceedsMaximumPrecision(29))); + + let result = Decimal::from_scientific("12345E28"); + assert!(result.is_err()); + assert_eq!(result.err(), Some(Error::ExceedsMaximumPossibleValue)); +} + +#[test] +fn it_can_parse_different_radix() { + let tests = &[ + // Input, Radix, Success, to_string() + ("123", 10, true, "123"), + ("123", 8, true, "83"), + ("123", 16, true, "291"), + ("abc", 10, false, ""), + ("abc", 16, true, "2748"), + ("78", 10, true, "78"), + ("78", 8, false, ""), + ("101", 2, true, "5"), + // Parse base 2 + ("1111_1111_1111_1111_1111_1111_1111_1111", 2, true, "4294967295"), + // Max supported value + ( + "1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_\ + 1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111", + 2, + true, + &Decimal::MAX.to_string(), + ), + // We limit to 28 dp + ( + "843.6500000000000000000000000000", + 10, + true, + "843.6500000000000000000000000", + ), + ]; + + for &(input, radix, success, expected) in tests { + let result = Decimal::from_str_radix(input, radix); + assert_eq!( + success, + result.is_ok(), + "Failed to parse: {} radix {}: {:?}", + input, + radix, + result.err() + ); + if result.is_ok() { + assert_eq!( + expected, + result.unwrap().to_string(), + "Original input: {} radix {}", + input, + radix + ); + } + } +} + +#[test] +fn it_can_calculate_signum() { + let tests = &[("123", 1), ("-123", -1), ("0", 0)]; + + for &(input, expected) in tests { + let input = Decimal::from_str(input).unwrap(); + assert_eq!(expected, input.signum().to_i32().unwrap(), "Input: {}", input); + } +} + +#[test] +fn it_can_calculate_abs_sub() { + let tests = &[ + ("123", "124", 0), + ("123", "123", 0), + ("123", "122", 123), + ("-123", "-124", 123), + ("-123", "-123", 0), + ("-123", "-122", 0), + ]; + + for &(input1, input2, expected) in tests { + let input1 = Decimal::from_str(input1).unwrap(); + let input2 = Decimal::from_str(input2).unwrap(); + assert_eq!( + expected, + input1.abs_sub(&input2).to_i32().unwrap(), + "Input: {} {}", + input1, + input2 + ); + } +} + +#[test] +#[should_panic(expected = "Scale exceeds the maximum precision allowed: 29 > 28")] +fn it_panics_when_scale_too_large() { + let _ = Decimal::new(1, 29); +} + +#[test] +fn test_zero_eq_negative_zero() { + let zero: Decimal = 0.into(); + + assert_eq!(zero, zero); + assert_eq!(-zero, zero); + assert_eq!(zero, -zero); +} + +#[test] +fn declarative_dec_product() { + let vs = (1..5).map(|i| i.into()).collect::<Vec<Decimal>>(); + let product: Decimal = vs.into_iter().product(); + assert_eq!(product, Decimal::from(24)) +} + +#[test] +fn declarative_ref_dec_product() { + let vs = (1..5).map(|i| i.into()).collect::<Vec<Decimal>>(); + let product: Decimal = vs.iter().product(); + assert_eq!(product, Decimal::from(24)) +} + +#[test] +fn declarative_dec_sum() { + let vs = (0..10).map(|i| i.into()).collect::<Vec<Decimal>>(); + let sum: Decimal = vs.into_iter().sum(); + assert_eq!(sum, Decimal::from(45)) +} + +#[test] +fn declarative_ref_dec_sum() { + let vs = (0..10).map(|i| i.into()).collect::<Vec<Decimal>>(); + let sum: Decimal = vs.iter().sum(); + assert_eq!(sum, Decimal::from(45)) +} + +#[cfg(feature = "postgres")] +#[test] +fn to_from_sql() { + use bytes::BytesMut; + use postgres::types::{FromSql, Kind, ToSql, Type}; + + let tests = &[ + "3950.123456", + "3950", + "0.1", + "0.01", + "0.001", + "0.0001", + "0.00001", + "0.000001", + "1", + "-100", + "-123.456", + "119996.25", + "1000000", + "9999999.99999", + "12340.56789", + "79228162514264337593543950335", // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF (96 bit) + "4951760157141521099596496895", // 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF (95 bit) + "4951760157141521099596496896", // 0x1000_0000_0000_0000_0000_0000 + "18446744073709551615", + "-18446744073709551615", + ]; + + let t = Type::new("".into(), 0, Kind::Simple, "".into()); + + for test in tests { + let input = Decimal::from_str(test).unwrap(); + let mut bytes = BytesMut::new(); + input.to_sql(&t, &mut bytes).unwrap(); + let output = Decimal::from_sql(&t, &bytes).unwrap(); + + assert_eq!(input, output); + } +} + +fn hash_it(d: Decimal) -> u64 { + use core::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + + let mut h = DefaultHasher::new(); + d.hash(&mut h); + h.finish() +} + +#[test] +fn it_computes_equal_hashes_for_equal_values() { + // From the Rust Hash docs: + // + // "When implementing both Hash and Eq, it is important that the following property holds: + // + // k1 == k2 -> hash(k1) == hash(k2)" + + let k1 = Decimal::from_str("1").unwrap(); + let k2 = Decimal::from_str("1.0").unwrap(); + let k3 = Decimal::from_str("1.00").unwrap(); + let k4 = Decimal::from_str("1.01").unwrap(); + + assert_eq!(k1, k2); + assert_eq!(k1, k3); + assert_ne!(k1, k4); + + let h1 = hash_it(k1); + let h2 = hash_it(k2); + let h3 = hash_it(k3); + let h4 = hash_it(k4); + + assert_eq!(h1, h2); + assert_eq!(h1, h3); + assert_ne!(h1, h4); + + // Test the application of Hash calculation to a HashMap. + + let mut map = std::collections::HashMap::new(); + + map.insert(k1, k1.to_string()); + // map[k2] should overwrite map[k1] because k1 == k2. + map.insert(k2, k2.to_string()); + + assert_eq!("1.0", map.get(&k3).expect("could not get k3")); + assert_eq!(1, map.len()); + + // map[k3] should overwrite map[k2] because k3 == k2. + map.insert(k3, k3.to_string()); + // map[k4] should not overwrite map[k3] because k4 != k3. + map.insert(k4, k4.to_string()); + + assert_eq!(2, map.len()); + assert_eq!("1.00", map.get(&k1).expect("could not get k1")); +} + +#[test] +fn it_computes_equal_hashes_for_positive_and_negative_zero() { + // Verify 0 and -0 have the same hash + let k1 = Decimal::from_str("0").unwrap(); + let k2 = Decimal::from_str("-0").unwrap(); + assert_eq!(k1, k2); + let h1 = hash_it(k1); + let h2 = hash_it(k2); + assert_eq!(h1, h2); + + // Verify 0 and -0.0 have the same hash + let k1 = Decimal::from_str("0").unwrap(); + let k2 = Decimal::from_str("-0.0").unwrap(); + assert_eq!(k1, k2); + let h1 = hash_it(k1); + let h2 = hash_it(k2); + assert_eq!(h1, h2); +} + +#[test] +#[should_panic(expected = "Number less than minimum value that can be represented.")] +fn it_handles_i128_min() { + let _ = Decimal::from_i128_with_scale(i128::MIN, 0); +} + +#[test] +fn it_handles_i128_min_safely() { + let result = Decimal::try_from_i128_with_scale(i128::MIN, 0); + assert!(result.is_err()); + assert_eq!(result.err().unwrap(), Error::LessThanMinimumPossibleValue); +} + +#[test] +fn it_can_rescale() { + let tests = &[ + ("0", 6, "0.000000", 6), + ("0.000000", 2, "0.00", 2), + ("0.12345600000", 6, "0.123456", 6), + ("0.123456", 12, "0.123456000000", 12), + ("0.123456", 0, "0", 0), + ("0.000001", 4, "0.0000", 4), + ("1233456", 4, "1233456.0000", 4), + // Cap to 28 + ("1.2", 30, "1.2000000000000000000000000000", 28), + ("79228162514264337593543950335", 0, "79228162514264337593543950335", 0), + ("4951760157141521099596496895", 1, "4951760157141521099596496895.0", 1), + ("4951760157141521099596496896", 1, "4951760157141521099596496896.0", 1), + ("18446744073709551615", 6, "18446744073709551615.000000", 6), + ("-18446744073709551615", 6, "-18446744073709551615.000000", 6), + // 27 since we can't fit a scale of 28 for this number + ("11.76470588235294", 28, "11.764705882352940000000000000", 27), + ]; + + for &(value_raw, new_scale, expected_value, expected_scale) in tests { + let mut value = Decimal::from_str(value_raw).unwrap(); + value.rescale(new_scale); + assert_eq!(expected_value, value.to_string()); + assert_eq!(expected_scale, value.scale()); + } +} + +#[test] +fn test_constants() { + assert_eq!("0", Decimal::ZERO.to_string()); + assert_eq!("1", Decimal::ONE.to_string()); + assert_eq!("-1", Decimal::NEGATIVE_ONE.to_string()); + assert_eq!("10", Decimal::TEN.to_string()); + assert_eq!("100", Decimal::ONE_HUNDRED.to_string()); + assert_eq!("1000", Decimal::ONE_THOUSAND.to_string()); + assert_eq!("2", Decimal::TWO.to_string()); +} + +#[test] +fn test_inv() { + assert_eq!("0.01", Decimal::ONE_HUNDRED.inv().to_string()); +} + +// Mathematical features +#[cfg(feature = "maths")] +mod maths { + use super::*; + use rust_decimal::MathematicalOps; + + use num_traits::One; + + #[test] + fn test_constants() { + assert_eq!("3.1415926535897932384626433833", Decimal::PI.to_string()); + assert_eq!("6.2831853071795864769252867666", Decimal::TWO_PI.to_string()); + assert_eq!("1.5707963267948966192313216916", Decimal::HALF_PI.to_string()); + assert_eq!("2.7182818284590452353602874714", Decimal::E.to_string()); + assert_eq!("0.3678794411714423215955237702", Decimal::E_INVERSE.to_string()); + } + + #[test] + fn test_powu() { + let test_cases = &[ + // x, y, expected x ^ y + ("4", 3_u64, "64"), + ("3.222", 5_u64, "347.238347228449632"), + ("0.1", 0_u64, "1"), + ("342.4", 1_u64, "342.4"), + ("2.0", 16_u64, "65536"), + ]; + for &(x, y, expected) in test_cases { + let x = Decimal::from_str(x).unwrap(); + let pow = x.powu(y); + assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); + } + } + + #[test] + #[should_panic(expected = "Pow overflowed")] + fn test_powu_panic() { + let two = Decimal::new(2, 0); + let _ = two.powu(128); + } + + #[test] + fn test_checked_powu() { + let test_cases = &[ + (Decimal::new(4, 0), 3_u64, Some(Decimal::new(64, 0))), + ( + Decimal::from_str("3.222").unwrap(), + 5_u64, + Some(Decimal::from_str("347.238347228449632").unwrap()), + ), + ( + Decimal::from_str("0.1").unwrap(), + 0_u64, + Some(Decimal::from_str("1").unwrap()), + ), + ( + Decimal::from_str("342.4").unwrap(), + 1_u64, + Some(Decimal::from_str("342.4").unwrap()), + ), + ( + Decimal::from_str("2.0").unwrap(), + 16_u64, + Some(Decimal::from_str("65536").unwrap()), + ), + (Decimal::from_str("2.0").unwrap(), 128_u64, None), + ]; + for case in test_cases { + assert_eq!(case.2, case.0.checked_powu(case.1)); + } + } + + #[test] + fn test_powi() { + let test_cases = &[ + // x, y, expected x ^ y + ("0", 0, "1"), + ("1", 0, "1"), + ("0", 1, "0"), + ("2", 3, "8"), + ("-2", 3, "-8"), + ("2", -3, "0.125"), + ("-2", -3, "-0.125"), + ( + "3", + -3, + either!("0.037037037037037037037037037", "0.0370370370370370370370370370"), + ), + ("6", 3, "216"), + ("0.5", 2, "0.25"), + ]; + for &(x, y, expected) in test_cases { + let x = Decimal::from_str(x).unwrap(); + let pow = x.powi(y); + assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); + } + } + + #[test] + fn test_powd() { + let test_cases = &[ + // x, y, expected x ^ y + ("0", "0", "1"), + ("1", "0", "1"), + ("0", "1", "0"), + ("2", "3", "8"), + ("-2", "3", "-8"), + ("2", "-3", "0.125"), + ("-2", "-3", "-0.125"), + ("2.0", "3.0", "8"), + ("-2.0", "3.0", "-8"), + ("2.0", "-3.0", "0.125"), + ("-2.0", "-3.0", "-0.125"), + ("2.00", "3.00", "8"), + ("-2.00", "3.00", "-8"), + ("2.00", "-3.00", "0.125"), + ("-2.00", "-3.00", "-0.125"), + ( + "3", + "-3", + either!("0.037037037037037037037037037", "0.0370370370370370370370370370"), + ), + ("6", "3", "216"), + ("0.5", "2", "0.25"), + ("6", "13", "13060694016"), + // Exact result: 1 / 6^7 + ("6", "-7", "0.0000035722450845907636031093"), + // ~= 0.8408964152537145 + ( + "0.5", + "0.25", + either!("0.8408964159265360661551317741", "0.8408964159265360661551317742"), + ), + // ~= 0.999999999999999999999999999790814 + ( + "0.1234567890123456789012345678", + "0.0000000000000000000000000001", + "0.9999999999999999999999999998", + ), + // ~= 611.0451043224257 + ( + "1234.5678", + "0.9012", + either!("611.04510415448740041442807964", "611.04510415448740041442807964"), + ), + ( + "-2", + "0.5", + either!("-1.4142135570048917090885260834", "-1.4142135570048917090885260835"), + ), + // ~= -1.1193003023312942 + ( + "-2.5", + "0.123", + either!("-1.1193002994383985239135362086", "-1.1193002994383985239135362086"), + ), + // ~= 0.0003493091 + ( + "0.0000000000000000000000000001", + "0.1234567890123456789012345678", + either!("0.0003533642875741443321850682", "0.0003305188683169079961720764"), + ), + ]; + for &(x, y, expected) in test_cases { + let x = Decimal::from_str(x).unwrap(); + let y = Decimal::from_str(y).unwrap(); + let pow = x.powd(y); + assert_eq!(pow.to_string(), expected, "{} ^ {}", x, y); + } + } + + #[test] + fn test_sqrt() { + let test_cases = &[ + ("4", "2"), + ("3.222", "1.7949930361981909371487724124"), + ("199.45", "14.122676800097069416754994263"), + ("342.4", "18.504053609952604112132102540"), + ("2", "1.414213562373095048801688724209698078569671875376948073176"), + ("0.0000000000000000000000000001", "0.0000000000000100000000000000"), + ]; + for case in test_cases { + let a = Decimal::from_str(case.0).unwrap(); + let expected = Decimal::from_str(case.1).unwrap(); + assert_eq!(expected, a.sqrt().unwrap()); + } + + assert_eq!(Decimal::new(-2, 0).sqrt(), None); + } + + #[cfg(not(feature = "legacy-ops"))] + #[test] + fn test_exp() { + // These are approximations + let test_cases = &[ + // e^10 ~= 22026.465794806703 + ("10", "22026.416157416030662013737698"), + // e^11 ~= 59874.14171519778 + ("11", "59873.388231055804982198781924"), + // e^3 ~= 20.085536923187664 + ("3", "20.085536911963143539758560764"), + // e^8 ~= 2980.957987041727 + ("8", "2980.9578998304103856663509017"), + // e^0.1 ~= 1.1051709180756477 + ("0.1", "1.1051709166666666666666666667"), + // e^2.0 ~= 7.3890560989306495 + ("2.0", "7.3890560703259115957528655940"), + // e^-2 ~= 0.1353352832366127 + ("-2", "0.1353352837605267572029589224"), + // e^-1 ~= 0.36787944117144233 + ("-1", "0.3678794414773748171422559335"), + // e^0.123456789 ~= 1.131401115 + ("0.123456789", "1.1314011144241455834073838005"), + // e^0.123456789123456789123456789 ~= 1.131401114651912752617990081 + ("0.123456789123456789123456789", "1.1314011145638247316063947842"), + ]; + for &(x, expected) in test_cases { + let x = Decimal::from_str(x).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + assert_eq!(expected, x.exp()); + assert_eq!(Some(expected), x.checked_exp()); + } + } + + #[cfg(not(feature = "legacy-ops"))] + #[test] + fn test_exp_with_tolerance() { + let test_cases = &[ + // e^0 = 1 + ("0", "0.0002", "1"), + // e^1 ~= 2.7182539682539682539682539683 + ("1", "0.0002", "2.7182539682539682539682539683"), + // e^10 ~= 22026.465794806703 + ( + "10", + "0.02", + either!("22026.416157416030662013737698", "22026.416157416030662013737699"), + ), + // e^11 ~= 59874.14171519778 + ("11", "0.0002", "59873.388231055804982198781924"), + // e^11.7578 ~= 127741.03548949540892948423052 + ("11.7578", "0.0002", "127741.03548949540892948423052"), + // e^3 ~= 20.085536923187664 + ("3", "0.00002", "20.085534430970814899386327955"), + // e^8 ~= 2980.957987041727 + ("8", "0.0002", "2980.9578998304103856663509017"), + // e^0.1 ~= 1.1051709180756477 + ("0.1", "0.0002", "1.1051666666666666666666666667"), + // e^2.0 ~= 7.3890560989306495 + ("2.0", "0.0002", "7.3890460157126823793490460156"), + // e^11.7578+ starts to overflow + ("11.7579", "0.0002", ""), + // e^11.7578+ starts to overflow + ("123", "0.0002", ""), + // e^-8+ starts to underflow + ("-8", "0.0002", "0.0003354626377168530220952633"), + // e^-9 continues to converge towards zero + ("-9", "0.0002", "0.0001234098417553083895710102"), + // e^-11 continues to converge towards zero + ("-11", "0.0002", "0.0000167019109748879838728391"), + // e^11.7579 has underflowed (by overflowing) + ("-11.7579", "0.0002", ""), + // e^-1024 has fully underflowed (by overflowing) + ("-1024", "0.0002", ""), + ]; + for &(x, tolerance, expected) in test_cases { + let x = Decimal::from_str(x).unwrap(); + let tolerance = Decimal::from_str(tolerance).unwrap(); + let expected = if expected.is_empty() { + None + } else { + Some(Decimal::from_str(expected).unwrap()) + }; + + if let Some(expected) = expected { + assert_eq!(expected, x.exp_with_tolerance(tolerance)); + assert_eq!(Some(expected), x.checked_exp_with_tolerance(tolerance)); + } else { + assert_eq!(None, x.checked_exp_with_tolerance(tolerance)); + } + } + } + + #[test] + #[should_panic(expected = "Exp overflowed")] + fn test_exp_expected_panic_from_overflow() { + let d = Decimal::from_str("1024").unwrap(); + let _ = d.exp(); + } + + #[test] + #[should_panic(expected = "Exp underflowed")] + fn test_exp_expected_panic_from_underflow() { + let d = Decimal::from_str("-1024").unwrap(); + let _ = d.exp(); + } + + #[test] + fn test_norm_cdf() { + let test_cases = &[ + ( + Decimal::from_str("-0.4").unwrap(), + either!( + Decimal::from_str("0.3445781286821245037094401704").unwrap(), + Decimal::from_str("0.3445781286821245037094401728").unwrap() + ), + ), + ( + Decimal::from_str("-0.1").unwrap(), + either!( + Decimal::from_str("0.4601722899186706579921922696").unwrap(), + Decimal::from_str("0.4601722899186706579921922711").unwrap() + ), + ), + ( + Decimal::from_str("0.1").unwrap(), + Decimal::from_str(either!( + "0.5398277100813293420078077304", + "0.5398277100813293420078077290" + )) + .unwrap(), + ), + ( + Decimal::from_str("0.4").unwrap(), + either!( + Decimal::from_str("0.6554218713178754962905598296").unwrap(), + Decimal::from_str("0.6554218713178754962905598272").unwrap() + ), + ), + ( + Decimal::from_str("2.0").unwrap(), + either!( + Decimal::from_str("0.9772497381095865280953380673").unwrap(), + Decimal::from_str("0.9772497381095865280953380672").unwrap() + ), + ), + ]; + for case in test_cases { + assert_eq!(case.1, case.0.norm_cdf()); + } + } + + #[test] + fn test_norm_pdf() { + let test_cases = &[ + ( + Decimal::from_str("-2.0").unwrap(), + Decimal::from_str("0.0539909667221995238993056051").unwrap(), + ), + ( + Decimal::from_str("-0.4").unwrap(), + Decimal::from_str("0.3682701404285264134348468378").unwrap(), + ), + ( + Decimal::from_str("-0.1").unwrap(), + Decimal::from_str("0.3969525474873078082322691394").unwrap(), + ), + ( + Decimal::from_str("0.1").unwrap(), + Decimal::from_str("0.3969525474873078082322691394").unwrap(), + ), + ( + Decimal::from_str("0.4").unwrap(), + Decimal::from_str("0.3682701404285264134348468378").unwrap(), + ), + ( + Decimal::from_str("2.0").unwrap(), + Decimal::from_str("0.0539909667221995238993056051").unwrap(), + ), + ]; + for case in test_cases { + assert_eq!(case.1, case.0.norm_pdf()); + } + } + + #[test] + fn test_ln() { + let test_cases = [ + ("1", "0"), + // Wolfram Alpha gives -1.46968 + ( + "0.23", + either!("-1.4696759700589416772292300779", "-1.4696759700589416772292300777"), + ), + // Wolfram Alpha gives 0.693147180559945309417232121458176568075500134360255254120 + ("2", "0.6931471805599453094172321218"), + // Wolfram Alpha gives 3.218875824868200749201518666452375279051202708537035443825 + ( + "25", + either!("3.2188758248682007492015186670", "3.2188758248682007492015186674"), + ), + // Wolfram Alpha gives 0.210721022 + ( + "1.234567890", + either!("0.2107210222156525610500017104", "0.2107210222156525610500017106"), + ), + ]; + + for (input, expected) in test_cases { + let input = Decimal::from_str(input).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + assert_eq!(expected, input.ln(), "Failed to calculate ln({})", input); + } + } + + #[test] + #[cfg(feature = "maths-nopanic")] + fn test_invalid_ln_nopanic() { + let test_cases = ["0", "-2.0"]; + + for input in test_cases { + let input = Decimal::from_str(input).unwrap(); + assert_eq!("0", input.ln().to_string(), "Failed to calculate ln({})", input); + } + } + + #[test] + #[should_panic(expected = "Unable to calculate ln for zero")] + #[cfg(not(feature = "maths-nopanic"))] + fn test_invalid_ln_zero_panic() { + let _ = Decimal::ZERO.ln(); + } + + #[test] + #[should_panic(expected = "Unable to calculate ln for negative numbers")] + #[cfg(not(feature = "maths-nopanic"))] + fn test_invalid_ln_negative_panic() { + let _ = Decimal::NEGATIVE_ONE.ln(); + } + + #[test] + fn test_log10() { + let test_cases = [ + ("1", "0"), + // Wolfram Alpha: 0.3010299956639811952137388947 + ( + "2", + either!("0.3010299956639811952137388949", "0.3010299956639811952137388948"), + ), + // Wolfram Alpha: 0.0915149772 + ( + "1.234567890", + either!("0.0915149771692704475183336230", "0.0915149771692704475183336231"), + ), + ("10", "1"), + ("100", "2"), + ("1000", "3"), + ("1000.00000000", "3"), + ("1000.000000000000000000000", "3"), + ("10.000000000000000000000000000", "1"), + ("100000000000000.0000000000", "14"), + ("0.10", "-1"), + ("0.1", "-1"), + ("0.01", "-2"), + ("0.010", "-2"), + ("0.0100000000000000000", "-2"), + ("0.000001", "-6"), + ("0.000001000000000", "-6"), + ]; + + for (input, expected) in test_cases { + let input = Decimal::from_str(input).unwrap(); + let expected = Decimal::from_str(expected).unwrap(); + assert_eq!(expected, input.log10(), "Failed to calculate log10({})", input); + } + } + + #[test] + #[cfg(feature = "maths-nopanic")] + fn test_invalid_log10_nopanic() { + let test_cases = ["0", "-2.0"]; + + for input in test_cases { + let input = Decimal::from_str(input).unwrap(); + assert_eq!("0", input.log10().to_string(), "Failed to calculate ln({})", input); + } + } + + #[test] + #[should_panic(expected = "Unable to calculate log10 for zero")] + #[cfg(not(feature = "maths-nopanic"))] + fn test_invalid_log10_zero_panic() { + let _ = Decimal::ZERO.log10(); + } + + #[test] + #[should_panic(expected = "Unable to calculate log10 for negative numbers")] + #[cfg(not(feature = "maths-nopanic"))] + fn test_invalid_log10_negative_panic() { + let _ = Decimal::NEGATIVE_ONE.log10(); + } + + #[test] + fn test_erf() { + let test_cases = &[ + ( + Decimal::from_str("-2.0").unwrap(), + // Wolfram give -0.9953222650189527 + Decimal::from_str("-0.9953225170750043399400930073").unwrap(), + ), + ( + Decimal::from_str("-0.4").unwrap(), + Decimal::from_str("-0.4283924127205154977961931420").unwrap(), + ), + ( + Decimal::from_str("0.4").unwrap(), + Decimal::from_str("0.4283924127205154977961931420").unwrap(), + ), + ( + Decimal::one(), + Decimal::from_str("0.8427010463338918630217928957").unwrap(), + ), + ( + Decimal::from_str("2").unwrap(), + Decimal::from_str("0.9953225170750043399400930073").unwrap(), + ), + ]; + for case in test_cases { + assert_eq!(case.1, case.0.erf()); + } + } + + #[test] + fn test_checked_sin() { + const ACCEPTED_PRECISION: u32 = 10; + let test_cases = &[ + // Sin(0) + ("0", Some("0")), + // Sin(PI/2) + ("1.5707963267948966192313216916", Some("1")), + // Sin(PI) + ("3.1415926535897932384626433833", Some("0")), + // Sin(3PI/2) + ("4.7123889803846898576939650749", Some("-1")), + // Sin(2PI) + ("6.2831853071795864769252867666", Some("0")), + // Sin(1) ~= 0.8414709848078965066525023216302989996225630607983710656727517099 + ("1", Some("0.8414709848078965066525023216")), + // Sin(2) ~= 0.9092974268256816953960198659117448427022549714478902683789730115 + ("2", Some("0.9092974268256816953960198659")), + // Sin(4) ~= -0.756802495307928251372639094511829094135912887336472571485416773 + ("4", Some("-0.7568024953079282513726390945")), + // Sin(6) ~= -0.279415498198925872811555446611894759627994864318204318483351369 + ("6", Some("-0.2794154981989258728115554466")), + // WA estimate: -0.893653245236708. Legacy ops is closer to f64 accuracy. + ( + "-79228162514264.337593543950335", + Some(either!("-0.893653245236708", "-0.8963358176")), + ), + ]; + for (input, result) in test_cases { + let radians = Decimal::from_str(input).unwrap(); + let sin = radians.checked_sin(); + if let Some(result) = result { + assert!(sin.is_some(), "Expected result for sin({})", input); + let result = Decimal::from_str(result).unwrap(); + assert_approx_eq!(sin.unwrap(), result, ACCEPTED_PRECISION, "sin({})", input); + } else { + assert!(sin.is_none(), "Unexpected result for sin({})", input); + } + } + } + + #[test] + fn test_checked_cos() { + const ACCEPTED_PRECISION: u32 = 10; + let test_cases = &[ + // Cos(0) + ("0", Some("1")), + // Cos(PI/2) + ("1.5707963267948966192313216916", Some("0")), + // Cos(PI) + ("3.1415926535897932384626433833", Some("-1")), + // Cos(3PI/2) + ("4.7123889803846898576939650749", Some("0")), + // Cos(2PI) + ("6.2831853071795864769252867666", Some("1")), + // Cos(1) ~= 0.5403023058681397174009366074429766037323104206179222276700972553 + ("1", Some("0.5403023058681397174009366074")), + // Cos(2) ~= -0.416146836547142386997568229500762189766000771075544890755149973 + ("2", Some("-0.4161468365471423869975682295")), + // Cos(4) ~= -0.653643620863611914639168183097750381424133596646218247007010283 + ("4", Some("-0.6536436208636119146391681831")), + // Cos(6) ~= 0.9601702866503660205456522979229244054519376792110126981292864260 + ("6", Some("0.9601702866503660205456522979")), + // WA estimate: 0.448758150096352. Legacy ops is closer to f64 accuracy. + ( + "-79228162514264.337593543950335", + Some(either!("0.448758150096352", "0.443375802326")), + ), + ]; + for (input, result) in test_cases { + let radians = Decimal::from_str(input).unwrap(); + let cos = radians.checked_cos(); + if let Some(result) = result { + assert!(cos.is_some(), "Expected result for cos({})", input); + let result = Decimal::from_str(result).unwrap(); + assert_approx_eq!(cos.unwrap(), result, ACCEPTED_PRECISION, "cos({})", input); + } else { + assert!(cos.is_none(), "Unexpected result for cos({})", input); + } + } + } + + #[test] + fn test_checked_tan() { + const ACCEPTED_PRECISION: u32 = 8; + let test_cases = &[ + // Tan(0) + ("0", Some("0")), + // Tan(PI/2) + ("1.5707963267948966192313216916", None), + // Tan(PI) + ("3.1415926535897932384626433833", Some("0")), + // Tan(3PI/2) + ("4.7123889803846898576939650749", None), + // Tan(2PI) + ("6.2831853071795864769252867666", Some("0")), + // Tan(1) ~= 1.5574077246549022305069748074583601730872507723815200383839466056 + ("1", Some("1.5574077246549022305069748075")), + // Tan(2) ~= -2.185039863261518991643306102313682543432017746227663164562955869 + ("2", Some("-2.1850398632615189916433061023")), + // Tan(4) ~= 1.1578212823495775831373424182673239231197627673671421300848571893 + ("4", Some("1.1578212823495775831373424183")), + // Tan(6) ~= -0.291006191384749157053699588868175542831155570912339131608827193 + ("6", Some("-0.2910061913847491570536995889")), + // WA estimate: -1.99139167733184. Legacy ops is closer to f64 accuracy. + ( + "-79228162514264.337593543950335", + Some(either!("-1.99139167733184", "-2.021616454709")), + ), + ]; + for (input, result) in test_cases { + let radians = Decimal::from_str(input).unwrap(); + let tan = radians.checked_tan(); + if let Some(result) = result { + assert!(tan.is_some(), "Expected result for tan({})", input); + let result = Decimal::from_str(result).unwrap(); + assert_approx_eq!(tan.unwrap(), result, ACCEPTED_PRECISION, "tan({})", input); + } else { + assert!(tan.is_none(), "Unexpected result for tan({})", input); + } + } + } +} + +// Generated tests +#[cfg(not(feature = "legacy-ops"))] +mod generated { + use rust_decimal::prelude::*; + + macro_rules! gen_test { + ($name:ident, $csv:expr, $method:tt) => { + #[test] + fn $name() { + let path = std::env::current_dir().unwrap(); + let mut rdr = csv::Reader::from_reader( + std::fs::File::open(format!("{}/tests/generated/{}", path.display(), $csv)).unwrap(), + ); + let mut row = 0; + for result in rdr.records() { + let record = result.unwrap(); + row += 1; + + // Extract the data + let d1 = record.get(0).unwrap(); + let d2 = record.get(1).unwrap(); + let result = record.get(2).unwrap(); + let error = record.get(3).unwrap(); + + // Do the calc + let d1 = Decimal::from_str(&d1).unwrap(); + let d2 = Decimal::from_str(&d2).unwrap(); + let expected = Decimal::from_str(&result).unwrap(); + match d1.$method(d2) { + Some(v) => assert_eq!(expected, v, "Row {}", row), + None => assert!(!error.is_empty()), + } + } + } + }; + } + + gen_test!(test_add_000_001, "Add_000_001.csv", checked_add); + gen_test!(test_add_000_010, "Add_000_010.csv", checked_add); + gen_test!(test_add_000_011, "Add_000_011.csv", checked_add); + gen_test!(test_add_000_100, "Add_000_100.csv", checked_add); + gen_test!(test_add_000_101, "Add_000_101.csv", checked_add); + gen_test!(test_add_000_110, "Add_000_110.csv", checked_add); + gen_test!(test_add_000_111, "Add_000_111.csv", checked_add); + gen_test!(test_add_001_000, "Add_001_000.csv", checked_add); + gen_test!(test_add_001_001, "Add_001_001.csv", checked_add); + gen_test!(test_add_001_010, "Add_001_010.csv", checked_add); + gen_test!(test_add_001_011, "Add_001_011.csv", checked_add); + gen_test!(test_add_001_100, "Add_001_100.csv", checked_add); + gen_test!(test_add_001_101, "Add_001_101.csv", checked_add); + gen_test!(test_add_001_110, "Add_001_110.csv", checked_add); + gen_test!(test_add_001_111, "Add_001_111.csv", checked_add); + gen_test!(test_add_010_000, "Add_010_000.csv", checked_add); + gen_test!(test_add_010_001, "Add_010_001.csv", checked_add); + gen_test!(test_add_010_010, "Add_010_010.csv", checked_add); + gen_test!(test_add_010_011, "Add_010_011.csv", checked_add); + gen_test!(test_add_010_100, "Add_010_100.csv", checked_add); + gen_test!(test_add_010_101, "Add_010_101.csv", checked_add); + gen_test!(test_add_010_110, "Add_010_110.csv", checked_add); + gen_test!(test_add_010_111, "Add_010_111.csv", checked_add); + gen_test!(test_add_011_000, "Add_011_000.csv", checked_add); + gen_test!(test_add_011_001, "Add_011_001.csv", checked_add); + gen_test!(test_add_011_010, "Add_011_010.csv", checked_add); + gen_test!(test_add_011_011, "Add_011_011.csv", checked_add); + gen_test!(test_add_011_100, "Add_011_100.csv", checked_add); + gen_test!(test_add_011_101, "Add_011_101.csv", checked_add); + gen_test!(test_add_011_110, "Add_011_110.csv", checked_add); + gen_test!(test_add_011_111, "Add_011_111.csv", checked_add); + gen_test!(test_add_100_000, "Add_100_000.csv", checked_add); + gen_test!(test_add_100_001, "Add_100_001.csv", checked_add); + gen_test!(test_add_100_010, "Add_100_010.csv", checked_add); + gen_test!(test_add_100_011, "Add_100_011.csv", checked_add); + gen_test!(test_add_100_100, "Add_100_100.csv", checked_add); + gen_test!(test_add_100_101, "Add_100_101.csv", checked_add); + gen_test!(test_add_100_110, "Add_100_110.csv", checked_add); + gen_test!(test_add_100_111, "Add_100_111.csv", checked_add); + gen_test!(test_add_101_000, "Add_101_000.csv", checked_add); + gen_test!(test_add_101_001, "Add_101_001.csv", checked_add); + gen_test!(test_add_101_010, "Add_101_010.csv", checked_add); + gen_test!(test_add_101_011, "Add_101_011.csv", checked_add); + gen_test!(test_add_101_100, "Add_101_100.csv", checked_add); + gen_test!(test_add_101_101, "Add_101_101.csv", checked_add); + gen_test!(test_add_101_110, "Add_101_110.csv", checked_add); + gen_test!(test_add_101_111, "Add_101_111.csv", checked_add); + gen_test!(test_add_110_000, "Add_110_000.csv", checked_add); + gen_test!(test_add_110_001, "Add_110_001.csv", checked_add); + gen_test!(test_add_110_010, "Add_110_010.csv", checked_add); + gen_test!(test_add_110_011, "Add_110_011.csv", checked_add); + gen_test!(test_add_110_100, "Add_110_100.csv", checked_add); + gen_test!(test_add_110_101, "Add_110_101.csv", checked_add); + gen_test!(test_add_110_110, "Add_110_110.csv", checked_add); + gen_test!(test_add_110_111, "Add_110_111.csv", checked_add); + gen_test!(test_add_111_000, "Add_111_000.csv", checked_add); + gen_test!(test_add_111_001, "Add_111_001.csv", checked_add); + gen_test!(test_add_111_010, "Add_111_010.csv", checked_add); + gen_test!(test_add_111_011, "Add_111_011.csv", checked_add); + gen_test!(test_add_111_100, "Add_111_100.csv", checked_add); + gen_test!(test_add_111_101, "Add_111_101.csv", checked_add); + gen_test!(test_add_111_110, "Add_111_110.csv", checked_add); + gen_test!(test_add_111_111, "Add_111_111.csv", checked_add); + + gen_test!(test_div_000_001, "Div_000_001.csv", checked_div); + gen_test!(test_div_000_010, "Div_000_010.csv", checked_div); + gen_test!(test_div_000_011, "Div_000_011.csv", checked_div); + gen_test!(test_div_000_100, "Div_000_100.csv", checked_div); + gen_test!(test_div_000_101, "Div_000_101.csv", checked_div); + gen_test!(test_div_000_110, "Div_000_110.csv", checked_div); + gen_test!(test_div_000_111, "Div_000_111.csv", checked_div); + gen_test!(test_div_001_000, "Div_001_000.csv", checked_div); + gen_test!(test_div_001_001, "Div_001_001.csv", checked_div); + gen_test!(test_div_001_010, "Div_001_010.csv", checked_div); + gen_test!(test_div_001_011, "Div_001_011.csv", checked_div); + gen_test!(test_div_001_100, "Div_001_100.csv", checked_div); + gen_test!(test_div_001_101, "Div_001_101.csv", checked_div); + gen_test!(test_div_001_110, "Div_001_110.csv", checked_div); + gen_test!(test_div_001_111, "Div_001_111.csv", checked_div); + gen_test!(test_div_010_000, "Div_010_000.csv", checked_div); + gen_test!(test_div_010_001, "Div_010_001.csv", checked_div); + gen_test!(test_div_010_010, "Div_010_010.csv", checked_div); + gen_test!(test_div_010_011, "Div_010_011.csv", checked_div); + gen_test!(test_div_010_100, "Div_010_100.csv", checked_div); + gen_test!(test_div_010_101, "Div_010_101.csv", checked_div); + gen_test!(test_div_010_110, "Div_010_110.csv", checked_div); + gen_test!(test_div_010_111, "Div_010_111.csv", checked_div); + gen_test!(test_div_011_000, "Div_011_000.csv", checked_div); + gen_test!(test_div_011_001, "Div_011_001.csv", checked_div); + gen_test!(test_div_011_010, "Div_011_010.csv", checked_div); + gen_test!(test_div_011_011, "Div_011_011.csv", checked_div); + gen_test!(test_div_011_100, "Div_011_100.csv", checked_div); + gen_test!(test_div_011_101, "Div_011_101.csv", checked_div); + gen_test!(test_div_011_110, "Div_011_110.csv", checked_div); + gen_test!(test_div_011_111, "Div_011_111.csv", checked_div); + gen_test!(test_div_100_000, "Div_100_000.csv", checked_div); + gen_test!(test_div_100_001, "Div_100_001.csv", checked_div); + gen_test!(test_div_100_010, "Div_100_010.csv", checked_div); + gen_test!(test_div_100_011, "Div_100_011.csv", checked_div); + gen_test!(test_div_100_100, "Div_100_100.csv", checked_div); + gen_test!(test_div_100_101, "Div_100_101.csv", checked_div); + gen_test!(test_div_100_110, "Div_100_110.csv", checked_div); + gen_test!(test_div_100_111, "Div_100_111.csv", checked_div); + gen_test!(test_div_101_000, "Div_101_000.csv", checked_div); + gen_test!(test_div_101_001, "Div_101_001.csv", checked_div); + gen_test!(test_div_101_010, "Div_101_010.csv", checked_div); + gen_test!(test_div_101_011, "Div_101_011.csv", checked_div); + gen_test!(test_div_101_100, "Div_101_100.csv", checked_div); + gen_test!(test_div_101_101, "Div_101_101.csv", checked_div); + gen_test!(test_div_101_110, "Div_101_110.csv", checked_div); + gen_test!(test_div_101_111, "Div_101_111.csv", checked_div); + gen_test!(test_div_110_000, "Div_110_000.csv", checked_div); + gen_test!(test_div_110_001, "Div_110_001.csv", checked_div); + gen_test!(test_div_110_010, "Div_110_010.csv", checked_div); + gen_test!(test_div_110_011, "Div_110_011.csv", checked_div); + gen_test!(test_div_110_100, "Div_110_100.csv", checked_div); + gen_test!(test_div_110_101, "Div_110_101.csv", checked_div); + gen_test!(test_div_110_110, "Div_110_110.csv", checked_div); + gen_test!(test_div_110_111, "Div_110_111.csv", checked_div); + gen_test!(test_div_111_000, "Div_111_000.csv", checked_div); + gen_test!(test_div_111_001, "Div_111_001.csv", checked_div); + gen_test!(test_div_111_010, "Div_111_010.csv", checked_div); + gen_test!(test_div_111_011, "Div_111_011.csv", checked_div); + gen_test!(test_div_111_100, "Div_111_100.csv", checked_div); + gen_test!(test_div_111_101, "Div_111_101.csv", checked_div); + gen_test!(test_div_111_110, "Div_111_110.csv", checked_div); + gen_test!(test_div_111_111, "Div_111_111.csv", checked_div); + + gen_test!(test_mul_000_001, "Mul_000_001.csv", checked_mul); + gen_test!(test_mul_000_010, "Mul_000_010.csv", checked_mul); + gen_test!(test_mul_000_011, "Mul_000_011.csv", checked_mul); + gen_test!(test_mul_000_100, "Mul_000_100.csv", checked_mul); + gen_test!(test_mul_000_101, "Mul_000_101.csv", checked_mul); + gen_test!(test_mul_000_110, "Mul_000_110.csv", checked_mul); + gen_test!(test_mul_000_111, "Mul_000_111.csv", checked_mul); + gen_test!(test_mul_001_000, "Mul_001_000.csv", checked_mul); + gen_test!(test_mul_001_001, "Mul_001_001.csv", checked_mul); + gen_test!(test_mul_001_010, "Mul_001_010.csv", checked_mul); + gen_test!(test_mul_001_011, "Mul_001_011.csv", checked_mul); + gen_test!(test_mul_001_100, "Mul_001_100.csv", checked_mul); + gen_test!(test_mul_001_101, "Mul_001_101.csv", checked_mul); + gen_test!(test_mul_001_110, "Mul_001_110.csv", checked_mul); + gen_test!(test_mul_001_111, "Mul_001_111.csv", checked_mul); + gen_test!(test_mul_010_000, "Mul_010_000.csv", checked_mul); + gen_test!(test_mul_010_001, "Mul_010_001.csv", checked_mul); + gen_test!(test_mul_010_010, "Mul_010_010.csv", checked_mul); + gen_test!(test_mul_010_011, "Mul_010_011.csv", checked_mul); + gen_test!(test_mul_010_100, "Mul_010_100.csv", checked_mul); + gen_test!(test_mul_010_101, "Mul_010_101.csv", checked_mul); + gen_test!(test_mul_010_110, "Mul_010_110.csv", checked_mul); + gen_test!(test_mul_010_111, "Mul_010_111.csv", checked_mul); + gen_test!(test_mul_011_000, "Mul_011_000.csv", checked_mul); + gen_test!(test_mul_011_001, "Mul_011_001.csv", checked_mul); + gen_test!(test_mul_011_010, "Mul_011_010.csv", checked_mul); + gen_test!(test_mul_011_011, "Mul_011_011.csv", checked_mul); + gen_test!(test_mul_011_100, "Mul_011_100.csv", checked_mul); + gen_test!(test_mul_011_101, "Mul_011_101.csv", checked_mul); + gen_test!(test_mul_011_110, "Mul_011_110.csv", checked_mul); + gen_test!(test_mul_011_111, "Mul_011_111.csv", checked_mul); + gen_test!(test_mul_100_000, "Mul_100_000.csv", checked_mul); + gen_test!(test_mul_100_001, "Mul_100_001.csv", checked_mul); + gen_test!(test_mul_100_010, "Mul_100_010.csv", checked_mul); + gen_test!(test_mul_100_011, "Mul_100_011.csv", checked_mul); + gen_test!(test_mul_100_100, "Mul_100_100.csv", checked_mul); + gen_test!(test_mul_100_101, "Mul_100_101.csv", checked_mul); + gen_test!(test_mul_100_110, "Mul_100_110.csv", checked_mul); + gen_test!(test_mul_100_111, "Mul_100_111.csv", checked_mul); + gen_test!(test_mul_101_000, "Mul_101_000.csv", checked_mul); + gen_test!(test_mul_101_001, "Mul_101_001.csv", checked_mul); + gen_test!(test_mul_101_010, "Mul_101_010.csv", checked_mul); + gen_test!(test_mul_101_011, "Mul_101_011.csv", checked_mul); + gen_test!(test_mul_101_100, "Mul_101_100.csv", checked_mul); + gen_test!(test_mul_101_101, "Mul_101_101.csv", checked_mul); + gen_test!(test_mul_101_110, "Mul_101_110.csv", checked_mul); + gen_test!(test_mul_101_111, "Mul_101_111.csv", checked_mul); + gen_test!(test_mul_110_000, "Mul_110_000.csv", checked_mul); + gen_test!(test_mul_110_001, "Mul_110_001.csv", checked_mul); + gen_test!(test_mul_110_010, "Mul_110_010.csv", checked_mul); + gen_test!(test_mul_110_011, "Mul_110_011.csv", checked_mul); + gen_test!(test_mul_110_100, "Mul_110_100.csv", checked_mul); + gen_test!(test_mul_110_101, "Mul_110_101.csv", checked_mul); + gen_test!(test_mul_110_110, "Mul_110_110.csv", checked_mul); + gen_test!(test_mul_110_111, "Mul_110_111.csv", checked_mul); + gen_test!(test_mul_111_000, "Mul_111_000.csv", checked_mul); + gen_test!(test_mul_111_001, "Mul_111_001.csv", checked_mul); + gen_test!(test_mul_111_010, "Mul_111_010.csv", checked_mul); + gen_test!(test_mul_111_011, "Mul_111_011.csv", checked_mul); + gen_test!(test_mul_111_100, "Mul_111_100.csv", checked_mul); + gen_test!(test_mul_111_101, "Mul_111_101.csv", checked_mul); + gen_test!(test_mul_111_110, "Mul_111_110.csv", checked_mul); + gen_test!(test_mul_111_111, "Mul_111_111.csv", checked_mul); + + gen_test!(test_rem_000_001, "Rem_000_001.csv", checked_rem); + gen_test!(test_rem_000_010, "Rem_000_010.csv", checked_rem); + gen_test!(test_rem_000_011, "Rem_000_011.csv", checked_rem); + gen_test!(test_rem_000_100, "Rem_000_100.csv", checked_rem); + gen_test!(test_rem_000_101, "Rem_000_101.csv", checked_rem); + gen_test!(test_rem_000_110, "Rem_000_110.csv", checked_rem); + gen_test!(test_rem_000_111, "Rem_000_111.csv", checked_rem); + gen_test!(test_rem_001_000, "Rem_001_000.csv", checked_rem); + gen_test!(test_rem_001_001, "Rem_001_001.csv", checked_rem); + gen_test!(test_rem_001_010, "Rem_001_010.csv", checked_rem); + gen_test!(test_rem_001_011, "Rem_001_011.csv", checked_rem); + gen_test!(test_rem_001_100, "Rem_001_100.csv", checked_rem); + gen_test!(test_rem_001_101, "Rem_001_101.csv", checked_rem); + gen_test!(test_rem_001_110, "Rem_001_110.csv", checked_rem); + gen_test!(test_rem_001_111, "Rem_001_111.csv", checked_rem); + gen_test!(test_rem_010_000, "Rem_010_000.csv", checked_rem); + gen_test!(test_rem_010_001, "Rem_010_001.csv", checked_rem); + gen_test!(test_rem_010_010, "Rem_010_010.csv", checked_rem); + gen_test!(test_rem_010_011, "Rem_010_011.csv", checked_rem); + gen_test!(test_rem_010_100, "Rem_010_100.csv", checked_rem); + gen_test!(test_rem_010_101, "Rem_010_101.csv", checked_rem); + gen_test!(test_rem_010_110, "Rem_010_110.csv", checked_rem); + gen_test!(test_rem_010_111, "Rem_010_111.csv", checked_rem); + gen_test!(test_rem_011_000, "Rem_011_000.csv", checked_rem); + gen_test!(test_rem_011_001, "Rem_011_001.csv", checked_rem); + gen_test!(test_rem_011_010, "Rem_011_010.csv", checked_rem); + gen_test!(test_rem_011_011, "Rem_011_011.csv", checked_rem); + gen_test!(test_rem_011_100, "Rem_011_100.csv", checked_rem); + gen_test!(test_rem_011_101, "Rem_011_101.csv", checked_rem); + gen_test!(test_rem_011_110, "Rem_011_110.csv", checked_rem); + gen_test!(test_rem_011_111, "Rem_011_111.csv", checked_rem); + gen_test!(test_rem_100_000, "Rem_100_000.csv", checked_rem); + gen_test!(test_rem_100_001, "Rem_100_001.csv", checked_rem); + gen_test!(test_rem_100_010, "Rem_100_010.csv", checked_rem); + gen_test!(test_rem_100_011, "Rem_100_011.csv", checked_rem); + gen_test!(test_rem_100_100, "Rem_100_100.csv", checked_rem); + gen_test!(test_rem_100_101, "Rem_100_101.csv", checked_rem); + gen_test!(test_rem_100_110, "Rem_100_110.csv", checked_rem); + gen_test!(test_rem_100_111, "Rem_100_111.csv", checked_rem); + gen_test!(test_rem_101_000, "Rem_101_000.csv", checked_rem); + gen_test!(test_rem_101_001, "Rem_101_001.csv", checked_rem); + gen_test!(test_rem_101_010, "Rem_101_010.csv", checked_rem); + gen_test!(test_rem_101_011, "Rem_101_011.csv", checked_rem); + gen_test!(test_rem_101_100, "Rem_101_100.csv", checked_rem); + gen_test!(test_rem_101_101, "Rem_101_101.csv", checked_rem); + gen_test!(test_rem_101_110, "Rem_101_110.csv", checked_rem); + gen_test!(test_rem_101_111, "Rem_101_111.csv", checked_rem); + gen_test!(test_rem_110_000, "Rem_110_000.csv", checked_rem); + gen_test!(test_rem_110_001, "Rem_110_001.csv", checked_rem); + gen_test!(test_rem_110_010, "Rem_110_010.csv", checked_rem); + gen_test!(test_rem_110_011, "Rem_110_011.csv", checked_rem); + gen_test!(test_rem_110_100, "Rem_110_100.csv", checked_rem); + gen_test!(test_rem_110_101, "Rem_110_101.csv", checked_rem); + gen_test!(test_rem_110_110, "Rem_110_110.csv", checked_rem); + gen_test!(test_rem_110_111, "Rem_110_111.csv", checked_rem); + gen_test!(test_rem_111_000, "Rem_111_000.csv", checked_rem); + gen_test!(test_rem_111_001, "Rem_111_001.csv", checked_rem); + gen_test!(test_rem_111_010, "Rem_111_010.csv", checked_rem); + gen_test!(test_rem_111_011, "Rem_111_011.csv", checked_rem); + gen_test!(test_rem_111_100, "Rem_111_100.csv", checked_rem); + gen_test!(test_rem_111_101, "Rem_111_101.csv", checked_rem); + gen_test!(test_rem_111_110, "Rem_111_110.csv", checked_rem); + gen_test!(test_rem_111_111, "Rem_111_111.csv", checked_rem); + + gen_test!(test_sub_000_001, "Sub_000_001.csv", checked_sub); + gen_test!(test_sub_000_010, "Sub_000_010.csv", checked_sub); + gen_test!(test_sub_000_011, "Sub_000_011.csv", checked_sub); + gen_test!(test_sub_000_100, "Sub_000_100.csv", checked_sub); + gen_test!(test_sub_000_101, "Sub_000_101.csv", checked_sub); + gen_test!(test_sub_000_110, "Sub_000_110.csv", checked_sub); + gen_test!(test_sub_000_111, "Sub_000_111.csv", checked_sub); + gen_test!(test_sub_001_000, "Sub_001_000.csv", checked_sub); + gen_test!(test_sub_001_001, "Sub_001_001.csv", checked_sub); + gen_test!(test_sub_001_010, "Sub_001_010.csv", checked_sub); + gen_test!(test_sub_001_011, "Sub_001_011.csv", checked_sub); + gen_test!(test_sub_001_100, "Sub_001_100.csv", checked_sub); + gen_test!(test_sub_001_101, "Sub_001_101.csv", checked_sub); + gen_test!(test_sub_001_110, "Sub_001_110.csv", checked_sub); + gen_test!(test_sub_001_111, "Sub_001_111.csv", checked_sub); + gen_test!(test_sub_010_000, "Sub_010_000.csv", checked_sub); + gen_test!(test_sub_010_001, "Sub_010_001.csv", checked_sub); + gen_test!(test_sub_010_010, "Sub_010_010.csv", checked_sub); + gen_test!(test_sub_010_011, "Sub_010_011.csv", checked_sub); + gen_test!(test_sub_010_100, "Sub_010_100.csv", checked_sub); + gen_test!(test_sub_010_101, "Sub_010_101.csv", checked_sub); + gen_test!(test_sub_010_110, "Sub_010_110.csv", checked_sub); + gen_test!(test_sub_010_111, "Sub_010_111.csv", checked_sub); + gen_test!(test_sub_011_000, "Sub_011_000.csv", checked_sub); + gen_test!(test_sub_011_001, "Sub_011_001.csv", checked_sub); + gen_test!(test_sub_011_010, "Sub_011_010.csv", checked_sub); + gen_test!(test_sub_011_011, "Sub_011_011.csv", checked_sub); + gen_test!(test_sub_011_100, "Sub_011_100.csv", checked_sub); + gen_test!(test_sub_011_101, "Sub_011_101.csv", checked_sub); + gen_test!(test_sub_011_110, "Sub_011_110.csv", checked_sub); + gen_test!(test_sub_011_111, "Sub_011_111.csv", checked_sub); + gen_test!(test_sub_100_000, "Sub_100_000.csv", checked_sub); + gen_test!(test_sub_100_001, "Sub_100_001.csv", checked_sub); + gen_test!(test_sub_100_010, "Sub_100_010.csv", checked_sub); + gen_test!(test_sub_100_011, "Sub_100_011.csv", checked_sub); + gen_test!(test_sub_100_100, "Sub_100_100.csv", checked_sub); + gen_test!(test_sub_100_101, "Sub_100_101.csv", checked_sub); + gen_test!(test_sub_100_110, "Sub_100_110.csv", checked_sub); + gen_test!(test_sub_100_111, "Sub_100_111.csv", checked_sub); + gen_test!(test_sub_101_000, "Sub_101_000.csv", checked_sub); + gen_test!(test_sub_101_001, "Sub_101_001.csv", checked_sub); + gen_test!(test_sub_101_010, "Sub_101_010.csv", checked_sub); + gen_test!(test_sub_101_011, "Sub_101_011.csv", checked_sub); + gen_test!(test_sub_101_100, "Sub_101_100.csv", checked_sub); + gen_test!(test_sub_101_101, "Sub_101_101.csv", checked_sub); + gen_test!(test_sub_101_110, "Sub_101_110.csv", checked_sub); + gen_test!(test_sub_101_111, "Sub_101_111.csv", checked_sub); + gen_test!(test_sub_110_000, "Sub_110_000.csv", checked_sub); + gen_test!(test_sub_110_001, "Sub_110_001.csv", checked_sub); + gen_test!(test_sub_110_010, "Sub_110_010.csv", checked_sub); + gen_test!(test_sub_110_011, "Sub_110_011.csv", checked_sub); + gen_test!(test_sub_110_100, "Sub_110_100.csv", checked_sub); + gen_test!(test_sub_110_101, "Sub_110_101.csv", checked_sub); + gen_test!(test_sub_110_110, "Sub_110_110.csv", checked_sub); + gen_test!(test_sub_110_111, "Sub_110_111.csv", checked_sub); + gen_test!(test_sub_111_000, "Sub_111_000.csv", checked_sub); + gen_test!(test_sub_111_001, "Sub_111_001.csv", checked_sub); + gen_test!(test_sub_111_010, "Sub_111_010.csv", checked_sub); + gen_test!(test_sub_111_011, "Sub_111_011.csv", checked_sub); + gen_test!(test_sub_111_100, "Sub_111_100.csv", checked_sub); + gen_test!(test_sub_111_101, "Sub_111_101.csv", checked_sub); + gen_test!(test_sub_111_110, "Sub_111_110.csv", checked_sub); + gen_test!(test_sub_111_111, "Sub_111_111.csv", checked_sub); +} + +#[cfg(feature = "rocket-traits")] +mod rocket { + use crate::Decimal; + use rocket::form::{Form, FromForm}; + use std::str::FromStr; + + #[derive(FromForm)] + struct Example { + foo: Decimal, + bar: Decimal, + } + + #[test] + fn it_can_parse_form() { + let parsed: Example = Form::parse("bar=0.12345678901234567890123456789&foo=-123456.78").unwrap(); + assert_eq!(parsed.foo, Decimal::from_str("-123456.78").unwrap()); + assert_eq!( + parsed.bar, + Decimal::from_str("0.12345678901234567890123456789").unwrap() + ); + } +} + +#[cfg(feature = "rust-fuzz")] +mod rust_fuzz { + use arbitrary::{Arbitrary, Unstructured}; + + use super::*; + + #[test] + fn it_can_generate_arbitrary_decimals() { + let mut u = Unstructured::new(b"it_can_generate_arbitrary_decimals"); + let d = Decimal::arbitrary(&mut u); + assert!(d.is_ok()); + } +} + +mod issues { + use rust_decimal::prelude::*; + + #[test] + fn issue_384_neg_overflow_during_subtract_carry() { + // 288230376151711744 + let a = Decimal::from_parts(0, 67108864, 0, false, 0); + // 714606955844629274884780.85120 + let b = Decimal::from_parts(0, 0, 3873892070, false, 3873892070); + let c = a.checked_sub(b); + assert!(c.is_some()); + + // 288230376151711744. + // - 714606955844629274884780.85120 + // = + // - 714606667614253123173036.85120 + assert_eq!("-714606667614253123173036.85120", c.unwrap().to_string()); + } + + #[test] + fn issue_392_overflow_during_remainder() { + let a = Decimal::from_str("-79228157791897.854723898738431").unwrap(); + let b = Decimal::from_str("184512476.73336922111").unwrap(); + let c = a.checked_div(b); + assert!(c.is_some()); + assert_eq!("-429391.87200000000002327170816", c.unwrap().to_string()) + } +} diff --git a/third_party/rust/rust_decimal/tests/macros.rs b/third_party/rust/rust_decimal/tests/macros.rs new file mode 100644 index 0000000000..39cf0edf60 --- /dev/null +++ b/third_party/rust/rust_decimal/tests/macros.rs @@ -0,0 +1,49 @@ +#[macro_export] +macro_rules! assert_approx_eq { + ($a:expr, $b:expr, $precision:expr) => {{ + let (a, b) = (&$a, &$b); + let eps = Decimal::new(1, $precision); + let abs = (*a - *b).abs(); + assert!( + abs <= eps, + "assertion failed: `(left ~= right)` \n \ + left: `{:?}`,\n \ + right: `{:?}`,\n \ + expect: `{:?}`,\n \ + real: `{:?}`", + *a, + *b, + eps, + abs, + ); + }}; + ($a:expr, $b:expr, $precision:expr, $($arg:tt)+) => {{ + let (a, b) = (&$a, &$b); + let eps = Decimal::new(1, $precision); + let abs = (*a - *b).abs(); + assert!( + abs <= eps, + "assertion failed: `(left ~= right)` \n \ + left: `{:?}`,\n \ + right: `{:?}`,\n \ + expect: `{:?}`,\n \ + real: `{:?}`: {}", + *a, + *b, + eps, + abs, + format_args!($($arg)+), + ); + }}; +} + +#[macro_export] +macro_rules! either { + ($result:expr, $legacy_result:expr) => { + if cfg!(feature = "legacy-ops") { + $legacy_result + } else { + $result + } + }; +} diff --git a/third_party/rust/rust_decimal/tests/version-numbers.rs b/third_party/rust/rust_decimal/tests/version-numbers.rs new file mode 100644 index 0000000000..288592d02f --- /dev/null +++ b/third_party/rust/rust_decimal/tests/version-numbers.rs @@ -0,0 +1,9 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} |