fraction-0.15.3/.cargo_vcs_info.json0000644000000001360000000000100127270ustar { "git": { "sha1": "b4f32c14ab0d7bb6b8873eab900c5cf765a6adbc" }, "path_in_vcs": "" }fraction-0.15.3/.github/FUNDING.yml000064400000000000000000000000761046102023000146770ustar 00000000000000# These are supported funding model platforms github: dnsl48 fraction-0.15.3/.github/workflows/main.yml000064400000000000000000000027731046102023000165740ustar 00000000000000on: [push, pull_request] name: Main CI jobs: check: name: Check runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Run cargo check uses: actions-rs/cargo@v1 with: command: check test: name: Test runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Run cargo test uses: actions-rs/cargo@v1 with: command: test args: --verbose --all-features lints: name: Lints runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: rustfmt, clippy - name: Run cargo fmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - name: Run cargo clippy uses: actions-rs/cargo@v1 with: command: clippy args: --all-features -- -D warnings -A deprecatedfraction-0.15.3/.gitignore000064400000000000000000000000411046102023000135020ustar 00000000000000target Cargo.lock .idea/ .vscode fraction-0.15.3/CHANGELOG.md000064400000000000000000000315721046102023000133400ustar 00000000000000# Change Log ## [0.15.3] - 2024-05-12 ### Changed - `num` crate min required version is now `0.4.2` ## [0.15.2] - 2024-04-28 ### Added - GenericFraction ConstOne and ConstZero trait implementations (special thanks to Raimundo Saona, aka @saona-raimundo) ## [0.15.1] - 2024-02-11 ### Added - "with-unicode" feature implementation to format (and parse) floats with Unicode characters (special thanks to @feefladder) ## [0.15.0] - 2024-01-01 ### Added - GenericFraction try_from/try_into implementations for primitive types (u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64) - GenericFraction try_from/try_into implementations for BigInt/BigUint ("with-bigint" feature) - GenericDecimal try_from/try_into implementations for primitive types (u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64) - GenericDecimal try_from/try_into implementations for BigInt/BigUint ("with-bigint" feature) - Tests for all GenericDecimal ops (overloadable operators - std::ops) ### Changed - GenericDecimal ops (overloadable operators) refactoring. Each operator is now implemented separately in its own module, allowing decoupled code with its own optimisations and tests. ### Fixed - PartialOrd will now reuse Ord implementation where possible (refactor to make clippy happier) - Fixed logic in `sub_assign` and `checked_sub` that could sometimes produce incorrect results for a couple of edge cases with a negative zero (`-0`) as an operand. ## [0.14.0] - 2023-09-27 ### Added - `approx` module with "Approximate mathematical operations", `fn sqrt` (special thanks to @squ1dd13 for the contribution!) - `with-approx` feature, enabling the `approx` module ## [0.13.1] - 2023-02-25 ### Added - Clippy hint to allow manual filtering in GenericFraction::to_i64 implementation - A couple of tests for floor/ceil functions ### Fixed - ceil & floor incorrect behaviour for negative numbers (special thanks to Morris Hansing aka morri2) ## [0.13.0] - 2023-01-01 ### Changed - `PartialCmp` now compares `NaN` with the other values and returns it as the smallest possible value. Thus, `NaN < -Inf`. ### Fixed - `partial_cmp` now behaves exactly the same as `cmp` (PartialOrd is now consistent with Ord). This fixes an issue introduced in `0.12.2` with the implementation of `Ord`, where `Ord` would behave differently from `PartialOrd` with `NaN` values. Special thanks to Hsingai Tigris Altaica aka DrAlta for fixing this. ## [0.12.2] - 2022-12-04 ### Added - `Ord` trait implementation for GenericFraction and GenericDecimal (special thanks to Hsingai Tigris Altaica aka DrAlta) ## [0.12.1] - 2022-10-18 ### Added - Support to `add` castable values (e.g. `f + 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `sub` castable values (e.g. `f - 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `div` castable values (e.g. `f / 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `mul` castable values (e.g. `f * 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `add_assign` castable values (e.g. `f += 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `sub_assign` castable values (e.g. `f -= 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `div_assign` castable values (e.g. `f /= 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) - Support to `mul_assign` castable values (e.g. `f *= 1u8`, 1u8 will be transparently casted to `Fraction` with `.into()`) ### Changed - Refactoring of the fraction module. std::ops implementations moved into separate submodules. - generic::read_generic_integer performance improved for when target type matches source (~83% improvement, which is 5 times faster). As the result this can affect GenericFraction::from performance for non-float types. - From<(A, B)> implementation is migrated to GenericFraction::new_generic (~85% performance improvement and with no heap allocations, which is ~7 times faster). ## [0.12.0] - 2022-10-13 ### Changed - `num` version `0.4` is now required (`0.2`, `0.3` are no longer supported) - Multiple functions made const in GenericFraction, GenericDecimal and fraction::display::Format Special thanks to Stijn Frishert (aka stijnfrishert). ### Deprecated - fn `decimal::GenericDecimal::apply_ref` is deprecated. ### Removed - Removed deprecated fn `decimal::GenericDecimal::from_decimal_str`. Use `FromStr::from_str` instead. - Removed deprecated fn `fraction::GenericFraction::from_decimal_str`. Use `FromStr::from_str` instead. - Removed deprecated fn `fraction::GenericFraction::format_as_decimal`. Use `format!(\"{:.1$}\", fraction, precision)` instead. - Removed deprecated fn `fraction::GenericFraction::new_raw_neg`. Use `new_raw_signed` instead. ## [0.11.2] - 2022-09-18 - `DynaInt` now implements serde `Serialize & Unserialize` (Thanks to Richard Davies aka @optevo for the contribution!) ## [0.11.1] - 2022-08-07 ### Fixed - Fraction::from\_str trims trailing zeroes before calculating denom (Thanks to @khigia for the contribution!) ## [0.11.0] - 2022-06-19 ### Changed - `num` dependency versions extended from `0.2` to `>=0.2,<5` (Thanks to Joel Natividad aka jqnatividad for the contribution!) - `lazy_static` dependency versions extended from `1.1` to `1` ## [0.10.0] - 2022-02-07 ### Added - `std::str::FromStr` trait implementation for GenericFraction and GenericDecimal Special thanks to Scott Wilson for the contribution - Deprecated `GenericFraction::from_decimal_str` and `GenericDecimal::from_decimal_str` in favour of of `std::str::FromStr` ## [0.9.0] - 2021-08-22 ### Added - Default trait implementation for GenericFraction and GenericDecimal - `postgres-types: ^0.2` and `bytes: 1` are new optional dependencies (feature: with-postgres-support) - `GenericFraction::new_raw_signed` constructor ### Changed - Juniper supported version upgraded from 0.11 to 0.15 - Postgres supported version upgraded from 0.15 to 0.19 (might be down to 0.16, but untested). ### Removed - `postgres` crate is not a dependency any longer - Deprecated fn `GenericFraction::format_as_decimal` is removed ## [0.8.0] - 2020-12-17 ### Changed - More efficient f32/f64 conversion to Fractions and Decimals (up to 10 times faster and not using memory allocation anymore) Special thanks to Christopher Rabotin for the contribution! ## [0.7.0] - 2020-12-05 ### Added - fraction::display::Format implements Clone trait (becomes cloneable) - fraction::Sign implements PartialOrd and Ord traits (becomes orderable) - GenericDecimal::calc_precision max_precision optional argument to limit the calculation ### Changed - Decimal::from_fraction now limits precision calculation to 255 - rustfmt for the whole codebase - small readability refactoring for some methods ## [0.6.3] - 2020-05-07 ### Added - std::iter::{Sum, Product} implemented for GenericFraction and GenericDecimal ## [0.6.2] - 2019-05-20 ### Addad - std::error::Error implemented for error::ParseError ## [0.6.1] - 2019-03-29 ### Added - dynaint, `Into` implementation - fraction, `GenericFraction::into_fraction` method implementation ## [0.6.0] - 2019-02-05 ### Added - division::divide_to_callback implementation. Some other functions refactored to be using it internally - fraction::display module implementation, Fraction ::std::fmt::Display implementation supporting all the features of the std::fmt::Formatter - Decimal ::std::fmt::Display implementation supporting all the features of the std::fmt::Formatter ### Changed - Juniper updated to 0.11 - GenericFraction::format_as_decimal is now deprecated. Use format! macro instead, or division module if you need more control - division module functions signatures now have flags for trailing zeroes ### Removed - Deprecated functions ## [0.5.0] - 2018-11-26 ### Changed - Division module API; functions to return division state for later reuse (remainder and divisor) ## [0.4.1] - 2018-10-19 ### Added - `DynaInt`, initial `std::fmt::Display` implementation ## [0.4.0] - 2018-10-10 ### Bugs - `Hash` implementation for `GenericFraction` now returns equal hashes for negative and positive zeroes ### Added - Lossless division, fraction decimal representation with infinite precision - Decimal type, built on top of Fraction - PostgreSQL integration - Juniper integration - Types with dynamic growth into heap on overflow - Generic integer conversions (usize -> i8, i8 -> u8, etc) - Examples and documentation for new features - Re-exporting the bunch of `num` traits so that the library can be used without explicit dependency on `num` ### Refactoring - The lib has been split into modules with separate features ### Modules - `convert` module with traits for optimistic convesion - `decimal` module with `GenericDecimal` implementation, `Juniper` and `PostgreSQL` integration for it - `division` module with lossless infinite division implementation without memory allocation - `dynaint` module with `DynaInt` type implementation (dynamically growing integer) - `error` module with shared library error types - `fraction` module with `GenericFraction` implementation, `Juniper` and `PostgreSQL` integration for it - `generic` module with `GenericInteger` trait implementation, generic integer types conversion - `prelude` module with some predefined type aliases such as `Fraction` and `Decimal` - `tests` module with some tests ### Features - `with-bigint` (default), integration with `num::{BigInt, BigUint}` types - `with-decimal` (default), `GenericDecimal` type implementation - `with-dynaint` (default), `DynaInt` type implementation - `with-juniper-support`, `Juniper 0.10` integration - `with-postgres-support`, `Postgres 0.15` integration ### Changes - `GenericFraction` redundant methods deprecated: `new_nan`, `new_inf`, `new_inf_neg`, `into_big`, `format_as_float` ## [0.3.7] - 2017-11-10 ### Bugs - Fix comparisons with negative numbers ## [0.3.6] - 2017-07-27 ### Bugs - `T in GenericFraction` is `Clone + Integer` from now onwards (thanks to Taryn Hill aka Phrohdoh) ## [0.3.5] - 2017-04-17 ### Changed - `num` package dependency version updated from "0.1.36" to "0.1.37" ## [0.3.4] - 2016-12-11 ### Bugs - `fn _new` now returns NaN for 0/0 (was Infinity before) - `fn sign` now returns values for GenericFraction::Infinite values too - `fn neg_zero` now returns zero with negative sign (was positive before) - `fn recip` now handles zero values gracefully (does not panic, returns Infinity) ### Added - Lots of documentation ## [0.3.3] - 2016-11-18 ### Refactoring - More efficient implementation of `From<[unsigned ints]>` - More generic implementation of `From` ## [0.3.2] - 2016-11-13 ### Refactoring - `Zero::is_zero` to be used everywhere in math, rather than making new zero vals + comparing with them ### Added - `fn new_nan` constructor - `fn new_inf` constructor - `fn new_inf_neg` constructor ## [0.3.1] - 2016-11-12 ### Added - `fn format_as_float` implemented for `GenericFraction` (it was only available for BigFraction before) ### Changed - `Into` to be used in bounds rather than `From`, since it's more flexible (thanks to Alexander Altman for the patch) - number of bug fixes within `fn format_as_float` + test coverage ## [0.3.0] - 2016-11-08 ### Added - `From<(N, D)>` generic implementation (through std::fmt::Display) ### Changed - `GenericFraction` copy semantic to be applied only when `T: Clone` - `GenericFraction` impl, constructors refactoring (new, new_raw) - `num` upgraded up to `0.1.36` (from `0.1.34`) ### Removed - `fn new` does not perform type casting through fmt::Display anymore (that functionality moved out as `From<(N, D)>`) ## [0.2.2] - 2016-09-17 ### Added - `impl From for BigFraction` - `impl From for BigFraction` ## [0.2.1] - 2016-08-28 ### Changed - Package description has been changed ## [0.2.0] - 2016-08-28 ### Added - [num crate](https://crates.io/crates/num) is a dependency now - `GenericFraction` implemented upon `num::Ratio` - `BigFraction` implementation based on `num::BigRational` (using heap) - `num::traits::Bounded` trait implemented - `fn min_positive_value` implemented - `num::traits::ToPrimitive` trait implemented - `num::traits::Signed` trait implemented - `From` trait implementation now relies on `format!` macro instead of `f64::fract` - `BigFraction` struct using `num::BigUint` - `fn format_as_float` for BigFraction has been implemented ### Changed - The codebase has been rewritten and the license has been changed from `LGPL-3` to `MIT/Apache2` dual - no more convertions into INFINITY on arithmetic overflows - `fn to_f64` now returns `Option` instead of `f64` (`num::trait::ToPrimitive` implementation) - `From` trait implementation uses `fmt::Display` from now on ### Removed - `fn unpack` removed - `std::cmp::Ord` implementation removed in regard to `NaN` values ## [0.1.0] - 2016-01-24 ### Added - Basic implementation fraction-0.15.3/Cargo.toml0000644000000040400000000000100107230ustar # 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] name = "fraction" version = "0.15.3" authors = ["dnsl48 "] exclude = ["src/tests/division/*"] description = "Lossless fractions and decimals; drop-in float replacement" homepage = "https://github.com/dnsl48/fraction.git" documentation = "https://docs.rs/fraction/" readme = "README.md" keywords = [ "fraction", "decimal", "float", "numeric", ] categories = ["data-structures"] license = "MIT/Apache-2.0" repository = "https://github.com/dnsl48/fraction.git" [package.metadata.docs.rs] all-features = true [profile.test] opt-level = 3 [[bench]] name = "bench_fraction" harness = false [dependencies.byteorder] version = "1" optional = true [dependencies.bytes] version = "1" optional = true [dependencies.juniper] version = "0.15" optional = true [dependencies.lazy_static] version = "1" optional = true [dependencies.num] version = "0.4.2" default-features = false [dependencies.postgres-types] version = "0.2" optional = true [dependencies.serde] version = "1" optional = true [dependencies.serde_derive] version = "1" optional = true [dev-dependencies.criterion] version = "0.4" [dev-dependencies.rand] version = "0.8.5" [features] default = [ "with-bigint", "with-decimal", "with-dynaint", ] with-approx = ["with-bigint"] with-bigint = [ "num/num-bigint", "num/std", "lazy_static", ] with-decimal = [] with-dynaint = [] with-juniper-support = ["juniper"] with-postgres-support = [ "postgres-types", "byteorder", "bytes", ] with-serde-support = [ "serde", "serde_derive", "num/serde", ] with-unicode = [] fraction-0.15.3/Cargo.toml.orig000064400000000000000000000026621046102023000144140ustar 00000000000000[package] name = "fraction" version = "0.15.3" authors = ["dnsl48 "] description = "Lossless fractions and decimals; drop-in float replacement" keywords = ["fraction", "decimal", "float", "numeric"] categories = ["data-structures"] license = "MIT/Apache-2.0" homepage = "https://github.com/dnsl48/fraction.git" repository = "https://github.com/dnsl48/fraction.git" readme = "README.md" documentation = "https://docs.rs/fraction/" exclude = ["src/tests/division/*"] [package.metadata.docs.rs] all-features = true [profile.test] opt-level = 3 [dependencies] num = { version = "0.4.2", default-features = false } byteorder = { version = "1", optional = true } bytes = { version = "1", optional = true } postgres-types = { version = "0.2", optional = true } serde = { version = "1", optional = true } serde_derive = { version = "1", optional = true } juniper = { version = "0.15", optional = true } lazy_static = { version = "1", optional = true } [features] default = ["with-bigint", "with-decimal", "with-dynaint"] with-bigint = ["num/num-bigint", "num/std", "lazy_static"] with-decimal = [] with-dynaint = [] with-approx = ["with-bigint"] with-juniper-support = ["juniper"] with-postgres-support = ["postgres-types", "byteorder", "bytes"] with-serde-support = ["serde", "serde_derive", "num/serde"] with-unicode = [] [dev-dependencies] criterion = "0.4" rand = "0.8.5" [[bench]] name = "bench_fraction" harness = false fraction-0.15.3/LICENSE-APACHE000064400000000000000000000251371046102023000134530ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. fraction-0.15.3/LICENSE-MIT000064400000000000000000000020571046102023000131570ustar 00000000000000Copyright (c) 2014 The Rust Project Developers 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. fraction-0.15.3/README.md000064400000000000000000000200761046102023000130030ustar 00000000000000# Fraction Lossless fractions and decimals; drop-in float replacement ------ [![GitHub Actions](https://github.com/dnsl48/fraction/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/dnsl48/fraction/actions/workflows/main.yml?query=branch%3Amaster) [![Documentation](https://docs.rs/fraction/badge.svg)](https://docs.rs/fraction/) [![Current Version on crates.io](https://img.shields.io/crates/v/fraction.svg)](https://crates.io/crates/fraction/) [![MIT / Apache2 License](https://img.shields.io/badge/license-MIT%20/%20Apache2-blue.svg)]() ------ # Features - Fraction type, representing floats as fractions - Decimal type, based on Fraction type with explicit precision - Fractions are drop-in replacement for floats with the exception for NaN == NaN && NaN < NegInfinity, thus it's hasheable and orderable. - Fractions are hashable and orderable, so they may be used in Sets, HashMaps and BTrees. - DynaInt type, which is an int dynamically growing into heap. Performs checked math and avoids stack overflows. - PostgreSQL integration for Numeric/Decimal type (with no extra memory allocations) - Juniper integration for both fractions and decimals - Support for generic integer conversions, such as `i8 -> u8`, `usize -> u8` and so on - Lossless division with infinite precision and no memory allocations # Documentation Here: [![Documentation](https://docs.rs/fraction/badge.svg)](https://docs.rs/fraction/) # Examples ## Simple use: ```rust type F = fraction::Fraction; // choose the type accordingly with your needs (see prelude module docs) let two = F::from(0) + 2; // 0 + 2 = 2 let two_third = two / 3; // 2/3 = 0.666666[...] assert_eq!(F::from(2), two); assert_eq!(F::new(2u64, 3u64), two_third); assert_eq!("2/3", format!("{}", two_third)); // print as Fraction (by default) assert_eq!("0.6666", format!("{:.4}", two_third)); // format as decimal and print up to 4 digits after floating point ``` Decimal is implemented as a representation layer on top of Fraction. Thus, it is also lossless and may require explicit control over "precision" for comparison and formatting operations. ```rust type D = fraction::Decimal; // choose the type accordingly with your needs (see prelude module docs) let result = D::from(0.5) / 0.3; assert_eq!(format!("{}", result), "1.6"); // calculation result uses precision of the operands assert_eq!(format!("{:.4}", result), "1.6666"); // explicitly passing precision to format assert_eq!("1.6666", format!("{}", result.set_precision(4))); // the other way to set precision explicitly on Decimal ``` ## Construct: Fraction: ```rust use std::str::FromStr; use fraction::{Fraction, Sign}; // fraction crate also re-exports num::{One, Zero} traits for convenience. use fraction::{One, Zero}; // There are several ways to construct a fraction, depending on your use case // `new` - construct with numerator/denominator and normalize the fraction. // "Normalization" means it will always find the least common denominator // and convert the input accordingly. let f = Fraction::new(1u8, 2u8); // `new_generic` - construct with numerator/denominator of different integer types assert_eq!(f, Fraction::new_generic(Sign::Plus, 1i32, 2u8).unwrap()); // `from` - converts from primitive types such as i32 and f32. assert_eq!(f, Fraction::from(0.5)); // convert from float (f32, f64) // `from_str` - tries parse a string fraction. Supports the usual decimal notation. assert_eq!(f, Fraction::from_str("0.5").unwrap()); // parse a string // `from_str` - also supports _fraction_ notation such as "numerator/denominator" delimited by slash (`/`). assert_eq!(f, Fraction::from_str("1/2").unwrap()); // parse a string // `new_raw` - construct with numerator/denominator but do not normalize the fraction. // This is the most performant constructor, but does not calculate the common denominator, // so may lead to unexpected results in following calculations if the fraction is not normalised. // WARNING: Only use if you are sure numerator/denominator are already normalized. assert_eq!(f, Fraction::new_raw(1u64, 2u64)); // `one` - implements num::One trait assert_eq!(f * 2, Fraction::one()); // `zero` - implements num::Zero trait assert_eq!(f - f, Fraction::zero()); ``` Decimal: ```rust use std::str::FromStr; use fraction::{Decimal, Fraction}; fn main() { // There are similar ways to construct Decimal. Underneath it is always represented as Fraction. // When constructed, Decimal preserves its precision (number of digits after floating point). // When two decimals are calculated, the result takes the biggest precision of both. // The precision is used for visual representation (formatting and printing) and for comparison of two decimals. // Precision is NOT used in any calculations. All calculations are lossless and implemented through Fraction. // To override the precision use Decimal::set_precision. let d = Decimal::from(1); // from integer, precision = 0 assert_eq!(d, Decimal::from_fraction(Fraction::from(1))); // from fraction, precision is calculated from fraction let d = Decimal::from(1.3); // from float (f32, f64) assert_eq!(d, Decimal::from_str("1.3").unwrap()); let d = Decimal::from(0.5); // from float (f32, f64) assert_eq!(d, Decimal::from_str("1/2").unwrap()); } ``` ## Convert into/from other types Both `fraction` and `decimal` types implement - `from` and `try_into` for all built-in primitive types. - `from` and `try_into` for `BigInt` and `BigUint` when `with-bigint` feature enabled. ```rust use fraction::{Fraction, One, BigInt, BigUint}; use std::convert::TryInto; // Convert from examples (from primitives always succeed) assert_eq!(Fraction::from(1i8), Fraction::one()); assert_eq!(Fraction::from(1u8), Fraction::one()); assert_eq!(Fraction::from(BigInt::one()), Fraction::one()); assert_eq!(Fraction::from(BigUint::one()), Fraction::one()); assert_eq!(Fraction::from(1f32), Fraction::one()); assert_eq!(Fraction::from(1f64), Fraction::one()); // Convert into examples (try_into returns Result) assert_eq!(Ok(1i8), Fraction::one().try_into()); assert_eq!(Ok(1u8), Fraction::one().try_into()); assert_eq!(Ok(BigInt::one()), Fraction::one().try_into()); assert_eq!(Ok(BigUint::one()), Fraction::one().try_into()); assert_eq!(Ok(1f32), Fraction::one().try_into()); assert_eq!(Ok(1f64), Fraction::one().try_into()); ``` ### Format (convert to string) Formatting works the same for both Decimal and Fraction (Decimal uses Fraction internally). The format implementation closely follows the rust Format trait documentation. ```rust type F = fraction::Fraction; let result = F::from(0.7) / 0.4; assert_eq!(format!("{}", result), "7/4"); assert_eq!(format!("{:.2}", result), "1.75"); assert_eq!(format!("{:#.3}", result), "1.750"); ``` ### Generic integer constructor (construct with loose num/denom types) If you have `numerator` and `denominator` of two incompatible types, which cannot be implicitly casted to a single common type. E.g. - numerator `i32` - denominator `u32` ```rust use fraction::{Sign, GenericFraction}; type F = GenericFraction; let fra = F::new_generic(Sign::Plus, 1i8, 42usize).unwrap(); assert_eq!(fra, F::new(1u32, 42u32)); ``` ### Postgres usage notes It is recommended to use Decimal over Fraction for PostgreSQL interactions. When interacting with PostgreSQL, fraction type keeps the highest achievable precision up to 16383 digits after floating point. That may lead to suboptimal performance for such values as 1/3 or 1/7. Decimal has its own explicit precision, so there won't be accidental calculation of tens of thousands digits. PostgreSQL uses i16 for its binary protocol, so you'll have to use at least u16 as the base type for your GenericFraction/GenericDecimal. However, it is also possible to workaround via DynaInt. It is recommended to use `DynaInt` so that by default you have on-stack math, and if necessary heap memory gets allocated. Otherwise, both types (fractions and decimals) should work transparently in accordance with Postgres crate documentation. # Change Log Look into the [CHANGELOG.md](CHANGELOG.md) file for details fraction-0.15.3/benches/bench_fraction.rs000064400000000000000000000054311046102023000164430ustar 00000000000000extern crate criterion; extern crate fraction; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use fraction::generic; use fraction::{GenericDecimal, GenericFraction}; use std::str::FromStr; #[allow(clippy::missing_panics_doc)] pub fn criterion_benchmark(c: &mut Criterion) { c.bench_function("Decimal u128/u16 init", |b| { b.iter(|| GenericDecimal::::from(black_box(15978.649))); }); c.bench_function("Decimal i64/u16 init", |b| { b.iter(|| { let a = GenericDecimal::::from(black_box(15978.649)); let b = GenericDecimal::::from(black_box(-15978.649)); (a, b) }); }); c.bench_function("Convert int like from str", |b| { b.iter(|| { let a = GenericFraction::::from_str(black_box("1")); let b = GenericFraction::::from_str(black_box("100")); (a, b) }); }); c.bench_function("Convert float like from str", |b| { b.iter(|| { let a = GenericFraction::::from_str(black_box("1.0")); let b = GenericFraction::::from_str(black_box("100.001")); (a, b) }); }); c.bench_function("Convert fraction like from str", |b| { b.iter(|| { let a = GenericFraction::::from_str(black_box("1/1")); let b = GenericFraction::::from_str(black_box("255/255")); (a, b) }); }); c.bench_function("generic::read_generic_integer / i8 to u8", |b| { b.iter(|| generic::read_generic_integer::(black_box(14i8)).unwrap()); }); c.bench_function("generic::read_generic_integer / u8 to u8", |b| { b.iter(|| generic::read_generic_integer::(black_box(14u8)).unwrap()); }); c.bench_function("From couple", |b| { b.iter(|| GenericFraction::::from(black_box((3u8, 4u8)))); }); #[cfg(feature = "with-approx")] { let num2 = GenericFraction::::from(2); let small_num = fraction::BigFraction::new(1_u8, u128::MAX) / u128::MAX; let big_num = fraction::BigFraction::new(u128::MAX, 1_u8) * u128::MAX; let mut bench_dp = |n: u32| { let mut group = c.benchmark_group(format!("Sqrt {n}dp raw")); group.bench_function("2", |b| { b.iter(|| num2.sqrt_raw(n)); }); group.bench_function("Small", |b| { b.iter(|| small_num.sqrt_raw(n)); }); group.bench_function("Big", |b| { b.iter(|| big_num.sqrt_raw(n)); }); group.finish(); }; bench_dp(10_000); bench_dp(1_000); bench_dp(100); } } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); fraction-0.15.3/src/convert.rs000064400000000000000000000131651046102023000143420ustar 00000000000000//! Optimistic type conversion //! //! When [std::convert::TryFrom] and [std::convert::TryInto] get stabilised //! they should be used instead. However, at this point we need our //! own implementation for optional conversion between types. //! //! The particular use case is when we have a data type claiming //! more space than it's using and we want to move the value into //! another, smaller data type. Such operation may only be performed //! at runtime with particular checks that the value fits into the //! smaller data type. Otherwise we cannot perform safe cast. //! //! # Examples //! Integer conversion //! ``` //! use fraction::convert::TryToConvertFrom; //! //! assert_eq!(Some(255u8), u8::try_to_convert_from(255u16)); //! assert_eq!(None, u8::try_to_convert_from(256u16)); //! ``` //! //! Fraction conversion //! ``` //! use fraction::{GenericFraction, convert::TryToConvertFrom, One}; //! //! type F8 = GenericFraction; //! type F16 = GenericFraction; //! //! let f8_255 = F8::new(255u8, 1u8); //! let f16_255 = F16::try_to_convert_from(f8_255).unwrap(); //! let f16_256 = f16_255 + F16::one(); //! //! assert_eq!(Some(f8_255), F8::try_to_convert_from(f16_255)); //! assert_eq!(None, F8::try_to_convert_from(f16_256)); //! ``` use generic::{read_generic_integer, GenericInteger}; use num::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Integer, One}; use super::GenericFraction; #[cfg(feature = "with-bigint")] use super::{BigInt, BigUint}; pub trait TryToConvertFrom: Sized { fn try_to_convert_from(src: F) -> Option; } macro_rules! convert_impl { (unsigned; $($T:ty),+) => { $( impl TryToConvertFrom for $T where T: GenericInteger + Clone + One + CheckedAdd + CheckedDiv + CheckedMul + CheckedSub + PartialOrd, $T: GenericInteger { fn try_to_convert_from(src: T) -> Option { read_generic_integer::(src) .map_or_else( || None, |(sign, val)| if sign.is_positive () { Some(val) } else { None } ) } } )+ }; (signed; $($T:ty),+) => { $( impl TryToConvertFrom for $T where T: GenericInteger + Clone + One + CheckedAdd + CheckedDiv + CheckedMul + CheckedSub + PartialOrd, $T: GenericInteger { fn try_to_convert_from(src: T) -> Option { read_generic_integer::(src) .map_or_else( || None, |(sign, val)| Some(if sign.is_negative() {-val} else {val}) ) } } )+ }; /* when Rust gets specializations we'll be able to do cheaper conversions for some types (cast; $($F:ty => $T:ty),+) => { $( impl TryToConvertFrom<$F> for $T { fn try_to_convert_from(src: $F) -> Option<$T> { Some(src as $T) } } )+ }; */ } convert_impl!(unsigned; u8, u16, u32, u64, u128, usize); convert_impl!(signed; i8, i16, i32, i64, i128, isize); #[cfg(feature = "with-bigint")] convert_impl!(unsigned; BigUint); #[cfg(feature = "with-bigint")] convert_impl!(signed; BigInt); impl TryToConvertFrom> for GenericFraction where T: TryToConvertFrom + Clone + Integer, F: Clone + Integer, { fn try_to_convert_from(src: GenericFraction) -> Option { match src { GenericFraction::NaN => Some(GenericFraction::NaN), GenericFraction::Infinity(sign) => Some(GenericFraction::Infinity(sign)), GenericFraction::Rational(sign, ratio) => { let (n, d): (F, F) = ratio.into(); let numer = >::try_to_convert_from(n)?; let denom = >::try_to_convert_from(d)?; Some(GenericFraction::Rational( sign, super::Ratio::new_raw(numer, denom), )) } } } } #[cfg(test)] mod tests { use convert::TryToConvertFrom; use fraction::GenericFraction; type F1 = GenericFraction; type F2 = GenericFraction; #[test] fn try_to_convert_integers() { { assert_eq!(Some(127i8), i8::try_to_convert_from(127u8)); assert_eq!(Some(127u8), u8::try_to_convert_from(127i8)); assert_eq!(None, i8::try_to_convert_from(128u8)); assert_eq!(None, u8::try_to_convert_from(-127i8)); assert_eq!(None, u8::try_to_convert_from(256u16)); } } #[test] fn try_to_convert_generic_fraction() { { let fu = F1::new(1u8, 2u8); let fi = F2::try_to_convert_from(fu); assert!(fi.is_some()); assert_eq!(fi.unwrap(), F2::new(1i8, 2i8)); } { let fi = F2::new(1i8, 2i8); let fu = F1::try_to_convert_from(fi); assert!(fu.is_some()); assert_eq!(fu.unwrap(), F1::new(1u8, 2u8)); } { let fu = F1::infinity(); let fi = F2::try_to_convert_from(fu); assert!(fi.is_some()); assert_eq!(fi.unwrap(), F2::infinity()); } { let fu = F1::neg_infinity(); let fi = F2::try_to_convert_from(fu); assert!(fi.is_some()); assert_eq!(fi.unwrap(), F2::neg_infinity()); } } } fraction-0.15.3/src/decimal/approx.rs000064400000000000000000000036561046102023000155750ustar 00000000000000use crate::{ approx::sqrt::RawApprox, fraction::approx::Accuracy, generic::GenericInteger, GenericDecimal, }; use num::{ bigint::{ToBigInt, ToBigUint}, BigUint, Integer, }; use std::borrow::Borrow; impl< T: Clone + Integer + ToBigUint + ToBigInt + GenericInteger, P: Copy + Integer + Into, > GenericDecimal { /// See `GenericFraction::sqrt_abs_with_accuracy_raw`. pub fn sqrt_abs_with_accuracy_raw(&self, accuracy: impl Borrow) -> RawApprox { self.0.sqrt_abs_with_accuracy_raw(accuracy) } /// See `GenericFraction::sqrt_abs_with_accuracy`. pub fn sqrt_abs_with_accuracy( &self, accuracy: impl Borrow, ) -> GenericDecimal { GenericDecimal(self.0.sqrt_abs_with_accuracy(accuracy), self.1) } /// See `GenericFraction::sqrt_abs_raw`. pub fn sqrt_abs_raw(&self, decimal_places: u32) -> RawApprox { self.0.sqrt_abs_raw(decimal_places) } /// See `GenericFraction::sqrt_abs`. pub fn sqrt_abs(&self, decimal_places: u32) -> GenericDecimal { GenericDecimal(self.0.sqrt_abs(decimal_places), self.1) } /// See `GenericFraction::sqrt_with_accuracy_raw`. pub fn sqrt_with_accuracy_raw(&self, accuracy: impl Borrow) -> RawApprox { self.0.sqrt_with_accuracy_raw(accuracy) } /// See `GenericFraction::sqrt_with_accuracy`. pub fn sqrt_with_accuracy( &self, accuracy: impl Borrow, ) -> GenericDecimal { GenericDecimal(self.0.sqrt_with_accuracy(accuracy), self.1) } /// See `GenericFraction::sqrt_raw`. pub fn sqrt_raw(&self, decimal_places: u32) -> RawApprox { self.0.sqrt_raw(decimal_places) } /// See `GenericFraction::sqrt`. pub fn sqrt(&self, decimal_places: u32) -> GenericDecimal { GenericDecimal(self.0.sqrt(decimal_places), self.1) } } fraction-0.15.3/src/decimal/juniper_support.rs000064400000000000000000000122531046102023000175250ustar 00000000000000use super::GenericDecimal; use generic::GenericInteger; use juniper::{ParseScalarResult, ParseScalarValue, Value}; use std::fmt; use std::str::FromStr; impl<__S, T, P> ::juniper::GraphQLValueAsync<__S> for GenericDecimal where Self: Sync, Self::TypeInfo: Sync, Self::Context: Sync, T: Clone + GenericInteger + From + fmt::Display, P: Copy + GenericInteger + Into + From, __S: ::juniper::ScalarValue + Send + Sync, { fn resolve_async<'a>( &'a self, info: &'a Self::TypeInfo, selection_set: Option<&'a [::juniper::Selection<__S>]>, executor: &'a ::juniper::Executor, ) -> ::juniper::BoxFuture<'a, ::juniper::ExecutionResult<__S>> { use juniper::futures::future; let v = ::juniper::GraphQLValue::resolve(self, info, selection_set, executor); Box::pin(future::ready(v)) } } impl ::juniper::marker::IsInputType for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger + From + fmt::Display, P: Copy + GenericInteger + Into + From, { } impl ::juniper::marker::IsOutputType for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger, P: Copy + GenericInteger + Into + From, { } impl ::juniper::GraphQLType for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger + From + fmt::Display, P: Copy + GenericInteger + Into + From, { fn name(_: &Self::TypeInfo) -> Option<&'static str> { Some("Decimal") } fn meta<'r>( info: &Self::TypeInfo, registry: &mut ::juniper::Registry<'r, S>, ) -> ::juniper::meta::MetaType<'r, S> where S: 'r, { registry .build_scalar_type::(info) .description("Decimal") .into_meta() } } impl ::juniper::GraphQLValue for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger + From + fmt::Display, P: Copy + GenericInteger + Into + From, { type Context = (); type TypeInfo = (); fn type_name<'__i>(&self, info: &'__i Self::TypeInfo) -> Option<&'__i str> { >::name(info) } fn resolve( &self, _info: &(), _selection: Option<&[::juniper::Selection]>, _executor: &::juniper::Executor, ) -> ::juniper::ExecutionResult { Ok(Value::scalar(format!("{}", self))) } } impl ::juniper::ToInputValue for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn to_input_value(&self) -> ::juniper::InputValue { ::juniper::ToInputValue::to_input_value(&format!("{}", self)) } } impl ::juniper::FromInputValue for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger, P: Copy + GenericInteger + Into + From, { fn from_input_value(value: &::juniper::InputValue) -> Option { { let val = match value.as_string_value() { None => return None, Some(string) => string, }; Some(FromStr::from_str(val).ok())? } } } impl ::juniper::ParseScalarValue for GenericDecimal where S: ::juniper::ScalarValue, T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn from_str(value: ::juniper::parser::ScalarToken<'_>) -> ParseScalarResult<'_, S> { { >::from_str(value) } } } #[cfg(test)] mod tests { use super::*; use fraction::GenericFraction; use juniper::{FromInputValue, InputValue, ToInputValue}; type D = GenericDecimal; type F = GenericFraction; fn get_tests() -> Vec<(&'static str, D)> { vec![ ("NaN", D::nan()), ("-inf", D::neg_infinity()), ("inf", D::infinity()), ("0", D::from_fraction(F::new(0u64, 1u64))), ("2", D::from_fraction(F::new(2u64, 1u64))), ("-2", D::from_fraction(F::new_neg(2u64, 1u64))), ("0.5", D::from_fraction(F::new(1u64, 2u64))), ("-0.5", D::from_fraction(F::new_neg(1u64, 2u64))), ("0.625", D::from_fraction(F::new(5u64, 8u64))), ( "246.5856420264", D::from_fraction(F::new(308232052533u64, 1250000000u64)), ), ] } #[test] fn to_input_value() { for (s, v) in get_tests() { let value = ::to_input_value(&v); let str_value = value.as_scalar_value::(); assert!(str_value.is_some()); assert_eq!(s, str_value.unwrap()); } } #[test] fn from_input_value() { for (s, v) in get_tests() { let value = ::from_input_value(&InputValue::scalar(s.to_owned())); assert_eq!(value, Some(v)); assert_eq!(value.unwrap().get_precision(), v.get_precision()) } } } fraction-0.15.3/src/decimal/mod.rs000064400000000000000000001130061046102023000150320ustar 00000000000000use convert::TryToConvertFrom; use error; use num::integer::Integer; use num::traits::{ Bounded, CheckedAdd, CheckedMul, CheckedSub, FromPrimitive, Num, One, Signed, ToPrimitive, Zero, }; use std::cmp::{self, Eq, Ordering, PartialEq, PartialOrd}; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::num::FpCategory; use std::ops::{Add, DivAssign, Mul, MulAssign, Neg, SubAssign}; use std::str::FromStr; use super::{GenericFraction, Sign}; use division; use fraction::display; use generic::GenericInteger; #[cfg(feature = "with-bigint")] use super::{BigInt, BigUint}; #[cfg(feature = "with-postgres-support")] mod postgres_support; #[cfg(feature = "with-juniper-support")] mod juniper_support; #[cfg(feature = "with-approx")] mod approx; mod ops; mod try_from; /// Decimal type implementation /// /// T is the type for data /// P is the type for precision /// /// Uses [GenericFraction] internally to represent the data. /// Precision is being used for representation purposes only. /// Calculations do not use precision, but comparison does. /// /// # Examples /// /// ``` /// use fraction::GenericDecimal; /// /// type Decimal = GenericDecimal; /// /// let d1 = Decimal::from(12); /// let d2 = Decimal::from(0.5); /// /// let mul = d1 * d2; /// let div = d1 / d2; /// let add = d1 + d2; /// /// assert_eq!(mul, 6.into()); /// assert_eq!(div, Decimal::from("24.00")); /// assert_eq!(add, Decimal::from(12.5)); /// ``` #[derive(Clone)] #[cfg_attr(feature = "with-serde-support", derive(Serialize, Deserialize))] pub struct GenericDecimal(pub(crate) GenericFraction, pub(crate) P) where T: Clone + Integer, P: Copy + Integer + Into; impl Copy for GenericDecimal where T: Copy + Integer, P: Copy + Integer + Into, { } impl Default for GenericDecimal where T: Clone + Integer, P: Copy + Integer + Into, { fn default() -> Self { Self(GenericFraction::default(), P::zero()) } } impl fmt::Display for GenericDecimal where T: Clone + GenericInteger, P: Copy + Integer + Into, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self { GenericDecimal(ref fraction, precision) => { let format = display::Format::new(formatter).set_precision(Some( formatter.precision().unwrap_or_else(|| precision.into()), )); display::format_fraction(fraction, formatter, &format) } } } } impl fmt::Debug for GenericDecimal where T: Clone + GenericInteger + From + ToPrimitive + fmt::Debug, P: Copy + Integer + Into, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { GenericDecimal(ref fraction, precision) => { let prec = precision.into(); let debug_prec = f.precision().unwrap_or(32); let value = format!("{:.1$}", fraction, prec); let debug_value = format!("{:.1$}", fraction, debug_prec); write!( f, "GenericDecimal({} | prec={}; {:?}; {})", value, prec, fraction, debug_value ) } } } } impl FromStr for GenericDecimal where T: Clone + GenericInteger + CheckedAdd + CheckedMul + CheckedSub, P: Copy + GenericInteger + Into + From + CheckedAdd, { type Err = error::ParseError; fn from_str(val: &str) -> Result { if val == "NaN" { Ok(Self::nan()) } else if val == "-inf" { Ok(Self::neg_infinity()) } else if val == "+inf" || val == "inf" { Ok(Self::infinity()) } else { // Check if the number is float like (1.0, 123.456, etc). if let Some(split_idx) = val.find('.') { let mut prec_iter = val.len() - split_idx - 1; let mut precision: P = P::zero(); loop { if prec_iter == 0 { break; } prec_iter -= 1; if let Some(p) = precision.checked_add(&P::one()) { precision = p; } else { break; } } Ok(GenericDecimal::from_str_radix(val, 10)?.set_precision(precision)) // Check if the number is fraction like (1/1, 123/456, etc). } else if val.find('/').is_some() { Ok(GenericDecimal(GenericFraction::from_str(val)?, 16u8.into())) // Check if the number is int like (1, 123, etc). } else { Ok(GenericDecimal::from_str_radix(val, 10)?.set_precision(P::zero())) } } } } macro_rules! dec_impl { (impl_trait_math_unary; $trait:ident, $fn:ident) => { impl $trait for GenericDecimal where T: Clone + GenericInteger, P: Copy + Integer + Into { type Output = Self; fn $fn(self) -> Self::Output { match self { GenericDecimal(sf, sp) => GenericDecimal($trait::$fn(sf), sp) } } } impl<'a, T, P> $trait for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + Integer + Into, &'a T: $trait { type Output = GenericDecimal; fn $fn(self) -> Self::Output { match self { GenericDecimal(sf, sp) => GenericDecimal($trait::$fn(sf), *sp) } } } }; (impl_trait_cmp; $trait:ident; $fn:ident; $return:ty) => { impl $trait for GenericDecimal where T: Clone + GenericInteger + $trait, P: Copy + GenericInteger + Into { fn $fn(&self, other: &Self) -> $return { match self { GenericDecimal(sf, _) => match other { GenericDecimal(of, _) => $trait::$fn(sf, of) } } } } }; (impl_trait_proxy; $trait:ident; $(($fn:ident ; $self:tt ; ; $return:ty)),*) => { impl $trait for GenericDecimal where T: Clone + GenericInteger + $trait, P: Copy + GenericInteger + Into {$( dec_impl!(_impl_trait_proxy_fn; $trait; $self; $fn; ; $return); )*} }; (_impl_trait_proxy_fn; $trait:ident; rself; $fn:ident ; ; $return:ty) => { fn $fn(&self) -> $return { match self { GenericDecimal(f, _) => { as $trait>::$fn(f) } } } }; (impl_trait_from_int; $($t:ty),*) => {$( impl From<$t> for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into { fn from(value: $t) -> Self { GenericDecimal(GenericFraction::from(value), P::zero()) } } )*}; (impl_trait_from_float; $($t:ty),*) => {$( impl From<$t> for GenericDecimal where T: Clone + GenericInteger + FromPrimitive, P: Copy + GenericInteger + Into + From + Bounded { fn from(value: $t) -> Self { if value.is_nan () { return GenericDecimal::nan() }; if value.is_infinite () { return if value.is_sign_negative () { GenericDecimal::neg_infinity() } else { GenericDecimal::infinity() } }; GenericDecimal(GenericFraction::from(value), P::zero()).calc_precision(None) } } )*} } dec_impl!(impl_trait_from_float; f32, f64); dec_impl!(impl_trait_from_int; u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize); impl<'a, T, P> From<&'a str> for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into + From, { fn from(value: &'a str) -> Self { GenericDecimal::from_str(value).unwrap_or_else(|_| GenericDecimal::nan()) } } #[cfg(feature = "with-bigint")] dec_impl!(impl_trait_from_int; BigUint, BigInt); dec_impl!(impl_trait_math_unary; Neg, neg); dec_impl!(impl_trait_proxy; ToPrimitive; (to_i64; rself;; Option), (to_u64; rself;; Option), (to_f64; rself;; Option) ); impl Sum for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn sum>(iter: I) -> Self { iter.fold(GenericDecimal::::zero(), Add::add) } } impl<'a, T, P> Sum<&'a GenericDecimal> for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn sum>(iter: I) -> Self { let mut sum = Self::zero(); for x in iter { sum += x; } sum } } impl Product for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn product>(iter: I) -> Self { iter.fold(GenericDecimal::::one(), Mul::mul) } } impl<'a, T, P> Product<&'a GenericDecimal> for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn product>(iter: I) -> Self { let mut sum = Self::one(); for x in iter { sum *= x; } sum } } dec_impl!(impl_trait_cmp; Ord; cmp; Ordering); impl PartialOrd for GenericDecimal where T: Clone + GenericInteger + PartialOrd, P: Copy + GenericInteger + Into, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl PartialEq for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn eq(&self, other: &Self) -> bool { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => { if sf.sign() != of.sign() { return false; } if sf.is_zero() { return of.is_zero(); } if of.is_zero() { return false; } if !sf.is_normal() || !of.is_normal() { return PartialEq::eq(sf, of); } if sp == op && PartialEq::eq(sf, of) { return true; } // if either precision or fractions are not equal, // then we potentially have different numbers represented so // we need to figure out their numeric representation and compare it if let GenericFraction::Rational(_, sr) = sf { if let GenericFraction::Rational(_, or) = of { let (si, srem) = sr.numer().div_rem(sr.denom()); let (oi, orem) = or.numer().div_rem(or.denom()); if si != oi { return false; } let mut s_state = Some(division::DivisionState::new(srem, sr.denom().clone())); let mut o_state = Some(division::DivisionState::new(orem, or.denom().clone())); let mut s_digit: u8 = 0; let mut o_digit: u8 = 0; let mut precision: usize = 0; loop { s_state = if let Ok(s) = division::divide_rem_resume(s_state.take().unwrap(), |s, d| { s_digit = d; Ok(Err(s)) }) { if s.remainder.is_zero() { None } else { Some(s) } } else { return false; }; o_state = if let Ok(s) = division::divide_rem_resume(o_state.take().unwrap(), |s, d| { o_digit = d; Ok(Err(s)) }) { if s.remainder.is_zero() { None } else { Some(s) } } else { return false; }; if s_digit != o_digit { return false; } precision += 1; if precision == (*sp).into() { if sp == op { return true; } if op > sp { for _ in 0..(*op - *sp).into() { if let Some(state) = o_state.take() { let div_result = division::divide_rem_resume( state, |state, digit| { o_digit = digit; Ok(Err(state)) }, ); o_state = match div_result { Ok(s) => { if s.remainder.is_zero() { None } else { Some(s) } } Err(_) => return false, }; if o_digit != 0 { return false; } } else { return true; } } return true; } else { return o_state.is_none(); } } if precision == (*op).into() { if sp > op { for _ in 0..(*sp - *op).into() { if let Some(state) = s_state.take() { let div_result = division::divide_rem_resume( state, |state, digit| { s_digit = digit; Ok(Err(state)) }, ); s_state = match div_result { Ok(s) => { if s.remainder.is_zero() { None } else { Some(s) } } Err(_) => return false, }; if s_digit != 0 { return false; } } else { return true; } } return true; } else { return s_state.is_none(); } } if s_state.is_none() { return o_state.is_none(); } if o_state.is_none() { return s_state.is_none(); } } } else { unreachable!() } } else { unreachable!() } } }, } } } impl Hash for GenericDecimal where T: Clone + GenericInteger + PartialEq, P: Copy + GenericInteger + Into, { fn hash(&self, state: &mut H) { match self { GenericDecimal(fraction, precision) => match fraction { GenericFraction::NaN => state.write_u8(0u8), GenericFraction::Infinity(sign) => { if let Sign::Plus = sign { state.write_u8(1u8) } else { state.write_u8(2u8) } } GenericFraction::Rational(sign, ratio) => { if let Sign::Plus = sign { state.write_u8(3u8) } else { state.write_u8(4u8) } let num = ratio.numer(); let den = ratio.denom(); let div_state = division::divide_integral(num.clone(), den.clone(), |digit: u8| { state.write_u8(digit); Ok(true) }) .ok(); if !precision.is_zero() { let mut dot = false; let mut trailing_zeroes: usize = 0; if let Some(div_state) = div_state { if !div_state.remainder.is_zero() { let mut precision = *precision; division::divide_rem( div_state.remainder, div_state.divisor, |s, digit: u8| { precision -= P::one(); if digit == 0 { trailing_zeroes += 1; } else { if !dot { dot = true; state.write_u8(10u8); } if trailing_zeroes > 0 { trailing_zeroes = 0; state.write_usize(trailing_zeroes); } state.write_u8(digit); } Ok(if precision.is_zero() { Err(s) } else { Ok(s) }) }, ) .ok(); } } } } }, }; } } impl Eq for GenericDecimal where T: Clone + GenericInteger + Eq, P: Copy + GenericInteger + Into, { } impl Bounded for GenericDecimal where T: Clone + GenericInteger + Bounded, P: Copy + GenericInteger + Into + Bounded, { fn min_value() -> Self { GenericDecimal(GenericFraction::min_value(), P::max_value()) } fn max_value() -> Self { GenericDecimal(GenericFraction::max_value(), P::max_value()) } } impl Zero for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into + Zero, { fn zero() -> Self { GenericDecimal(GenericFraction::zero(), P::zero()) } fn is_zero(&self) -> bool { match self { GenericDecimal(fra, _) => fra.is_zero(), } } } impl One for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn one() -> Self { GenericDecimal(GenericFraction::one(), P::zero()) } } impl Num for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into + From, { type FromStrRadixErr = error::ParseError; fn from_str_radix(value: &str, base: u32) -> Result { if base != 10 { return Err(error::ParseError::UnsupportedBase); } Ok(GenericDecimal( GenericFraction::from_str(value)?, 16u8.into(), )) } } impl Signed for GenericDecimal where T: Clone + GenericInteger + Neg, P: Copy + GenericInteger + Into + From, { fn abs(&self) -> Self { match self { GenericDecimal(fra, pres) => GenericDecimal(fra.abs(), *pres), } } fn abs_sub(&self, other: &Self) -> Self { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(sf.abs_sub(of), cmp::max(*sp, *op)), }, } } fn signum(&self) -> Self { match self { GenericDecimal(fra, pres) => GenericDecimal(fra.signum(), *pres), } } fn is_positive(&self) -> bool { match self { GenericDecimal(f, _) => f.is_positive(), } } fn is_negative(&self) -> bool { match self { GenericDecimal(f, _) => f.is_negative(), } } } impl GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { /// Returns Some(Sign) of the decimal, or None if NaN is the value pub const fn sign(&self) -> Option where T: CheckedAdd + CheckedMul + CheckedSub, { self.0.sign() } /// Sets representational precision for the Decimal /// The precision is only used for comparison and representation, not for calculations. /// /// This is suggested method for you to utilise if you know what precision you want to /// work with. /// /// # Examples /// /// ``` /// use fraction::GenericDecimal; /// /// type D = GenericDecimal; /// /// let first = D::from("0.004") // initial precision is 4 /// .set_precision(2); // but we want to work with 2 /// let second = D::from("0.006").set_precision(2); /// /// // Even though "first" and "second" both have precision 2 /// // the actual calculations are still performed with their /// // exact initial precision /// assert_eq!(first + second, D::from("0.01")); /// /// // The comparison, on the other hand, takes the precision into account /// // so the actual compared numbers will be calculated up the highest /// // precision of both operands /// assert_ne!( // compares "0.010" with "0.011" /// D::from("0.01"), // has precision 2 /// D::from("0.011") // has precision 3 /// ); /// /// assert_eq!( // compares "0.01" with "0.01" /// D::from("0.01").set_precision(2), /// D::from("0.011").set_precision(2) /// ); /// ``` pub fn set_precision(self, precision: P) -> Self { match self { GenericDecimal(fraction, _) => GenericDecimal(fraction, precision), } } /// Returns the current representational precision for the Decimal pub const fn get_precision(&self) -> P { match self { GenericDecimal(_, precision) => *precision, } } /// Try to recalculate the representational precision /// depending on the internal Fraction, which is the actual value. /// /// Performs the actual division until the exact decimal value is calculated, /// the precision type (P) capacity is reached (e.g. 255 for u8) or max_precision /// is reached, if it is given. /// /// # WARNING /// You only need this method if you want to find the max available /// precision for the current decimal value. /// However, keep in mind that irrational values (such as 1/3) do not have finite precision, /// so if this method returns P::MAX (or max_precision), most likely you have /// an irrational value. /// Be careful with max numbers for `usize` - that can take very long time to /// compute (more than a minute) pub fn calc_precision(self, max_precision: Option

) -> Self where T: CheckedMul + DivAssign + MulAssign + SubAssign + ToPrimitive + GenericInteger, P: Bounded + CheckedAdd, { match self { GenericDecimal(fraction, _) => { let precision = match fraction { GenericFraction::NaN => P::zero(), GenericFraction::Infinity(_) => P::zero(), GenericFraction::Rational(_, ref ratio) => { let mut precision: P = P::zero(); let max_precision: P = max_precision.unwrap_or_else(P::max_value); let num = ratio.numer(); let den = ratio.denom(); if let Ok(div_state) = division::divide_integral(num.clone(), den.clone(), |_| Ok(true)) { if !div_state.remainder.is_zero() { let one = P::one(); let _result = division::divide_rem( div_state.remainder, div_state.divisor, |s, _| { if precision >= max_precision { // stop here, we have reached the limit return Ok(Err(s)); } precision = if let Some(p) = precision.checked_add(&one) { p } else { return Ok(Err(s)); }; Ok(Ok(s)) }, ); } } precision } }; GenericDecimal(fraction, precision) } } } #[inline] pub fn nan() -> Self { GenericDecimal(GenericFraction::nan(), P::zero()) } #[inline] pub fn infinity() -> Self { GenericDecimal(GenericFraction::infinity(), P::zero()) } #[inline] pub fn neg_infinity() -> Self { GenericDecimal(GenericFraction::neg_infinity(), P::zero()) } #[inline] pub fn neg_zero() -> Self { GenericDecimal(GenericFraction::neg_zero(), P::zero()) } pub fn min_positive_value() -> Self where T: Bounded, P: Bounded, { GenericDecimal(GenericFraction::min_positive_value(), P::max_value()) } pub const fn is_nan(&self) -> bool { self.0.is_nan() } pub const fn is_infinite(&self) -> bool { self.0.is_infinite() } pub const fn is_finite(&self) -> bool { self.0.is_finite() } pub fn is_normal(&self) -> bool { self.0.is_normal() } pub fn classify(&self) -> FpCategory { self.0.classify() } pub fn floor(&self) -> Self { match self { GenericDecimal(f, _) => GenericDecimal(f.floor(), P::zero()), } } pub fn ceil(&self) -> Self { match self { GenericDecimal(f, _) => GenericDecimal(f.ceil(), P::zero()), } } pub fn round(&self) -> Self { match self { GenericDecimal(f, _) => GenericDecimal(f.round(), P::zero()), } } pub fn trunc(&self) -> Self { match self { GenericDecimal(f, _) => GenericDecimal(f.trunc(), P::zero()), } } pub fn fract(&self) -> Self { self.map_ref(|f| f.fract()) } pub fn abs(&self) -> Self { self.map_ref(|f| f.abs()) } pub fn signum(&self) -> Self { self.map_ref(|f| f.signum()) } pub const fn is_sign_positive(&self) -> bool { self.0.is_sign_positive() } pub const fn is_sign_negative(&self) -> bool { self.0.is_sign_negative() } pub fn mul_add(&self, a: Self, b: Self) -> Self { self.clone() * a + b } pub fn recip(&self) -> Self { self.map_ref(|f| f.recip()) } pub fn map(self, fun: impl FnOnce(GenericFraction) -> GenericFraction) -> Self { match self { GenericDecimal(fra, pres) => GenericDecimal(fun(fra), pres), } } pub fn map_mut(&mut self, fun: impl FnOnce(&mut GenericFraction)) { match self { GenericDecimal(fra, _) => fun(fra), } } pub fn map_ref(&self, fun: impl FnOnce(&GenericFraction) -> GenericFraction) -> Self { match self { GenericDecimal(fra, pres) => GenericDecimal(fun(fra), *pres), } } #[deprecated(note = "Use `match decimal {GenericDecimal(fraction, precision) => ... }`")] pub fn apply_ref(&self, fun: impl FnOnce(&GenericFraction, P) -> R) -> R { match self { GenericDecimal(fra, pres) => fun(fra, *pres), } } /// Convert from a GenericFraction /// /// Automatically calculates precision, so for "bad" numbers /// may take a lot of CPU cycles, especially if precision /// represented by big types (e.g. usize) /// /// # Examples /// /// ``` /// use fraction::{Fraction, Decimal}; /// /// let from_fraction = Decimal::from_fraction(Fraction::new(1u64, 3u64)); /// let from_division = Decimal::from(1) / Decimal::from(3); /// /// let d1 = Decimal::from(4) / from_fraction; /// let d2 = Decimal::from(4) / from_division; /// /// assert_eq!(d1, d2); /// assert_eq!(d1, Decimal::from(12)); /// ``` #[inline] pub fn from_fraction(fraction: GenericFraction) -> Self where T: GenericInteger + ToPrimitive, P: Bounded + CheckedAdd, { let two = P::one() + P::one(); let hun = P::_10() * P::_10(); let max_precision = two * hun + hun / two + P::_10() / two; // 255 GenericDecimal(fraction, P::zero()).calc_precision(Some(max_precision)) } } impl TryToConvertFrom> for GenericDecimal where T: Copy + Integer + TryToConvertFrom, F: Copy + Integer, P2: Copy + Integer + Into + TryToConvertFrom, P1: Copy + Integer + Into, { fn try_to_convert_from(src: GenericDecimal) -> Option { Some(match src { GenericDecimal(fraction, precision) => GenericDecimal( GenericFraction::try_to_convert_from(fraction)?, P2::try_to_convert_from(precision)?, ), }) } } #[cfg(test)] mod tests { use {CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; use super::{GenericDecimal, One}; use fraction::GenericFraction; use prelude::Decimal; use std::hash::{Hash, Hasher}; type D = GenericDecimal; fn hash_it(target: &impl Hash) -> u64 { use std::collections::hash_map::DefaultHasher; let mut h = DefaultHasher::new(); target.hash(&mut h); h.finish() } generate_ops_tests! ( NaN => {D::nan()}; NegInf => {D::neg_infinity()}; PosInf => {D::infinity()}; Zero => {D::from(0)}; Half => {D::from(0.5)}; One => {D::from(1)}; Two => {D::from(2)}; Three => {D::from(3)}; Four => {D::from(4)}; ); #[test] fn hash_and_partial_eq() { { let one = Decimal::from(152.568); let two = Decimal::from(328.76842); let div = two / one.set_precision(16); let red = Decimal::from("2.1548976194221592"); assert_eq!(div.get_precision(), 16); assert_eq!(div, red); assert_eq!(hash_it(&div), hash_it(&red)); } { let one = Decimal::from(152.568); let two = Decimal::from(328.76842); let mul = one * two; assert_eq!(mul.get_precision(), 5); assert_eq!(mul, Decimal::from("50159.5403")); assert_eq!(hash_it(&mul), hash_it(&Decimal::from("50159.5403"))); assert_eq!(mul.set_precision(6), Decimal::from("50159.540302")); assert_eq!( hash_it(&mul.set_precision(6)), hash_it(&Decimal::from("50159.540302")) ); } } #[test] fn fmt_debug() { type F = GenericFraction; assert_eq!( format!("{:?}", Decimal::one()), format!("GenericDecimal(1 | prec=0; {:?}; 1)", F::one()) ); } #[test] fn summing_iterator() { let values = vec![Decimal::from(152.568), Decimal::from(328.76842)]; let sum: Decimal = values.iter().sum(); assert_eq!(sum, values[0] + values[1]) } #[test] fn product_iterator() { let values = vec![Decimal::from(152.568), Decimal::from(328.76842)]; let product: Decimal = values.iter().product(); assert_eq!(product, values[0] * values[1]) } #[test] fn calc_precision() { use super::BigUint; type BigDecimal = GenericDecimal; let one = BigDecimal::from(1); let two = BigDecimal::from(2); let three = BigDecimal::from(3); let half = BigDecimal::from(1) / BigDecimal::from(2); let onethird = BigDecimal::from(1) / BigDecimal::from(3); assert_eq!(0, one.get_precision()); assert_eq!(0, two.get_precision()); assert_eq!(0, three.get_precision()); assert_eq!(0, half.get_precision()); assert_eq!(0, onethird.get_precision()); assert_eq!(1, half.clone().calc_precision(None).get_precision()); assert_eq!(0, half.clone().calc_precision(Some(0)).get_precision()); assert_eq!(1, half.clone().calc_precision(Some(1)).get_precision()); assert_eq!(1, half.clone().calc_precision(Some(255)).get_precision()); assert_eq!(0, onethird.clone().calc_precision(Some(0)).get_precision()); assert_eq!(1, onethird.clone().calc_precision(Some(1)).get_precision()); assert_eq!( 255, onethird.clone().calc_precision(Some(255)).get_precision() ); assert_eq!( 2056, onethird.clone().calc_precision(Some(2056)).get_precision() ); type D = GenericDecimal; let one = D::from(1); let two = D::from(2); let three = D::from(3); let half = one / two; let onethird = one / three; assert_eq!(0, one.get_precision()); assert_eq!(0, two.get_precision()); assert_eq!(0, three.get_precision()); assert_eq!(0, half.get_precision()); assert_eq!(0, onethird.get_precision()); assert_eq!(1, half.calc_precision(None).get_precision()); assert_eq!(255, onethird.calc_precision(None).get_precision()); } #[test] fn decimal_test_default() { let dec = D::default(); assert_eq!("0", format!("{}", dec)); assert_eq!(0, dec.get_precision()); #[cfg(feature = "with-bigint")] { use crate::BigDecimal; let dec = BigDecimal::default(); assert_eq!("0", format!("{}", dec)); assert_eq!(0, dec.get_precision()); } } // TODO: more tests } fraction-0.15.3/src/decimal/ops/add.rs000064400000000000000000000042021046102023000156010ustar 00000000000000use crate::generic::GenericInteger; use crate::GenericDecimal; use std::cmp; use std::ops::Add; impl Add for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into>, { type Output = Self; fn add(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Add::add(sf, of), cmp::max(sp, op)), }, } } } impl<'a, T, P> Add for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Add, { type Output = GenericDecimal; fn add(self, other: Self) -> Self::Output { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Add::add(sf, of), cmp::max(*sp, *op)), }, } } } impl<'a, O, T, P> Add for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Add, O: Into>, { type Output = GenericDecimal; fn add(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Add::add(sf, of), cmp::max(*sp, op)), }, } } } #[cfg(test)] mod tests { use super::GenericDecimal; use crate::Zero; type F = GenericDecimal; #[test] fn add_scalar() { assert_eq!(F::zero() + 1, F::from(1)); assert_eq!(F::zero() + 1.5, F::from(1.5)); assert_eq!(&F::zero() + 1, F::from(1)); assert_eq!(&F::zero() + 1.5, F::from(1.5)); assert_eq!(F::zero() + F::from(1), F::from(1)); assert_eq!(F::zero() + F::from(1.5), F::from(1.5)); assert_eq!(&F::zero() + F::from(1), F::from(1)); assert_eq!(&F::zero() + F::from(1.5), F::from(1.5)); } } fraction-0.15.3/src/decimal/ops/add_assign.rs000064400000000000000000000103211046102023000171440ustar 00000000000000use std::cmp; use std::ops::{AddAssign, MulAssign}; use crate::{decimal::GenericDecimal, generic::GenericInteger}; impl AddAssign for GenericDecimal where T: Clone + GenericInteger + AddAssign + AddAssign + MulAssign, P: Copy + GenericInteger + Into, O: Into>, { fn add_assign(&mut self, other: O) { let other: GenericDecimal = other.into(); match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { AddAssign::add_assign(sf, of); *sp = cmp::max(*sp, op); } }, }; } } impl<'a, T, P> AddAssign<&'a Self> for GenericDecimal where T: Clone + GenericInteger + AddAssign, P: Copy + GenericInteger + Into, { fn add_assign(&mut self, other: &'a Self) { match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { AddAssign::add_assign(sf, of); *sp = cmp::max(*sp, *op); } }, }; } } #[cfg(test)] mod tests { use super::GenericDecimal; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn owned() { { let mut v = F::zero(); v += F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v += -F::one(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v += -F::from(2); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v += -F::one(); assert_eq!(v, -F::from(2)); } { let mut v = -F::one(); v += F::one(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v += -F::one(); assert_eq!(v, F::zero()); } { let mut v = -F::from(2); v += F::one(); assert_eq!(v, -F::one()); } } #[test] fn refs() { { let mut v = F::zero(); v += &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += &F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v += &(-F::one()); assert_eq!(v, F::zero()); } { let mut v = F::one(); v += &(-F::from(2)); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v += &(-F::one()); assert_eq!(v, -F::from(2)); } { let mut v = -F::one(); v += &F::one(); assert_eq!(v, F::zero()); } { let mut v = -F::from(2); v += &F::one(); assert_eq!(v, -F::one()); } } #[test] fn cast() { { let mut v = F::zero(); v += 1; assert_eq!(v, F::one()); } { let mut v = F::zero(); v += 1; assert_eq!(v, F::one()); } } } fraction-0.15.3/src/decimal/ops/checked_add.rs000064400000000000000000000036071046102023000172570ustar 00000000000000use CheckedAdd; use crate::decimal::GenericDecimal; use crate::generic::GenericInteger; use std::cmp; impl CheckedAdd for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn checked_add(&self, other: &Self) -> Option { match *self { GenericDecimal(ref sf, sp) => match *other { GenericDecimal(ref of, op) => { CheckedAdd::checked_add(sf, of).map(|val| GenericDecimal(val, cmp::max(sp, op))) } }, } } } #[cfg(test)] mod tests { use super::{CheckedAdd, GenericDecimal}; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn checked_add() { assert_eq!(Some(F::nan()), F::nan().checked_add(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_add(&F::nan())); assert_eq!( Some(F::infinity()), F::infinity().checked_add(&F::infinity()) ); assert_eq!( Some(F::nan()), F::infinity().checked_add(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_add(&F::one())); assert_eq!(Some(F::nan()), F::one().checked_add(&F::nan())); assert_eq!(Some(F::infinity()), F::one().checked_add(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::one().checked_add(&F::neg_infinity()) ); assert_eq!(Some(F::from(2)), F::one().checked_add(&F::one())); assert_eq!(Some(F::zero()), F::one().checked_add(&(-F::one()))); assert_eq!(Some(F::zero()), (-F::one()).checked_add(&F::one())); assert_eq!(Some(-F::from(2)), (-F::one()).checked_add(&(-F::one()))); assert_eq!(Some(-F::one()), F::one().checked_add(&(-F::from(2)))); assert_eq!(Some(-F::one()), (-F::from(2)).checked_add(&F::one())); } } fraction-0.15.3/src/decimal/ops/checked_div.rs000064400000000000000000000050701046102023000173050ustar 00000000000000use CheckedDiv; use crate::decimal::GenericDecimal; use crate::generic::GenericInteger; use std::cmp; impl CheckedDiv for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn checked_div(&self, other: &Self) -> Option { match *self { GenericDecimal(ref sf, sp) => match *other { GenericDecimal(ref of, op) => { CheckedDiv::checked_div(sf, of).map(|val| GenericDecimal(val, cmp::max(sp, op))) } }, } } } #[cfg(test)] mod tests { use super::{CheckedDiv, GenericDecimal}; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn checked_div() { assert_eq!(Some(F::nan()), F::nan().checked_div(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::infinity())); assert_eq!( Some(F::nan()), F::infinity().checked_div(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::one())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_div(&(-F::one())) ); assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::zero())); assert_eq!(Some(F::zero()), F::zero().checked_div(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_div(&(-F::one())) ); assert_eq!(Some(F::nan()), F::one().checked_div(&F::nan())); assert_eq!(Some(F::zero()), F::one().checked_div(&F::infinity())); assert_eq!(Some(F::zero()), F::one().checked_div(&F::neg_infinity())); assert_eq!(Some(F::one()), F::one().checked_div(&F::one())); assert_eq!(Some(-F::one()), F::one().checked_div(&(-F::one()))); assert_eq!(Some(-F::one()), (-F::one()).checked_div(&F::one())); assert_eq!(Some(F::one()), (-F::one()).checked_div(&(-F::one()))); assert_eq!(Some(F::from(0.5)), F::one().checked_div(&F::from(2))); assert_eq!(Some(F::from(0.5)), (-F::one()).checked_div(&(-F::from(2)))); assert_eq!(Some(F::infinity()), F::one().checked_div(&F::zero())); assert_eq!(Some(F::neg_infinity()), (-F::one()).checked_div(&F::zero())); assert_eq!(Some(F::zero()), F::zero().checked_div(&F::one())); assert_eq!(Some(F::zero()), F::zero().checked_div(&(-F::one()))); assert_eq!(Some(F::nan()), F::zero().checked_div(&F::zero())); } } fraction-0.15.3/src/decimal/ops/checked_mul.rs000064400000000000000000000047001046102023000173170ustar 00000000000000use CheckedMul; use crate::decimal::GenericDecimal; use crate::generic::GenericInteger; use std::cmp; impl CheckedMul for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn checked_mul(&self, other: &Self) -> Option { match *self { GenericDecimal(ref sf, sp) => match *other { GenericDecimal(ref of, op) => { CheckedMul::checked_mul(sf, of).map(|val| GenericDecimal(val, cmp::max(sp, op))) } }, } } } #[cfg(test)] mod tests { use super::{CheckedMul, GenericDecimal}; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn checked_mul() { assert_eq!(Some(F::nan()), F::nan().checked_mul(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_mul(&F::nan())); assert_eq!( Some(F::infinity()), F::infinity().checked_mul(&F::infinity()) ); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_mul(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_mul(&F::one())); assert_eq!(Some(F::nan()), F::infinity().checked_mul(&F::zero())); assert_eq!(Some(F::nan()), F::zero().checked_mul(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_mul(&(-F::one())) ); assert_eq!(Some(F::nan()), F::one().checked_mul(&F::nan())); assert_eq!(Some(F::infinity()), F::one().checked_mul(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::one().checked_mul(&F::neg_infinity()) ); assert_eq!(Some(F::one()), F::one().checked_mul(&F::one())); assert_eq!(Some(-F::one()), F::one().checked_mul(&(-F::one()))); assert_eq!(Some(-F::one()), (-F::one()).checked_mul(&F::one())); assert_eq!(Some(F::one()), (-F::one()).checked_mul(&(-F::one()))); assert_eq!(Some(F::from(2)), F::one().checked_mul(&F::from(2))); assert_eq!(Some(F::from(2)), (-F::one()).checked_mul(&(-F::from(2)))); assert_eq!(Some(F::zero()), F::one().checked_mul(&F::zero())); assert_eq!(Some(F::zero()), (-F::one()).checked_mul(&F::zero())); assert_eq!(Some(F::zero()), F::zero().checked_mul(&F::one())); assert_eq!(Some(F::zero()), F::zero().checked_mul(&(-F::one()))); } } fraction-0.15.3/src/decimal/ops/checked_sub.rs000064400000000000000000000036061046102023000173170ustar 00000000000000use CheckedSub; use crate::decimal::GenericDecimal; use crate::generic::GenericInteger; use std::cmp; impl CheckedSub for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn checked_sub(&self, other: &Self) -> Option { match *self { GenericDecimal(ref sf, sp) => match *other { GenericDecimal(ref of, op) => { CheckedSub::checked_sub(sf, of).map(|val| GenericDecimal(val, cmp::max(sp, op))) } }, } } } #[cfg(test)] mod tests { use super::{CheckedSub, GenericDecimal}; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn checked_sub() { assert_eq!(Some(F::nan()), F::nan().checked_sub(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_sub(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_sub(&F::infinity())); assert_eq!( Some(F::infinity()), F::infinity().checked_sub(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_sub(&F::one())); assert_eq!(Some(F::nan()), F::one().checked_sub(&F::nan())); assert_eq!( Some(F::neg_infinity()), F::one().checked_sub(&F::infinity()) ); assert_eq!( Some(F::infinity()), F::one().checked_sub(&F::neg_infinity()) ); assert_eq!(Some(F::zero()), F::one().checked_sub(&F::one())); assert_eq!(Some(F::from(2)), F::one().checked_sub(&(-F::one()))); assert_eq!(Some(-F::from(2)), (-F::one()).checked_sub(&F::one())); assert_eq!(Some(F::zero()), (-F::one()).checked_sub(&(-F::one()))); assert_eq!(Some(-F::one()), F::one().checked_sub(&F::from(2))); assert_eq!(Some(F::one()), (-F::one()).checked_sub(&(-F::from(2)))); } } fraction-0.15.3/src/decimal/ops/div.rs000064400000000000000000000041611046102023000156370ustar 00000000000000use crate::decimal::GenericDecimal; use crate::generic::GenericInteger; use std::cmp; use std::ops::Div; impl Div for GenericDecimal where T: Clone + GenericInteger + Div, P: Copy + GenericInteger + Into, O: Into, { type Output = Self; fn div(self, other: O) -> Self::Output { let other: Self = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Div::div(sf, of), cmp::max(sp, op)), }, } } } impl<'a, T, P> Div for &'a GenericDecimal where T: Clone + GenericInteger + Div, P: Copy + GenericInteger + Into, &'a T: Div, { type Output = GenericDecimal; fn div(self, other: Self) -> Self::Output { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Div::div(sf, of), cmp::max(*sp, *op)), }, } } } impl<'a, O, T, P> Div for &'a GenericDecimal where T: Clone + GenericInteger + Div, P: Copy + GenericInteger + Into, &'a T: Div, O: Into>, { type Output = GenericDecimal; fn div(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Div::div(sf, of), cmp::max(*sp, op)), }, } } } #[cfg(test)] mod tests { use super::GenericDecimal; type F = GenericDecimal; #[test] fn div_scalar() { assert_eq!(F::from(3) / 2, F::from(1.5)); assert_eq!(F::from(3) / 1.5, F::from(2)); assert_eq!(&F::from(3) / 2, F::from(1.5)); assert_eq!(&F::from(3) / 1.5, F::from(2)); assert_eq!(F::from(3) / F::from(2), F::from(1.5)); assert_eq!(F::from(3) / F::from(1.5), F::from(2)); assert_eq!(&F::from(3) / &F::from(2), F::from(1.5)); assert_eq!(&F::from(3) / &F::from(1.5), F::from(2)); } } fraction-0.15.3/src/decimal/ops/div_assign.rs000064400000000000000000000123621046102023000172050ustar 00000000000000use crate::{decimal::GenericDecimal, generic::GenericInteger}; use std::cmp; use std::ops::DivAssign; impl DivAssign for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into, { fn div_assign(&mut self, other: O) { let other: Self = other.into(); match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { DivAssign::div_assign(sf, of); *sp = cmp::max(*sp, op); } }, }; } } impl<'a, T, P> DivAssign<&'a Self> for GenericDecimal where T: Clone + GenericInteger + DivAssign, P: Copy + GenericInteger + Into, { fn div_assign(&mut self, other: &'a Self) { match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { DivAssign::div_assign(sf, of); *sp = cmp::max(*sp, *op); } }, }; } } #[cfg(test)] mod tests { use super::GenericDecimal; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn owned() { { let mut v = F::nan(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v /= F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= F::neg_infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= -F::one(); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v /= F::from(2); assert_eq!(v, -F::from(0.5)); } { let mut v = -F::one(); v /= -F::one(); assert_eq!(v, F::one()); } { let mut v = -F::one(); v /= -F::from(2); assert_eq!(v, F::from(0.5)); } { let mut v = F::infinity(); v /= F::zero(); assert_eq!(v, F::infinity()); } { let mut v = F::zero(); v /= F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= F::zero(); assert_eq!(v, F::infinity()); } } #[test] fn refs() { { let mut v = F::nan(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v /= &F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &F::neg_infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &(-F::one()); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v /= &F::from(2); assert_eq!(v, -F::from(0.5)); } { let mut v = -F::one(); v /= &(-F::one()); assert_eq!(v, F::one()); } { let mut v = -F::one(); v /= &(-F::from(2)); assert_eq!(v, F::from(0.5)); } { let mut v = F::infinity(); v /= &F::zero(); assert_eq!(v, F::infinity()); } { let mut v = F::zero(); v /= &F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &F::zero(); assert_eq!(v, F::infinity()); } } #[test] fn cast() { { let mut v = F::from(3); v /= 1.5; assert_eq!(v, F::from(2)); } { let mut v = F::from(3); v /= 2; assert_eq!(v, F::from(1.5)); } } } fraction-0.15.3/src/decimal/ops/mod.rs000064400000000000000000000003011046102023000156240ustar 00000000000000mod add; mod add_assign; mod checked_add; mod checked_div; mod checked_mul; mod checked_sub; mod div; mod div_assign; mod mul; mod mul_assign; mod rem; mod rem_assign; mod sub; mod sub_assign; fraction-0.15.3/src/decimal/ops/mul.rs000064400000000000000000000041671046102023000156600ustar 00000000000000use crate::generic::GenericInteger; use crate::GenericDecimal; use std::cmp; use std::ops::Mul; impl Mul for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into>, { type Output = Self; fn mul(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Mul::mul(sf, of), cmp::max(sp, op)), }, } } } impl<'a, T, P> Mul for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Mul, { type Output = GenericDecimal; fn mul(self, other: Self) -> Self::Output { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Mul::mul(sf, of), cmp::max(*sp, *op)), }, } } } impl<'a, O, T, P> Mul for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Mul, O: Into>, { type Output = GenericDecimal; fn mul(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Mul::mul(sf, of), cmp::max(*sp, op)), }, } } } #[cfg(test)] mod tests { use super::GenericDecimal; type F = GenericDecimal; #[test] fn mul_scalar() { assert_eq!(F::from(1.5) * 2, F::from(3)); assert_eq!(F::from(2) * 1.5, F::from(3)); assert_eq!(&F::from(1.5) * 2, F::from(3)); assert_eq!(&F::from(2) * 1.5, F::from(3)); assert_eq!(F::from(1.5) * F::from(2), F::from(3)); assert_eq!(F::from(2) * F::from(1.5), F::from(3)); assert_eq!(&F::from(1.5) * &F::from(2), F::from(3)); assert_eq!(&F::from(2) * &F::from(1.5), F::from(3)); } } fraction-0.15.3/src/decimal/ops/mul_assign.rs000064400000000000000000000120131046102023000172110ustar 00000000000000use std::cmp; use std::ops::MulAssign; use crate::{decimal::GenericDecimal, generic::GenericInteger}; impl MulAssign for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into, { fn mul_assign(&mut self, other: O) { let other: Self = other.into(); match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { MulAssign::mul_assign(sf, of); *sp = cmp::max(*sp, op); } }, }; } } impl<'a, T, P> MulAssign<&'a Self> for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn mul_assign(&mut self, other: &'a Self) { match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { MulAssign::mul_assign(sf, of); *sp = cmp::max(*sp, *op); } }, }; } } #[cfg(test)] mod tests { use super::GenericDecimal; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn owned() { { let mut v = F::nan(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v *= F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::infinity(); v *= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v *= F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v *= -F::one(); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v *= F::from(2); assert_eq!(v, -F::from(2)); } { let mut v = -F::one(); v *= -F::one(); assert_eq!(v, F::one()); } { let mut v = -F::one(); v *= -F::from(2); assert_eq!(v, F::from(2)); } { let mut v = F::infinity(); v *= F::zero(); assert_eq!(v, F::nan()); } { let mut v = F::zero(); v *= F::infinity(); assert_eq!(v, F::nan()); } } #[test] fn refs() { { let mut v = F::nan(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v *= &F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::infinity(); v *= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v *= &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= &F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v *= &(-F::one()); assert_eq!(v, -F::one()); } { let mut v = -F::one(); v *= &F::from(2); assert_eq!(v, -F::from(2)); } { let mut v = -F::one(); v *= &(-F::one()); assert_eq!(v, F::one()); } { let mut v = -F::one(); v *= &(-F::from(2)); assert_eq!(v, F::from(2)); } { let mut v = F::infinity(); v *= &F::zero(); assert_eq!(v, F::nan()); } { let mut v = F::zero(); v *= &F::infinity(); assert_eq!(v, F::nan()); } } #[test] fn cast() { { let mut v = F::from(1.5); v *= 2; assert_eq!(v, F::from(3)); } { let mut v = F::from(2); v *= 1.5; assert_eq!(v, F::from(3)); } } } fraction-0.15.3/src/decimal/ops/rem.rs000064400000000000000000000041571046102023000156450ustar 00000000000000use crate::generic::GenericInteger; use crate::GenericDecimal; use std::cmp; use std::ops::Rem; impl Rem for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into>, { type Output = Self; fn rem(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Rem::rem(sf, of), cmp::max(sp, op)), }, } } } impl<'a, T, P> Rem for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Rem, { type Output = GenericDecimal; fn rem(self, other: Self) -> Self::Output { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Rem::rem(sf, of), cmp::max(*sp, *op)), }, } } } impl<'a, O, T, P> Rem for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Rem, O: Into>, { type Output = GenericDecimal; fn rem(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Rem::rem(sf, of), cmp::max(*sp, op)), }, } } } #[cfg(test)] mod tests { use super::GenericDecimal; type F = GenericDecimal; #[test] fn rem_scalar() { assert_eq!(F::from(3) % 2, F::from(1)); assert_eq!(F::from(3) % 1.5, F::from(0)); assert_eq!(&F::from(3) % 2, F::from(1)); assert_eq!(&F::from(3) % 1.5, F::from(0)); assert_eq!(F::from(3) % F::from(2), F::from(1)); assert_eq!(F::from(3) % F::from(1.5), F::from(0)); assert_eq!(&F::from(3) % &F::from(2), F::from(1)); assert_eq!(&F::from(3) % &F::from(1.5), F::from(0)); } } fraction-0.15.3/src/decimal/ops/rem_assign.rs000064400000000000000000000050631046102023000172060ustar 00000000000000use std::cmp; use std::ops::RemAssign; use crate::{decimal::GenericDecimal, generic::GenericInteger}; // impl RemAssign for GenericDecimal impl RemAssign for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into, { fn rem_assign(&mut self, other: O) { let other: Self = other.into(); match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { RemAssign::rem_assign(sf, of); *sp = cmp::max(*sp, op); } }, }; } } impl<'a, T, P> RemAssign<&'a Self> for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { fn rem_assign(&mut self, other: &'a Self) { match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { RemAssign::rem_assign(sf, of); *sp = cmp::max(*sp, *op); } }, }; } } #[cfg(test)] mod tests { use super::GenericDecimal; use crate::{One, Zero}; type F = GenericDecimal; #[test] fn owned() { { let mut v = F::infinity(); v %= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= F::one(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v %= F::infinity(); assert_eq!(v, F::one()); } } #[test] fn refs() { { let mut v = F::infinity(); v %= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= &F::one(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v %= &F::infinity(); assert_eq!(v, F::one()); } } #[test] fn cast() { { let mut v = F::from(4); v %= 1.5; assert_eq!(v, F::one()); } { let mut v = F::from(4); v %= 2; assert_eq!(v, F::zero()); } } } fraction-0.15.3/src/decimal/ops/sub.rs000064400000000000000000000041601046102023000156450ustar 00000000000000use crate::generic::GenericInteger; use crate::GenericDecimal; use std::cmp; use std::ops::Sub; impl Sub for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into>, { type Output = Self; fn sub(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Sub::sub(sf, of), cmp::max(sp, op)), }, } } } impl<'a, T, P> Sub for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Sub, { type Output = GenericDecimal; fn sub(self, other: Self) -> Self::Output { match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Sub::sub(sf, of), cmp::max(*sp, *op)), }, } } } impl<'a, O, T, P> Sub for &'a GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, &'a T: Sub, O: Into>, { type Output = GenericDecimal; fn sub(self, other: O) -> Self::Output { let other: GenericDecimal = other.into(); match self { GenericDecimal(sf, sp) => match other { GenericDecimal(of, op) => GenericDecimal(Sub::sub(sf, of), cmp::max(*sp, op)), }, } } } #[cfg(test)] mod tests { use crate::{GenericDecimal, One, Zero}; type F = GenericDecimal; #[test] fn sub_scalar() { assert_eq!(F::one() - 1, F::zero()); assert_eq!(F::one() - 0.5, F::from(0.5)); assert_eq!(&F::one() - 1, F::zero()); assert_eq!(&F::one() - 0.5, F::from(0.5)); assert_eq!(F::one() - F::from(1), F::zero()); assert_eq!(F::one() - F::from(0.5), F::from(0.5)); assert_eq!(&F::one() - &F::from(1), F::zero()); assert_eq!(&F::one() - &F::from(0.5), F::from(0.5)); } } fraction-0.15.3/src/decimal/ops/sub_assign.rs000064400000000000000000000110071046102023000172070ustar 00000000000000use std::cmp; use std::ops::SubAssign; use crate::{decimal::GenericDecimal, generic::GenericInteger}; impl SubAssign for GenericDecimal where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, O: Into, { fn sub_assign(&mut self, other: O) { let other: Self = other.into(); match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { SubAssign::sub_assign(sf, of); *sp = cmp::max(*sp, op); } }, }; } } impl<'a, T, P> SubAssign<&'a Self> for GenericDecimal where T: Clone + GenericInteger, // + $trait + $trait<&'a T>, P: Copy + GenericInteger + Into, { fn sub_assign(&mut self, other: &'a Self) { match *self { GenericDecimal(ref mut sf, ref mut sp) => match other { GenericDecimal(of, op) => { SubAssign::sub_assign(sf, of); *sp = cmp::max(*sp, *op); } }, }; } } #[cfg(test)] mod tests { use crate::{GenericDecimal, One, Zero}; type F = GenericDecimal; #[test] fn owned() { { let mut v = F::nan(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v -= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v -= F::infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v -= F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= -F::one(); assert_eq!(v, F::from(2)); } { let mut v = -F::one(); v -= F::from(2); assert_eq!(v, -F::from(3)); } { let mut v = -F::one(); v -= -F::one(); assert_eq!(v, F::zero()); } { let mut v = -F::one(); v -= -F::from(2); assert_eq!(v, F::one()); } } #[test] fn refs() { { let mut v = F::nan(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v -= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v -= &F::infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v -= &F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= &(-F::one()); assert_eq!(v, F::from(2)); } { let mut v = -F::one(); v -= &F::from(2); assert_eq!(v, -F::from(3)); } { let mut v = -F::one(); v -= &(-F::one()); assert_eq!(v, F::zero()); } { let mut v = -F::one(); v -= &(-F::from(2)); assert_eq!(v, F::one()); } } #[test] fn cast() { { let mut v = F::one(); v -= 1; assert_eq!(v, F::zero()); } { let mut v = F::one(); v -= 1; assert_eq!(v, F::zero()); } } } fraction-0.15.3/src/decimal/postgres_support.rs000064400000000000000000000203511046102023000177150ustar 00000000000000extern crate bytes; use self::bytes::BytesMut; use num::traits::Bounded; use postgres_types::{FromSql, IsNull, ToSql, Type}; use std::error::Error; use std::fmt; use super::GenericDecimal; use convert::TryToConvertFrom; use fraction::GenericFraction; use generic::GenericInteger; use fraction::postgres_support::{fraction_to_sql_buf, read_i16, PG_MAX_PRECISION}; impl<'a, T, P> FromSql<'a> for GenericDecimal where T: Clone + GenericInteger + From, P: Copy + GenericInteger + Into + Bounded + TryToConvertFrom + fmt::Display, { fn from_sql(ty: &Type, raw: &[u8]) -> Result> { if raw.len() < 8 { return Err("unexpected data package from the database".into()); } let scale: i16 = read_i16(&raw[6..8])?; let precision = if let Some(precision) = P::try_to_convert_from(scale) { precision } else { return Err(format!( "{} {};\n{} {};\n {} {};\n{} {}", r#"The precision of the source is too big: "#, scale, r#" your decimal type supports up to "#, P::max_value(), r#"you may increase the precision type size up to "usize", which is "#, usize::max_value(), r#"PostgreSQL supports precision up to "#, PG_MAX_PRECISION ) .into()); }; Ok(GenericDecimal( as FromSql>::from_sql(ty, raw)?, precision, )) } accepts!(NUMERIC); } impl ToSql for GenericDecimal where T: Clone + GenericInteger + From + fmt::Debug, P: Copy + GenericInteger + Into + fmt::Debug, { fn to_sql( &self, ty: &Type, buf: &mut BytesMut, ) -> Result> { match *self { GenericDecimal(ref fraction, precision) => { fraction_to_sql_buf(fraction, ty, buf, precision.into()) } } } accepts!(NUMERIC); to_sql_checked!(); } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; use {One, Zero}; type Decimal = GenericDecimal; const NUMERIC_OID: u32 = 1700; fn get_tests() -> Vec<(Decimal, &'static [u8])> { vec![ ( Decimal::from_str("-12345678901234").unwrap(), &[0, 4, 0, 3, 64, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210], ), ( Decimal::from_str("-12345678").unwrap(), &[0, 2, 0, 1, 64, 0, 0, 0, 4, 210, 22, 46], ), ( Decimal::from_str("-1000000000.0000000001").unwrap(), &[ 0, 6, 0, 2, 64, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, ], ), ( Decimal::from_str("-1000000000").unwrap(), &[0, 1, 0, 2, 64, 0, 0, 0, 0, 10], ), ( Decimal::from_str("-1234").unwrap(), &[0, 1, 0, 0, 64, 0, 0, 0, 4, 210], ), ( Decimal::from_str("-256").unwrap(), &[0, 1, 0, 0, 64, 0, 0, 0, 1, 0], ), ( Decimal::from_str("-42").unwrap(), &[0, 1, 0, 0, 64, 0, 0, 0, 0, 42], ), ( Decimal::from_str("-1").unwrap(), &[0, 1, 0, 0, 64, 0, 0, 0, 0, 1], ), ( Decimal::from_str("-0.5").unwrap(), &[0, 1, 255, 255, 64, 0, 0, 1, 19, 136], ), ( Decimal::from_str("-0.1").unwrap(), &[0, 1, 255, 255, 64, 0, 0, 1, 3, 232], ), ( Decimal::from_str("-0.66").unwrap(), &[0, 1, 255, 255, 64, 0, 0, 2, 25, 200], ), ( Decimal::from_str("-0.12345678901234").unwrap(), &[0, 4, 255, 255, 64, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72], ), ( Decimal::from_str("-0.01").unwrap(), &[0, 1, 255, 255, 64, 0, 0, 2, 0, 100], ), ( Decimal::from_str("-0.2404").unwrap(), &[0, 1, 255, 255, 64, 0, 0, 4, 9, 100], ), ( Decimal::from_str("-0.000000001").unwrap(), &[0, 1, 255, 253, 64, 0, 0, 9, 3, 232], ), ( Decimal::from_str("-12345678901234.12345678901234").unwrap(), &[ 0, 8, 0, 3, 64, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35, 52, 13, 72, ], ), (Decimal::zero(), &[0, 0, 0, 0, 0, 0, 0, 0]), (Decimal::nan(), &[0, 0, 0, 0, 192, 0, 0, 0]), ( Decimal::from_str("12345678901234.12345678901234").unwrap(), &[ 0, 8, 0, 3, 0, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35, 52, 13, 72, ], ), ( Decimal::from("0.000000001"), &[0, 1, 255, 253, 0, 0, 0, 9, 3, 232], ), ( Decimal::from(0.2404f64), &[0, 1, 255, 255, 0, 0, 0, 4, 9, 100], ), ( Decimal::from(0.01f32), &[0, 1, 255, 255, 0, 0, 0, 2, 0, 100], ), ( Decimal::from(0.66f32), &[0, 1, 255, 255, 0, 0, 0, 2, 25, 200], ), ( Decimal::from_str("1000000.0000000000000001").unwrap(), &[ 0, 6, 0, 1, 0, 0, 0, 16, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ], ), ( Decimal::from_str("0.12345678901234").unwrap(), &[0, 4, 255, 255, 0, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72], ), (Decimal::from(0.1f32), &[0, 1, 255, 255, 0, 0, 0, 1, 3, 232]), ( Decimal::from(0.5f32), &[0, 1, 255, 255, 0, 0, 0, 1, 19, 136], ), ( Decimal::from(0.55f32).set_precision(1), &[0, 1, 255, 255, 0, 0, 0, 1, 19, 136], ), (Decimal::one(), &[0, 1, 0, 0, 0, 0, 0, 0, 0, 1]), (Decimal::from(42u8), &[0, 1, 0, 0, 0, 0, 0, 0, 0, 42]), (Decimal::from(256u16), &[0, 1, 0, 0, 0, 0, 0, 0, 1, 0]), (Decimal::from(1234u16), &[0, 1, 0, 0, 0, 0, 0, 0, 4, 210]), ( Decimal::from(1000000000u64), &[0, 1, 0, 2, 0, 0, 0, 0, 0, 10], ), ( Decimal::from("1000000000.0000000001"), &[ 0, 6, 0, 2, 0, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, ], ), ( Decimal::from(12345678u32), &[0, 2, 0, 1, 0, 0, 0, 0, 4, 210, 22, 46], ), ( Decimal::from(12345678901234u64), &[0, 4, 0, 3, 0, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210], ), ( Decimal::from("0.33333333333333333333"), &[ 0, 5, 255, 255, 0, 0, 0, 20, 13, 5, 13, 5, 13, 5, 13, 5, 13, 5, ], ), ] } #[test] fn test_from_sql() { let t = Type::from_oid(NUMERIC_OID).unwrap(); for ref test in &get_tests() { assert_eq!( test.0, ::from_sql(&t, test.1).ok().unwrap() ) } } #[test] fn test_to_sql() { let t = Type::from_oid(NUMERIC_OID).unwrap(); let mut buf = BytesMut::with_capacity(1024); for ref test in &get_tests() { buf.clear(); let res = ::to_sql(&test.0, &t, &mut buf) .ok() .unwrap(); match res { IsNull::Yes => assert!(false), IsNull::No => assert!(true), }; assert_eq!(&buf, &test.1); } } } fraction-0.15.3/src/decimal/try_from.rs000064400000000000000000000741011046102023000161160ustar 00000000000000use super::GenericDecimal; use generic::GenericInteger; use num::ToPrimitive; use std::convert::TryFrom; macro_rules! generic_decimal_into_primitive { ( $( $(#[$cfg:meta])* fn $method:ident -> $IntoT:ident ; )*) => {$( $(#[$cfg])* impl TryFrom> for $IntoT where T: Clone + ToPrimitive + GenericInteger, P: Copy + Into + GenericInteger { type Error = (); fn try_from(value: GenericDecimal) -> Result { value.$method().ok_or(()) } } )*} } generic_decimal_into_primitive!( fn to_u8 -> u8 ; fn to_i8 -> i8 ; fn to_u16 -> u16 ; fn to_i16 -> i16 ; fn to_u32 -> u32 ; fn to_i32 -> i32 ; fn to_u64 -> u64 ; fn to_i64 -> i64 ; fn to_u128 -> u128 ; fn to_i128 -> i128 ; fn to_usize -> usize ; fn to_isize -> isize ; fn to_f32 -> f32 ; fn to_f64 -> f64 ; ); #[cfg(feature = "with-bigint")] pub mod with_bigint { use num::BigInt; use crate::{generic::GenericInteger, BigUint, GenericDecimal}; use std::convert::TryFrom; impl TryFrom> for BigUint where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { type Error = (); fn try_from(value: GenericDecimal) -> Result { Self::try_from(value.0) } } impl TryFrom> for BigInt where T: Clone + GenericInteger, P: Copy + GenericInteger + Into, { type Error = (); fn try_from(value: GenericDecimal) -> Result { Self::try_from(value.0) } } } #[cfg(test)] mod test { use crate::prelude::Decimal; use num::{One, Zero}; use std::convert::TryInto; #[test] fn decimal_to_i() { let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into(), Ok(-1i8)); assert_eq!(n_one.try_into(), Ok(-1i16)); assert_eq!(n_one.try_into(), Ok(-1i32)); assert_eq!(n_one.try_into(), Ok(-1i64)); assert_eq!(n_one.try_into(), Ok(-1i128)); assert_eq!(n_one.try_into(), Ok(-1isize)); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(0i8)); assert_eq!(zero.try_into(), Ok(0i16)); assert_eq!(zero.try_into(), Ok(0i32)); assert_eq!(zero.try_into(), Ok(0i64)); assert_eq!(zero.try_into(), Ok(0i128)); assert_eq!(zero.try_into(), Ok(0isize)); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(1i8)); assert_eq!(p_one.try_into(), Ok(1i16)); assert_eq!(p_one.try_into(), Ok(1i32)); assert_eq!(p_one.try_into(), Ok(1i64)); assert_eq!(p_one.try_into(), Ok(1i128)); assert_eq!(p_one.try_into(), Ok(1isize)); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[test] fn decimal_to_u() { let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(0u8)); assert_eq!(zero.try_into(), Ok(0u16)); assert_eq!(zero.try_into(), Ok(0u32)); assert_eq!(zero.try_into(), Ok(0u64)); assert_eq!(zero.try_into(), Ok(0u128)); assert_eq!(zero.try_into(), Ok(0usize)); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(1u8)); assert_eq!(p_one.try_into(), Ok(1u16)); assert_eq!(p_one.try_into(), Ok(1u32)); assert_eq!(p_one.try_into(), Ok(1u64)); assert_eq!(p_one.try_into(), Ok(1u128)); assert_eq!(p_one.try_into(), Ok(1usize)); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[test] fn decimal_to_f32() { let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[test] fn decimal_to_f64() { let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn decimal_to_bigint() { use crate::BigInt; let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into(), Ok(BigInt::one() * -1)); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(BigInt::zero())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(BigInt::one())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn decimal_to_biguint() { use crate::BigUint; let n_inf = Decimal::neg_infinity(); let n_one = Decimal::one() * -1; let n_half = n_one / 2; let zero = Decimal::zero(); let p_one = Decimal::one(); let p_half = p_one / 2; let p_inf = Decimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(BigUint::zero())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(BigUint::one())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigdecimal_to_i() { use crate::BigDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into(), Ok(-1i8)); assert_eq!(n_one.clone().try_into(), Ok(-1i16)); assert_eq!(n_one.clone().try_into(), Ok(-1i32)); assert_eq!(n_one.clone().try_into(), Ok(-1i64)); assert_eq!(n_one.clone().try_into(), Ok(-1i128)); assert_eq!(n_one.clone().try_into(), Ok(-1isize)); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0i8)); assert_eq!(zero.clone().try_into(), Ok(0i16)); assert_eq!(zero.clone().try_into(), Ok(0i32)); assert_eq!(zero.clone().try_into(), Ok(0i64)); assert_eq!(zero.clone().try_into(), Ok(0i128)); assert_eq!(zero.clone().try_into(), Ok(0isize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1i8)); assert_eq!(p_one.clone().try_into(), Ok(1i16)); assert_eq!(p_one.clone().try_into(), Ok(1i32)); assert_eq!(p_one.clone().try_into(), Ok(1i64)); assert_eq!(p_one.clone().try_into(), Ok(1i128)); assert_eq!(p_one.clone().try_into(), Ok(1isize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigintdecimal_to_i() { use crate::GenericDecimal; use num::BigInt; type BigDecimal = GenericDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into(), Ok(-1i8)); assert_eq!(n_one.clone().try_into(), Ok(-1i16)); assert_eq!(n_one.clone().try_into(), Ok(-1i32)); assert_eq!(n_one.clone().try_into(), Ok(-1i64)); assert_eq!(n_one.clone().try_into(), Ok(-1i128)); assert_eq!(n_one.clone().try_into(), Ok(-1isize)); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0i8)); assert_eq!(zero.clone().try_into(), Ok(0i16)); assert_eq!(zero.clone().try_into(), Ok(0i32)); assert_eq!(zero.clone().try_into(), Ok(0i64)); assert_eq!(zero.clone().try_into(), Ok(0i128)); assert_eq!(zero.clone().try_into(), Ok(0isize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1i8)); assert_eq!(p_one.clone().try_into(), Ok(1i16)); assert_eq!(p_one.clone().try_into(), Ok(1i32)); assert_eq!(p_one.clone().try_into(), Ok(1i64)); assert_eq!(p_one.clone().try_into(), Ok(1i128)); assert_eq!(p_one.clone().try_into(), Ok(1isize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigdecimal_to_u() { use crate::BigDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0u8)); assert_eq!(zero.clone().try_into(), Ok(0u16)); assert_eq!(zero.clone().try_into(), Ok(0u32)); assert_eq!(zero.clone().try_into(), Ok(0u64)); assert_eq!(zero.clone().try_into(), Ok(0u128)); assert_eq!(zero.clone().try_into(), Ok(0usize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1u8)); assert_eq!(p_one.clone().try_into(), Ok(1u16)); assert_eq!(p_one.clone().try_into(), Ok(1u32)); assert_eq!(p_one.clone().try_into(), Ok(1u64)); assert_eq!(p_one.clone().try_into(), Ok(1u128)); assert_eq!(p_one.clone().try_into(), Ok(1usize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigintdecimal_to_u() { use crate::GenericDecimal; use num::BigInt; type BigDecimal = GenericDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = Decimal::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0u8)); assert_eq!(zero.clone().try_into(), Ok(0u16)); assert_eq!(zero.clone().try_into(), Ok(0u32)); assert_eq!(zero.clone().try_into(), Ok(0u64)); assert_eq!(zero.clone().try_into(), Ok(0u128)); assert_eq!(zero.clone().try_into(), Ok(0usize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1u8)); assert_eq!(p_one.clone().try_into(), Ok(1u16)); assert_eq!(p_one.clone().try_into(), Ok(1u32)); assert_eq!(p_one.clone().try_into(), Ok(1u64)); assert_eq!(p_one.clone().try_into(), Ok(1u128)); assert_eq!(p_one.clone().try_into(), Ok(1usize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigdecimal_to_f32() { use crate::BigDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigintdecimal_to_f32() { use crate::GenericDecimal; use num::BigInt; type BigDecimal = GenericDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigdecimal_to_f64() { use crate::BigDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigintdecimal_to_f64() { use crate::GenericDecimal; use num::BigInt; type BigDecimal = GenericDecimal; let n_inf = BigDecimal::neg_infinity(); let n_one = BigDecimal::one() * -1; let n_half = n_one.clone() / 2; let zero = BigDecimal::zero(); let p_one = BigDecimal::one(); let p_half = p_one.clone() / 2; let p_inf = BigDecimal::infinity(); let nan = BigDecimal::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } } fraction-0.15.3/src/division.rs000064400000000000000000001214361046102023000145070ustar 00000000000000//! Lossless integer division //! - The algorithm uses stack only, no introduced heap allocations for calculation (although underlying integer type implementation may perform those) //! - Linear complexity, O(n) //! - Abstract from a particular integer implementation, may be used on primitive types (as i32 or u32) as well as complex ones (num::BigInt, num::BigUint) //! Thus can be efficiently used on any integer type implementing a bunch of required traits (which all primitive ints and num::bigint implement out of the box). //! Although in that case the underlying math will be using heap. use error::DivisionError; use generic::GenericInteger; use std::cmp::{Eq, Ordering, PartialEq}; use std::fmt::Write; /// Division state encapsulates remainder and divisor #[derive(Clone, Debug)] pub struct DivisionState { pub remainder: I, pub divisor: I, } impl DivisionState { pub fn new(remainder: I, divisor: I) -> Self { DivisionState { remainder, divisor } } } impl PartialEq for DivisionState where I: PartialEq, { fn eq(&self, other: &Self) -> bool { self.remainder.eq(&other.remainder) && self.divisor.eq(&other.divisor) } } impl Eq for DivisionState where I: Eq {} /// Divide two numbers and produce every single digit of the whole part of the resulting number /// /// WARNING: Negative numbers as arguments are not supported. /// /// Returns remainder of the division /// If the consumer returns `Ok(true)` keeps on calculation /// If the consumer returns `Ok(false)` stops calculation and returns the remainder /// If the consumer returns `Err(_)` the calculation will be stopped and the error will be passed as the result value /// /// # Examples /// /// ``` /// use fraction::division::divide_integral; /// /// let mut result: [u8; 2] = [0; 2]; /// let mut ptr: usize = 0; /// /// let state = divide_integral(30, 2, |d| { /// result[ptr] = d; /// ptr += 1; /// Ok(true) /// }).unwrap(); /// /// assert_eq!([1, 5], result); /// assert_eq!(state.remainder, 0); /// assert_eq!(state.divisor, 2); /// ``` /// /// ``` /// use fraction::division::divide_integral; /// /// let mut result: u8 = 0; /// /// let state = divide_integral(30, 2, |d| { /// result = d; /// Ok(false) /// }).unwrap(); /// /// assert_eq!(result, 1); /// assert_eq!(state.remainder, 10); /// assert_eq!(state.divisor, 2); /// ``` pub fn divide_integral( mut dividend: I, divisor: I, mut consumer: Consumer, ) -> Result, DivisionError> where Consumer: FnMut(u8) -> Result, I: Clone + GenericInteger, { if divisor.is_zero() { return Err(DivisionError::DivisionByZero); } /* === Figuring out the number size === */ let mut ptr: I = match dividend.cmp(&divisor) { Ordering::Equal => { consumer(1u8)?; return Ok(DivisionState::new(I::zero(), divisor)); } Ordering::Less => { consumer(0u8)?; return Ok(DivisionState::new(dividend, divisor)); } Ordering::Greater => { let mut ptr: I = I::_1(); loop { if ptr > dividend { if I::_1r().map_or_else(|| ptr > I::_1(), |_1| ptr > *_1) { I::_10r().map(|_10| ptr /= _10).or_else(|| { ptr /= I::_10(); None }); } break; } ptr = match I::_10r() .map_or_else(|| ptr.checked_mul(&I::_10()), |_10| ptr.checked_mul(_10)) { Some(n) => n, None => break, }; } ptr } }; let mut passed_leading_zeroes: bool = false; loop { let digit = dividend.div_floor(&ptr).div_floor(&divisor); if I::_0r().map_or_else(|| digit > I::_0(), |_0| digit > *_0) { passed_leading_zeroes = true; dividend -= digit.clone() * &divisor * &ptr; } if passed_leading_zeroes { let d: Option = digit.to_u8(); match d { Some(n) => { if !consumer(n)? { return Ok(DivisionState::new(dividend, divisor)); } } None => unreachable!(), }; } if I::_1r().map_or_else(|| ptr == I::_1(), |_1| ptr == *_1) { break; } I::_10r().map(|_10| ptr /= _10).or_else(|| { ptr /= I::_10(); None }); } Ok(DivisionState::new(dividend, divisor)) } /// Produces the fractional part of the decimal from a rest part left after division /// /// WARNING: Negative numbers as arguments are not supported. /// /// Returns remainder of the division /// If the consumer returns `Ok(Ok(state))` keeps on calculation /// If the consumer returns `Ok(Err(state))` stops calculation and returns the remainder /// If the consumer returns `Err(_)` the calculation will be stopped and the error will be passed as the result value /// /// # Examples /// /// ``` /// use fraction::division::divide_rem; /// /// let mut result: [u8; 2] = [0; 2]; /// let mut ptr: usize = 0; /// /// let state = divide_rem(3, 4, |state, digit| { /// result[ptr] = digit; /// ptr += 1; /// Ok(Ok(state)) /// }).unwrap(); /// /// assert_eq!(state.remainder, 0); /// assert_eq!(state.divisor, 4); /// assert_eq!(result, [7, 5]); // 3/4 == 0.75 /// ``` #[inline] pub fn divide_rem( dividend: I, divisor: I, consumer: Consumer, ) -> Result, DivisionError> where Consumer: FnMut( DivisionState, u8, ) -> Result, DivisionState>, DivisionError>, I: Clone + GenericInteger, { divide_rem_resume(DivisionState::new(dividend, divisor), consumer) } /// [divide_rem] co-routine implementation /// Performs the division, changes the state and returns it /// /// # Examples /// /// ``` /// use fraction::division::{DivisionState, divide_rem_resume}; /// /// let mut state = Some(DivisionState::new(1, 3)); /// let mut precision = 5; /// /// let mut result: Vec = Vec::new(); /// /// loop { /// if precision == 0 { break } /// /// state = Some( /// divide_rem_resume(state.take().unwrap(), |s, digit| { /// precision -= 1; /// result.push(digit); /// /// Ok(Err(s)) /// }).unwrap() /// ); /// /// // perform some other operations /// } /// /// assert_eq!(result, vec![3, 3, 3, 3, 3]); /// ``` pub fn divide_rem_resume( mut state: DivisionState, mut consumer: Consumer, ) -> Result, DivisionError> where Consumer: FnMut( DivisionState, u8, ) -> Result, DivisionState>, DivisionError>, I: Clone + GenericInteger, { loop { if state.remainder.is_zero() { break; } let digit = I::_10r() .map(|_10| { let (rem, digit) = state.remainder.checked_mul(_10).map_or_else( || { let (reduced_divisor, reduced_divisor_rem) = state.divisor.div_rem(_10); let mut digit = state.remainder.div_floor(&reduced_divisor); let mut remainder = state.remainder.clone(); remainder -= digit.clone() * &reduced_divisor; let mut red_div_rem_diff = (reduced_divisor_rem.clone() * &digit).div_rem(_10); loop { if red_div_rem_diff.0 > remainder { digit -= I::one(); remainder += &reduced_divisor; red_div_rem_diff = (reduced_divisor_rem.clone() * &digit).div_rem(_10); } else { break; } } remainder -= red_div_rem_diff.0; remainder *= _10; if red_div_rem_diff.1 > remainder { digit -= I::one(); remainder += &state.divisor; } remainder -= red_div_rem_diff.1; (remainder, digit.to_u8()) }, |mut remainder| { let digit = remainder.div_floor(&state.divisor); remainder -= digit.clone() * &state.divisor; (remainder, digit.to_u8()) }, ); state.remainder = rem; digit }) .or_else(|| { let ten = I::_10(); let (rem, digit) = state.remainder.checked_mul(&ten).map_or_else( || { let (reduced_divisor, reduced_divisor_rem) = state.divisor.div_rem(&ten); let mut digit = state.remainder.div_floor(&reduced_divisor); let mut remainder = state.remainder.clone(); remainder -= digit.clone() * &reduced_divisor; let mut red_div_rem_diff = (reduced_divisor_rem.clone() * &digit).div_rem(&ten); loop { if red_div_rem_diff.0 > remainder { digit -= I::one(); remainder += &reduced_divisor; red_div_rem_diff = (reduced_divisor_rem.clone() * &digit).div_rem(&ten); } else { break; } } remainder -= red_div_rem_diff.0; remainder *= &ten; if red_div_rem_diff.1 > remainder { digit -= I::one(); remainder += &state.divisor; } remainder -= red_div_rem_diff.1; (remainder, digit.to_u8()) }, |mut remainder: I| { let digit = remainder.div_floor(&state.divisor); remainder -= digit.clone() * &state.divisor; (remainder, digit.to_u8()) }, ); state.remainder = rem; Some(digit) }) .unwrap(); match digit { Some(n) => { state = match consumer(state, n)? { Ok(state) => state, Err(s) => return Ok(s), }; } None => unreachable!(), }; } Ok(state) } /// Calculate the max possible length of division in characters (including floating point) /// This may be useful for string/vector pre-allocations /// /// WARNING: Negative numbers as arguments are not supported. /// /// # Examples /// ``` /// use fraction::division::division_result_max_char_length; /// /// assert_eq!(division_result_max_char_length(&1, 0), 1); /// assert_eq!(division_result_max_char_length(&10, 0), 2); /// assert_eq!(division_result_max_char_length(&10, 1), 4); /// assert_eq!(division_result_max_char_length(&100, 2), 6); /// assert_eq!(division_result_max_char_length(&900, 2), 6); /// ``` pub fn division_result_max_char_length(dividend: &I, precision: usize) -> usize where I: Clone + GenericInteger, { let mut ptr: I = I::_1(); let mut len: usize = 0; loop { len += 1; ptr = match I::_10r().map_or_else(|| ptr.checked_mul(&I::_10()), |_10| ptr.checked_mul(_10)) { Some(n) => n, None => break, }; if ptr > *dividend { break; } } len + precision + usize::from(precision > 0) } /// Divide a fraction into a [`String`] /// /// WARNING: Negative numbers as arguments are not supported. /// /// - Makes only one allocation for the resulting string /// - Does not round the last digit /// /// Calculates the resulting string length first, allocates it, /// then makes the division and puts the result into the preallocated /// string. /// /// # Examples /// /// ``` /// use fraction::division::divide_to_string; /// /// assert_eq! (divide_to_string(2, 4, 2, false).unwrap(), "0.5"); /// assert_eq! (divide_to_string(2, 4, 2, true).unwrap(), "0.50"); /// assert_eq! (divide_to_string(5, 7, 16, false).unwrap(), "0.7142857142857142"); /// assert_eq! (divide_to_string(1, 3, 3, false).unwrap(), "0.333"); /// assert_eq! (divide_to_string(1, 3, 3, true).unwrap(), "0.333"); /// ``` /// /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html /// [`divide_integral`]: ./fn.divide_integral.html /// [`divide_rem`]: ./fn.divide_rem.html #[inline] pub fn divide_to_string( dividend: I, divisor: I, precision: usize, trail_zeroes: bool, ) -> Result where I: Clone + GenericInteger, { let mut result = String::with_capacity(division_result_max_char_length(÷nd, precision)); divide_to_writeable(&mut result, dividend, divisor, precision, trail_zeroes)?; Ok(result) } /// Divide a fraction into a [`Vec`] of ASCII(utf8) chars /// /// WARNING: Negative numbers as arguments are not supported. /// /// - Makes only one allocation for the resulting vector /// - Does not round the last digit /// /// Calculates the resulting vector length first, allocates it, /// then makes the division and puts the result into the preallocated /// vector. /// Uses [`divide_integral`] and [`divide_rem`] functions internally. /// /// # Examples /// /// ``` /// use fraction::division::divide_to_ascii_vec; /// /// assert_eq! (divide_to_ascii_vec(2, 4, 2, false).unwrap(), vec![48, 46, 53]); // "0.5" in ascii /// assert_eq! (divide_to_ascii_vec(2, 4, 2, true).unwrap(), vec![48, 46, 53, 48]); // "0.50" in ascii /// assert_eq! (divide_to_ascii_vec(5, 7, 16, false).unwrap(), vec![48, 46, 55, 49, 52, 50, 56, 53, 55, 49, 52, 50, 56, 53, 55, 49, 52, 50]); // "0.7142857142857142" in ascii /// assert_eq! (divide_to_ascii_vec(1, 3, 3, false).unwrap(), vec![48, 46, 51, 51, 51]); // "0.333" in ascii /// ``` /// /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html /// [`divide_integral`]: ./fn.divide_integral.html /// [`divide_rem`]: ./fn.divide_rem.html #[inline] pub fn divide_to_ascii_vec( dividend: I, divisor: I, precision: usize, trail_zeroes: bool, ) -> Result, DivisionError> where I: Clone + GenericInteger, { const ZERO: u8 = 48u8; const DOT: u8 = 46u8; let mut result = Vec::with_capacity(division_result_max_char_length(÷nd, precision)); divide_to_callback(dividend, divisor, precision, trail_zeroes, |digit| { if digit == 10u8 { result.push(DOT); } else { result.push(ZERO + digit); } Ok(true) })?; Ok(result) } /// Divide a fraction into a writeable target implementing [`std::fmt::Write`] /// Returns the remainder of the division /// /// - No allocations /// - Does not round the last digit /// /// Makes the division and puts the result into the formatter. /// Uses [`divide_integral`] and [`divide_rem`] functions internally. /// /// WARNING: Negative numbers as arguments are not supported. /// /// # Examples /// /// ``` /// use fraction::division::{ divide_to_writeable, division_result_max_char_length }; /// /// let num = 7; /// let denom = 4; /// /// let mut string = String::with_capacity(division_result_max_char_length(&num, 2)); /// /// divide_to_writeable(&mut string, num, denom, 2, false).ok(); /// /// assert_eq!(string, "1.75"); /// ``` /// /// ``` /// use fraction::division::divide_to_writeable; /// /// use std::fmt; /// /// struct Foo(i32, i32); /// /// impl fmt::Display for Foo { /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { /// let precision = formatter.precision().unwrap_or(2); /// match divide_to_writeable(formatter, self.0, self.1, precision, false) { /// Err(_) => Err(fmt::Error), /// _ => Ok(()) /// } /// } /// } /// /// assert_eq! (format!("{}", Foo(1, 2)), "0.5"); /// assert_eq! (format!("{:.3}", Foo(1, 3)), "0.333"); /// assert_eq! (format!("{:.16}", Foo(5, 7)), "0.7142857142857142"); /// ``` /// /// [`std::fmt::Write`]: https://doc.rust-lang.org/std/fmt/trait.Write.html /// [`divide_integral`]: ./fn.divide_integral.html /// [`divide_rem`]: ./fn.divide_rem.html #[inline] pub fn divide_to_writeable( writeable: &mut dyn Write, dividend: I, divisor: I, precision: usize, trail_zeroes: bool, ) -> Result where I: Clone + GenericInteger, { divide_to_callback(dividend, divisor, precision, trail_zeroes, |digit| { write_digit(writeable, digit) }) } /// Calculate the division result and pass every character into the callback /// Returns the remainder of the division /// /// - Makes no allocations /// /// Uses [`divide_integral`] and [`divide_rem`] functions internally. /// /// WARNING: Negative numbers as arguments are not supported. /// /// # Callback /// /// Callback receives every digit of the result as a u8 value. /// The floating point character (dot) is passed to the callback as `10u8`. /// If the callback returns `Ok(true)`, the function keeps on calculation /// If the callback returns `Ok(false)`, the function stops calculation and returns the remainder /// If the callback returns `Err(_)` the calculation will be stopped and the error will propagate as the result value /// /// # Examples /// /// ``` /// use fraction::division::divide_to_callback; /// /// let mut result = Vec::new(); /// /// // calculate 7/4, which is 1.75 /// divide_to_callback(7, 4, 2, false, |d| { result.push(d as i32); Ok(true) }).ok(); /// /// assert_eq!(&result, &[1, 10, 7, 5]); /// ``` pub fn divide_to_callback( dividend: I, divisor: I, mut precision: usize, trail_zeroes: bool, mut callback: C, ) -> Result where C: FnMut(u8) -> Result, I: Clone + GenericInteger, { let mut keep_going = true; let mut div_state = divide_integral(dividend, divisor, |digit: u8| match callback(digit) { result @ Ok(false) => { keep_going = false; result } result => result, })?; if !keep_going { return Ok(div_state.remainder); } if precision > 0 { let mut dot = false; let mut trailing_zeroes = 0; if !div_state.remainder.is_zero() { div_state = divide_rem( div_state.remainder, div_state.divisor, |state, digit: u8| { precision -= 1; if digit == 0 { trailing_zeroes += 1; } else { if !dot { dot = true; match callback(10u8) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(Err(state)); } } while trailing_zeroes > 0 { trailing_zeroes -= 1; match callback(0u8) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(Err(state)); } } match callback(digit) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(Err(state)); } } if precision > 0 { Ok(Ok(state)) } else { Ok(Err(state)) } }, )?; } if keep_going && trail_zeroes { if !dot { match callback(10u8) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(div_state.remainder); } } while trailing_zeroes > 0 { trailing_zeroes -= 1; match callback(0u8) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(div_state.remainder); } } while precision > 0 { precision -= 1; match callback(0u8) { result @ Ok(false) => { keep_going = false; result } result => result, }?; if !keep_going { return Ok(div_state.remainder); } } } } Ok(div_state.remainder) } /// A helper function to use in conjunction with [divide_to_callback] /// /// divide_to_callback passes digits to its callback, this function can /// be used to write digits (and dots) into the writeable buffer after /// your callback performs its side-effects. /// /// # Examples /// /// ``` /// use fraction::division::{divide_to_callback, write_digit}; /// /// let mut result = String::new(); /// let mut length = 0; /// /// // calculate 7/4, which is 1.75 /// divide_to_callback(7, 4, 2, false, |d| { length += 1; write_digit(&mut result, d) }).ok(); /// /// assert_eq!(&result, "1.75"); /// assert_eq!(length, result.len()); /// ``` pub fn write_digit(writeable: &mut dyn Write, digit: u8) -> Result { if digit == 10u8 { match writeable.write_char('.') { Ok(_) => Ok(true), Err(e) => Err(DivisionError::from(e)), } } else { match writeable.write_fmt(format_args!("{}", digit)) { Ok(_) => Ok(true), Err(e) => Err(DivisionError::from(e)), } } } #[cfg(test)] mod tests { use super::*; use error::DivisionError; use num::Zero; // mod divide_to_string_u8; #[cfg(feature = "with-bigint")] use num::{bigint::BigUint, Num}; #[test] fn test_division() { const PRECISION: usize = 64; let data: Vec<(u8, u8, &'static str, bool)> = vec![ ( 1u8, 255u8, "0.0039215686274509803921568627450980392156862745098039215686274509", false, ), ( 1u8, 254u8, "0.0039370078740157480314960629921259842519685039370078740157480314", false, ), ( 1u8, 253u8, "0.003952569169960474308300395256916996047430830039525691699604743", false, ), ( 1u8, 252u8, "0.0039682539682539682539682539682539682539682539682539682539682539", false, ), ( 1u8, 251u8, "0.0039840637450199203187250996015936254980079681274900398406374501", false, ), (1u8, 250u8, "0.004", true), ( 1u8, 112u8, "0.0089285714285714285714285714285714285714285714285714285714285714", false, ), ( 1u8, 111u8, "0.009009009009009009009009009009009009009009009009009009009009009", false, ), ( 1u8, 96u8, "0.0104166666666666666666666666666666666666666666666666666666666666", false, ), ( 1u8, 92u8, "0.0108695652173913043478260869565217391304347826086956521739130434", false, ), ( 1u8, 91u8, "0.0109890109890109890109890109890109890109890109890109890109890109", false, ), ( 1u8, 90u8, "0.0111111111111111111111111111111111111111111111111111111111111111", false, ), ( 1u8, 9u8, "0.1111111111111111111111111111111111111111111111111111111111111111", false, ), (1u8, 8u8, "0.125", true), ( 1u8, 7u8, "0.1428571428571428571428571428571428571428571428571428571428571428", false, ), ( 1u8, 6u8, "0.1666666666666666666666666666666666666666666666666666666666666666", false, ), (1u8, 5u8, "0.2", true), (1u8, 4u8, "0.25", true), ( 1u8, 3u8, "0.3333333333333333333333333333333333333333333333333333333333333333", false, ), (1u8, 2u8, "0.5", true), (1u8, 1u8, "1", true), ( 49u8, 255u8, "0.192156862745098039215686274509803921568627450980392156862745098", false, ), ( 49u8, 254u8, "0.1929133858267716535433070866141732283464566929133858267716535433", false, ), ( 49u8, 253u8, "0.193675889328063241106719367588932806324110671936758893280632411", false, ), ( 49u8, 252u8, "0.1944444444444444444444444444444444444444444444444444444444444444", false, ), ( 49u8, 251u8, "0.1952191235059760956175298804780876494023904382470119521912350597", false, ), (49u8, 250u8, "0.196", true), ( 49u8, 249u8, "0.1967871485943775100401606425702811244979919678714859437751004016", false, ), ( 49u8, 248u8, "0.1975806451612903225806451612903225806451612903225806451612903225", false, ), ( 49u8, 247u8, "0.1983805668016194331983805668016194331983805668016194331983805668", false, ), ( 49u8, 246u8, "0.1991869918699186991869918699186991869918699186991869918699186991", false, ), (49u8, 245u8, "0.2", true), ( 49u8, 69u8, "0.7101449275362318840579710144927536231884057971014492753623188405", false, ), ( 49u8, 68u8, "0.7205882352941176470588235294117647058823529411764705882352941176", false, ), ( 49u8, 67u8, "0.7313432835820895522388059701492537313432835820895522388059701492", false, ), ( 49u8, 66u8, "0.7424242424242424242424242424242424242424242424242424242424242424", false, ), ( 49u8, 65u8, "0.7538461538461538461538461538461538461538461538461538461538461538", false, ), (49u8, 64u8, "0.765625", true), ( 49u8, 63u8, "0.7777777777777777777777777777777777777777777777777777777777777777", false, ), ( 77u8, 255u8, "0.3019607843137254901960784313725490196078431372549019607843137254", false, ), ( 77u8, 254u8, "0.3031496062992125984251968503937007874015748031496062992125984251", false, ), ( 77u8, 253u8, "0.3043478260869565217391304347826086956521739130434782608695652173", false, ), ( 77u8, 252u8, "0.3055555555555555555555555555555555555555555555555555555555555555", false, ), ( 77u8, 251u8, "0.3067729083665338645418326693227091633466135458167330677290836653", false, ), (77u8, 250u8, "0.308", true), ( 77u8, 249u8, "0.3092369477911646586345381526104417670682730923694779116465863453", false, ), ( 77u8, 248u8, "0.3104838709677419354838709677419354838709677419354838709677419354", false, ), ( 77u8, 231u8, "0.3333333333333333333333333333333333333333333333333333333333333333", false, ), ( 77u8, 230u8, "0.3347826086956521739130434782608695652173913043478260869565217391", false, ), ( 77u8, 229u8, "0.3362445414847161572052401746724890829694323144104803493449781659", false, ), ( 77u8, 228u8, "0.3377192982456140350877192982456140350877192982456140350877192982", false, ), ( 77u8, 227u8, "0.3392070484581497797356828193832599118942731277533039647577092511", false, ), ( 77u8, 226u8, "0.3407079646017699115044247787610619469026548672566371681415929203", false, ), ( 77u8, 225u8, "0.3422222222222222222222222222222222222222222222222222222222222222", false, ), (77u8, 224u8, "0.34375", true), ( 77u8, 223u8, "0.3452914798206278026905829596412556053811659192825112107623318385", false, ), ( 77u8, 222u8, "0.3468468468468468468468468468468468468468468468468468468468468468", false, ), ( 77u8, 221u8, "0.3484162895927601809954751131221719457013574660633484162895927601", false, ), (77u8, 220u8, "0.35", true), ]; for i in data.iter() { assert_eq!(divide_to_string(i.0, i.1, PRECISION, false).unwrap(), i.2); { let mut string: String = String::new(); let remainder = divide_to_writeable(&mut string, i.0, i.1, PRECISION, false).unwrap(); assert_eq!(string, i.2); assert_eq!(remainder.is_zero(), i.3); } } #[cfg(feature = "with-bigint")] { for i in data { assert_eq!( divide_to_string(BigUint::from(i.0), BigUint::from(i.1), PRECISION, false) .unwrap(), i.2 ); } } } #[test] fn test_divide_to_string() { assert_eq!(divide_to_string(0, 5, 5, false).unwrap(), "0"); assert_eq!(divide_to_string(0, 5, 5, true).unwrap(), "0.00000"); assert_eq!(divide_to_string(30, 2, 5, false).unwrap(), "15"); assert_eq!(divide_to_string(30, 2, 5, true).unwrap(), "15.00000"); assert_eq!(divide_to_string(2, 4, 2, false).unwrap(), "0.5"); assert_eq!(divide_to_string(2, 4, 2, true).unwrap(), "0.50"); assert_eq!(divide_to_string(255u8, 3u8, 5, false).unwrap(), "85"); assert_eq!(divide_to_string(255u8, 3u8, 5, true).unwrap(), "85.00000"); assert_eq!(divide_to_string(155u8, 253u8, 5, false).unwrap(), "0.61264"); assert_eq!(divide_to_string(1u8, 2u8, 1, false).unwrap(), "0.5"); assert_eq!(divide_to_string(1u8, 2u8, 1, true).unwrap(), "0.5"); assert_eq!( divide_to_string(1, 3, 28, false).unwrap(), "0.3333333333333333333333333333" ); assert_eq!( divide_to_string(1, 3, 28, true).unwrap(), "0.3333333333333333333333333333" ); assert_eq!(divide_to_string(806, 31, 0, false).unwrap(), "26"); assert_eq!(divide_to_string(806, 31, 0, true).unwrap(), "26"); assert_eq!(divide_to_string(807, 31, 4, false).unwrap(), "26.0322"); assert_eq!(divide_to_string(807, 31, 4, true).unwrap(), "26.0322"); if let Err(DivisionError::DivisionByZero) = divide_to_string(1, 0, 1, false) { } else { assert!(false); }; if let Err(DivisionError::DivisionByZero) = divide_to_string(1, 0, 1, true) { } else { assert!(false); }; assert_eq!(divide_to_string(1, 10000, 2, false).unwrap(), "0"); assert_eq!(divide_to_string(1, 10000, 2, true).unwrap(), "0.00"); #[cfg(feature = "with-bigint")] { let num = "820123456789012345678901234567890123456789"; let den = "420420420420240240420240420240420420420"; let result = "1950.7222222204153880534352079149419688493160244330759069204925259490806291881834407646199555744655166719234903156227406500953778757619920899563294795746797267246703798051247915744867884912121330007705286911209791422213223230426822190127666529437572025264829201539629594175021000386486667365851813562015885600393107707737453721144405603009059457352854387023006301508907770762997683254372534811586343569328131455924964081595076622200116354403760742833073621862325616259444084808295834752749082040590201012701034118255745516514972270054835928011374231619434684843847682844123336846713100440285930668493675706096464476187809105398269815519780303174023949885603320678109566772031394249633001137109155600514761384776616827086908396960584246277151426685095767130738996420461009015758184659365258827537226581854494195009714400547427050697946126012192691851549545189173091941689299722345297086508711845865506663262915436874050594522308464492248968916100678819937355384703664061966410613989688966690413319849627099468906113991173042101218"; let asrt1 = divide_to_string( BigUint::from_str_radix(num, 10).ok().unwrap(), BigUint::from_str_radix(den, 10).ok().unwrap(), 1024, false, ); assert_eq!(&asrt1.ok().unwrap(), result); let asrt1 = divide_to_string( BigUint::from_str_radix(num, 10).ok().unwrap(), BigUint::from_str_radix(den, 10).ok().unwrap(), 1024, true, ); assert_eq!(&asrt1.ok().unwrap(), result); } } #[test] fn test_divide_to_ascii_vec() { assert_eq!(divide_to_ascii_vec(0, 5, 5, false).unwrap(), vec![48]); assert_eq!( divide_to_ascii_vec(0, 5, 5, true).unwrap(), vec![48, 46, 48, 48, 48, 48, 48] ); assert_eq!(divide_to_ascii_vec(30, 2, 5, false).unwrap(), vec![49, 53]); assert_eq!( divide_to_ascii_vec(2, 4, 2, false).unwrap(), vec![48, 46, 53] ); assert_eq!( divide_to_ascii_vec(2, 4, 2, true).unwrap(), vec![48, 46, 53, 48] ); assert_eq!( divide_to_ascii_vec(255u8, 3u8, 5, false).unwrap(), vec![56, 53] ); assert_eq!( divide_to_ascii_vec(1000001u64, 10000u64, 3, false).unwrap(), vec![49, 48, 48] ); assert_eq!( divide_to_ascii_vec(1000001u64, 10000u64, 3, true).unwrap(), vec![49, 48, 48, 46, 48, 48, 48] ); } #[test] fn test_divide_integral() { { let mut r1: [u8; 1] = [0; 1]; let mut p1: usize = 0; let mut r2: [u8; 2] = [0; 2]; let mut p2: usize = 0; let mut r3: [u8; 3] = [0; 3]; let mut p3: usize = 0; let rest1 = divide_integral(2, 4, |d| { r1[p1] = d; p1 += 1; Ok(true) }); assert_eq!(r1, [0]); assert_eq!(p1, 1); assert_eq!(rest1.unwrap(), DivisionState::new(2, 4)); let rest2 = divide_integral(82, 3, |d| { r2[p2] = d; p2 += 1; Ok(true) }); assert_eq!(r2, [2, 7]); assert_eq!(p2, 2); assert_eq!(rest2.unwrap(), DivisionState::new(1, 3)); let rest3 = divide_integral(2020, 4, |d| { r3[p3] = d; p3 += 1; Ok(true) }); assert_eq!(r3, [5, 0, 5]); assert_eq!(p3, 3); assert_eq!(rest3.unwrap(), DivisionState::new(0, 4)); } { let mut result = Vec::new(); let rem = divide_integral(255u8, 3u8, |d| { result.push(d); Ok(true) }); assert_eq!(rem.ok(), Some(DivisionState::new(0, 3))); assert_eq!(result, vec![8, 5]); } { let mut result = Vec::new(); let rem = divide_integral(255u8, 3u8, |d| { result.push(d); Ok(false) }); assert_eq!(rem.ok(), Some(DivisionState::new(15, 3))); assert_eq!(result, vec![8]); } } #[test] fn test_divide_rem() { let mut r1: [u8; 1] = [0; 1]; let mut p1: usize = 0; let mut r2: [u8; 2] = [0; 2]; let mut p2: usize = 0; let mut r3: [u8; 3] = [0; 3]; let mut p3: usize = 0; let mut rest1 = None; divide_rem(1, 3, |s, d| { r1[p1] = d; p1 += 1; rest1 = Some(s.remainder); Ok(Err(s)) }) .ok(); assert_eq!(r1, [3]); assert_eq!(p1, 1); assert_eq!(rest1, Some(1)); p1 = 0; let rest1 = divide_rem(1, 2, |s, d| { r1[p1] = d; p1 += 1; Ok(Ok(s)) }); assert_eq!(r1, [5]); assert_eq!(p1, 1); assert_eq!(rest1.unwrap(), DivisionState::new(0, 2)); let rest2 = divide_rem(500, 2000, |s, d| { r2[p2] = d; p2 += 1; Ok(Ok(s)) }); assert_eq!(r2, [2, 5]); assert_eq!(p2, 2); assert_eq!(rest2.unwrap(), DivisionState::new(0, 2000)); let rest3 = divide_rem(2, 1000, |s, d| { r3[p3] = d; p3 += 1; Ok(Ok(s)) }); assert_eq!(r3, [0, 0, 2]); assert_eq!(p3, 3); assert_eq!(rest3.unwrap(), DivisionState::new(0, 1000)); p3 = 0; let rest3 = divide_rem(502, 1000, |s, d| { r3[p3] = d; p3 += 1; Ok(Ok(s)) }); assert_eq!(r3, [5, 0, 2]); assert_eq!(p3, 3); assert_eq!(rest3.unwrap(), DivisionState::new(0, 1000)); } } fraction-0.15.3/src/dynaint.rs000064400000000000000000001236411046102023000143310ustar 00000000000000//! Dynamic unsigned integer type selection //! //! Implements a wrapper around two unsigned integer types that picks the size //! dynamically (at runtime) depending on the value. //! //! Most useful in combination with heap allocated integers such as [BigUint]. //! If we expect small numbers to operate with, and we wish to work without //! allocations by default. However, we want to handle huge numbers //! gracefully, without stack overflows. //! //! # Examples //! ``` //! use fraction::{dynaint::DynaInt, One}; //! type DI = DynaInt; //! //! let one: DI = DynaInt::one(); //! let mut num: DI = DynaInt::from(255u8); //! assert_eq!(num.unpack(), Ok(255u8)); //! //! num += one; //! assert_eq!(num.unpack(), Err(256u16)); //! //! num -= one; // here it automatically detects the number fits into u8 again //! assert_eq!(num.unpack(), Ok(255u8)); //! ``` use std::mem; use num::{ bigint::{ToBigInt, ToBigUint}, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Integer, Num, One, ToPrimitive, Zero, }; use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use std::fmt; use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; use super::Sign; #[cfg(feature = "with-bigint")] use num::BigUint; use convert::TryToConvertFrom; use generic::{read_generic_integer, GenericInteger}; /// The wrapper implementation /// /// Keeps data within S (small) whenever possible, performing /// checked arithmetic and moving onto __H (huge) when /// overflows happen. Every math operation on __H performs a read of the /// resulting number with [TryToConvertFrom::try_to_convert_from]. /// Every math operation on S is checked for overflows. #[cfg_attr(feature = "with-serde-support", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] pub enum DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { /// Represents the small type, implementing Copy and allocated on stack. /// The wrapper tries to reduce contained values to this type whenever possible S(T), /// Represents the huge type, implementing Clone. /// To be used when values overflow T __H(G), } impl Copy for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Copy + GenericInteger, { } impl fmt::Display for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + fmt::Display, G: Clone + GenericInteger + fmt::Display, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { DynaInt::S(value) => write!(formatter, "{}", value), DynaInt::__H(value) => write!(formatter, "{}", value), } } } impl Bounded for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + Bounded, G: Clone + GenericInteger + Bounded, { fn min_value() -> Self { DynaInt::S(T::min_value()) } fn max_value() -> Self { DynaInt::__H(G::max_value()) } } impl DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { fn h(value: G) -> Self { T::try_to_convert_from(value.clone()).map_or(DynaInt::__H(value), DynaInt::S) } /// Unpacks the value /// /// Utilises [Result::Ok] for S(small) numbers and [Result::Err] for __H(huge) ones /// /// # Examples /// ``` /// use fraction::dynaint::DynaInt; /// type D = DynaInt; /// /// assert_eq!(Ok(1u8), D::from(1u8).unpack()); /// assert_eq!(Err(256u16), D::from(256u16).unpack()); /// ``` pub fn unpack(self) -> Result { match self { DynaInt::S(value) => Ok(value), DynaInt::__H(value) => Err(value), } } } impl ToPrimitive for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + ToPrimitive, G: Clone + GenericInteger, { fn to_i64(&self) -> Option { match self { DynaInt::S(val) => val.to_i64(), DynaInt::__H(val) => val.to_i64(), } } fn to_u64(&self) -> Option { match self { DynaInt::S(val) => val.to_u64(), DynaInt::__H(val) => val.to_u64(), } } } impl GenericInteger for DynaInt where T: GenericInteger + Copy + Integer + Into + TryToConvertFrom + From, G: Clone + GenericInteger + 'static, { #[inline] fn _0() -> Self { DynaInt::S(T::_0()) } #[inline] fn _1() -> Self { DynaInt::S(T::_1()) } #[inline] fn _10() -> Self { DynaInt::S(T::_10()) } #[inline] fn _0r() -> Option<&'static Self> { None } #[inline] fn _1r() -> Option<&'static Self> { None } #[inline] fn _10r() -> Option<&'static Self> { None } #[inline] fn get_signed_value(self) -> (Sign, Self) { (Sign::Plus, self) } } macro_rules! dyna_impl { (impl_trait_birefs_customret; $trait:ident, $fn:ident, $ret:ty) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { fn $fn(&self, other: &Self) -> $ret { match *self { DynaInt::S(a) => match *other { DynaInt::S(b) => $trait::$fn(&a, &b), DynaInt::__H(ref b) => $trait::$fn(&>::into(a), b), }, DynaInt::__H(ref a) => match *other { DynaInt::S(b) => $trait::$fn(a, &>::into(b)), DynaInt::__H(ref b) => $trait::$fn(a, b), }, } } } }; (impl_trait_math_unary; $trait:ident, $fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { type Output = Self; fn $fn(self) -> Self::Output { match self { DynaInt::S(v) => DynaInt::S($trait::$fn(v)), DynaInt::__H(v) => DynaInt::h($trait::$fn(v)) } } } }; (impl_trait_math_assign; $trait:ident, $fn:ident, $checked_trait:ident, $checked_fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait + $checked_trait, G: Clone + GenericInteger + $trait + $checked_trait, { fn $fn(&mut self, other: Self) { *self = match *self { DynaInt::S(a) => match other { DynaInt::S(b) => $checked_trait::$checked_fn(&a, &b).map_or_else( || { let mut a_: G = a.into(); $trait::$fn(&mut a_, b.into()); DynaInt::h(a_) }, DynaInt::S ), DynaInt::__H(b) => { let mut a_: G = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) }, }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, b.into()); DynaInt::h(a_) }, DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } } } } } impl<'a, T, G> $trait<&'a Self> for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait + $checked_trait, G: Clone + GenericInteger + $trait + $checked_trait, { fn $fn(&mut self, other: &'a Self) { *self = match *self { DynaInt::S(a) => match other { DynaInt::S(b) => $checked_trait::$checked_fn(&a, b).map_or_else( || { let mut a_: G = a.into(); $trait::$fn(&mut a_, (*b).into()); DynaInt::h(a_) }, DynaInt::S ), DynaInt::__H(b) => { let mut a_: G = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) }, }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, (*b).into()); DynaInt::h(a_) }, DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } } } } } }; (impl_trait_math; $trait:ident, $fn:ident, $checked_trait:ident, $checked_fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait + $checked_trait, G: Clone + GenericInteger + $trait + $checked_trait, { type Output = Self; fn $fn(self, other: Self) -> Self::Output { match self { DynaInt::S(a) => match other { DynaInt::S(b) => $checked_trait::$checked_fn(&a, &b).map_or_else( || DynaInt::h($trait::$fn(a.into(), b.into())), DynaInt::S ), DynaInt::__H(b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(a) => match other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(b) => DynaInt::h($trait::$fn(a, b)), }, } } } impl<'a, T, G> $trait for &'a DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait + $checked_trait, G: Clone + GenericInteger + $trait + $checked_trait, &'a G: $trait + $trait { type Output = DynaInt; fn $fn(self, other: Self) -> Self::Output { match *self { DynaInt::S(a) => match *other { DynaInt::S(b) => $checked_trait::$checked_fn(&a, &b).map_or_else( || DynaInt::h($trait::$fn(a.into(), b.into())), DynaInt::S ), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(ref a) => match *other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a, b)), }, } } } impl<'a, T, G> $trait<&'a DynaInt> for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger { type Output = DynaInt; fn $fn(self, other: &'a Self) -> Self::Output { match self { DynaInt::S(a) => match other { DynaInt::S(b) => $checked_trait::$checked_fn(&a, b).map_or_else( || DynaInt::h($trait::$fn(a.into(), (*b).into())), DynaInt::S ), DynaInt::__H(b) => DynaInt::h($trait::$fn(a.into(), b)) }, DynaInt::__H(a) => match other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, (*b).into())), DynaInt::__H(b) => DynaInt::h($trait::$fn(a, b)) }, } } } }; (impl_trait_math_unchecked; $trait:ident, $fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { type Output = Self; fn $fn(self, other: Self) -> Self::Output { match self { DynaInt::S(a) => match other { DynaInt::S(b) => DynaInt::S($trait::$fn(a, b)), DynaInt::__H(b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(a) => match other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(b) => DynaInt::h($trait::$fn(a, b)), }, } } } impl<'a, T, G> $trait for &'a DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait + $trait<&'a G, Output=G>, &'a G: $trait + $trait { type Output = DynaInt; fn $fn(self, other: Self) -> Self::Output { match *self { DynaInt::S(a) => match *other { DynaInt::S(b) => DynaInt::S($trait::$fn(a, b)), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(ref a) => match *other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a, b)), }, } } } }; (impl_trait_math_unchecked_no_g_to_ref; $trait:ident, $fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { type Output = Self; fn $fn(self, other: Self) -> Self::Output { match self { DynaInt::S(a) => match other { DynaInt::S(b) => DynaInt::S($trait::$fn(a, b)), DynaInt::__H(b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(a) => match other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(b) => DynaInt::h($trait::$fn(a, b)), }, } } } impl<'a, T, G> $trait for &'a DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait, &'a G: $trait + $trait { type Output = DynaInt; fn $fn(self, other: Self) -> Self::Output { match *self { DynaInt::S(a) => match *other { DynaInt::S(b) => DynaInt::S($trait::$fn(a, b)), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a.into(), b)), }, DynaInt::__H(ref a) => match *other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, b.into())), DynaInt::__H(ref b) => DynaInt::h($trait::$fn(a, b)), }, } } } impl<'a, T, G> $trait<&'a DynaInt> for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger { type Output = DynaInt; fn $fn(self, other: &'a Self) -> Self::Output { match self { DynaInt::S(a) => match other { DynaInt::S(b) => DynaInt::S($trait::$fn(a, b)), DynaInt::__H(b) => DynaInt::h($trait::$fn(a.into(), b)) }, DynaInt::__H(a) => match other { DynaInt::S(b) => DynaInt::h($trait::$fn(a, (*b).into())), DynaInt::__H(b) => DynaInt::h($trait::$fn(a, b)) }, } } } }; (impl_trait_math_unchecked_assigned; $trait:ident, $fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { fn $fn(&mut self, other: Self) { *self = match *self { DynaInt::S(mut a) => match other { DynaInt::S(b) => { $trait::$fn(&mut a, b); DynaInt::S(a) } DynaInt::__H(b) => { let mut a_ = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) } }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, b.into()); DynaInt::h(a_) } DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } }, } } } impl<'a, T, G> $trait<&'a Self> for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait<&'a T>, G: Clone + GenericInteger + $trait<&'a G> + $trait, { fn $fn(&mut self, other: &'a Self) { *self = match *self { DynaInt::S(mut a) => match other { DynaInt::S(b) => { $trait::$fn(&mut a, b); DynaInt::S(a) } DynaInt::__H(b) => { let mut a_ = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) } }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, (*b).into()); DynaInt::h(a_) } DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } }, } } } }; (impl_trait_math_unchecked_assigned_no_trait_for_ref; $trait:ident, $fn:ident) => { impl $trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $trait, G: Clone + GenericInteger + $trait { fn $fn(&mut self, other: Self) { *self = match *self { DynaInt::S(mut a) => match other { DynaInt::S(b) => { $trait::$fn(&mut a, b); DynaInt::S(a) } DynaInt::__H(b) => { let mut a_ = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) } }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, b.into()); DynaInt::h(a_) } DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } }, } } } impl<'a, T, G> $trait<&'a Self> for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { fn $fn(&mut self, other: &'a Self) { *self = match *self { DynaInt::S(mut a) => match other { DynaInt::S(b) => { $trait::$fn(&mut a, b); DynaInt::S(a) } DynaInt::__H(b) => { let mut a_ = a.into(); $trait::$fn(&mut a_, b); DynaInt::h(a_) } }, DynaInt::__H(ref mut a) => { let mut a_ = mem::replace(a, G::zero()); match other { DynaInt::S(b) => { $trait::$fn(&mut a_, (*b).into()); DynaInt::h(a_) } DynaInt::__H(b) => { $trait::$fn(&mut a_, b); DynaInt::h(a_) } } }, } } } }; (impl_trait_math_checked; $checked_trait:ident, $checked_fn:ident) => { impl $checked_trait for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + $checked_trait, G: Clone + GenericInteger + $checked_trait { fn $checked_fn(&self, other: &Self) -> Option { match self { DynaInt::S(a) => match other { DynaInt::S(b) => $checked_trait::$checked_fn(a, b).map_or_else( || { $checked_trait::$checked_fn( &>::into(*a), &>::into(*b), ).map(DynaInt::h) }, |val| Some(DynaInt::S(val)), ), DynaInt::__H(b) => { $checked_trait::$checked_fn(&>::into(*a), b) .map(DynaInt::h) } }, DynaInt::__H(a) => match other { DynaInt::S(b) => { $checked_trait::$checked_fn(a, &>::into(*b)) .map(DynaInt::h) } DynaInt::__H(b) => { $checked_trait::$checked_fn(a, b).map(DynaInt::h) } }, } } } }; (impl_trait_from_uint; $($F:ty),+) => { $( impl From<$F> for DynaInt where $F: GenericInteger + PartialOrd + Into, T: GenericInteger + Copy + Into + TryToConvertFrom + From, G: Clone + GenericInteger { fn from(v: $F) -> DynaInt { read_generic_integer::(v) .map_or_else( || DynaInt::h(v.into()), |(_, val)| DynaInt::S(val) ) } } )+ }; (impl_fn_refmath; $fn:ident) => { fn $fn(&self, other: &Self) -> Self { match *self { DynaInt::S(ref a) => match *other { DynaInt::S(ref b) => DynaInt::S(T::$fn(a, b)), DynaInt::__H(ref b) => DynaInt::h(G::$fn(&>::into(*a), b)) }, DynaInt::__H(ref a) => match *other { DynaInt::S(ref b) => DynaInt::h(G::$fn(a, &>::into(*b))), DynaInt::__H(ref b) => DynaInt::h(G::$fn(a, b)), }, } } }; (impl_fn_refmath_bool; $fn:ident) => { fn $fn(&self, other: &Self) -> bool { match *self { DynaInt::S(ref a) => match *other { DynaInt::S(ref b) => T::$fn(a, b), DynaInt::__H(ref b) => G::$fn(&>::into(*a), b) }, DynaInt::__H(ref a) => match *other { DynaInt::S(ref b) => G::$fn(a, &>::into(*b)), DynaInt::__H(ref b) => G::$fn(a, b) } } } }; (impl_fn_refmath_tuple2self; $fn:ident) => { fn $fn(&self, other: &Self) -> (Self, Self) { match *self { DynaInt::S(ref a) => match *other { DynaInt::S(ref b) => { let (c, d) = T::$fn(a, b); (DynaInt::S(c), DynaInt::S(d)) } DynaInt::__H(ref b) => { let (c, d) = G::$fn(&>::into(*a), b); (DynaInt::h(c), DynaInt::h(d)) } }, DynaInt::__H(ref a) => match *other { DynaInt::S(ref b) => { let (c, d) = G::$fn(a, &>::into(*b)); (DynaInt::h(c), DynaInt::h(d)) } DynaInt::__H(ref b) => { let (c, d) = G::$fn(a, b); (DynaInt::h(c), DynaInt::h(d)) } } } } }; (impl_fn_isref; $fn:ident) => { fn $fn(&self) -> bool { match *self { DynaInt::S(ref a) => a.$fn(), DynaInt::__H(ref b) => b.$fn() } } }; } dyna_impl! (impl_trait_birefs_customret; PartialEq, eq, bool); dyna_impl! (impl_trait_birefs_customret; Ord, cmp, Ordering); dyna_impl! (impl_trait_math_unary; Neg, neg); dyna_impl! (impl_trait_math_unary; Not, not); dyna_impl! (impl_trait_math_unchecked; BitAnd, bitand); dyna_impl! (impl_trait_math_unchecked; BitOr, bitor); dyna_impl! (impl_trait_math_unchecked; BitXor, bitxor); dyna_impl! (impl_trait_math_unchecked; Shl, shl); dyna_impl! (impl_trait_math_unchecked; Shr, shr); dyna_impl! (impl_trait_math_unchecked_no_g_to_ref; Rem, rem); dyna_impl! (impl_trait_math_unchecked_assigned; BitAndAssign, bitand_assign); dyna_impl! (impl_trait_math_unchecked_assigned; BitOrAssign, bitor_assign); dyna_impl! (impl_trait_math_unchecked_assigned; BitXorAssign, bitxor_assign); dyna_impl! (impl_trait_math_unchecked_assigned; ShlAssign, shl_assign); dyna_impl! (impl_trait_math_unchecked_assigned; ShrAssign, shr_assign); dyna_impl! (impl_trait_math_unchecked_assigned_no_trait_for_ref; RemAssign, rem_assign); dyna_impl! (impl_trait_math; Add, add, CheckedAdd, checked_add); dyna_impl! (impl_trait_math; Div, div, CheckedDiv, checked_div); dyna_impl! (impl_trait_math; Mul, mul, CheckedMul, checked_mul); dyna_impl! (impl_trait_math; Sub, sub, CheckedSub, checked_sub); dyna_impl! (impl_trait_math_assign; AddAssign, add_assign, CheckedAdd, checked_add); dyna_impl! (impl_trait_math_assign; DivAssign, div_assign, CheckedDiv, checked_div); dyna_impl! (impl_trait_math_assign; MulAssign, mul_assign, CheckedMul, checked_mul); dyna_impl! (impl_trait_math_assign; SubAssign, sub_assign, CheckedSub, checked_sub); dyna_impl! (impl_trait_math_checked; CheckedAdd, checked_add); dyna_impl! (impl_trait_math_checked; CheckedDiv, checked_div); dyna_impl! (impl_trait_math_checked; CheckedMul, checked_mul); dyna_impl! (impl_trait_math_checked; CheckedSub, checked_sub); dyna_impl!(impl_trait_from_uint; u16, u32, u64, u128, usize); impl PartialOrd for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + PartialOrd, G: Clone + GenericInteger + PartialOrd, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Eq for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + Eq, G: Clone + GenericInteger + Eq, { } impl From for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { fn from(value: u8) -> DynaInt { DynaInt::S(value.into()) } } #[cfg(feature = "with-bigint")] impl From for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger + From, { fn from(value: BigUint) -> DynaInt { DynaInt::h(G::from(value)) } } #[cfg(feature = "with-bigint")] impl From> for BigUint where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger + Into, { fn from(value: DynaInt) -> BigUint { match value { DynaInt::S(v) => >::into(v).into(), DynaInt::__H(v) => v.into(), } } } impl Zero for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { #[inline] fn zero() -> Self { DynaInt::S(T::zero()) } #[inline] fn is_zero(&self) -> bool { *self == Self::zero() } } impl One for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { #[inline] fn one() -> Self { DynaInt::S(T::one()) } } impl Num for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + Num, G: Clone + GenericInteger, { type FromStrRadixErr = ::FromStrRadixErr; fn from_str_radix(s: &str, radix: u32) -> Result { T::from_str_radix(s, radix) .map(DynaInt::S) .or_else(|_| G::from_str_radix(s, radix).map(DynaInt::h)) } } impl Integer for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From, G: Clone + GenericInteger, { dyna_impl!(impl_fn_refmath; div_floor); dyna_impl!(impl_fn_refmath; mod_floor); dyna_impl!(impl_fn_refmath; gcd); dyna_impl!(impl_fn_refmath; lcm); dyna_impl!(impl_fn_refmath_bool; divides); dyna_impl!(impl_fn_refmath_bool; is_multiple_of); dyna_impl!(impl_fn_isref; is_even); dyna_impl!(impl_fn_isref; is_odd); dyna_impl!(impl_fn_refmath_tuple2self; div_rem); } #[cfg(feature = "with-bigint")] impl ToBigInt for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + ToBigInt, G: Clone + GenericInteger + ToBigInt, { fn to_bigint(&self) -> Option { match self { DynaInt::S(s) => s.to_bigint(), DynaInt::__H(h) => h.to_bigint(), } } } #[cfg(feature = "with-bigint")] impl ToBigUint for DynaInt where T: Copy + GenericInteger + Into + TryToConvertFrom + From + ToBigUint, G: Clone + GenericInteger + ToBigUint, { fn to_biguint(&self) -> Option { match self { DynaInt::S(s) => s.to_biguint(), DynaInt::__H(h) => h.to_biguint(), } } } #[cfg(test)] mod tests { use super::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, DynaInt, GenericInteger, Integer, Num, One, Sign, ToPrimitive, Zero, }; type D = DynaInt; generate_ops_tests!( Zero => {D::zero()}; One => {D::one()}; Two => {D::from(2u8)}; Three => {D::from(3u8)}; Four => {D::from(4u8)}; ); #[test] fn growth() { let m8 = u8::max_value(); let mut val = D::from(m8); assert_eq!(m8, val.clone().unpack().ok().unwrap()); val += D::zero(); assert_eq!(m8, val.clone().unpack().ok().unwrap()); val += D::one(); assert_eq!(u16::from(m8) + 1, val.clone().unpack().err().unwrap()); val += D::one(); assert_eq!(u16::from(m8) + 2, val.clone().unpack().err().unwrap()); val -= D::from(2u8); assert_eq!(m8, val.clone().unpack().ok().unwrap()); } #[test] fn to_primitive() { { let mut val = D::from(255u8); assert_eq!(Some(255u64), val.to_u64()); val += D::one(); assert_eq!(Some(256u64), val.to_u64()); } { let mut val = D::from(255u8); assert_eq!(Some(255i64), val.to_i64()); val += D::one(); assert_eq!(Some(256i64), val.to_i64()); } } #[test] fn generic_integer() { assert_eq!(0u8, D::_0().unpack().ok().unwrap()); assert_eq!(1u8, D::_1().unpack().ok().unwrap()); assert_eq!(10u8, D::_10().unpack().ok().unwrap()); assert!(D::_0r().is_none()); assert!(D::_1r().is_none()); assert!(D::_10r().is_none()); let (s, v) = D::from(254u8).get_signed_value(); assert_eq!(s, Sign::Plus); assert_eq!(254u8, v.unpack().ok().unwrap()); } #[test] fn partial_eq() { assert_ne!(D::from(1u8), D::from(256u16)); assert_eq!(D::from(256u16), D::from(256u16)); assert_ne!(D::from(256u16), D::from(257u16)); } #[test] fn add_assign() { { let mut val = D::from(255u8); val += D::from(256u16); assert_eq!(511u16, val.unpack().err().unwrap()); } { let mut val = D::from(255u8); val += &D::from(256u16); assert_eq!(511u16, val.unpack().err().unwrap()); } { let mut val = D::from(256u16); val += &D::one(); assert_eq!(257u16, val.unpack().err().unwrap()); } { let mut val = D::from(255u8); val += &D::from(255u8); assert_eq!(510u16, val.unpack().err().unwrap()); } { let mut val = D::from(256u16); val += D::from(256u16); assert_eq!(512u16, val.unpack().err().unwrap()); } { let mut val = D::from(256u16); val += &D::from(256u16); assert_eq!(512u16, val.unpack().err().unwrap()); } } #[test] fn op_add() { assert_eq!(2u8, (D::one() + &D::one()).unpack().ok().unwrap()); assert_eq!(257u16, (D::one() + D::from(256u16)).unpack().err().unwrap()); assert_eq!(256u16, (D::one() + &D::from(255u8)).unpack().err().unwrap()); assert_eq!( 257u16, (D::one() + &D::from(256u16)).unpack().err().unwrap() ); assert_eq!( 257u16, (&D::one() + &D::from(256u16)).unpack().err().unwrap() ); assert_eq!(257u16, (D::from(256u16) + D::one()).unpack().err().unwrap()); assert_eq!( 257u16, (D::from(256u16) + &D::one()).unpack().err().unwrap() ); assert_eq!( 257u16, (&D::from(256u16) + &D::one()).unpack().err().unwrap() ); assert_eq!( 512u16, (D::from(256u16) + D::from(256u16)).unpack().err().unwrap() ); assert_eq!( 512u16, (D::from(256u16) + &D::from(256u16)).unpack().err().unwrap() ); assert_eq!( 512u16, (&D::from(256u16) + &D::from(256u16)) .unpack() .err() .unwrap() ); } #[test] fn op_bit_and() { assert_eq!(D::one(), D::one() & D::one()); assert_eq!(D::one(), &D::one() & &D::one()); assert_eq!(D::zero(), D::one() & D::from(256u16)); assert_eq!(D::zero(), &D::one() & &D::from(256u16)); assert_eq!(D::zero(), D::from(256u16) & D::one()); assert_eq!(D::zero(), &D::from(256u16) & &D::one()); assert_eq!(D::from(256u16), D::from(256u16) & D::from(257u16)); assert_eq!(D::from(256u16), &D::from(256u16) & &D::from(257u16)); { let mut v = D::one(); v &= D::one(); assert_eq!(v, D::one()); } { let mut v = D::one(); v &= &D::one(); assert_eq!(v, D::one()); } { let mut v = D::one(); v &= D::from(256u16); assert_eq!(v, D::zero()); } { let mut v = D::one(); v &= &D::from(256u16); assert_eq!(v, D::zero()); } { let mut v = D::from(256u16); v &= D::one(); assert_eq!(v, D::zero()); } { let mut v = D::from(256u16); v &= &D::one(); assert_eq!(v, D::zero()); } { let mut v = D::from(256u16); v &= D::from(257u16); assert_eq!(v, D::from(256u16)); } { let mut v = D::from(256u16); v &= &D::from(257u16); assert_eq!(v, D::from(256u16)); } } #[test] fn op_rem() { assert_eq!(D::one(), D::from(3u8) % D::from(2u8)); assert_eq!(D::one(), D::from(3u8) % &D::from(2u8)); assert_eq!(D::one(), &D::from(3u8) % &D::from(2u8)); { let mut v = D::from(3u8); v %= D::from(2u8); assert_eq!(v, D::one()); } { let mut v = D::from(3u8); v %= &D::from(2u8); assert_eq!(v, D::one()); } assert_eq!(D::one(), D::one() % D::from(256u16)); assert_eq!(D::one(), D::one() % &D::from(256u16)); assert_eq!(D::one(), &D::one() % &D::from(256u16)); { let mut v = D::from(D::one()); v %= D::from(256u16); assert_eq!(v, D::one()); } { let mut v = D::from(D::one()); v %= &D::from(256u16); assert_eq!(v, D::one()); } assert_eq!(D::zero(), D::from(256u16) % D::one()); assert_eq!(D::zero(), D::from(256u16) % &D::one()); assert_eq!(D::zero(), &D::from(256u16) % &D::one()); { let mut v = D::from(256u16); v %= D::one(); assert_eq!(v, D::zero()); } { let mut v = D::from(256u16); v %= &D::one(); assert_eq!(v, D::zero()); } assert_eq!(D::zero(), D::from(256u16) % D::from(256u16)); assert_eq!(D::zero(), D::from(256u16) % &D::from(256u16)); assert_eq!(D::zero(), &D::from(256u16) % &D::from(256u16)); { let mut v = D::from(D::from(256u16)); v %= D::from(256u16); assert_eq!(v, D::zero()); } { let mut v = D::from(D::from(256u16)); v %= &D::from(256u16); assert_eq!(v, D::zero()); } } #[test] fn checked_math() { assert_eq!(Some(D::zero()), D::zero().checked_add(&D::zero())); assert_eq!(Some(D::from(256u16)), D::one().checked_add(&D::from(255u8))); assert_eq!( Some(D::from(257u16)), D::one().checked_add(&D::from(256u16)) ); assert_eq!( Some(D::from(257u16)), D::from(256u16).checked_add(&D::one()) ); assert_eq!( Some(D::from(512u16)), D::from(256u16).checked_add(&D::from(256u16)) ); assert_eq!(None, D::from(u16::max_value()).checked_add(&D::one())); assert_eq!(None, D::one().checked_add(&D::from(u16::max_value()))); } #[test] fn from_uint() { type D = DynaInt; assert_eq!(D::one(), D::from(1u16)); } #[cfg(feature = "with-bigint")] #[test] fn from_biguint() { use super::BigUint; type D = DynaInt; assert_eq!(D::one(), D::from(BigUint::one())); } #[test] fn zero() { assert!(D::zero().is_zero()); } #[test] fn from_str_radix() { assert_eq!(D::from(1u8), D::from_str_radix("1", 10).unwrap()); assert_eq!(D::from(256u16), D::from_str_radix("256", 10).unwrap()); } #[test] fn integer() { assert_eq!(D::from(127u8), D::from(255u8).div_floor(&D::from(2u8))); assert_eq!(D::zero(), D::one().div_floor(&D::from(256u16))); assert_eq!(D::from(128u8), D::from(256u16).div_floor(&D::from(2u8))); assert_eq!(D::one(), D::from(257u16).div_floor(&D::from(256u16))); assert!(D::one().divides(&D::one())); assert!(!D::one().divides(&D::from(257u16))); assert!(D::from(257u16).divides(&D::one())); assert!(!D::from(257u16).divides(&D::from(256u16))); assert!(D::one().is_odd()); assert!(D::from(256u16).is_even()); assert_eq!((D::one(), D::zero()), D::one().div_rem(&D::one())); assert_eq!((D::zero(), D::one()), D::one().div_rem(&D::from(256u16))); assert_eq!( (D::from(256u16), D::zero()), D::from(256u16).div_rem(&D::one()) ); assert_eq!( (D::one(), D::one()), D::from(257u16).div_rem(&D::from(256u16)) ); } } fraction-0.15.3/src/error.rs000064400000000000000000000044461046102023000140150ustar 00000000000000//! Crate error types //! //! Contains error types utilised by other modules within the crate use std::error::Error; use std::fmt; use std::io; /// Happens when we parse stuff from strings #[derive(Debug, Clone, Eq, PartialEq)] pub enum ParseError { /// Not enough capacity in underlying integer to perform a math operation OverflowError, /// Could not convert a character into a digit or a string into a number ParseIntError, /// The base is not supported. E.g. a type only supports base 10, but we try to /// parse with the base 7. UnsupportedBase, } unsafe impl Send for ParseError {} unsafe impl Sync for ParseError {} impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseError::OverflowError => write!(f, "Overflow"), ParseError::ParseIntError => write!(f, "Could not parse integer"), ParseError::UnsupportedBase => write!(f, "Unsupported base"), } } } impl Error for ParseError {} /// Could not perform division, or fill in the resulting buffer #[derive(Debug)] pub enum DivisionError { /// Trying to divide something by Zero DivisionByZero, /// Incapsulates [fmt::Error] FmtError(fmt::Error), /// Incapsulates [io::Error] IoError(io::Error), /// Errors external to the division algorithm still may be passed /// through the co-routines wrapped up with this constructor ExternalError(Box), } unsafe impl Sync for DivisionError {} unsafe impl Send for DivisionError {} impl fmt::Display for DivisionError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { DivisionError::DivisionByZero => write!(f, "Division by zero"), DivisionError::FmtError(e) => write!(f, "Fmt error: {}", e), DivisionError::IoError(e) => write!(f, "IO error: {}", e), DivisionError::ExternalError(e) => write!(f, "External error: {}", e), } } } impl Error for DivisionError {} impl From for DivisionError { fn from(error: io::Error) -> DivisionError { DivisionError::IoError(error) } } impl From for DivisionError { fn from(error: fmt::Error) -> DivisionError { DivisionError::FmtError(error) } } fraction-0.15.3/src/fraction/approx/sqrt.rs000064400000000000000000000646571046102023000170050ustar 00000000000000//! Implements an arbitrary-precision square-root approximation algorithm. //! //! This module extends [`GenericFraction`] with eight (8) methods: //! - [`sqrt`](GenericFraction::sqrt) //! - [`sqrt_raw`](GenericFraction::sqrt_raw) //! - [`sqrt_with_accuracy`](GenericFraction::sqrt_with_accuracy) //! - [`sqrt_with_accuracy_raw`](GenericFraction::sqrt_with_accuracy_raw) //! - [`sqrt_abs`](GenericFraction::sqrt_abs) //! - [`sqrt_abs_raw`](GenericFraction::sqrt_abs_raw) //! - [`sqrt_abs_with_accuracy`](GenericFraction::sqrt_abs_with_accuracy) //! - [`sqrt_abs_with_accuracy_raw`](GenericFraction::sqrt_abs_with_accuracy_raw) //! //! All of these methods return some form of an approximation for the square root, and they all use //! the same algorithm but behave slightly differently. //! //! **A note on performance:** These methods are designed to be fast in release builds. An //! unfortunate side effect of this is that performance is significantly degraded when compiler //! optimisations are disabled. Please consider enabling compiler optimisations in your project if //! you wish to use these methods. //! //! # Accuracy of the output //! //! *The behaviour detailed in this section is common to all methods.* //! //! The algorithm in use is iterative: an initial estimate is generated, and this is repeatedly //! refined until it is suitably accurate. Of course, it is not possible to check the approximation //! against the true square root value, because we don't know it. (If we did, we'd just return that //! instead.) Because of this, the "closeness" must be determined by squaring the approximation to //! see how close *it* comes to the original value. //! //! For example, let's say we wanted to find the square root of `0.0152399025` //! (`6095961/400000000`). We might guess `0.1`. To check how close we are, we square our guess of //! `0.1` and get `0.01`. This square is accurate to 2 decimal places (0, 1). We might guess again, //! this time with `0.12`. This gives us `0.0144`, so we're still only accurate to 2 decimal //! places. Guessing `0.123` yields a square of `0.015129`, which is now accurate to 3 decimal //! places. //! //! The above is the checking process that happens when you call any of the methods defined in this //! module. You must supply a value denoting how accurate you want the square of the approximation //! to be. **It is very important to realise that this is *not* the same as the accuracy of the //! square root!** However, for `x > 1`, `x.sqrt(n)` where `n` is the number of decimal places that //! the square must be accurate to will actually produce a square root which is accurate to *more* //! than `n` decimal places. In other words: as long as you aren't using small numbers, you can //! consider `n` to be a lower bound on the accuracy of the square root itself. When you are //! working with numbers less than 1, this is no longer the case, and the square root may be less //! accurate than the square (although this is still not typically the case). **In all cases, the //! square of the result you get will be accurate to the level of accuracy you gave to the //! method.** //! //! Due to the nature of the algorithm, most outputs will actually be vastly more accurate than //! requested. This should not be relied on, and is subject to change. In some instances, the //! result will actually be exactly correct (e.g. for small square numbers like 4, 9, 16, 25, //! etc.). //! //! # Method variants //! ## `raw` //! `raw` methods return the approximation in unsimplified form via [`RawApprox`], which can (in //! most cases; see its documentation for more details) be destructured into a //! [`Ratio`](num::rational::Ratio). These methods are typically faster than non-`raw` ones by an //! order of magnitude or more, especially for extreme inputs (very large or very small). Do not //! use `raw` methods unless you are writing *very* performance-critical code: most arithmetic //! operations on [`Ratio`](num::rational::Ratio) and [`GenericFraction`] will automatically //! simplify the result so although the `sqrt_raw` method will still be faster than the non-`raw` //! version, you will still incur the overhead of simplification, just somewhere else instead. //! //! ```no_run //! # extern crate num; //! # use crate::fraction::approx::sqrt::RawApprox; //! # use num::rational::Ratio; //! # use num::BigUint; //! # use num::Zero; //! # let x: crate::fraction::GenericFraction = 2.into(); //! // We save time here by using `raw`... //! match x.sqrt_raw(10_000) { //! RawApprox::Rational(ratio) => { //! // ...but we lose the time again here, because the `+` simplifies the ratio. //! let _ = ratio + Ratio::::zero(); //! }, //! //! _ => (), //! } //! ``` //! //! Be aware that unsimplified values can take up excessive amounts of memory (100 KB and beyond, //! particularly when high levels of accuracy are used). //! //! For a few (limited) examples of how you can write performance-critical code with //! [`Ratio`](num::rational::Ratio) and [`GenericFraction`], see this module's own implementation. //! //! ## `with_accuracy` //! These methods accept an [`Accuracy`] value instead of a `u32` representing a number of decimal //! places. It is recommended that you use `with_accuracy` whenever you need to approximate more //! than one square root to the same level of accuracy. This avoids unnecessary heap allocations. //! //! ```no_run //! # use crate::fraction::GenericFraction; //! # use crate::fraction::approx::Accuracy; //! # fn some_fn(_: GenericFraction) {} //! // Don't do this! `sqrt` allocates a new `Accuracy` each time. //! for n in 0..100 { //! let x: GenericFraction = n.into(); //! some_fn(x.sqrt(1_000)); //! } //! //! // Instead, do this: //! let accuracy = Accuracy::decimal_places(1_000_u32); //! //! for n in 0..100 { //! let x: GenericFraction = n.into(); //! some_fn(x.sqrt_with_accuracy(&accuracy)); //! } //! ``` //! //! If you only perform one `sqrt` computation, there is no benefit to using `sqrt_with_accuracy`. //! //! Keep in mind that [`Accuracy`] has some pre-allocated values which you may wish to use. //! //! ## `abs` //! These methods ignore the sign of the value and instead approximate the square root of the //! *magnitude* only. //! //! The default behaviour is as follows: //! //! ``` //! # use crate::fraction::GenericFraction; //! let something_positive: GenericFraction = 2.into(); //! let something_negative: GenericFraction = (-2).into(); //! //! // Calling `sqrt` on a negative number will return `NaN`. //! assert_eq!(something_negative.sqrt(100), GenericFraction::NaN); //! //! // Calling `sqrt_abs` on a negative number will not return `NaN`. //! assert_ne!(something_negative.sqrt_abs(100), GenericFraction::NaN); //! //! // `sqrt +2` is equal to `sqrt_abs -2`. //! assert_eq!(something_positive.sqrt(100), something_negative.sqrt_abs(100)); //! ``` //! //! The following are guaranteed to hold for any pair of `abs` and non-`abs` methods: //! //! ``` //! # use crate::fraction::GenericFraction; //! # let x: GenericFraction = 2.into(); //! // When `x` is positive: //! assert_eq!(x.sqrt(100), (-x).sqrt_abs(100)); //! //! # let x: GenericFraction = (-2).into(); //! // When `x` is negative: //! assert_eq!((-x).sqrt(100), x.sqrt_abs(100)); //! ``` use super::Accuracy; use crate::{generic::GenericInteger, GenericFraction, Sign}; use num::{ bigint::{ToBigInt, ToBigUint}, rational::Ratio, BigUint, Integer, ToPrimitive, Zero, }; use std::borrow::Borrow; /// An unsimplified approximation of a square root. pub enum RawApprox { /// A rational (i.e. fractional) approximation. /// /// Depending on the accuracy used, these numbers can be *very* large to store (>100 KB with /// excessive accuracy), so cloning is likely to be expensive. Rational(Ratio), /// Positive infinity. This is returned when the square root of positive infinity is requested. PlusInf, /// Zero. This only occurs when the input is zero. Zero, /// An invalid number. /// /// `abs` methods will only return this for `NaN` input, but other methods will return `NaN` if /// the value to calculate the square root of is negative. NaN, } impl RawApprox { /// Returns `self` in the simplest form. This only modifies `Rational` values. #[must_use] pub fn simplified(self) -> Self { match self { RawApprox::Rational(ratio) => { // We could call `ratio.reduced()`, but that would clone the numerator and // denominator. If we take ownership of them and recreate the ratio using `new`, // we can reduce it without the clone. let (n, d) = ratio.into(); RawApprox::Rational(Ratio::new(n, d)) } other => other, } } } impl From for GenericFraction { fn from(v: RawApprox) -> Self { match v { RawApprox::Rational(ratio) => GenericFraction::Rational(Sign::Plus, ratio), RawApprox::PlusInf => GenericFraction::infinity(), RawApprox::Zero => GenericFraction::zero(), RawApprox::NaN => GenericFraction::nan(), } } } /// Different setup outputs. enum SqrtSetup { /// Setup result which can be used as an exact answer without further processing. ShortCircuited(RawApprox), /// Setup result providing an initial estimate, not an exact value. Estimated { /// The initial estimate of the square root. estimate: Ratio, /// The input value represented as a `Ratio`. This is produced as a byproduct of /// the setup but is useful in the rest of the algorithm too, so we return it. square_ratio: Ratio, }, } impl SqrtSetup { /// Produces setup values for finding the square root of `value.abs()`. fn for_value_abs(value: &GenericFraction) -> SqrtSetup where Nd: Clone + GenericInteger + ToBigInt + ToBigUint, { match value { GenericFraction::Rational(_, ratio) if ratio.is_zero() => { SqrtSetup::ShortCircuited(RawApprox::Zero) } GenericFraction::Rational(_, ratio) => { // If we can convert the components of `ratio` into `f64`s, we can approximate the // square root using `f64::sqrt`. This gives an excellent starting point. let float_estimate = ratio .to_f64() .map(f64::sqrt) // `from_float` will give `None` if the result of `sqrt` is not finite (incl. // NaN), so we'll automatically fall back to the alternative method if `sqrt` // fails here. .and_then(|float| { // Note: We use `abs` here even though `float` will never reasonably be // negative. This is because `Nd` could *technically* be a signed type, // in which case `float` is not guaranteed to be positive. let (n, d) = Ratio::::from_float(float.abs())?.into(); // Using `into_parts` allows us to avoid having to clone the underlying // `BigUint` data within the two values. Some(Ratio::new_raw(n.into_parts().1, d.into_parts().1)) }); // Safety: `to_bigint` is guaranteed not to fail for any integer type, and we know // that `Nd` is an integer type. let ratio = Ratio::new_raw( // `.into_parts().1` just takes the `BigUint` component of the `BigInt`, so is // equivalent to `.abs()` without the clone that happens inside `abs`. ratio.numer().to_bigint().unwrap().into_parts().1, ratio.denom().to_bigint().unwrap().into_parts().1, ); SqrtSetup::Estimated { estimate: float_estimate.unwrap_or_else(|| { // If we couldn't use floats, we fall back to a crude estimate using // truncated integer square roots. This still isn't too bad. Ratio::new(ratio.numer().sqrt(), ratio.denom().sqrt()) }), square_ratio: ratio, } } // The absolute value of `-inf` is just `+inf`, so we can handle both infinities the // same. GenericFraction::Infinity(_) => SqrtSetup::ShortCircuited(RawApprox::PlusInf), GenericFraction::NaN => SqrtSetup::ShortCircuited(RawApprox::NaN), } } } /// Halves `value` in-place while keeping it in simplest form. This is faster than standard /// division. fn halve_in_place_pos_rational(ratio: &mut Ratio) { // To mutate the numerator and denominator of the ratio we'll take ownership of both and then // put them back when we're done. let (mut numer, mut denom) = std::mem::take(ratio).into(); if numer.is_even() { numer /= 2_u32; } else { denom *= 2_u32; } *ratio = Ratio::new_raw(numer, denom); } /// Adds two `Ratio`s together without reducing the result to simplest form. This is significantly /// faster than using the standard addition operator provided by `num`, and may be used as long as /// the result does not need to be in simplest form (e.g. within an algorithm which reduces the /// output ratio before returning). fn add_ratios_raw(lhs: Ratio, rhs: Ratio) -> Ratio { // Don't bother comparing the denominators because it's highly unlikely that they're equal. // Instead, we just go straight to giving the fractions equal denominators. let (mut lhs_numer, lhs_denom) = lhs.into(); let (mut rhs_numer, rhs_denom) = rhs.into(); let common_denom = lhs_denom.lcm(&rhs_denom); let lhs_multiplier = &common_denom / &lhs_denom; let rhs_multiplier = &common_denom / &rhs_denom; lhs_numer *= lhs_multiplier; rhs_numer *= rhs_multiplier; Ratio::new_raw(lhs_numer + rhs_numer, common_denom) } /// Various square root operations for `GenericFraction`. impl GenericFraction { /// Returns an unsimplified rational approximation of the principal square root of the absolute /// value of `self`. `accuracy` is the accuracy of the square of the return value relative to /// `self`. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_abs_with_accuracy_raw(&self, accuracy: impl Borrow) -> RawApprox { let accuracy = accuracy.borrow(); let (estimate, value_as_ratio) = match SqrtSetup::for_value_abs(self) { SqrtSetup::Estimated { estimate, square_ratio, } => (estimate, square_ratio), SqrtSetup::ShortCircuited(exact) => return exact, }; // Take ownership of the two parts of the target ratio. This allows us to treat them // separately. For example, we must clone the numerator for the next step, but only a // reference to the denominator is needed. Therefore, we can avoid needlessly cloning both // halves. let (target_numer, target_denom) = value_as_ratio.into(); // Truncate the target square so we can check against it to determine when to finish. The // implied denominator for the numerator returned by the chop operation (choperation?) is // `accuracy.multiplier()`, so we don't need to store it. let truncated_target_numerator = accuracy.chopped_numerator_raw(&target_numer, &target_denom); let mut current_approx = estimate; loop { // We're using a highly optimised version of Newton's method here, broken into three // steps. Mathematically we would write // r1 = 0.5(r0 + A/r0) // where r0 is the current guess, r1 is the next guess, and A is the value we're // finding the square root of. // One of the key optimisations is to avoid using the `Ratio` type's `std::ops` // implementations, as they ensure that the resulting `Ratio` is always in simplest // form. That's normally a useful property, but here we need to be able to perform many // iterations very quickly, and the process of reducing a fraction to simplest form is // really quite slow. This is especially true when dealing with the size of numbers // that we'll be dealing with after only a couple of iterations - as the approximation // gets more accurate, the numerator and denominator become larger. To get around this, // we use a lot of `new_raw` and explicit manipulation of the numerators and // denominators of fractions. // A/r0 let divided = Ratio::new_raw( &target_numer * current_approx.denom(), &target_denom * current_approx.numer(), ); // r0 + A/r0 current_approx = add_ratios_raw(current_approx, divided); // 0.5(r0 + A/r0) halve_in_place_pos_rational(&mut current_approx); // For checking the approximation, we square it to see how close the result is to the // original input value. Again, the implied denominator is `accuracy.multiplier()`. // This is the same as for `truncated_target_numerator`, so we just need to compare the // numerators. let squared_and_truncated_numerator = accuracy.chopped_numerator_raw( &(current_approx.numer() * current_approx.numer()), &(current_approx.denom() * current_approx.denom()), ); // Stop and yield the current guess if it's close enough to the true value. if squared_and_truncated_numerator == truncated_target_numerator { // This is `_raw`, so we don't reduce. break RawApprox::Rational(current_approx); } } } /// Returns a rational approximation of the principal square root of the absolute value of /// `self`. `accuracy` is the accuracy of the square of the return value relative to `self`. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_abs_with_accuracy( &self, accuracy: impl Borrow, ) -> GenericFraction { self.sqrt_abs_with_accuracy_raw(accuracy) .simplified() .into() } /// Returns an unsimplified rational approximation of the principal square root of the absolute /// value of `self`. `decimal_places` refers to the accuracy of the square of the return value /// relative to `self`. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_abs_raw(&self, decimal_places: u32) -> RawApprox { self.sqrt_abs_with_accuracy_raw(Accuracy::decimal_places(decimal_places)) } /// Returns a rational approximation of the principal square root of the absolute value of /// `self`. `decimal_places` refers to the accuracy of the square of the return value relative /// to `self`. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_abs(&self, decimal_places: u32) -> GenericFraction { self.sqrt_abs_raw(decimal_places).simplified().into() } /// Returns an unsimplified rational approximation of the principal square root of `self`. /// `accuracy` refers to the square of the return value relative to `self`. /// /// This method returns `NaN` if `self` is negative. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_with_accuracy_raw(&self, accuracy: impl Borrow) -> RawApprox { match self { GenericFraction::Infinity(Sign::Minus) => RawApprox::NaN, // Short-circuit for zero so we don't have to worry about it when checking the signs. GenericFraction::Rational(_, r) if r.is_zero() => RawApprox::Zero, GenericFraction::Rational(sign, ratio) if { // `num` doesn't seem to give us a way to check the sign of a `T` given the // current trait bounds we have on it, but we can work around this by checking // against zero. let numer_sign = if ratio.numer() > &T::zero() { Sign::Plus } else { Sign::Minus }; // Combine the whole fraction's sign with the sign of the numerator. This // allows us to recognise stuff like -(-1/2) as being positive, for example. let numer_sign = *sign * numer_sign; let denom_sign = if ratio.denom() > &T::zero() { Sign::Plus } else { Sign::Minus }; numer_sign != denom_sign } => { RawApprox::NaN } _positive => self.sqrt_abs_with_accuracy_raw(accuracy), } } /// Returns a rational approximation of the principal square root of `self`. `accuracy` refers /// to the square of the return value relative to `self`. /// /// This method returns `NaN` if `self` is negative. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_with_accuracy(&self, accuracy: impl Borrow) -> GenericFraction { self.sqrt_with_accuracy_raw(accuracy).simplified().into() } /// Returns an unsimplified rational approximation of the principal square root of `self`. /// `decimal_places` refers to the accuracy of the square of the return value relative to /// `self`. /// /// This method returns `NaN` if `self` is negative. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt_raw(&self, decimal_places: u32) -> RawApprox { self.sqrt_with_accuracy_raw(Accuracy::decimal_places(decimal_places)) } /// Returns a rational approximation of the principal square root of `self`. `decimal_places` /// refers to the accuracy of the square of the return value relative to `self`. /// /// This method returns `NaN` if `self` is negative. /// /// See the [module-level documentation](`self`) for more details. pub fn sqrt(&self, decimal_places: u32) -> GenericFraction { self.sqrt_raw(decimal_places).simplified().into() } } #[cfg(test)] mod tests { use crate::{approx::Accuracy, BigFraction, GenericFraction}; use num::{traits::Pow, BigUint, Zero}; use std::str::FromStr; /// Converts `f` to a string. This is useful because `f.to_string()` will round sometimes. /// Ideal tests would use this, but it takes far too long to convert `BigUint`s to strings for /// that to be realistic. #[allow(dead_code)] fn fraction_to_decimal_string(f: &BigFraction, decimal_places: usize) -> String { let GenericFraction::Rational(sign, ratio) = f else { return f.to_string(); }; let sign_str = match sign { crate::Sign::Plus => "", crate::Sign::Minus => "-", }; let integer = ratio.to_integer(); let multiplier: BigUint = Pow::pow(&BigUint::from(10_u8), decimal_places); let fractional = (ratio.fract() * multiplier).to_integer(); // Since `fractional` is an integer, converting it to a string removes any leading zeros. // We're using it to represent the fractional part of the number, though, so we need to add // back those leading zeros. format!("{sign_str}{integer}.{fractional:0>decimal_places$}") } fn test_sqrt_of(value: impl std::borrow::Borrow, simplify: bool, n: usize) { let value = value.borrow(); let accuracy = Accuracy::decimal_places(n); println!("calculating..."); let sqrt = value.sqrt_with_accuracy_raw(&accuracy); let sqrt: BigFraction = if simplify { { println!("simplifying..."); sqrt.simplified() } } else { sqrt } .into(); println!("checking..."); assert_eq!( accuracy.chopped_numerator_raw( &(sqrt.numer().unwrap() * sqrt.numer().unwrap()), &(sqrt.denom().unwrap() * sqrt.denom().unwrap()), ), accuracy.chopped_numerator_raw(value.numer().unwrap(), value.denom().unwrap()) ); } lazy_static! { static ref VALUES: [BigFraction; 5] = [ BigFraction::from(2), BigFraction::from(123_654), BigFraction::from(123_655), BigFraction::from_str( "5735874745115151552958367280658028638020529468164964850251033802750\ 727314244020586751748892724760644\ /\ 478953213143537128483961697945367179924659061093095449962100933428918126\ 6216833845985099376094324166" ) .unwrap(), BigFraction::from(0.2), ]; } const ACCURACIES: [usize; 5] = [10, 100, 1000, 10000, 100_000]; #[test] fn test_raw() { for value in &*VALUES { for accuracy in ACCURACIES { println!("sqrt({value}) to {accuracy} d.p., unsimplified"); test_sqrt_of(value, false, accuracy); } } } #[test] fn test_negative() { let x: GenericFraction = (-2).into(); assert_eq!(x.sqrt(10), GenericFraction::NaN); } #[test] fn test_abs() { let x: GenericFraction = 2.into(); assert_eq!(x.sqrt(10), (-x).sqrt_abs(10)); } #[test] fn test_weird_numbers() { let x: GenericFraction = f32::NAN.into(); assert_eq!(GenericFraction::NaN, x.sqrt(10)); let x: GenericFraction = f32::INFINITY.into(); assert_eq!(GenericFraction::Infinity(crate::Sign::Plus), x.sqrt(10)); let x: GenericFraction = f32::NEG_INFINITY.into(); assert_eq!(GenericFraction::Infinity(crate::Sign::Plus), x.sqrt_abs(10)); let x: GenericFraction = 0.into(); assert!(x.sqrt(10).is_zero()); } #[test] fn test_negative_inf() { let x: GenericFraction = f32::NEG_INFINITY.into(); assert_eq!(x.sqrt(10), GenericFraction::NaN); } } fraction-0.15.3/src/fraction/approx.rs000064400000000000000000000141111046102023000157700ustar 00000000000000//! Approximate mathematical operations. //! //! This module implements operations that do not guarantee lossless results, but which are //! nonetheless useful. Using any functionality from within this module requires a compromise to be //! made between performance and accuracy. //! //! Approximations are grouped into modules; for information on a particular approximation or group //! of approximations, consult the relevant module's documentation. use crate::{generic::GenericInteger, BigFraction, GenericFraction}; use num::{rational::Ratio, traits::Pow, BigUint, Integer}; pub mod sqrt; /// Levels of accuracy for an approximation. #[derive(Clone, Debug)] pub enum Accuracy { /// At least 20 digits correct after the decimal point. #[cfg(feature = "lazy_static")] Dp20, /// At least 100 digits correct after the decimal point. #[cfg(feature = "lazy_static")] Dp100, /// At least 500 digits correct after the decimal point. #[cfg(feature = "lazy_static")] Dp500, /// An arbitrary number of correct digits. Custom { /// The multiplier used to check values for equality to the desired accuracy. **You /// probably want this to be `10^{n}`, where `n` is the number of decimal places of /// accuracy you need.** /// /// Normally this will have the form `10^n` where `n` is the number of correct decimal /// places, but this also holds for other bases. For instance, a value of `2^n` here has /// little meaning when the result is printed as decimal, but if the result was represented /// as a binary string in the form `a.b`, `b` would be correct to `n` digits (and `a` would /// be completely correct). multiplier: BigUint, }, } impl Accuracy { /// Returns an [`Accuracy`] of `n` decimal places. #[must_use] pub fn decimal_places(n: N) -> Self where BigUint: Pow, >::Output: Into, { #[cfg(feature = "lazy_static")] { // If we have access to pre-allocated `Accuracy` values, use them instead of allocating // a new multiplier. match n.to_u16() { Some(20) => return Self::Dp20, Some(100) => return Self::Dp100, Some(500) => return Self::Dp500, _ => (), } } Self::Custom { multiplier: Pow::pow(BigUint::from(10_u8), n).into(), } } /// Returns an [`Accuracy`] of `n` digits after the point (`.`) in the representation of the /// result in the given `base`. /// /// For example, `base_places(2, 5)` means "correct to at least 5 digits after the `.` when /// printed as binary". /// /// Prefer using [`Accuracy::decimal_places`] when `base == 10`. pub fn base_places(base: B, n: N) -> Self where // Assuming `n` is anything other than really small, `base^n` will likely be pretty big, so // we calculate the multiplier using `BigUint`. B: Into, // We need to be able to raise `BigUint(base)` to the power of `n`... BigUint: Pow, // ...and get back something that we can convert straight to `BigUint`. >::Output: Into, { Self::Custom { multiplier: Pow::pow(base.into(), n).into(), } } /// Returns a [`BigFraction`] which is equal to `fraction` according to this [`Accuracy`] by /// "chopping off" any irrelevant digits. /// /// The result will be equal to `(fraction * self.multiplier()).floor() / self.multiplier()`. /// /// This method propagates infinity and NaN values. pub fn chop(&self, fraction: &GenericFraction) -> BigFraction where T: Clone + Integer, BigUint: From, { match fraction { GenericFraction::Rational(sign, ratio) => BigFraction::Rational(*sign, { self.chop_ratio(&Ratio::new_raw( ratio.numer().clone().into(), ratio.denom().clone().into(), )) }), GenericFraction::Infinity(sign) => BigFraction::Infinity(*sign), GenericFraction::NaN => BigFraction::NaN, } } /// Returns a chopped and simplified version of `ratio`. #[must_use] fn chop_ratio(&self, ratio: &Ratio) -> Ratio { Ratio::new( self.chopped_numerator_raw(ratio.numer(), ratio.denom()), self.multiplier().clone(), ) } /// Returns the numerator of the chopped but unsimplified version of `numer / denom`, where the /// implied denominator is `self.multiplier()`. fn chopped_numerator_raw(&self, numer: &BigUint, denom: &BigUint) -> BigUint { (numer * self.multiplier()) / denom } /// Returns a reference to the multiplier used by `self` to chop off irrelevant digits. #[must_use] pub fn multiplier(&self) -> &BigUint { #[cfg(feature = "lazy_static")] { lazy_static! { static ref DP20_MUL: BigUint = BigUint::from(10_u8).pow(20_u32); static ref DP100_MUL: BigUint = BigUint::from(10_u8).pow(100_u32); static ref DP500_MUL: BigUint = BigUint::from(10_u8).pow(500_u32); }; return match self { Accuracy::Dp20 => &DP20_MUL, Accuracy::Dp100 => &DP100_MUL, Accuracy::Dp500 => &DP500_MUL, Accuracy::Custom { multiplier } => multiplier, }; } // When `lazy_static` is enabled, this gets flagged as unreachable which it technically is, // but *only* when `lazy_static` is on. #[allow(unreachable_code)] { let Accuracy::Custom { multiplier } = self else { // `Custom` is the only available variant when `lazy_static` is off. unreachable!() }; multiplier } } } impl Default for Accuracy { fn default() -> Self { Self::Dp100 } } fraction-0.15.3/src/fraction/display.rs000064400000000000000000000521151046102023000161320ustar 00000000000000//! Implementation of fmt::Display for [GenericFraction] and [Sign] structures //! //! Complete support of the [std::fmt] formatting syntax with exceptions for alternate //! integer types (hexadecimal, octal, binary) //! //! There are a couple of extensions on top of std::fmt, which are //! - Alternate flag (`#`) is used to enable `trailing zeroes` output //! - Negative sign (`-`) is used to suppress sign output (even negative) //! //! Otherwise, the standard formatting is followed as is. //! //! # Examples //! //! ``` //! use fraction::prelude::Fraction; //! //! let fraction = Fraction::from(1.75); //! //! assert_eq!("7/4", format!("{}", fraction)); //! assert_eq!("1.75", format!("{:.4}", fraction)); //! assert_eq!("1.7500", format!("{:#.4}", fraction)); //! assert_eq!("-1.75", format!("{:.2}", -fraction)); //! assert_eq!("1.75", format!("{:-.2}", -fraction)); //! ``` use division; use fraction::{GenericFraction, Sign}; use generic::GenericInteger; use std::fmt; /// Same as fmt::Formatter, but allowing to change flags /// so we can reuse it in recursive format implementations pub struct Format { flags: u8, fill: char, width: Option, precision: Option, align: Option, } const SIGN_PLUS: u8 = 1; const SIGN_MINUS: u8 = 2; const ALTERNATE: u8 = 4; const SIGAZEPAD: u8 = 8; impl Format { pub const fn empty() -> Self { Format { fill: ' ', align: None, width: None, precision: None, flags: 0, } } pub fn new(formatter: &fmt::Formatter) -> Self { let sign_plus = if formatter.sign_plus() { SIGN_PLUS } else { 0 }; let sign_minus = if formatter.sign_minus() { SIGN_MINUS } else { 0 }; let alternate = if formatter.alternate() { ALTERNATE } else { 0 }; let sigazepad = if formatter.sign_aware_zero_pad() { SIGAZEPAD } else { 0 }; let flags = sign_plus | sign_minus | alternate | sigazepad; Format { fill: formatter.fill(), align: formatter.align(), width: formatter.width(), precision: formatter.precision(), flags, } } pub const fn fill(&self) -> char { self.fill } pub const fn cloned_align(&self) -> Option { match self.align { Some(ref align) => match *align { fmt::Alignment::Left => Some(fmt::Alignment::Left), fmt::Alignment::Right => Some(fmt::Alignment::Right), fmt::Alignment::Center => Some(fmt::Alignment::Center), }, None => None, } } pub const fn align(&self) -> &Option { &self.align } pub const fn set_align(mut self, align: Option) -> Self { self.align = align; self } pub const fn width(&self) -> &Option { &self.width } pub const fn set_width(mut self, width: Option) -> Self { self.width = width; self } pub const fn precision(&self) -> &Option { &self.precision } pub const fn set_precision(mut self, precision: Option) -> Self { self.precision = precision; self } pub const fn sign_plus(&self) -> bool { self.flags & SIGN_PLUS > 0 } pub const fn set_sign_plus(mut self, flag: bool) -> Self { if flag { self.flags |= SIGN_PLUS; } else { self.flags &= !SIGN_PLUS; } self } pub const fn sign_minus(&self) -> bool { self.flags & SIGN_MINUS > 0 } pub const fn set_sign_minus(mut self, flag: bool) -> Self { if flag { self.flags |= SIGN_MINUS; } else { self.flags &= !SIGN_MINUS; } self } pub const fn alternate(&self) -> bool { self.flags & ALTERNATE > 0 } pub const fn set_alternate(mut self, flag: bool) -> Self { if flag { self.flags |= ALTERNATE; } else { self.flags &= !ALTERNATE; } self } pub const fn sign_aware_zero_pad(&self) -> bool { self.flags & SIGAZEPAD > 0 } pub const fn set_sign_aware_zero_pad(mut self, flag: bool) -> Self { if flag { self.flags |= SIGAZEPAD; } else { self.flags &= !SIGAZEPAD; } self } } impl Clone for Format { fn clone(&self) -> Self { Format { fill: self.fill(), align: self.cloned_align(), width: *self.width(), precision: *self.precision(), flags: self.flags, } } } pub fn format_sign(sign: Sign, buffer: &mut dyn fmt::Write, format: &Format) -> fmt::Result { if format.sign_plus() || (!format.sign_minus() && sign.is_negative()) { if format.sign_aware_zero_pad() { let format = format.clone().set_sign_aware_zero_pad(false); format_value( buffer, &format, None, |_| 1, |b, _| { b.write_char(sign.into())?; Ok(1) }, ) } else { format_value( buffer, format, None, |_| 1, |b, _| { b.write_char(sign.into())?; Ok(1) }, ) } } else { Ok(()) } } pub fn format_fraction( fraction: &GenericFraction, buffer: &mut dyn fmt::Write, format: &Format, ) -> fmt::Result where T: Clone + GenericInteger, { match *fraction { GenericFraction::NaN => format_value( buffer, format, None, |_| 3, |b, _| { b.write_str("NaN")?; Ok(3) }, ), GenericFraction::Infinity(sign) => format_value( buffer, format, Some(sign), |_| 3, |b, _| { b.write_str("inf")?; Ok(3) }, ), GenericFraction::Rational(sign, ref ratio) => format_value( buffer, format, Some(sign), |format| { let numer = ratio.numer().clone(); let denom = ratio.denom().clone(); let mut length = 0; if let Some(precision) = format.precision() { division::divide_to_callback( numer, denom, *precision, format.alternate(), |_| { length += 1; Ok(true) }, ) .ok(); } else { division::divide_to_callback(numer, T::one(), 0, false, |_| { length += 1; Ok(true) }) .ok(); if !ratio.numer().is_zero() && !ratio.denom().is_one() { length += 1; division::divide_to_callback(denom, T::one(), 0, false, |_| { length += 1; Ok(true) }) .ok(); } } length }, |buffer, format| { let numer = ratio.numer().clone(); let denom = ratio.denom().clone(); let mut length = 0; if let Some(precision) = format.precision() { let callback = |digit| { length += 1; division::write_digit(buffer, digit) }; if division::divide_to_callback( numer, denom, *precision, format.alternate(), callback, ) .is_err() { return Err(fmt::Error); } } else { let callback = |digit| { length += 1; division::write_digit(buffer, digit) }; if division::divide_to_callback(numer, T::one(), 0, false, callback).is_err() { return Err(fmt::Error); } if !ratio.numer().is_zero() && !ratio.denom().is_one() { length += 1; buffer.write_char('/')?; let callback = |digit| { length += 1; division::write_digit(buffer, digit) }; if division::divide_to_callback(denom, T::one(), 0, false, callback) .is_err() { return Err(fmt::Error); }; } } Ok(length) }, ), } } fn format_value( buffer: &mut dyn fmt::Write, format: &Format, sign: Option, value_length: L, value: V, ) -> fmt::Result where L: Fn(&Format) -> usize, V: FnOnce(&mut dyn fmt::Write, &Format) -> Result, { if let Some(width) = format.width() { let width = *width; let sign_len = if let Some(sign) = sign { usize::from(format.sign_plus() || (!format.sign_minus() && sign.is_negative())) } else { 0 }; if format.sign_aware_zero_pad() { let value_len = value_length(format); if sign_len > 0 { buffer.write_char(sign.unwrap().into())?; } if width > sign_len + value_len { for _ in sign_len + value_len..width { buffer.write_char('0')?; } } value(buffer, format)?; } else { match format.align() { Some(fmt::Alignment::Center) => { let value_len = value_length(format) + sign_len; let mut prefix_len = 0; let mut excess = 0; if width > value_len { excess = width - value_len; prefix_len = excess / 2; for _ in 0..prefix_len { buffer.write_char(format.fill())?; } }; if sign_len > 0 { buffer.write_char(sign.unwrap().into())?; } value(buffer, format)?; if width > value_len { for _ in 0..excess - prefix_len { buffer.write_char(format.fill())?; } } } None | Some(fmt::Alignment::Right) => { let value_len = value_length(format); for _ in sign_len + value_len..width { buffer.write_char(format.fill())?; } if sign_len > 0 { buffer.write_char(sign.unwrap().into())?; } value(buffer, format)?; } Some(fmt::Alignment::Left) => { if sign_len > 0 { buffer.write_char(sign.unwrap().into())?; } let value_len = value(buffer, format)?; if width > sign_len + value_len { for _ in sign_len + value_len..width { buffer.write_char(format.fill())?; } } } } } } else { if let Some(sign) = sign { if format.sign_plus() || (!format.sign_minus() && sign.is_negative()) { buffer.write_char(sign.into())?; } } value(buffer, format)?; } Ok(()) } #[cfg(test)] mod tests { #[cfg(feature = "with-bigint")] use prelude::{BigFraction, BigUint}; use super::super::super::{One, Zero}; use prelude::GenericFraction; type F32 = GenericFraction; type F64 = GenericFraction; #[test] fn display() { assert_eq!("NaN", format!("{}", F32::nan())); assert_eq!(format!("{}", ::std::f64::NAN), format!("{}", F32::nan())); assert_eq!("NaN", format!("{:+}", F32::nan())); assert_eq!( format!("{:+}", ::std::f64::NAN), format!("{:+}", F32::nan()) ); assert_eq!("==NaN==", format!("{:=^7}", F32::nan())); assert_eq!("=NaN==", format!("{:=^6}", F32::nan())); assert_eq!("NaN====", format!("{:=<7}", F32::nan())); assert_eq!("====NaN", format!("{:=>7}", F32::nan())); assert_eq!("0000NaN", format!("{:=>07}", F32::nan())); assert_eq!("0000NaN", format!("{:07}", F32::nan())); assert_eq!("0000NaN", format!("{:+07}", F32::nan())); assert_eq!("inf", format!("{}", F32::infinity())); assert_eq!( format!("{}", ::std::f64::INFINITY), format!("{}", F32::infinity()) ); assert_eq!("==inf==", format!("{:=^7}", F32::infinity())); assert_eq!("inf====", format!("{:=<7}", F32::infinity())); assert_eq!("====inf", format!("{:=>7}", F32::infinity())); assert_eq!("0000inf", format!("{:=<07}", F32::infinity())); assert_eq!("+000inf", format!("{:=<+07}", F32::infinity())); assert_eq!("+inf", format!("{:+}", F32::infinity())); assert_eq!( format!("{:+}", ::std::f64::INFINITY), format!("{:+}", F32::infinity()) ); assert_eq!("=+inf==", format!("{:=^+7}", F32::infinity())); assert_eq!("-inf", format!("{}", F32::neg_infinity())); assert_eq!( format!("{}", ::std::f64::NEG_INFINITY), format!("{}", F32::neg_infinity()) ); assert_eq!("=-inf==", format!("{:=^7}", F32::neg_infinity())); assert_eq!("-inf", format!("{:+}", F32::neg_infinity())); assert_eq!( format!("{:+}", ::std::f64::NEG_INFINITY), format!("{:+}", F32::neg_infinity()) ); assert_eq!("=-inf==", format!("{:=^+7}", F32::neg_infinity())); // zero assert_eq!("0", format!("{}", F32::zero())); assert_eq!(format!("{}", 0.0f64), format!("{}", F32::zero())); assert_eq!("+0", format!("{:+}", F32::zero())); assert_eq!(format!("{:+}", 0.0f64), format!("{:+}", F32::zero())); assert_eq!("0", format!("{:.2}", F32::zero())); assert_eq!("+0", format!("{:+.2}", F32::zero())); assert_eq!("0", format!("{:-.2}", F32::zero())); assert_eq!("0.00", format!("{:#.2}", F32::zero())); assert_eq!("+0.00", format!("{:+#.2}", F32::zero())); assert_eq!("0.00", format!("{:-#.2}", F32::zero())); // neg zero assert_eq!("-0", format!("{}", F32::neg_zero())); assert_eq!("-0", format!("{:+}", F32::neg_zero())); assert_eq!("0", format!("{:-}", F32::neg_zero())); assert_eq!("-0", format!("{:.2}", F32::neg_zero())); assert_eq!("-0", format!("{:+.2}", F32::neg_zero())); assert_eq!("0", format!("{:-.2}", F32::neg_zero())); // positive whole assert_eq!("1", format!("{}", F32::one())); assert_eq!("+1", format!("{:+}", F32::one())); assert_eq!("1", format!("{:-}", F32::one())); assert_eq!("0001", format!("{:04}", F32::one())); assert_eq!("+001", format!("{:+04}", F32::one())); assert_eq!("=1==", format!("{:=^4}", F32::one())); assert_eq!("===1", format!("{:=>4}", F32::one())); assert_eq!("1===", format!("{:=<4}", F32::one())); assert_eq!("=+1=", format!("{:=^+4}", F32::one())); // negative whole assert_eq!("-1", format!("{}", -F32::one())); assert_eq!("-1", format!("{:+}", -F32::one())); assert_eq!("1", format!("{:-}", -F32::one())); assert_eq!("=-1=", format!("{:=^4}", -F32::one())); assert_eq!("=1==", format!("{:=^-4}", -F32::one())); assert_eq!("=-1=", format!("{:=^+4}", -F32::one())); // positive fraction assert_eq!("00.5", format!("{:04.1}", F32::new(1, 2))); assert_eq!("+0.5", format!("{:+04.1}", F32::new(1, 2))); assert_eq!("00.5", format!("{:-04.1}", F32::new(1, 2))); assert_eq!("0.5", format!("{:.16}", F32::new(1, 2))); assert_eq!("+0.5", format!("{:+.16}", F32::new(1, 2))); assert_eq!("+1.75", format!("{:=^+.4}", F32::new(7, 4))); assert_eq!("=+1.75==", format!("{:=^+8.4}", F32::new(7, 4))); assert_eq!(" +1.75", format!("{:+8.4}", F32::new(7, 4))); assert_eq!(" +1.75", format!("{:>+8.4}", F32::new(7, 4))); assert_eq!("+1.75 ", format!("{:<+8.4}", F32::new(7, 4))); assert_eq!("+0001.75", format!("{:<+08.4}", F32::new(7, 4))); // negative fraction assert_eq!("-00.5", format!("{:05.2}", -F32::new(1, 2))); assert_eq!("-00.5", format!("{:05.2}", -F32::new(1, 2))); assert_eq!("-00.5", format!("{:+05.2}", -F32::new(1, 2))); assert_eq!("000.5", format!("{:-05.2}", -F32::new(1, 2))); assert_eq!("-0.5", format!("{:.16}", -F32::new(1, 2))); assert_eq!("0.5", format!("{:-.16}", -F32::new(1, 2))); assert_eq!("-0.5", format!("{:+.16}", -F32::new(1, 2))); assert_eq!("-1.75", format!("{:=^+.4}", -F32::new(7, 4))); assert_eq!("=-1.75==", format!("{:=^+8.4}", -F32::new(7, 4))); assert_eq!(" -1.75", format!("{:+8.4}", -F32::new(7, 4))); assert_eq!(" -1.75", format!("{:>+8.4}", -F32::new(7, 4))); assert_eq!("-1.75 ", format!("{:<+8.4}", -F32::new(7, 4))); assert_eq!("-0001.75", format!("{:<+08.4}", -F32::new(7, 4))); assert_eq!("-1.75", format!("{:=^.4}", -F32::new(7, 4))); assert_eq!("=-1.75==", format!("{:=^8.4}", -F32::new(7, 4))); assert_eq!(" -1.75", format!("{:8.4}", -F32::new(7, 4))); assert_eq!(" -1.75", format!("{:>8.4}", -F32::new(7, 4))); assert_eq!("-1.75 ", format!("{:<8.4}", -F32::new(7, 4))); assert_eq!("-0001.75", format!("{:<08.4}", -F32::new(7, 4))); assert_eq!("-01.7500", format!("{:<#08.4}", -F32::new(7, 4))); assert_eq!("1.75", format!("{:=^-.4}", -F32::new(7, 4))); assert_eq!("==1.75==", format!("{:=^-8.4}", -F32::new(7, 4))); assert_eq!(" 1.75", format!("{:-8.4}", -F32::new(7, 4))); assert_eq!(" 1.75", format!("{:>-8.4}", -F32::new(7, 4))); assert_eq!("1.75 ", format!("{:<-8.4}", -F32::new(7, 4))); assert_eq!("00001.75", format!("{:<-08.4}", -F32::new(7, 4))); // ratios assert_eq!("7/4", format!("{}", F32::new(7, 4))); assert_eq!("7/4", format!("{:-}", F32::new(7, 4))); assert_eq!("+7/4", format!("{:+}", F32::new(7, 4))); assert_eq!("-7/4", format!("{}", -F32::new(7, 4))); assert_eq!("7/4", format!("{:-}", -F32::new(7, 4))); assert_eq!("-7/4", format!("{:+}", -F32::new(7, 4))); assert_eq!("07/4", format!("{:04}", F32::new(7, 4))); assert_eq!("-7/4", format!("{:+04}", -F32::new(7, 4))); assert_eq!("==7/4==", format!("{:=^7}", F32::new(7, 4))); assert_eq!("=-7/4==", format!("{:=^7}", -F32::new(7, 4))); assert_eq!(" 7/4", format!("{:7}", F32::new(7, 4))); assert_eq!("7/4 ", format!("{:<7}", F32::new(7, 4))); assert_eq!("-7/4 ", format!("{:<7}", -F32::new(7, 4))); assert_eq!("+7/4 ", format!("{:<+7}", F32::new(7, 4))); assert_eq!("+0007/4", format!("{:<+07}", F32::new(7, 4))); // former format_as_decimal tests assert_eq!("NaN", format!("{:.64}", F32::from(::std::f32::NAN))); assert_eq!("inf", format!("{:.64}", F32::from(::std::f32::INFINITY))); assert_eq!( "-inf", format!("{:.64}", F32::from(::std::f32::NEG_INFINITY)) ); assert_eq!("0.75", format!("{:.64}", F32::from(0.75))); assert_eq!("-0.75", format!("{:.64}", F32::from(-0.75))); assert_eq!("0.33", format!("{:.64}", F32::from((33, 100)))); assert_eq!( "0.0000000456", format!("{:.64}", F64::new(456u64, 10000000000u64)) ); #[cfg(feature = "with-bigint")] { let fra = BigFraction::new( BigUint::from(42u8), BigUint::from(1000000000000000u64) * BigUint::from(1000000000000000u64) * BigUint::from(1000000000000000u64), ); assert_eq!( "0.000000000000000000000000000000000000000000042", format!("{:.64}", fra) ); } } } fraction-0.15.3/src/fraction/generic_fraction.rs000064400000000000000000003201301046102023000177610ustar 00000000000000use crate::fraction::Sign; use crate::{ display, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConstOne, ConstZero, FromPrimitive, Integer, Num, One, ParseRatioError, Ratio, Signed, ToPrimitive, Zero, }; #[cfg(feature = "with-bigint")] use crate::{BigInt, BigUint}; use error::ParseError; use std::iter::{Product, Sum}; use generic::{read_generic_integer, GenericInteger}; use std::cmp::{Eq, Ordering, PartialEq, PartialOrd}; use std::hash::{Hash, Hasher}; use std::num::FpCategory; use std::ops::{Add, Mul, Neg}; use std::f64; use std::fmt; use std::str::FromStr; /// Generic implementation of the fraction type /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// /// type F = GenericFraction; /// /// let first = F::new (1u8, 2u8); /// let second = F::new (2u8, 8u8); /// /// assert_eq! (first + second, F::new (3u8, 4u8)); /// ``` /// /// /// Since GenericFraction keeps its sign explicitly and independently of the numerics, /// it is not recommended to use signed types, although it's completely valid with the cost of target type capacity. /// /// ``` /// use fraction::GenericFraction; /// /// type F = GenericFraction; /// /// let first = F::new (1, 2); /// let second = F::new (2, 8); /// /// assert_eq! (first + second, F::new (3, 4)); /// ``` #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde-support", derive(Serialize, Deserialize))] pub enum GenericFraction where T: Clone + Integer, { Rational(Sign, Ratio), Infinity(Sign), NaN, } /// Copy semantics to be applied for the target type, but only if T also has it. impl Copy for GenericFraction where T: Copy + Integer {} impl Default for GenericFraction where T: Clone + Integer, { fn default() -> Self { Self::zero() } } impl FromStr for GenericFraction where T: Clone + Integer + CheckedAdd + CheckedMul, { type Err = ParseError; fn from_str(src: &str) -> Result { let (sign, start) = if src.starts_with('-') { (Sign::Minus, 1) } else if src.starts_with('+') { (Sign::Plus, 1) } else { (Sign::Plus, 0) }; if let Some(split_idx) = src.find('.') { let who = &src[start..split_idx]; let mut num = match T::from_str_radix(who, 10) { Err(_) => return Err(ParseError::ParseIntError), Ok(value) => value, }; // skip trailing zeros let len = src[split_idx + 1..].trim_end_matches('0').len(); let fra = if len > 0 { let p = T::from_str_radix(&src[split_idx + 1..split_idx + 1 + len], 10); match p { Err(_) => return Err(ParseError::ParseIntError), Ok(value) => value, } } else { T::zero() }; let mut den = T::one(); if len > 0 { let mut t10 = T::one(); for _ in 0..9 { t10 = if let Some(t10) = t10.checked_add(&den) { t10 } else { return Err(ParseError::OverflowError); }; } for _ in 0..len { num = if let Some(num) = num.checked_mul(&t10) { num } else { return Err(ParseError::OverflowError); }; den = if let Some(den) = den.checked_mul(&t10) { den } else { return Err(ParseError::OverflowError); }; } } let num = if let Some(num) = num.checked_add(&fra) { num } else { return Err(ParseError::OverflowError); }; Ok(GenericFraction::Rational(sign, Ratio::new(num, den))) } else if let Some(split_idx) = src.find('/') { let num = match T::from_str_radix(&src[start..split_idx], 10) { Ok(value) => value, Err(_) => return Err(ParseError::ParseIntError), }; let den = match T::from_str_radix(&src[split_idx + 1..], 10) { Ok(value) => value, Err(_) => return Err(ParseError::ParseIntError), }; Ok(GenericFraction::Rational(sign, Ratio::new(num, den))) } else { let num = match T::from_str_radix(&src[start..], 10) { Ok(value) => value, Err(_) => return Err(ParseError::ParseIntError), }; Ok(GenericFraction::Rational(sign, Ratio::new(num, T::one()))) } } } impl GenericFraction where T: Clone + Integer, { /// Constructs a new fraction with the specified numerator and denominator /// Handles gracefully signed integers even if the storage type is unsigned and vise versa /// The arguments can be of any integer types imlementing the necessary traits /// /// # Examples /// /// ``` /// use fraction::{GenericFraction, Sign}; /// type F = GenericFraction; /// /// let f12 = F::new_generic(Sign::Plus, 1i8, 2u8).unwrap(); /// let f34 = F::new_generic(Sign::Plus, 3i16, 4u32).unwrap(); /// let f56 = F::new_generic(Sign::Plus, 5i64, 6u128).unwrap(); /// let f78 = F::new_generic(Sign::Plus, 7usize, 8isize).unwrap(); /// /// assert_eq! ((*f12.numer().unwrap(), *f12.denom().unwrap()), (1u16, 2u16)); /// assert_eq! ((*f34.numer().unwrap(), *f34.denom().unwrap()), (3u16, 4u16)); /// assert_eq! ((*f56.numer().unwrap(), *f56.denom().unwrap()), (5u16, 6u16)); /// assert_eq! ((*f78.numer().unwrap(), *f78.denom().unwrap()), (7u16, 8u16)); /// ```` pub fn new_generic(sign: Sign, num: N, den: D) -> Option> where N: GenericInteger + PartialOrd, D: GenericInteger + PartialOrd, T: GenericInteger, { let (ns, num): (Sign, T) = read_generic_integer(num)?; let (ds, den): (Sign, T) = read_generic_integer(den)?; let sign = ns * ds * sign; Some(Self::_new(sign, num, den)) } fn _new(sign: Sign, num: N, den: D) -> GenericFraction where N: Into, D: Into, { let num: T = num.into(); let den: T = den.into(); if den.is_zero() { if num.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(sign) } } else { GenericFraction::Rational(sign, Ratio::new(num, den)) } } /// Constructs a new fraction with the specified numerator and denominator /// /// The arguments must me either of `T` type, or implement `Into` trait. /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// let _f = F::new(1u8, 2u16); /// ``` pub fn new(num: N, den: D) -> GenericFraction where N: Into, D: Into, { Self::_new(Sign::Plus, num, den) } /// Constructs a new negative fraction with the specified numerator and denominator /// /// The arguments must be either of `T` type, or implement `Into` trait. /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// let _f = F::new_neg (1u8, 2u16); /// ``` pub fn new_neg(num: N, den: D) -> GenericFraction where N: Into, D: Into, { Self::_new(Sign::Minus, num, den) } /// Constructs a new fraction without types casting, checking for denom == 0 and reducing numbers. /// /// You must be careful with this function because all the other functionality parts rely on the /// numbers to be reduced. That said, in the normal case 2/4 has to be reduced to 1/2, but it will not /// happen with new_raw. /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// let _f = F::new_raw (1u8, 2u8); /// ``` pub const fn new_raw(num: T, den: T) -> GenericFraction { GenericFraction::Rational(Sign::Plus, Ratio::new_raw(num, den)) } /// The same as [fn new_raw](enum.GenericFraction.html#method.new_raw), but allows explicitly set sign. /// /// # Examples /// /// ``` /// use fraction::{GenericFraction, Sign}; /// type F = GenericFraction; /// /// let _f = F::new_raw_signed(Sign::Minus, 1u8, 2u8); /// ``` pub const fn new_raw_signed(sign: Sign, num: T, den: T) -> GenericFraction { GenericFraction::Rational(sign, Ratio::new_raw(num, den)) } /// Returns a reference to the numerator value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// let fra = F::new (5u8, 6u8); /// assert_eq! (5, *fra.numer ().unwrap ()); /// ``` pub const fn numer(&self) -> Option<&T> { match *self { GenericFraction::Rational(_, ref r) => Some(r.numer()), _ => None, } } /// Returns a reference to the denominator value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// let fra = F::new (5u8, 6u8); /// assert_eq! (6, *fra.denom ().unwrap ()); /// ``` pub const fn denom(&self) -> Option<&T> { match *self { GenericFraction::Rational(_, ref r) => Some(r.denom()), _ => None, } } /// Returns a reference to the sign value /// /// # Examples /// /// ``` /// use fraction::{ GenericFraction, Sign }; /// type F = GenericFraction; /// /// /// let fra = F::new (5u8, 6u8); /// assert_eq! (Sign::Plus, fra.sign ().unwrap ()); /// /// let fra = F::new_neg (5u8, 6u8); /// assert_eq! (Sign::Minus, fra.sign ().unwrap ()); /// /// /// let fra = F::infinity (); /// assert_eq! (Sign::Plus, fra.sign ().unwrap ()); /// /// let fra = F::neg_infinity (); /// assert_eq! (Sign::Minus, fra.sign ().unwrap ()); /// /// /// let fra = F::nan (); /// assert_eq! (None, fra.sign ()); /// ``` pub const fn sign(&self) -> Option { match self { GenericFraction::Rational(s, _) => Some(*s), GenericFraction::Infinity(s) => Some(*s), _ => None, } } /// Generates a GenericFraction from GenericFraction /// where T: From /// /// ``` /// use fraction::{ Fraction, GenericFraction }; /// type F8 = GenericFraction; /// /// let fra8 = F8::new (5u8, 6u8); /// assert_eq! (Fraction::new (5u64, 6u64), Fraction::from_fraction(fra8)); /// ``` pub fn from_fraction(from: GenericFraction) -> GenericFraction where T: From, F: Clone + Integer, { match from { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(sign) => GenericFraction::Infinity(sign), GenericFraction::Rational(sign, ratio) => { let (num, den): (F, F) = ratio.into(); GenericFraction::Rational(sign, Ratio::new_raw(T::from(num), T::from(den))) } } } /// Generates a GenericFraction from GenericFraction /// where T: Into /// /// ``` /// use fraction::{ Fraction, GenericFraction }; /// type F8 = GenericFraction; /// /// let fra8 = F8::new (5u8, 6u8); /// assert_eq! (Fraction::new (5u64, 6u64), fra8.into_fraction()); /// ``` pub fn into_fraction(self) -> GenericFraction where T: Into, I: Clone + Integer, { match self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(sign) => GenericFraction::Infinity(sign), GenericFraction::Rational(sign, ratio) => { let (num, den): (T, T) = ratio.into(); GenericFraction::Rational(sign, Ratio::new_raw(num.into(), den.into())) } } } } impl Bounded for GenericFraction { fn min_value() -> Self { let one = T::one(); let max = T::max_value(); GenericFraction::Rational(Sign::Minus, Ratio::new(max, one)) } fn max_value() -> Self { let one = T::one(); let max = T::max_value(); GenericFraction::Rational(Sign::Plus, Ratio::new(max, one)) } } impl PartialEq for GenericFraction { fn eq(&self, other: &Self) -> bool { match (self, other) { (GenericFraction::NaN, GenericFraction::NaN) => true, (GenericFraction::Infinity(sign), GenericFraction::Infinity(osign)) => sign == osign, ( GenericFraction::Rational(ref ls, ref l), GenericFraction::Rational(ref rs, ref r), ) => { if ls == rs { l.eq(r) } else { l.is_zero() && r.is_zero() } } _ => false, } } } impl Hash for GenericFraction { fn hash(&self, state: &mut H) { match self { GenericFraction::NaN => state.write_u8(0u8), GenericFraction::Infinity(sign) => { if let Sign::Plus = sign { state.write_u8(1u8) } else { state.write_u8(2u8) } } GenericFraction::Rational(sign, ratio) => { if *sign == Sign::Plus || ratio.is_zero() { state.write_u8(3u8); } else { state.write_u8(4u8); } ratio.hash(state); } } } } impl Eq for GenericFraction {} impl PartialOrd for GenericFraction { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for GenericFraction { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (GenericFraction::NaN, GenericFraction::NaN) => Ordering::Equal, (GenericFraction::NaN, _) => Ordering::Less, (_, GenericFraction::NaN) => Ordering::Greater, (GenericFraction::Infinity(sign), GenericFraction::Infinity(osign)) => sign.cmp(osign), (GenericFraction::Infinity(Sign::Plus), GenericFraction::Rational(_, _)) => { Ordering::Greater } (GenericFraction::Infinity(Sign::Minus), GenericFraction::Rational(_, _)) => { Ordering::Less } (GenericFraction::Rational(_, _), GenericFraction::Infinity(Sign::Plus)) => { Ordering::Less } (GenericFraction::Rational(_, _), GenericFraction::Infinity(Sign::Minus)) => { Ordering::Greater } ( GenericFraction::Rational(ref ls, ref l), GenericFraction::Rational(ref rs, ref r), ) => { if ls == rs { match *ls { Sign::Plus => l.cmp(r), Sign::Minus => r.cmp(l), } } else if l.is_zero() && r.is_zero() { Ordering::Equal } else if *ls == Sign::Minus { Ordering::Less } else { Ordering::Greater } } } } } impl Neg for GenericFraction { type Output = GenericFraction; fn neg(self) -> Self { match self { GenericFraction::NaN => self, GenericFraction::Infinity(sign) => GenericFraction::Infinity(-sign), GenericFraction::Rational(s, r) => { if r.is_zero() { GenericFraction::Rational(Sign::Plus, r) } else { GenericFraction::Rational(s.neg(), r) } } } } } impl<'a, T: Clone + Integer> Neg for &'a GenericFraction { type Output = GenericFraction; fn neg(self) -> Self::Output { match *self { GenericFraction::NaN => GenericFraction::nan(), GenericFraction::Infinity(sign) => GenericFraction::Infinity(-sign), GenericFraction::Rational(s, ref r) => { if r.is_zero() { GenericFraction::Rational(Sign::Plus, r.clone()) } else { GenericFraction::Rational(-s, r.clone()) } } } } } impl Sum for GenericFraction { fn sum>(iter: I) -> Self { iter.fold(GenericFraction::::zero(), Add::add) } } impl<'a, T: 'a + Clone + Integer> Sum<&'a GenericFraction> for GenericFraction { fn sum>(iter: I) -> Self { #[allow(clippy::redundant_closure)] iter.fold(GenericFraction::::zero(), |ref s, x| Add::add(s, x)) } } impl Product for GenericFraction { fn product>(iter: I) -> Self { iter.fold(GenericFraction::::one(), Mul::mul) } } impl<'a, T: 'a + Clone + Integer> Product<&'a GenericFraction> for GenericFraction { fn product>(iter: I) -> Self { #[allow(clippy::redundant_closure)] iter.fold(GenericFraction::::one(), |ref s, x| Mul::mul(s, x)) } } impl Zero for GenericFraction { fn zero() -> Self { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } fn is_zero(&self) -> bool { match *self { GenericFraction::Rational(_, ref r) => r.is_zero(), _ => false, } } } impl ConstZero for GenericFraction { const ZERO: GenericFraction = GenericFraction::Rational(Sign::Plus, Ratio::new_raw(ConstZero::ZERO, ConstOne::ONE)); } impl One for GenericFraction { fn one() -> Self { GenericFraction::Rational(Sign::Plus, Ratio::one()) } } impl ConstOne for GenericFraction { const ONE: GenericFraction = GenericFraction::Rational(Sign::Plus, Ratio::new_raw(ConstOne::ONE, ConstOne::ONE)); } impl Num for GenericFraction { type FromStrRadixErr = ParseRatioError; fn from_str_radix(str: &str, radix: u32) -> Result { if let Some(rem) = str.strip_prefix('-') { Ratio::from_str_radix(rem, radix) .map(|ratio| GenericFraction::Rational(Sign::Minus, ratio)) } else if let Some(rem) = str.strip_prefix('+') { Ratio::from_str_radix(rem, radix) .map(|ratio| GenericFraction::Rational(Sign::Plus, ratio)) } else { Ratio::from_str_radix(str, radix) .map(|ratio| GenericFraction::Rational(Sign::Plus, ratio)) } } } impl Signed for GenericFraction { fn abs(&self) -> Self { GenericFraction::abs(self) } fn abs_sub(&self, other: &Self) -> Self { match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(osign) => { if sign == Sign::Minus || osign == Sign::Plus { GenericFraction::zero() } else { GenericFraction::Infinity(Sign::Plus) } } GenericFraction::Rational(_, _) => { if sign == Sign::Plus { GenericFraction::Infinity(sign) } else { GenericFraction::zero() } } }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(osign) => { if osign == Sign::Plus { GenericFraction::zero() } else if sign == Sign::Minus { GenericFraction::Infinity(Sign::Minus) } else { GenericFraction::Infinity(Sign::Plus) } } GenericFraction::Rational(_, ref r) => { if l < r { GenericFraction::Rational(Sign::Plus, r - l) } else { GenericFraction::Rational(Sign::Plus, l - r) } } }, } } fn signum(&self) -> Self { GenericFraction::signum(self) } fn is_positive(&self) -> bool { GenericFraction::is_sign_positive(self) } fn is_negative(&self) -> bool { GenericFraction::is_sign_negative(self) } } impl ToPrimitive for GenericFraction { fn to_i64(&self) -> Option { match *self { GenericFraction::NaN => None, GenericFraction::Infinity(_) => None, #[allow(clippy::manual_filter)] GenericFraction::Rational(sign, ref r) if *r.denom() == T::one() => { if let Some(n) = r.numer().to_i64() { if sign == Sign::Minus { Some(-n) } else { Some(n) } } else { None } } _ => None, } } fn to_u64(&self) -> Option { match *self { GenericFraction::NaN => None, GenericFraction::Infinity(_) => None, GenericFraction::Rational(sign, ref r) if *r.denom() == T::one() => { if sign == Sign::Minus { None } else { r.numer().to_u64() } } _ => None, } } fn to_f64(&self) -> Option { match *self { GenericFraction::NaN => Some(f64::NAN), GenericFraction::Infinity(sign) => Some(if sign == Sign::Minus { f64::NEG_INFINITY } else { f64::INFINITY }), GenericFraction::Rational(sign, ref r) => r .numer() .to_f64() .and_then(|n| r.denom().to_f64().map(|d| n / d)) .map(|x| if sign == Sign::Minus { -x } else { x }), } } } impl GenericFraction { /// Returns NaN value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::nan (), F::new (0, 0)); /// ``` pub const fn nan() -> Self { GenericFraction::NaN } /// Returns positive Infinity value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::infinity (), F::new (1, 0)); /// ``` pub const fn infinity() -> Self { GenericFraction::Infinity(Sign::Plus) } /// Returns negative Infinity value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::neg_infinity (), F::new_neg (1, 0)); /// ``` pub const fn neg_infinity() -> Self { GenericFraction::Infinity(Sign::Minus) } /// Returns zero with negative sign /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::neg_zero (), F::new_neg (0, 1)); /// ``` pub fn neg_zero() -> Self { GenericFraction::Rational(Sign::Minus, Ratio::zero()) } /// Returns minimal value greater than zero /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F8 = GenericFraction; /// type F16 = GenericFraction; /// /// assert_eq! (F8::min_positive_value (), F8::new (1u8, 255u8)); /// assert_eq! (F16::min_positive_value (), F16::new (1u16, 65535u16)); /// ``` pub fn min_positive_value() -> Self where T: Bounded, { GenericFraction::Rational(Sign::Plus, Ratio::new(T::one(), T::max_value())) } /// Returns true if the value is NaN /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (F::nan ().is_nan ()); /// assert! (F::new (0, 0).is_nan ()); /// ``` pub const fn is_nan(&self) -> bool { matches!(*self, GenericFraction::NaN) } /// Returns true if the value is Infinity (does not matter positive or negative) /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (F::infinity ().is_infinite ()); /// assert! (F::new (1u8, 0).is_infinite ()); /// assert! (F::new_neg (1u8, 0).is_infinite ()); /// ``` pub const fn is_infinite(&self) -> bool { matches!(*self, GenericFraction::Infinity(_)) } /// Returns true if the value is not Infinity (does not matter positive or negative) /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (! F::infinity ().is_finite ()); /// assert! (! F::new (1u8, 0).is_finite ()); /// assert! (! F::new_neg (1u8, 0).is_finite ()); /// ``` pub const fn is_finite(&self) -> bool { !matches!(*self, GenericFraction::Infinity(_)) } /// Returns true if the number is neither zero, Infinity or NaN /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (! F::nan ().is_normal ()); /// assert! (! F::infinity ().is_normal ()); /// assert! (! F::neg_infinity ().is_normal ()); /// assert! (! F::new (0, 1u8).is_normal ()); /// assert! (! F::neg_zero ().is_normal ()); /// ``` pub fn is_normal(&self) -> bool { match *self { GenericFraction::Rational(_, ref v) => !v.is_zero(), _ => false, } } /// Returns the floating point category of the number /// /// # Examples /// /// ``` /// use std::num::FpCategory; /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::nan ().classify (), FpCategory::Nan); /// assert_eq! (F::infinity ().classify (), FpCategory::Infinite); /// assert_eq! (F::new (0, 1u8).classify (), FpCategory::Zero); /// assert_eq! (F::new (1u8, 1u8).classify (), FpCategory::Normal); /// ``` pub fn classify(&self) -> FpCategory { match *self { GenericFraction::NaN => FpCategory::Nan, GenericFraction::Infinity(_) => FpCategory::Infinite, GenericFraction::Rational(_, ref v) if v.is_zero() => FpCategory::Zero, _ => FpCategory::Normal, } } /// Returns the largest integer less than or equal to the value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (7u8, 5u8).floor (), F::new (5u8, 5u8)); /// assert_eq! (F::new_neg (4u8, 3u8).floor (), F::new_neg (2u8, 1u8)); /// ``` pub fn floor(&self) -> Self { match *self { GenericFraction::Rational(Sign::Plus, ref r) => { GenericFraction::Rational(Sign::Plus, r.floor()) } GenericFraction::Rational(Sign::Minus, ref r) => { // Ceil of the unsigned ratio results in a floor for the signed fraction GenericFraction::Rational(Sign::Minus, r.ceil()) } _ => self.clone(), } } /// Returns the smallest integer greater than or equal to the value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (7u8, 5u8).ceil (), F::new (10u8, 5u8)); /// assert_eq! (F::new_neg (4u8, 3u8).ceil (), F::new_neg (1u8, 1u8)); /// ``` pub fn ceil(&self) -> Self { match *self { GenericFraction::Rational(Sign::Plus, ref r) => { GenericFraction::Rational(Sign::Plus, r.ceil()) } GenericFraction::Rational(Sign::Minus, ref r) => { // Floor of the unsigned ratio results in a ceil for the signed fraction GenericFraction::Rational(Sign::Minus, r.floor()) } _ => self.clone(), } } /// Returns the nearest integer to the value (.5 goes away from zero) /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (7u8, 5u8).round (), F::new (5u8, 5u8)); /// assert_eq! (F::new (8u8, 5u8).round (), F::new (10u8, 5u8)); /// assert_eq! (F::new (3u8, 2u8).round (), F::new (4u8, 2u8)); /// assert_eq! (F::new (1u8, 2u8).round (), F::new (2u8, 2u8)); /// assert_eq! (F::new_neg (3u8, 2u8).round (), F::new_neg (2u8, 1u8)); /// ``` pub fn round(&self) -> Self { match *self { GenericFraction::Rational(s, ref r) => GenericFraction::Rational(s, r.round()), _ => self.clone(), } } /// Returns the integer part of the value /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (7u8, 5u8).trunc (), F::new (5u8, 5u8)); /// assert_eq! (F::new (8u8, 5u8).trunc (), F::new (5u8, 5u8)); /// ``` pub fn trunc(&self) -> Self { match *self { GenericFraction::Rational(s, ref r) => GenericFraction::Rational(s, r.trunc()), _ => self.clone(), } } /// Returns the fractional part of a number /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (7u8, 5u8).fract (), F::new (2u8, 5u8)); /// assert_eq! (F::new (8u8, 5u8).fract (), F::new (3u8, 5u8)); /// ``` pub fn fract(&self) -> Self { match *self { GenericFraction::Rational(s, ref r) => GenericFraction::Rational(s, r.fract()), _ => GenericFraction::NaN, } } /// Returns the absolute value of self /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::nan ().abs (), F::nan ()); /// assert_eq! (F::infinity ().abs (), F::infinity ()); /// assert_eq! (F::neg_infinity ().abs (), F::infinity ()); /// assert_eq! (F::new (1u8, 2u8).abs (), F::new (1u8, 2u8)); /// assert_eq! (F::new_neg (1u8, 2u8).abs (), F::new (1u8, 2u8)); /// ``` pub fn abs(&self) -> Self { match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::Infinity(Sign::Plus), GenericFraction::Rational(_, ref r) => GenericFraction::Rational(Sign::Plus, r.clone()), } } /// Returns a number that represents the sign of self /// /// * 1.0 if the number is positive, +0.0 or INFINITY /// * -1.0 if the number is negative, -0.0 or NEG_INFINITY /// * NAN if the number is NAN /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (1u8, 2u8).signum (), F::new (1u8, 1u8)); /// assert_eq! (F::new (0u8, 1u8).signum (), F::new (1u8, 1u8)); /// assert_eq! (F::infinity ().signum (), F::new (1u8, 1u8)); /// assert_eq! (F::new_neg (1u8, 2u8).signum (), F::new_neg (1u8, 1u8)); /// assert_eq! (F::neg_zero ().signum (), F::new_neg (1u8, 1u8)); /// assert_eq! (F::neg_infinity ().signum (), F::new_neg (1u8, 1u8)); /// assert_eq! (F::nan ().signum (), F::nan ()); /// ``` pub fn signum(&self) -> Self { match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(Sign::Plus) => { GenericFraction::Rational(Sign::Plus, Ratio::new(T::one(), T::one())) } GenericFraction::Infinity(Sign::Minus) => { GenericFraction::Rational(Sign::Minus, Ratio::new(T::one(), T::one())) } GenericFraction::Rational(Sign::Plus, _) => { GenericFraction::Rational(Sign::Plus, Ratio::new(T::one(), T::one())) } GenericFraction::Rational(Sign::Minus, _) => { GenericFraction::Rational(Sign::Minus, Ratio::new(T::one(), T::one())) } } } /// Returns true if the sign is positive /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (F::new (1u8, 2u8).is_sign_positive ()); /// assert! (F::infinity ().is_sign_positive ()); /// assert! (! F::nan ().is_sign_positive ()); /// ``` pub const fn is_sign_positive(&self) -> bool { match *self { GenericFraction::NaN => false, GenericFraction::Infinity(Sign::Plus) => true, GenericFraction::Infinity(Sign::Minus) => false, GenericFraction::Rational(Sign::Plus, _) => true, GenericFraction::Rational(Sign::Minus, _) => false, } } /// Returns true if the sign is negative /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert! (F::new_neg (1u8, 2u8).is_sign_negative ()); /// assert! (F::neg_zero ().is_sign_negative ()); /// assert! (F::neg_infinity ().is_sign_negative ()); /// assert! (! F::nan ().is_sign_negative ()); /// ``` pub const fn is_sign_negative(&self) -> bool { match *self { GenericFraction::NaN => false, GenericFraction::Infinity(Sign::Plus) => false, GenericFraction::Infinity(Sign::Minus) => true, GenericFraction::Rational(Sign::Plus, _) => false, GenericFraction::Rational(Sign::Minus, _) => true, } } /// self.clone () * a + b /// /// Added for interface compatibility with float types pub fn mul_add(&self, a: Self, b: Self) -> Self { self.clone() * a + b } /// Takes the reciprocal (inverse) of the value (1/x) /// /// # Examples /// /// ``` /// use fraction::GenericFraction; /// type F = GenericFraction; /// /// assert_eq! (F::new (1u8, 2u8).recip (), F::new (2u8, 1u8)); /// assert_eq! (F::new (0u8, 1u8).recip (), F::infinity ()); /// assert_eq! (F::infinity ().recip (), F::new (0u8, 1u8)); /// assert_eq! (F::nan ().recip (), F::nan ()); /// ``` pub fn recip(&self) -> Self { match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::new(T::zero(), T::one())) } GenericFraction::Rational(s, ref r) if r.is_zero() => GenericFraction::Infinity(s), GenericFraction::Rational(s, ref r) => GenericFraction::Rational(s, r.recip()), } } /* ... Some stuff here that has not been implemented for Ratio ... */ } impl fmt::Display for GenericFraction { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let format = display::Format::new(formatter); display::format_fraction(self, formatter, &format) } } macro_rules! fraction_from_generic_int { ( $($F:ty),* ) => { $( impl From<$F> for GenericFraction where T: Clone + Integer + GenericInteger + CheckedAdd + CheckedMul + CheckedSub, $F: GenericInteger + CheckedAdd + CheckedDiv + CheckedMul + CheckedSub + PartialOrd { fn from(val: $F) -> GenericFraction { if let Some((sign, value)) = read_generic_integer::(val) { GenericFraction::Rational(sign, Ratio::new (value, T::one())) } else { GenericFraction::nan() } } } )* }; } #[cfg(feature = "with-bigint")] fraction_from_generic_int!(BigUint, BigInt); fraction_from_generic_int!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize); macro_rules! generic_fraction_from_float { ( $($from:ty),*) => { $( impl From<$from> for GenericFraction { fn from(val: $from) -> GenericFraction { if val.is_nan () { return Self::NaN }; if val.is_infinite () { return Self::Infinity (if val.is_sign_negative () { Sign::Minus } else { Sign::Plus }) }; let sign = if val < 0.0 { Sign::Minus } else { Sign::Plus }; // Using https://math.stackexchange.com/a/1049723/17452 , but rely on Ratio::new() to compute the gcd. // Find the max precision of this number // Note: the power computations happen in i32 until the end. let mut p: i32 = 0; let mut new_val = val; let ten: $from = 10.0; let fallback_to_string_conversion = || Self::from_str(&format!("{:+}", val)).unwrap_or(Self::NaN); loop { if (new_val.floor() - new_val).abs() < <$from>::EPSILON { // Yay, we've found the precision of this number break; } // Multiply by the precision // Note: we multiply by powers of ten to avoid this kind of round error with f32s: // https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b760579f103b7192c20413ebbe167b90 p += 1; new_val = val * ten.powi(p); if new_val.is_infinite() { return fallback_to_string_conversion(); } } // We store the sign of the ratio externally, so let's oppose the numerator if need be. // The denominator is always positive. if new_val < 0.0 { new_val = -new_val; } let numer: T = match T::from_f64(new_val.into()) { Some(v) => v, None => { return fallback_to_string_conversion(); } }; let denom: T = match T::from_f64(ten.powi(p).into()) { Some(v) => v, None => { return fallback_to_string_conversion(); } }; Self::Rational(sign, Ratio::new(numer, denom)) } } )* }; } generic_fraction_from_float!(f32, f64); impl From<(N, D)> for GenericFraction where T: Clone + GenericInteger, N: GenericInteger + PartialOrd, D: GenericInteger + PartialOrd, { fn from(pair: (N, D)) -> GenericFraction { GenericFraction::new_generic(Sign::Plus, pair.0, pair.1).unwrap_or(GenericFraction::NaN) } } #[cfg(test)] mod tests { use num::Integer; #[cfg(feature = "with-bigint")] use prelude::BigFraction; use crate::error::ParseError; #[cfg(feature = "with-bigint")] use crate::{BigInt, BigUint}; use crate::{ Bounded, ConstOne, ConstZero, Fraction, GenericFraction, Num, One, Sign, Signed, ToPrimitive, Zero, }; use super::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; use std::cmp::Ordering; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::num::FpCategory; use std::str::FromStr; extern crate rand; use self::rand::Rng; use generic::GenericInteger; type Frac = GenericFraction; fn hash_it(target: &impl Hash) -> u64 { use std::collections::hash_map::DefaultHasher; let mut h = DefaultHasher::new(); target.hash(&mut h); h.finish() } generate_ops_tests! ( NaN => {Frac::nan()}; NegInf => {Frac::neg_infinity()}; PosInf => {Frac::infinity()}; Zero => {Frac::new(0, 1)}; Half => {Frac::new(1, 2)}; One => {Frac::new(1, 1)}; Two => {Frac::new(2, 1)}; Three => {Frac::new(3, 1)}; Four => {Frac::new(4, 1)}; ); #[test] fn op_ord() { let pin = Frac::infinity(); let nin = Frac::neg_infinity(); let nil = Frac::zero(); let a = Frac::new(3, 4); let b = Frac::new(5, 7); assert!(a > b); assert!(a > nil); assert!(b > nil); assert!(nin < nil); assert!(nil < pin); } #[test] fn from_i8() { let f = Fraction::from(-2i8); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0i8); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2i8); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_u8() { let f = Fraction::from(0u8); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2u8); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(u8::max_value()); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(u8::max_value() as u64, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_i16() { let f = Fraction::from(-2i16); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0i16); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2i16); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_u16() { let f = Fraction::from(0u16); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2u16); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_i32() { let f = Fraction::from(-2i32); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0i32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2i32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_u32() { let f = Fraction::from(0u32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2u32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_i64() { let f = Fraction::from(-2i64); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0i64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2i64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_u64() { let f = Fraction::from(0u64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2u64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_i128() { let f = Fraction::from(0i128); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2i128); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(-2i128); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(22460602606i128); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(22460602606, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_u128() { let f = Fraction::from(0u128); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2u128); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_isize() { let f = Fraction::from(-2isize); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0isize); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2isize); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_usize() { let f = Fraction::from(0usize); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(2usize); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(2, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_f32() { let f = Fraction::from(0f32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0.01f32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(1, *f.numer().unwrap()); assert_eq!(100, *f.denom().unwrap()); let f = Fraction::from(-0.01f32); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(1, *f.numer().unwrap()); assert_eq!(100, *f.denom().unwrap()); let f = Fraction::from(16584253f32); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(16584253u64, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn from_f64() { let f = Fraction::from(0f64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(0, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); let f = Fraction::from(0.01f64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(1, *f.numer().unwrap()); assert_eq!(100, *f.denom().unwrap()); let f = Fraction::from(-0.01f64); assert_eq!(Sign::Minus, f.sign().unwrap()); assert_eq!(1, *f.numer().unwrap()); assert_eq!(100, *f.denom().unwrap()); let f = Fraction::from(1658425342060f64); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(1658425342060u64, *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] #[cfg(feature = "with-bigint")] fn from_insanity() { let number = "2062065394209534095362056240654064520645230645230645230645230645206452064520645203642530940959212130935957"; let fraction = format!("{}/1", number); let f = BigFraction::from_str_radix(&fraction, 10); assert!(f.is_ok()); let f = f.ok().unwrap(); assert_eq!( BigUint::from_str_radix(&number, 10).ok().unwrap(), *f.numer().unwrap() ); assert_eq!(BigUint::from(1u8), *f.denom().unwrap()); } #[test] #[cfg(feature = "with-bigint")] fn from_bigint() { let number = BigInt::from(42); let frac = BigFraction::from(number); assert_eq!(frac, BigFraction::from((42, 1))); let number = BigInt::from(-44); let frac = BigFraction::from(number); assert_eq!(frac, -BigFraction::from((44, 1))); } #[test] #[cfg(feature = "with-bigint")] fn from_biguint() { let number = BigUint::from(42u32); let frac = BigFraction::from(number); assert_eq!(frac, BigFraction::from((42, 1))); } #[test] fn from_extremum() { type F8 = GenericFraction; let f = F8::from(u8::max_value()); assert_eq!(Sign::Plus, f.sign().unwrap()); assert_eq!(u8::max_value(), *f.numer().unwrap()); assert_eq!(1, *f.denom().unwrap()); } #[test] fn hashy() { { let mut map: HashMap = HashMap::new(); map.insert(Fraction::from(0.75), ()); assert!(map.contains_key(&Fraction::new(3u8, 4u8))); // 0.75 == 3/4 assert!(map.contains_key(&Fraction::new(6u16, 8u16))); // 0.75 == 6/8 assert!(map.contains_key(&Fraction::new(12u32, 16u32))); // 0.75 == 12/16 assert!(map.contains_key(&Fraction::new(24u64, 32u64))); // 0.75 == 24/32 assert!(map.contains_key(&Fraction::new(48u8, 64u16))); // 0.75 == 48/64 assert!(map.contains_key(&Fraction::from((3i8, 4i8)))); assert!(map.contains_key(&Fraction::from((6i16, 8i16)))); assert!(map.contains_key(&Fraction::from((12i32, 16i32)))); assert!(map.contains_key(&Fraction::from((24i64, 32i64)))); assert!(map.contains_key(&Fraction::from((48i8, 64i16)))); assert!(!map.contains_key(&Fraction::from(0.5))); // 0.75 != 1/2 } { assert_eq!(hash_it(&Fraction::nan()), hash_it(&Fraction::nan())); assert_ne!(hash_it(&Fraction::nan()), hash_it(&Fraction::zero())); assert_ne!( hash_it(&Fraction::infinity()), hash_it(&Fraction::neg_infinity()) ); assert_ne!(hash_it(&Fraction::infinity()), hash_it(&Fraction::nan())); assert_eq!( hash_it(&Fraction::infinity()), hash_it(&Fraction::infinity()) ); assert_eq!( hash_it(&Fraction::neg_infinity()), hash_it(&Fraction::neg_infinity()) ); assert_eq!( hash_it(&Fraction::neg_infinity()), hash_it(&Fraction::neg_infinity()) ); assert_eq!( hash_it(&Fraction::new(1u8, 2u8)), hash_it(&Fraction::new(2u8, 4u8)) ); assert_eq!( hash_it(&Fraction::new(1u8, 0u8)), hash_it(&Fraction::new(2u8, 0u8)) ); assert_eq!( hash_it(&Fraction::new(0u8, 1u8)), hash_it(&Fraction::new(0u8, 2u8)) ); assert_eq!(hash_it(&Fraction::zero()), hash_it(&Fraction::zero())); assert_eq!(hash_it(&Frac::zero()), hash_it(&Frac::neg_zero())); } } #[test] fn comparison() { assert_eq!(Frac::zero(), Frac::zero()); assert_eq!(Frac::zero(), Frac::neg_zero()); assert_eq!(Frac::from(0), Frac::zero()); assert_eq!(Frac::from(0), Frac::neg_zero()); assert_eq!(Frac::from(0.5), Frac::new(1u8, 2u8)); assert_eq!(Frac::from(-0.5), Frac::new_neg(1u8, 2u8)); assert_ne!(Frac::from(-0.5), Frac::new(1u8, 2u8)); assert!(!(Frac::zero() < Frac::neg_zero())); assert!(!(Frac::neg_zero() < Frac::zero())); assert!(!(Frac::zero() > Frac::neg_zero())); assert!(!(Frac::neg_zero() > Frac::zero())); assert!(Frac::neg_zero() < Frac::new(1u8, 2u8)); assert!(!(Frac::neg_zero() > Frac::new(1u8, 2u8))); assert!(Frac::zero() < Frac::new(1u8, 2u8)); assert!(!(Frac::zero() > Frac::new(1u8, 2u8))); assert!(Frac::new_neg(1u8, 2u8) < Frac::neg_zero()); assert!(Frac::new_neg(1u8, 2u8) < Frac::zero()); assert!(!(Frac::new_neg(1u8, 2u8) > Frac::neg_zero())); assert!(!(Frac::new_neg(1u8, 2u8) > Frac::zero())); assert_eq!(Frac::new(1u8, 2u8), Frac::new(1u8, 2u8)); assert_eq!(Frac::new_neg(1u8, 2u8), Frac::new_neg(1u8, 2u8)); assert!(Frac::new_neg(1u8, 2u8) < Frac::new(1u8, 2u8)); assert!(!(Frac::new(1u8, 2u8) < Frac::new_neg(1u8, 2u8))); assert!(!(Frac::new_neg(1u8, 2u8) < Frac::new_neg(1u8, 2u8))); assert!(Frac::new_neg(1u8, 2u8) < Frac::new_neg(1u8, 4u8)); assert!(Frac::new_neg(1u8, 2u8) < Frac::neg_zero()); assert!(Frac::new_neg(1u8, 2u8) < Frac::zero()); assert!(!(Frac::neg_zero() < Frac::new_neg(1u8, 2u8))); assert!(!(Frac::zero() < Frac::new_neg(1u8, 2u8))); assert!(Frac::neg_zero() < Frac::new(1u8, 2u8)); assert!(Frac::neg_zero() > Frac::new_neg(1u8, 2u8)); assert!(Frac::zero() > Frac::new_neg(1u8, 2u8)); assert!(Frac::new(1u8, 2u8) > Frac::neg_zero()); assert!(!(Frac::new(1u8, 2u8) < Frac::neg_zero())); assert!(Frac::zero() < Frac::new(1u8, 2u8)); } #[test] fn cmp() { /* CMP */ let nan = Frac::nan(); let neg_inf = Frac::neg_infinity(); let neg_one = Frac::one() * -1; let zero = Frac::zero(); let one = Frac::one(); let inf = Frac::infinity(); assert_eq!(nan.cmp(&nan), Ordering::Equal); assert_eq!(nan.cmp(&neg_inf), Ordering::Less); assert_eq!(nan.cmp(&neg_one), Ordering::Less); assert_eq!(nan.cmp(&zero), Ordering::Less); assert_eq!(nan.cmp(&one), Ordering::Less); assert_eq!(nan.cmp(&inf), Ordering::Less); assert_eq!(neg_inf.cmp(&nan), Ordering::Greater); assert_eq!(neg_inf.cmp(&neg_inf), Ordering::Equal); assert_eq!(neg_inf.cmp(&neg_one), Ordering::Less); assert_eq!(neg_inf.cmp(&zero), Ordering::Less); assert_eq!(neg_inf.cmp(&one), Ordering::Less); assert_eq!(neg_inf.cmp(&inf), Ordering::Less); assert_eq!(neg_one.cmp(&nan), Ordering::Greater); assert_eq!(neg_one.cmp(&neg_inf), Ordering::Greater); assert_eq!(neg_one.cmp(&neg_one), Ordering::Equal); assert_eq!(neg_one.cmp(&zero), Ordering::Less); assert_eq!(neg_one.cmp(&one), Ordering::Less); assert_eq!(neg_one.cmp(&inf), Ordering::Less); assert_eq!(zero.cmp(&nan), Ordering::Greater); assert_eq!(zero.cmp(&neg_inf), Ordering::Greater); assert_eq!(zero.cmp(&neg_one), Ordering::Greater); assert_eq!(zero.cmp(&zero), Ordering::Equal); assert_eq!(zero.cmp(&one), Ordering::Less); assert_eq!(zero.cmp(&inf), Ordering::Less); assert_eq!(one.cmp(&nan), Ordering::Greater); assert_eq!(one.cmp(&neg_inf), Ordering::Greater); assert_eq!(one.cmp(&neg_one), Ordering::Greater); assert_eq!(one.cmp(&zero), Ordering::Greater); assert_eq!(one.cmp(&one), Ordering::Equal); assert_eq!(one.cmp(&inf), Ordering::Less); assert_eq!(inf.cmp(&nan), Ordering::Greater); assert_eq!(inf.cmp(&neg_inf), Ordering::Greater); assert_eq!(inf.cmp(&neg_one), Ordering::Greater); assert_eq!(inf.cmp(&zero), Ordering::Greater); assert_eq!(inf.cmp(&one), Ordering::Greater); assert_eq!(inf.cmp(&inf), Ordering::Equal); } #[test] fn floor() { assert_eq!(Frac::zero(), Frac::new(1, 3).floor()); assert_eq!(Frac::one() * -1, Frac::new_neg(1, 3).floor()); } #[test] fn ceil() { assert_eq!(Frac::zero(), Frac::new_neg(1, 3).ceil()); assert_eq!(Frac::one(), Frac::new(1, 3).ceil()); } #[test] fn from_str() { assert_eq!(Ok(Frac::zero()), Frac::from_str("0")); assert_eq!(Ok(Frac::zero()), Frac::from_str("-0")); assert_eq!(Ok(Frac::zero()), Frac::from_str("+0")); assert_eq!(Ok(Frac::zero()), Frac::from_str("0.0")); assert_eq!(Ok(Frac::zero()), Frac::from_str("-0.0")); assert_eq!(Ok(Frac::zero()), Frac::from_str("+0.0")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("0.000000")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("-0.000000")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("+0.000000")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("0/1")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("-0/1")); assert_eq!(Ok(Fraction::zero()), Fraction::from_str("+0/1")); assert_eq!( Ok(Fraction::from(10000000000000_u64)), Fraction::from_str("10000000000000.0000000000") ); #[cfg(feature = "with-bigint")] { assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str( "00000000000000000000000000.0000000000000000000000000000000000000000000" ) ); assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str( "-0000000000000000000000000.0000000000000000000000000000000000000000000" ) ); assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str( "+0000000000000000000000000.0000000000000000000000000000000000000000000" ) ); assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str("00000000000000000000000000/1") ); assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str("-0000000000000000000000000/1") ); assert_eq!( Ok(BigFraction::zero()), BigFraction::from_str("+0000000000000000000000000/1") ); } assert_eq!(Ok(Frac::one()), Frac::from_str("1")); assert_eq!(Ok(Frac::new_neg(1, 1)), Frac::from_str("-1")); assert_eq!(Ok(Frac::one()), Frac::from_str("+1")); assert_eq!(Ok(Frac::one()), Frac::from_str("1.0")); assert_eq!(Ok(Frac::new_neg(1, 1)), Frac::from_str("-1.0")); assert_eq!(Ok(Frac::one()), Frac::from_str("+1.00")); assert_eq!(Ok(Frac::one()), Frac::from_str("1/1")); assert_eq!(Ok(Frac::new_neg(1, 1)), Frac::from_str("-1/1")); assert_eq!(Ok(Frac::one()), Frac::from_str("+1/1")); assert_eq!(Ok(Frac::new(1, 2)), Frac::from_str("0.5")); assert_eq!(Ok(Frac::new(1, 2)), Frac::from_str("1/2")); assert_eq!( Ok(Fraction::new(3333u64, 5000u64)), Fraction::from_str("0.6666") ); assert_eq!( Ok(Fraction::new(3333u64, 5000u64)), Fraction::from_str("3333/5000") ); assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("test")); assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("1test")); assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("1.26t8")); // this is due to the std library which issues ParseIntError on the whole part overflow assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("120202040")); assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("1.20602604")); assert_eq!(Err(ParseError::OverflowError), Frac::from_str("255.255")); assert_eq!(Err(ParseError::ParseIntError), Frac::from_str("256/256")); } #[test] fn new_generic() { { type F = GenericFraction; let f12 = F::new_generic(Sign::Plus, 1i8, 2u8).unwrap(); let f34 = F::new_generic(Sign::Minus, 3i16, 4u32).unwrap(); let f56 = F::new_generic(Sign::Plus, -5i64, 6u128).unwrap(); let f78 = F::new_generic(Sign::Minus, 7usize, -8isize).unwrap(); #[cfg(feature = "with-bigint")] { let fbig = F::new_generic(Sign::Minus, -BigInt::from(254), BigUint::from(255u32)).unwrap(); assert_eq!( ( fbig.sign().unwrap(), *fbig.numer().unwrap(), *fbig.denom().unwrap() ), (Sign::Plus, 254u8, 255u8) ); } assert_eq!( ( f12.sign().unwrap(), *f12.numer().unwrap(), *f12.denom().unwrap() ), (Sign::Plus, 1u8, 2u8) ); assert_eq!( ( f34.sign().unwrap(), *f34.numer().unwrap(), *f34.denom().unwrap() ), (Sign::Minus, 3u8, 4u8) ); assert_eq!( ( f56.sign().unwrap(), *f56.numer().unwrap(), *f56.denom().unwrap() ), (Sign::Minus, 5u8, 6u8) ); assert_eq!( ( f78.sign().unwrap(), *f78.numer().unwrap(), *f78.denom().unwrap() ), (Sign::Plus, 7u8, 8u8) ); assert_eq!(None, F::new_generic(Sign::Plus, 256, 1)); // overflow assert_eq!(None, F::new_generic(Sign::Plus, 1, 257)); // overflow } { type F = GenericFraction; let f12 = F::new_generic(Sign::Plus, 1i8, 2u8).unwrap(); let f34 = F::new_generic(Sign::Minus, 3i16, 4u32).unwrap(); let f56 = F::new_generic(Sign::Plus, -5i64, 6u128).unwrap(); let f78 = F::new_generic(Sign::Minus, 7usize, -8isize).unwrap(); assert_eq!( ( f12.sign().unwrap(), *f12.numer().unwrap(), *f12.denom().unwrap() ), (Sign::Plus, 1i8, 2i8) ); assert_eq!( ( f34.sign().unwrap(), *f34.numer().unwrap(), *f34.denom().unwrap() ), (Sign::Minus, 3i8, 4i8) ); assert_eq!( ( f56.sign().unwrap(), *f56.numer().unwrap(), *f56.denom().unwrap() ), (Sign::Minus, 5i8, 6i8) ); assert_eq!( ( f78.sign().unwrap(), *f78.numer().unwrap(), *f78.denom().unwrap() ), (Sign::Plus, 7i8, 8i8) ); assert_eq!(None, F::new_generic(Sign::Plus, 128, 1)); // overflow assert_eq!(None, F::new_generic(Sign::Plus, 256, 1)); // overflow #[cfg(feature = "with-bigint")] { let fbig = F::new_generic(Sign::Minus, -BigInt::from(126), BigUint::from(127u8)).unwrap(); assert_eq!( ( fbig.sign().unwrap(), *fbig.numer().unwrap(), *fbig.denom().unwrap() ), (Sign::Plus, 126i8, 127i8) ); } } #[cfg(feature = "with-bigint")] { type F = GenericFraction; let f12 = F::new_generic(Sign::Plus, 1i8, 2u8).unwrap(); let f34 = F::new_generic(Sign::Minus, 3i16, 4u32).unwrap(); let f56 = F::new_generic(Sign::Plus, -5i64, 6u128).unwrap(); let f78 = F::new_generic(Sign::Minus, 7usize, -8isize).unwrap(); let fbig = F::new_generic(Sign::Minus, -BigInt::from(254), BigUint::from(255u32)).unwrap(); assert_eq!( ( f12.sign().unwrap(), f12.numer().unwrap(), f12.denom().unwrap() ), (Sign::Plus, &BigUint::from(1u8), &BigUint::from(2u8)) ); assert_eq!( ( f34.sign().unwrap(), f34.numer().unwrap(), f34.denom().unwrap() ), (Sign::Minus, &BigUint::from(3u8), &BigUint::from(4u8)) ); assert_eq!( ( f56.sign().unwrap(), f56.numer().unwrap(), f56.denom().unwrap() ), (Sign::Minus, &BigUint::from(5u8), &BigUint::from(6u8)) ); assert_eq!( ( f78.sign().unwrap(), f78.numer().unwrap(), f78.denom().unwrap() ), (Sign::Plus, &BigUint::from(7u8), &BigUint::from(8u8)) ); assert_eq!( ( fbig.sign().unwrap(), fbig.numer().unwrap(), fbig.denom().unwrap() ), (Sign::Plus, &BigUint::from(254u8), &BigUint::from(255u8)) ); } #[cfg(feature = "with-bigint")] { type F = GenericFraction; let f12 = F::new_generic(Sign::Plus, 1i8, 2u8).unwrap(); let f34 = F::new_generic(Sign::Minus, 3i16, 4u32).unwrap(); let f56 = F::new_generic(Sign::Plus, -5i64, 6u128).unwrap(); let f78 = F::new_generic(Sign::Minus, 7usize, -8isize).unwrap(); let fbig = F::new_generic(Sign::Minus, -BigInt::from(254), BigUint::from(255u32)).unwrap(); assert_eq!( ( f12.sign().unwrap(), f12.numer().unwrap(), f12.denom().unwrap() ), (Sign::Plus, &BigInt::from(1u8), &BigInt::from(2u8)) ); assert_eq!( ( f34.sign().unwrap(), f34.numer().unwrap(), f34.denom().unwrap() ), (Sign::Minus, &BigInt::from(3u8), &BigInt::from(4u8)) ); assert_eq!( ( f56.sign().unwrap(), f56.numer().unwrap(), f56.denom().unwrap() ), (Sign::Minus, &BigInt::from(5u8), &BigInt::from(6u8)) ); assert_eq!( ( f78.sign().unwrap(), f78.numer().unwrap(), f78.denom().unwrap() ), (Sign::Plus, &BigInt::from(7u8), &BigInt::from(8u8)) ); assert_eq!( ( fbig.sign().unwrap(), fbig.numer().unwrap(), fbig.denom().unwrap() ), (Sign::Plus, &BigInt::from(254u8), &BigInt::from(255u8)) ); } { type F = GenericFraction; let f1 = F::new_generic(Sign::Plus, 123456788u64, 123456789i32).unwrap(); let f2 = F::new_generic(Sign::Minus, 1234567890122u64, -1234567890123i64).unwrap(); assert_eq!( ( f1.sign().unwrap(), *f1.numer().unwrap(), *f1.denom().unwrap() ), (Sign::Plus, 123456788u128, 123456789u128) ); assert_eq!( ( f2.sign().unwrap(), *f2.numer().unwrap(), *f2.denom().unwrap() ), (Sign::Plus, 1234567890122u128, 1234567890123u128) ); } } #[test] fn sign() { let p = Sign::Plus; let m = Sign::Minus; assert_ne!(p, m); assert_eq!(p, Sign::Plus); assert_eq!(m, Sign::Minus); assert_eq!(m, p * m); assert_eq!(p, p * p); assert_eq!(p, m * m); assert_eq!(m, m * p); assert!(p.is_positive()); assert!(!p.is_negative()); assert!(!m.is_positive()); assert!(m.is_negative()); } #[test] fn fraction() { assert_eq!(Frac::nan(), Frac::new(0, 0)); assert_eq!(Frac::infinity(), Frac::new(1, 0)); assert!(Frac::nan().numer().is_none()); assert!(Frac::nan().denom().is_none()); assert_eq!( Fraction::new(5u64, 8u64), Fraction::from_fraction(Frac::new(5u8, 8u8)) ); assert_eq!(Fraction::nan(), Fraction::from_fraction(Frac::nan())); assert_eq!( Fraction::infinity(), Fraction::from_fraction(Frac::infinity()) ); assert_eq!(Frac::min_value(), Frac::new_neg(255, 1)); assert_eq!(Frac::max_value(), Frac::new(255, 1)); assert_ne!(Frac::neg_infinity(), Frac::infinity()); assert_ne!(Frac::nan(), Frac::zero()); assert_ne!(Frac::zero(), Frac::nan()); assert_ne!(Frac::new_neg(1, 2), Frac::new(1, 2)); assert_eq!(Frac::neg_zero(), Frac::zero()); assert_ne!(Frac::infinity(), Frac::nan()); assert!(!(Frac::infinity() > Frac::infinity())); assert!((Frac::infinity() > Frac::neg_infinity())); assert!(!(Frac::neg_infinity() > Frac::infinity())); assert!(Frac::infinity() > Frac::max_value()); assert!(Frac::min_value() > Frac::neg_infinity()); assert_eq!(-Frac::infinity(), Frac::neg_infinity()); assert_eq!(-&Frac::nan(), Frac::nan()); assert_eq!(-&Frac::infinity(), Frac::neg_infinity()); assert_eq!(-&Frac::one(), Frac::new_neg(1, 1)); assert_eq!(-&Frac::zero(), Frac::zero()); assert_eq!( Frac::new_neg(1, 1) + Frac::new_neg(1, 1), Frac::new_neg(2, 1) ); assert_eq!( &Frac::new_neg(1, 1) + &Frac::new_neg(1, 1), Frac::new_neg(2, 1) ); assert_eq!(Frac::new_neg(1, 1) - Frac::new_neg(1, 1), Frac::zero()); assert_eq!(Frac::new_neg(1, 1) - Frac::new_neg(2, 1), Frac::one()); assert_eq!(&Frac::new_neg(1, 1) - &Frac::new_neg(1, 1), Frac::zero()); assert_eq!(&Frac::new_neg(1, 1) - &Frac::new_neg(2, 1), Frac::one()); assert_eq!(Frac::new(1, 255), Frac::min_positive_value()); assert!(Frac::infinity().is_infinite()); assert!(Frac::neg_infinity().is_infinite()); assert!(!Frac::one().is_infinite()); assert!(!Frac::infinity().is_finite()); assert!(!Frac::neg_infinity().is_finite()); assert!(Frac::one().is_finite()); assert_eq!(FpCategory::Normal, Frac::one().classify()); assert_eq!(FpCategory::Infinite, Frac::infinity().classify()); assert_eq!(FpCategory::Zero, Frac::zero().classify()); assert_eq!(FpCategory::Nan, Frac::nan().classify()); assert_eq!(Frac::nan(), Frac::nan().floor()); assert_eq!(Frac::one(), Frac::new(3, 2).floor()); assert_eq!(Frac::nan(), Frac::nan().ceil()); assert_eq!(Frac::one(), Frac::new(1, 2).ceil()); assert_eq!(Frac::nan(), Frac::nan().round()); assert_eq!(Frac::one(), Frac::new(1, 2).round()); assert_eq!(Frac::new(2, 1), Frac::new(3, 2).round()); assert_eq!(Frac::one(), Frac::new(4, 3).round()); assert_eq!(Frac::nan(), Frac::nan().trunc()); assert_eq!(Frac::zero(), Frac::new(1, 2).trunc()); assert_eq!(Frac::one(), Frac::new(3, 2).trunc()); assert_eq!(Frac::nan(), Frac::nan().fract()); assert_eq!(Frac::new(1, 2), Frac::new(1, 2).fract()); assert_eq!(Frac::new(1, 2), Frac::new(3, 2).fract()); assert!(!Frac::nan().is_sign_positive()); assert!(!Frac::nan().is_sign_negative()); assert!(Frac::infinity().is_sign_positive()); assert!(!Frac::neg_infinity().is_sign_positive()); assert!(!Frac::infinity().is_sign_negative()); assert!(Frac::neg_infinity().is_sign_negative()); assert!(Frac::one().is_sign_positive()); assert!(!Frac::one().is_sign_negative()); assert!(!Frac::new_neg(1, 1).is_sign_positive()); assert!(Frac::new_neg(1, 1).is_sign_negative()); assert_eq!( Frac::new(3, 1), Frac::one().mul_add(Frac::new(2, 1), Frac::one()) ); assert_eq!(Frac::nan(), Frac::nan().recip()); assert_eq!(Frac::zero(), Frac::infinity().recip()); assert_eq!(Frac::zero(), Frac::neg_infinity().recip()); assert_eq!(Frac::infinity(), Frac::zero().recip()); assert_eq!(Frac::new(2, 1), Frac::new(1, 2).recip()); } #[test] fn from_str_radix() { assert_eq!(Frac::one(), Frac::from_str_radix("5/5", 10).unwrap()); assert_eq!(Frac::one(), Frac::from_str_radix("+5/5", 10).unwrap()); assert_eq!(Frac::new(1, 2), Frac::from_str_radix("+5/10", 10).unwrap()); assert_eq!( Frac::new_neg(4, 3), Frac::from_str_radix("-4/3", 10).unwrap() ); } #[test] fn signed() { // abs assert_eq!(Frac::one(), ::abs(&Frac::new_neg(1, 1))); assert_eq!(Frac::nan(), ::abs(&Frac::nan())); assert_eq!(Frac::infinity(), ::abs(&Frac::infinity())); assert_eq!( Frac::infinity(), ::abs(&Frac::neg_infinity()) ); // abs_sub assert_eq!(Frac::nan(), Frac::nan().abs_sub(&Frac::nan())); assert_eq!(Frac::nan(), Frac::infinity().abs_sub(&Frac::nan())); assert_eq!(Frac::zero(), Frac::infinity().abs_sub(&Frac::infinity())); assert_eq!( Frac::infinity(), Frac::infinity().abs_sub(&Frac::neg_infinity()) ); assert_eq!( Frac::zero(), Frac::neg_infinity().abs_sub(&Frac::neg_infinity()) ); assert_eq!( Frac::zero(), Frac::neg_infinity().abs_sub(&Frac::infinity()) ); assert_eq!(Frac::infinity(), Frac::infinity().abs_sub(&Frac::one())); assert_eq!( Frac::infinity(), Frac::infinity().abs_sub(&Frac::new_neg(1, 1)) ); assert_eq!(Frac::zero(), Frac::neg_infinity().abs_sub(&Frac::one())); assert_eq!(Frac::nan(), Frac::one().abs_sub(&Frac::nan())); assert_eq!(Frac::zero(), Frac::one().abs_sub(&Frac::infinity())); assert_eq!(Frac::infinity(), Frac::one().abs_sub(&Frac::neg_infinity())); assert_eq!( Frac::neg_infinity(), Frac::new_neg(1, 1).abs_sub(&Frac::neg_infinity()) ); assert_eq!(Frac::one(), Frac::new(2, 1).abs_sub(&Frac::one())); assert_eq!(Frac::one(), Frac::one().abs_sub(&Frac::new(2, 1))); // signum assert_eq!(Frac::nan(), Frac::nan().signum()); assert_eq!(Frac::one(), Frac::infinity().signum()); assert_eq!(Frac::one(), Frac::zero().signum()); assert_eq!(Frac::one(), Frac::one().signum()); assert_eq!(-Frac::one(), Frac::neg_infinity().signum()); assert_eq!(-Frac::one(), Frac::new_neg(1, 1).signum()); } #[test] fn to_primitive() { assert!(Frac::nan().to_i64().is_none()); assert!(Frac::nan().to_u64().is_none()); assert!(Frac::infinity().to_i64().is_none()); assert!(Frac::infinity().to_u64().is_none()); assert!(Frac::neg_infinity().to_i64().is_none()); assert!(Frac::neg_infinity().to_u64().is_none()); assert_eq!(Some(1), Frac::one().to_i64()); assert_eq!(Some(1), Frac::one().to_u64()); assert!(Frac::new(1, 2).to_i64().is_none()); assert!(Frac::new(1, 2).to_u64().is_none()); assert_eq!(Some(-1), Frac::new_neg(1, 1).to_i64()); assert!(Frac::new_neg(1, 1).to_u64().is_none()); /* f64 */ assert!(Frac::nan().to_f64().unwrap().is_nan()); assert_eq!(::std::f64::INFINITY, Frac::infinity().to_f64().unwrap()); assert_eq!( ::std::f64::NEG_INFINITY, Frac::neg_infinity().to_f64().unwrap() ); assert_eq!(1f64, Frac::one().to_f64().unwrap()); } #[test] fn summing_iterator() { let values = vec![Fraction::new(2u64, 3u64), Fraction::new(1u64, 3u64)]; let sum: Fraction = values.iter().sum(); assert_eq!(sum, Fraction::new(1u8, 1u8)); } #[test] fn product_iterator() { let values = vec![Fraction::new(2u64, 3u64), Fraction::new(1u64, 3u64)]; let product: Fraction = values.iter().product(); assert_eq!(product, Fraction::new(2u8, 9u8)); } #[test] fn fraction_from_float() { macro_rules! test_for_smaller_t { ( $($t:ty),*) => { $( // f32 tests let f = GenericFraction::<$t>::from(-std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f32::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f32); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f32); assert_eq!(format!("{}", f), "1"); // f64 tests let f = GenericFraction::<$t>::from(-std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f64::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f64); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f64); assert_eq!(format!("{}", f), "1"); // Arbitrary tests assert_eq!(format!("{}", f), "1"); let f = GenericFraction::<$t>::from(2.0); assert_eq!(format!("{}", f), "2"); let f = GenericFraction::<$t>::from(0.5); assert_eq!(format!("{}", f), "1/2"); let f = GenericFraction::<$t>::from(15978.649); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-0.75); assert_eq!(format!("{}", f), "-3/4"); )* }; } macro_rules! test_for_larger_t { ( $($t:ty),*) => { $( // f32 tests let f = GenericFraction::<$t>::from(-std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f32::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f32); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f32); assert_eq!(format!("{}", f), "1"); // f64 tests let f = GenericFraction::<$t>::from(-std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::MIN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::MAX); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f64::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f64); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f64); assert_eq!(format!("{}", f), "1"); // Arbitrary tests let f = GenericFraction::<$t>::from(2.0); assert_eq!(format!("{}", f), "2"); let f = GenericFraction::<$t>::from(0.5); assert_eq!(format!("{}", f), "1/2"); let f = GenericFraction::<$t>::from(15978.649); assert_eq!(format!("{}", f), "15978649/1000"); let f = GenericFraction::<$t>::from(-0.75); assert_eq!(format!("{}", f), "-3/4"); )* }; } macro_rules! test_for_big_t { ( $($t:ty),*) => { $( // Note: we don't test min/max for big_t because the value depends on the type // f32 tests let f = GenericFraction::<$t>::from(-std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f32::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f32::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f32::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f32); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f32); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f32); assert_eq!(format!("{}", f), "1"); // f64 tests let f = GenericFraction::<$t>::from(-std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(std::f64::NAN); assert_eq!(format!("{}", f), "NaN"); let f = GenericFraction::<$t>::from(-std::f64::INFINITY); assert_eq!(format!("{}", f), "-inf"); let f = GenericFraction::<$t>::from(std::f64::INFINITY); assert_eq!(format!("{}", f), "inf"); let f = GenericFraction::<$t>::from(-0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(0.0_f64); assert_eq!(format!("{}", f), "0"); let f = GenericFraction::<$t>::from(-1.0_f64); assert_eq!(format!("{}", f), "-1"); let f = GenericFraction::<$t>::from(1.0_f64); assert_eq!(format!("{}", f), "1"); // Arbitrary tests let f = GenericFraction::<$t>::from(2.0); assert_eq!(format!("{}", f), "2"); let f = GenericFraction::<$t>::from(0.5); assert_eq!(format!("{}", f), "1/2"); let f = GenericFraction::<$t>::from(15978.649); assert_eq!(format!("{}", f), "15978649/1000"); let f = GenericFraction::<$t>::from(-0.75); assert_eq!(format!("{}", f), "-3/4"); let f = GenericFraction::<$t>::from(-0.5); assert_eq!(format!("{}", f), "-1/2"); )* }; } test_for_smaller_t!(u8, i8, u16, i16); test_for_larger_t!(u32, i32, u64, i64, usize, isize); test_for_big_t!(u128, i128); #[cfg(feature = "with-bigint")] { use crate::{BigInt, BigUint}; test_for_big_t!(BigUint, BigInt); } } #[test] fn fraction_test_default() { let fra = Frac::default(); assert_eq!(fra.numer(), Some(&0u8)); assert_eq!(fra.denom(), Some(&1u8)); #[cfg(feature = "with-bigint")] { let fra = BigFraction::default(); assert_eq!(fra.numer(), Some(&BigUint::from(0u8))); assert_eq!(fra.denom(), Some(&BigUint::from(1u8))); } } fn clamp_agree_with_cmp( min: &GenericFraction, max: &GenericFraction, test_value: &GenericFraction, ) { if min.cmp(max) == Ordering::Greater { panic!("min is greater than max"); } let clamped = test_value.clamp(min, max); match (test_value.cmp(min), test_value.cmp(max)) { (Ordering::Less, Ordering::Less) => assert_eq!(clamped, min), (Ordering::Less, Ordering::Equal) => assert_eq!(clamped, min), (Ordering::Less, Ordering::Greater) => { panic!("Shouldn't be possible to be less than min and greater than max") } (Ordering::Equal, Ordering::Less) => assert_eq!(clamped, min), (Ordering::Equal, Ordering::Equal) => { assert_eq!(clamped, min); assert_eq!(clamped, max); } (Ordering::Equal, Ordering::Greater) => assert_eq!(clamped, max), (Ordering::Greater, Ordering::Less) => assert_eq!(clamped, test_value), (Ordering::Greater, Ordering::Equal) => assert_eq!(clamped, max), (Ordering::Greater, Ordering::Greater) => assert_eq!(clamped, max), } } #[test] fn rational_rational_rational_clamp_cmp_agreement_test() { let mut rng = rand::thread_rng(); for _ in 0..10 { let base_value: i8 = rng.gen(); let min = GenericFraction::::from(base_value); let bump: u8 = rng.gen(); let bump: i32 = base_value as i32 + bump as i32; let max = GenericFraction::::from(bump); let test_value = GenericFraction::::new(rng.gen::(), rng.gen::()); clamp_agree_with_cmp(&min, &max, &test_value); } } #[test] fn rational_nan_rational_clamp_cmp_agreement_test() { let mut rng = rand::thread_rng(); for _ in 0..10 { let base_value: i8 = rng.gen(); let min = GenericFraction::::from(base_value); let bump: u8 = rng.gen(); let bump: i32 = base_value as i32 + bump as i32; let max = GenericFraction::::from(bump); let nan = GenericFraction::::NaN; clamp_agree_with_cmp(&min, &max, &nan); } } #[test] fn nan_pos_infinity_partail_cmp_cmp_agreement_test() { let nan = GenericFraction::::NaN; let pos_inf = GenericFraction::::infinity(); assert_eq!(nan.partial_cmp(&pos_inf), Some(nan.cmp(&pos_inf))); } #[test] fn nan_neg_infinity_partail_cmp_cmp_agreement_test() { let nan = GenericFraction::::NaN; let neg_inf = GenericFraction::::neg_infinity(); assert_eq!(nan.partial_cmp(&neg_inf), Some(nan.cmp(&neg_inf))); } #[test] fn nan_rational_partail_cmp_cmp_agreement_test() { let mut rng = rand::thread_rng(); let nan = GenericFraction::::NaN; for _ in 0..10 { let base_value: i8 = rng.gen(); let test_value = GenericFraction::::from(base_value); assert_eq!(nan.partial_cmp(&test_value), Some(nan.cmp(&test_value))); } } #[test] fn pos_inf_rational_partail_cmp_cmp_agreement_test() { let mut rng = rand::thread_rng(); let pos_inf = GenericFraction::::infinity(); for _ in 0..10 { let base_value: i8 = rng.gen(); let test_value = GenericFraction::::from(base_value); assert_eq!( pos_inf.partial_cmp(&test_value), Some(pos_inf.cmp(&test_value)) ); } } #[test] fn neg_inf_rational_partail_cmp_cmp_agreement_test() { let mut rng = rand::thread_rng(); let neg_inf = GenericFraction::::neg_infinity(); for _ in 0..10 { let base_value: i8 = rng.gen(); let test_value = GenericFraction::::from(base_value); assert_eq!( neg_inf.partial_cmp(&test_value), Some(neg_inf.cmp(&test_value)) ); } } #[test] fn constant_one() { let constant_one = ::ONE; let one = Frac::one(); assert_eq!(constant_one, one); } #[test] fn constant_zero() { let constant_zero = ::ZERO; let zero = Frac::zero(); assert_eq!(constant_zero, zero); } #[test] fn consistency_partial_cmp() { let nan = Frac::nan(); let neg_inf = Frac::neg_infinity(); let neg_one = Frac::one() * -1; let zero = Frac::zero(); let one = Frac::one(); let inf = Frac::infinity(); // 1. a == b if and only if partial_cmp(a, b) == Some(Equal). assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&nan, &nan) ); assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&neg_inf, &neg_inf) ); assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&neg_one, &neg_one) ); assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&zero, &zero) ); assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&one, &one) ); assert_eq!( Some(Ordering::Equal), GenericFraction::partial_cmp(&inf, &inf) ); // 2. a < b if and only if partial_cmp(a, b) == Some(Less) assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&nan, &neg_inf) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&nan, &neg_one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&nan, &zero) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&nan, &one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&nan, &inf) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_inf, &neg_one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_inf, &zero) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_inf, &one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_inf, &inf) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_one, &zero) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_one, &one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&neg_one, &inf) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&zero, &one) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&zero, &inf) ); assert_eq!( Some(Ordering::Less), GenericFraction::partial_cmp(&one, &inf) ); // 3. a > b if and only if partial_cmp(a, b) == Some(Greater) assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&neg_inf, &nan) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&neg_one, &neg_inf) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&neg_one, &nan) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&zero, &nan) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&zero, &neg_inf) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&zero, &neg_one) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&one, &nan) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&one, &neg_inf) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&one, &neg_one) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&one, &zero) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&inf, &nan) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&inf, &neg_inf) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&inf, &neg_one) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&inf, &zero) ); assert_eq!( Some(Ordering::Greater), GenericFraction::partial_cmp(&inf, &one) ); } #[test] fn consistency_cmp() { let nan = Frac::nan(); let neg_inf = Frac::neg_infinity(); let neg_one = Frac::one() * -1; let zero = Frac::zero(); let one = Frac::one(); let inf = Frac::infinity(); // 1. a == b if and only if partial_cmp(a, b) == Some(Equal). assert_eq!(Ordering::Equal, GenericFraction::cmp(&nan, &nan)); assert_eq!(Ordering::Equal, GenericFraction::cmp(&neg_inf, &neg_inf)); assert_eq!(Ordering::Equal, GenericFraction::cmp(&neg_one, &neg_one)); assert_eq!(Ordering::Equal, GenericFraction::cmp(&zero, &zero)); assert_eq!(Ordering::Equal, GenericFraction::cmp(&one, &one)); assert_eq!(Ordering::Equal, GenericFraction::cmp(&inf, &inf)); // 2. a < b if and only if partial_cmp(a, b) == Some(Less) assert_eq!(Ordering::Less, GenericFraction::cmp(&nan, &neg_inf)); assert_eq!(Ordering::Less, GenericFraction::cmp(&nan, &neg_one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&nan, &zero)); assert_eq!(Ordering::Less, GenericFraction::cmp(&nan, &one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&nan, &inf)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_inf, &neg_one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_inf, &zero)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_inf, &one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_inf, &inf)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_one, &zero)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_one, &one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&neg_one, &inf)); assert_eq!(Ordering::Less, GenericFraction::cmp(&zero, &one)); assert_eq!(Ordering::Less, GenericFraction::cmp(&zero, &inf)); assert_eq!(Ordering::Less, GenericFraction::cmp(&one, &inf)); // 3. a > b if and only if partial_cmp(a, b) == Some(Greater) assert_eq!(Ordering::Greater, GenericFraction::cmp(&neg_inf, &nan)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&neg_one, &neg_inf)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&neg_one, &nan)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&zero, &nan)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&zero, &neg_inf)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&zero, &neg_one)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&one, &nan)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&one, &neg_inf)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&one, &neg_one)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&one, &zero)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&inf, &nan)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&inf, &neg_inf)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&inf, &neg_one)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&inf, &zero)); assert_eq!(Ordering::Greater, GenericFraction::cmp(&inf, &one)); } #[test] fn consistency_eq() { let nan = Frac::nan(); let neg_inf = Frac::neg_infinity(); let neg_one = Frac::one() * -1; let zero = Frac::zero(); let one = Frac::one(); let inf = Frac::infinity(); assert!(GenericFraction::eq(&nan, &nan)); assert!(!GenericFraction::eq(&nan, &neg_inf)); assert!(!GenericFraction::eq(&nan, &neg_one)); assert!(!GenericFraction::eq(&nan, &zero)); assert!(!GenericFraction::eq(&nan, &one)); assert!(!GenericFraction::eq(&nan, &inf)); assert!(GenericFraction::eq(&neg_inf, &neg_inf)); assert!(!GenericFraction::eq(&neg_inf, &neg_one)); assert!(!GenericFraction::eq(&neg_inf, &zero)); assert!(!GenericFraction::eq(&neg_inf, &one)); assert!(!GenericFraction::eq(&neg_inf, &inf)); assert!(GenericFraction::eq(&neg_one, &neg_one)); assert!(!GenericFraction::eq(&neg_one, &zero)); assert!(!GenericFraction::eq(&neg_one, &one)); assert!(!GenericFraction::eq(&neg_one, &inf)); assert!(GenericFraction::eq(&zero, &zero)); assert!(!GenericFraction::eq(&zero, &one)); assert!(!GenericFraction::eq(&zero, &inf)); assert!(GenericFraction::eq(&one, &one)); assert!(!GenericFraction::eq(&one, &inf)); assert!(GenericFraction::eq(&inf, &inf)); assert!(!GenericFraction::ne(&nan, &nan)); assert!(GenericFraction::ne(&nan, &neg_inf)); assert!(GenericFraction::ne(&nan, &neg_one)); assert!(GenericFraction::ne(&nan, &zero)); assert!(GenericFraction::ne(&nan, &one)); assert!(GenericFraction::ne(&nan, &inf)); assert!(!GenericFraction::ne(&neg_inf, &neg_inf)); assert!(GenericFraction::ne(&neg_inf, &neg_one)); assert!(GenericFraction::ne(&neg_inf, &zero)); assert!(GenericFraction::ne(&neg_inf, &one)); assert!(GenericFraction::ne(&neg_inf, &inf)); assert!(!GenericFraction::ne(&neg_one, &neg_one)); assert!(GenericFraction::ne(&neg_one, &zero)); assert!(GenericFraction::ne(&neg_one, &one)); assert!(GenericFraction::ne(&neg_one, &inf)); assert!(!GenericFraction::ne(&zero, &zero)); assert!(GenericFraction::ne(&zero, &one)); assert!(GenericFraction::ne(&zero, &inf)); assert!(!GenericFraction::ne(&one, &one)); assert!(GenericFraction::ne(&one, &inf)); assert!(!GenericFraction::ne(&inf, &inf)); } } fraction-0.15.3/src/fraction/juniper_support.rs000064400000000000000000000171001046102023000177300ustar 00000000000000//! Juniper values conversion for GenericFraction //! The format is case sensitive text representation of the sign and numbers. //! Those are: //! * Special cases: `NaN`, `+inf`, `-inf` //! * Zero: `+0`, `-0` //! * Whole numbers: `+1`, `-2` //! * Fractions: `+1/2`, `-3/4` use super::{GenericFraction, Sign}; use generic::GenericInteger; use juniper::{ParseScalarResult, ParseScalarValue, Value}; impl<__S, T> ::juniper::GraphQLValueAsync<__S> for GenericFraction where Self: Sync, Self::TypeInfo: Sync, Self::Context: Sync, T: Clone + GenericInteger, __S: ::juniper::ScalarValue + Send + Sync, { fn resolve_async<'a>( &'a self, info: &'a Self::TypeInfo, selection_set: Option<&'a [::juniper::Selection<__S>]>, executor: &'a ::juniper::Executor, ) -> ::juniper::BoxFuture<'a, ::juniper::ExecutionResult<__S>> { use juniper::futures::future; let v = ::juniper::GraphQLValue::resolve(self, info, selection_set, executor); Box::pin(future::ready(v)) } } impl ::juniper::marker::IsInputType for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { } impl ::juniper::marker::IsOutputType for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { } impl ::juniper::GraphQLType for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { fn name(_: &Self::TypeInfo) -> Option<&'static str> { Some("Fraction") } fn meta<'r>( info: &Self::TypeInfo, registry: &mut ::juniper::Registry<'r, S>, ) -> ::juniper::meta::MetaType<'r, S> where S: 'r, { registry .build_scalar_type::(info) .description("Fraction") .into_meta() } } impl ::juniper::GraphQLValue for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { type Context = (); type TypeInfo = (); fn type_name<'__i>(&self, info: &'__i Self::TypeInfo) -> Option<&'__i str> { >::name(info) } fn resolve( &self, _info: &(), _selection: Option<&[::juniper::Selection]>, _executor: &::juniper::Executor, ) -> ::juniper::ExecutionResult { Ok(Value::scalar(self.to_string())) } } impl ::juniper::ToInputValue for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { fn to_input_value(&self) -> ::juniper::InputValue { ::juniper::ToInputValue::to_input_value(&format!("{:+}", &self)) } } impl ::juniper::FromInputValue for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { fn from_input_value(value: &::juniper::InputValue) -> Option { { let val = match value.as_string_value() { None => return None, Some(string) => string, }; if val.len() < 2 { None } else if val == "NaN" { Some(GenericFraction::nan()) } else if val == "-inf" { Some(GenericFraction::neg_infinity()) } else if val == "+inf" { Some(GenericFraction::infinity()) } else { let sign = match val.as_bytes()[0] { 43 => Sign::Plus, 45 => Sign::Minus, _ => return None, }; let (denom, split): (T, usize) = if let Some(idx) = val.find('/') { let (_, denom) = val.split_at(idx + 1); match T::from_str_radix(denom, 10) { Ok(val) => (val, idx), Err(_) => return None, } } else { (T::one(), val.len()) }; let (snum, _) = val.split_at(split); let (_, num) = snum.split_at(1); match T::from_str_radix(num, 10) { Ok(num) => match sign { Sign::Plus => Some(GenericFraction::new(num, denom)), Sign::Minus => Some(GenericFraction::new_neg(num, denom)), }, Err(_) => None, } } } } } impl ::juniper::ParseScalarValue for GenericFraction where S: ::juniper::ScalarValue, T: Clone + GenericInteger, { fn from_str(value: ::juniper::parser::ScalarToken<'_>) -> ParseScalarResult<'_, S> { { >::from_str(value) } } } #[cfg(test)] mod tests { use super::*; use juniper::{FromInputValue, InputValue, ToInputValue}; #[cfg(feature = "with-bigint")] use crate::BigFraction; type F = GenericFraction; fn get_tests() -> Vec<(&'static str, F)> { vec![ ("NaN", F::nan()), ("-inf", F::neg_infinity()), ("+inf", F::infinity()), ("+0", F::new(0u8, 1u8)), ("-0", F::new_neg(0u8, 1u8)), ("+2", F::new(2u8, 1u8)), ("-2", F::new_neg(2u8, 1u8)), ("+1/2", F::new(1u8, 2u8)), ("-1/2", F::new_neg(1u8, 2u8)), ("+5/6", F::new(10u8, 12u8)), ("-1/255", F::new_neg(1u8, 255u8)), ] } #[cfg(feature = "with-bigint")] fn get_big_tests() -> Vec<(&'static str, BigFraction)> { use crate::Num; vec![ ("NaN", BigFraction::nan()), ("-inf", BigFraction::neg_infinity()), ("+inf", BigFraction::infinity()), ("+0", BigFraction::new(0u8, 1u8)), ("-0", BigFraction::new_neg(0u8, 1u8)), ("+2", BigFraction::new(2u8, 1u8)), ("-2", BigFraction::new_neg(2u8, 1u8)), ("+1/2", BigFraction::new(1u8, 2u8)), ("-1/2", BigFraction::new_neg(1u8, 2u8)), ("+5/6", BigFraction::new(10u8, 12u8)), ("-1/255", BigFraction::new_neg(1u8, 255u8)), ( "+42090291092428642826240949012091044820/42090291092428642826240949012091044821", BigFraction::from_str_radix( "42090291092428642826240949012091044820/42090291092428642826240949012091044821", 10, ) .unwrap(), ), ] } #[test] fn to_input_value() { for (s, v) in get_tests() { let value = ::to_input_value(&v); let str_value = value.as_scalar_value::(); assert!(str_value.is_some()); assert_eq!(s, str_value.unwrap()); } #[cfg(feature = "with-bigint")] { for (s, v) in get_big_tests() { let value = ::to_input_value(&v); let str_value = value.as_string_value(); assert!(str_value.is_some()); assert_eq!(s, str_value.unwrap()); } } } #[test] fn from_input_value() { for (s, v) in get_tests() { let value = ::from_input_value(&InputValue::scalar(s.to_owned())); assert_eq!(value, Some(v)); } #[cfg(feature = "with-bigint")] { for (s, v) in get_big_tests() { let value = ::from_input_value(&InputValue::scalar(s)); assert_eq!(value, Some(v)); } } } } fraction-0.15.3/src/fraction/mod.rs000064400000000000000000000006101046102023000152350ustar 00000000000000pub use self::generic_fraction::GenericFraction; pub use self::sign::Sign; #[cfg(feature = "with-approx")] pub mod approx; pub mod display; #[cfg(feature = "with-unicode")] pub mod unicode_str_io; #[cfg(feature = "with-juniper-support")] pub mod juniper_support; #[cfg(feature = "with-postgres-support")] pub mod postgres_support; mod generic_fraction; mod ops; mod sign; mod try_from; fraction-0.15.3/src/fraction/ops/add.rs000064400000000000000000000140471046102023000160200ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::Integer; use std::ops::{Add, Sub}; impl Add for GenericFraction where T: Clone + Integer, O: Into>, { type Output = Self; fn add(self, other: O) -> Self { let other = other.into(); match self { GenericFraction::NaN => self, GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Rational(_, _) => self, GenericFraction::Infinity(osign) => { if sign != osign { GenericFraction::NaN } else { self } } }, GenericFraction::Rational(ls, l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => other, GenericFraction::Rational(rs, r) => { if ls == Sign::Plus && rs == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if ls == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if rs == Sign::Plus { if r < l { GenericFraction::Rational(Sign::Minus, l.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l)) } } else { GenericFraction::Rational(Sign::Minus, l.add(r)) } } }, } } } impl<'a, T, O> Add for &'a GenericFraction where T: Clone + Integer, O: Into>, { type Output = GenericFraction; fn add(self, other: O) -> GenericFraction { let other = other.into(); match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Rational(_, _) => self.clone(), GenericFraction::Infinity(osign) => { if sign != osign { GenericFraction::NaN } else { self.clone() } } }, GenericFraction::Rational(ls, ref l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => other, GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if ls == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if rs == Sign::Plus { if r < l { GenericFraction::Rational(Sign::Minus, l.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l)) } } else { GenericFraction::Rational(Sign::Minus, l.add(r)) } } }, } } } impl<'a, T> Add for &'a GenericFraction where T: Clone + Integer, { type Output = GenericFraction; fn add(self, other: Self) -> GenericFraction { match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Rational(_, _) => self.clone(), GenericFraction::Infinity(osign) => { if sign != osign { GenericFraction::NaN } else { self.clone() } } }, GenericFraction::Rational(ls, ref l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => other.clone(), GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if ls == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if rs == Sign::Plus { if r < l { GenericFraction::Rational(Sign::Minus, l.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l)) } } else { GenericFraction::Rational(Sign::Minus, l.add(r)) } } }, } } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::Zero; type F = GenericFraction; #[test] fn add_scalar() { assert_eq!(F::zero() + 1, F::from(1)); assert_eq!(F::zero() + 1.5, F::from(1.5)); assert_eq!(&F::zero() + 1, F::from(1)); assert_eq!(&F::zero() + 1.5, F::from(1.5)); assert_eq!(F::zero() + F::from(1), F::from(1)); assert_eq!(F::zero() + F::from(1.5), F::from(1.5)); assert_eq!(&F::zero() + F::from(1), F::from(1)); assert_eq!(&F::zero() + F::from(1.5), F::from(1.5)); } } fraction-0.15.3/src/fraction/ops/add_assign.rs000064400000000000000000000157751046102023000173750ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio}; use std::mem; use std::ops::{Add, AddAssign, Sub}; impl AddAssign for GenericFraction where T: Clone + Integer, O: Into>, { fn add_assign(&mut self, other: O) { let other = other.into(); *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::Infinity(ls), GenericFraction::Infinity(rs) => { if ls != rs { GenericFraction::NaN } else { GenericFraction::Infinity(ls) } } }, GenericFraction::Rational(ls, ref mut l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => other, GenericFraction::Rational(rs, r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if ls == Sign::Plus && rs == Sign::Plus { GenericFraction::Rational(Sign::Plus, l_.add(r)) } else if ls == Sign::Plus { if l_ < r { GenericFraction::Rational(Sign::Minus, r.sub(l_)) } else { GenericFraction::Rational(Sign::Plus, l_.sub(r)) } } else if rs == Sign::Plus { if r < l_ { GenericFraction::Rational(Sign::Minus, l_.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l_)) } } else { GenericFraction::Rational(Sign::Minus, l_.add(r)) } } }, }; } } impl<'a, T> AddAssign<&'a GenericFraction> for GenericFraction where T: Clone + Integer, { fn add_assign(&mut self, other: &'a Self) { *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::Infinity(ls), GenericFraction::Infinity(rs) => { if ls != rs { GenericFraction::NaN } else { GenericFraction::Infinity(ls) } } }, GenericFraction::Rational(ls, ref mut l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => other.clone(), GenericFraction::Rational(rs, ref r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if ls == Sign::Plus && rs == Sign::Plus { GenericFraction::Rational(Sign::Plus, l_.add(r)) } else if ls == Sign::Plus { if l_ < *r { GenericFraction::Rational(Sign::Minus, r.sub(l_)) } else { GenericFraction::Rational(Sign::Plus, l_.sub(r)) } } else if rs == Sign::Plus { if *r < l_ { GenericFraction::Rational(Sign::Minus, l_.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l_)) } } else { GenericFraction::Rational(Sign::Minus, l_.add(r)) } } }, }; } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn owned() { { let mut v = F::zero(); v += F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v += F::new_neg(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::one(); v += F::new_neg(2, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v += F::new_neg(1, 1); assert_eq!(v, F::new_neg(2, 1)); } { let mut v = F::new_neg(1, 1); v += F::new(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::new_neg(2, 1); v += F::new(1, 1); assert_eq!(v, F::new_neg(1, 1)); } } #[test] fn refs() { { let mut v = F::zero(); v += &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v += &F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v += &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v += &F::new_neg(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::one(); v += &F::new_neg(2, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v += &F::new_neg(1, 1); assert_eq!(v, F::new_neg(2, 1)); } { let mut v = F::new_neg(1, 1); v += &F::new(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::new_neg(2, 1); v += &F::new(1, 1); assert_eq!(v, F::new_neg(1, 1)); } } #[test] fn cast() { { let mut v = F::zero(); v += 1; assert_eq!(v, F::one()); } { let mut v = F::zero(); v += 1; assert_eq!(v, F::one()); } } } fraction-0.15.3/src/fraction/ops/checked_add.rs000064400000000000000000000074041046102023000174650ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{CheckedAdd, CheckedMul, CheckedSub, Integer}; impl CheckedAdd for GenericFraction where T: Clone + Integer + CheckedAdd + CheckedSub + CheckedMul, { fn checked_add(&self, other: &Self) -> Option> { match *self { GenericFraction::NaN => Some(self.clone()), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Rational(_, _) => Some(self.clone()), GenericFraction::Infinity(osign) => { if sign != osign { Some(GenericFraction::NaN) } else { Some(self.clone()) } } }, GenericFraction::Rational(ls, ref l) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(_) => Some(other.clone()), GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { l.checked_add(r) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } else if ls == Sign::Plus { if l < r { r.checked_sub(l) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } else { l.checked_sub(r) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } } else if rs == Sign::Plus { if r < l { l.checked_sub(r) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } else { r.checked_sub(l) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } } else { l.checked_add(r) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } } }, } } } #[cfg(test)] mod tests { use super::{CheckedAdd, GenericFraction}; use crate::{One, Zero}; type F = GenericFraction; #[test] fn checked_add() { assert_eq!(Some(F::nan()), F::nan().checked_add(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_add(&F::nan())); assert_eq!( Some(F::infinity()), F::infinity().checked_add(&F::infinity()) ); assert_eq!( Some(F::nan()), F::infinity().checked_add(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_add(&F::one())); assert_eq!(Some(F::nan()), F::one().checked_add(&F::nan())); assert_eq!(Some(F::infinity()), F::one().checked_add(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::one().checked_add(&F::neg_infinity()) ); assert_eq!(Some(F::new(2, 1)), F::one().checked_add(&F::one())); assert_eq!(Some(F::zero()), F::one().checked_add(&F::new_neg(1, 1))); assert_eq!(Some(F::zero()), F::new_neg(1, 1).checked_add(&F::one())); assert_eq!( Some(F::new_neg(2, 1)), F::new_neg(1, 1).checked_add(&F::new_neg(1, 1)) ); assert_eq!( Some(F::new_neg(1, 1)), F::one().checked_add(&F::new_neg(2, 1)) ); assert_eq!( Some(F::new_neg(1, 1)), F::new_neg(2, 1).checked_add(&F::one()) ); } } fraction-0.15.3/src/fraction/ops/checked_div.rs000064400000000000000000000103341046102023000175130ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{CheckedDiv, CheckedMul, Integer, Ratio, Zero}; impl CheckedDiv for GenericFraction where T: Clone + Integer + CheckedDiv + CheckedMul, { fn checked_div(&self, other: &Self) -> Option> { match *self { GenericFraction::NaN => Some(self.clone()), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(_) => Some(GenericFraction::NaN), GenericFraction::Rational(osign, _) => { Some(GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus })) } }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(_) => { Some(GenericFraction::Rational(Sign::Plus, Ratio::zero())) } GenericFraction::Rational(osign, ref r) => { if l.is_zero() && r.is_zero() { Some(GenericFraction::NaN) } else if r.is_zero() { Some(GenericFraction::Infinity(sign)) } else if l.is_zero() { Some(GenericFraction::Rational(Sign::Plus, l.clone())) } else { l.checked_div(r).map(|value| { GenericFraction::Rational( if sign == osign { Sign::Plus } else { Sign::Minus }, value, ) }) } } }, } } } #[cfg(test)] mod tests { use super::{CheckedDiv, GenericFraction}; use crate::{One, Zero}; type F = GenericFraction; #[test] fn checked_div() { assert_eq!(Some(F::nan()), F::nan().checked_div(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::infinity())); assert_eq!( Some(F::nan()), F::infinity().checked_div(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::one())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_div(&F::new_neg(1, 1)) ); assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::zero())); assert_eq!(Some(F::zero()), F::zero().checked_div(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_div(&F::new_neg(1, 1)) ); assert_eq!(Some(F::nan()), F::one().checked_div(&F::nan())); assert_eq!(Some(F::zero()), F::one().checked_div(&F::infinity())); assert_eq!(Some(F::zero()), F::one().checked_div(&F::neg_infinity())); assert_eq!(Some(F::one()), F::one().checked_div(&F::one())); assert_eq!( Some(F::new_neg(1, 1)), F::one().checked_div(&F::new_neg(1, 1)) ); assert_eq!( Some(F::new_neg(1, 1)), F::new_neg(1, 1).checked_div(&F::one()) ); assert_eq!( Some(F::one()), F::new_neg(1, 1).checked_div(&F::new_neg(1, 1)) ); assert_eq!(Some(F::new(1, 2)), F::one().checked_div(&F::new(2, 1))); assert_eq!( Some(F::new(1, 2)), F::new_neg(1, 1).checked_div(&F::new_neg(2, 1)) ); assert_eq!(Some(F::infinity()), F::one().checked_div(&F::zero())); assert_eq!( Some(F::neg_infinity()), F::new_neg(1, 1).checked_div(&F::zero()) ); assert_eq!(Some(F::zero()), F::zero().checked_div(&F::one())); assert_eq!(Some(F::zero()), F::zero().checked_div(&F::new_neg(1, 1))); assert_eq!(Some(F::nan()), F::zero().checked_div(&F::zero())); } } fraction-0.15.3/src/fraction/ops/checked_mul.rs000064400000000000000000000103041046102023000175230ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{CheckedMul, Integer, Zero}; impl CheckedMul for GenericFraction where T: Clone + Integer + CheckedMul, { fn checked_mul(&self, other: &Self) -> Option> { match *self { GenericFraction::NaN => Some(self.clone()), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(osign) => { Some(GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus })) } GenericFraction::Rational(osign, ref l) => { if l.is_zero() { Some(GenericFraction::NaN) } else { Some(GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus })) } } }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(osign) => { if l.is_zero() { Some(GenericFraction::NaN) } else { Some(GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus })) } } GenericFraction::Rational(osign, ref r) => l.checked_mul(r).map(|value| { GenericFraction::Rational( if l.is_zero() || r.is_zero() || sign == osign { Sign::Plus } else { Sign::Minus }, value, ) }), }, } } } #[cfg(test)] mod tests { use super::{CheckedMul, GenericFraction}; use crate::{One, Zero}; type F = GenericFraction; #[test] fn checked_mul() { assert_eq!(Some(F::nan()), F::nan().checked_mul(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_mul(&F::nan())); assert_eq!( Some(F::infinity()), F::infinity().checked_mul(&F::infinity()) ); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_mul(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_mul(&F::one())); assert_eq!(Some(F::nan()), F::infinity().checked_mul(&F::zero())); assert_eq!(Some(F::nan()), F::zero().checked_mul(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::infinity().checked_mul(&F::new_neg(1, 1)) ); assert_eq!(Some(F::nan()), F::one().checked_mul(&F::nan())); assert_eq!(Some(F::infinity()), F::one().checked_mul(&F::infinity())); assert_eq!( Some(F::neg_infinity()), F::one().checked_mul(&F::neg_infinity()) ); assert_eq!(Some(F::one()), F::one().checked_mul(&F::one())); assert_eq!( Some(F::new_neg(1, 1)), F::one().checked_mul(&F::new_neg(1, 1)) ); assert_eq!( Some(F::new_neg(1, 1)), F::new_neg(1, 1).checked_mul(&F::one()) ); assert_eq!( Some(F::one()), F::new_neg(1, 1).checked_mul(&F::new_neg(1, 1)) ); assert_eq!(Some(F::new(2, 1)), F::one().checked_mul(&F::new(2, 1))); assert_eq!( Some(F::new(2, 1)), F::new_neg(1, 1).checked_mul(&F::new_neg(2, 1)) ); assert_eq!(Some(F::zero()), F::one().checked_mul(&F::zero())); assert_eq!(Some(F::zero()), F::new_neg(1, 1).checked_mul(&F::zero())); assert_eq!(Some(F::zero()), F::zero().checked_mul(&F::one())); assert_eq!(Some(F::zero()), F::zero().checked_mul(&F::new_neg(1, 1))); } } fraction-0.15.3/src/fraction/ops/checked_sub.rs000064400000000000000000000073161046102023000175300ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{CheckedAdd, CheckedMul, CheckedSub, Integer}; impl CheckedSub for GenericFraction where T: Clone + Integer + CheckedAdd + CheckedSub + CheckedMul, { fn checked_sub(&self, other: &Self) -> Option> { match *self { GenericFraction::NaN => Some(self.clone()), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(osign) => { if sign == osign { Some(GenericFraction::NaN) } else { Some(self.clone()) } } GenericFraction::Rational(_, _) => Some(self.clone()), }, GenericFraction::Rational(ls, ref l) => match *other { GenericFraction::NaN => Some(other.clone()), GenericFraction::Infinity(sign) => Some(GenericFraction::Infinity(-sign)), GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { if l < r { r.checked_sub(l) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } else { l.checked_sub(r) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } } else if ls == Sign::Plus { l.checked_add(r) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } else if rs == Sign::Plus { l.checked_add(r) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } else if l > r { l.checked_sub(r) .map(|value| GenericFraction::Rational(Sign::Minus, value)) } else { r.checked_sub(l) .map(|value| GenericFraction::Rational(Sign::Plus, value)) } } }, } } } #[cfg(test)] mod tests { use super::{CheckedSub, GenericFraction}; use crate::{One, Zero}; type F = GenericFraction; #[test] fn checked_sub() { assert_eq!(Some(F::nan()), F::nan().checked_sub(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_sub(&F::nan())); assert_eq!(Some(F::nan()), F::infinity().checked_sub(&F::infinity())); assert_eq!( Some(F::infinity()), F::infinity().checked_sub(&F::neg_infinity()) ); assert_eq!(Some(F::infinity()), F::infinity().checked_sub(&F::one())); assert_eq!(Some(F::nan()), F::one().checked_sub(&F::nan())); assert_eq!( Some(F::neg_infinity()), F::one().checked_sub(&F::infinity()) ); assert_eq!( Some(F::infinity()), F::one().checked_sub(&F::neg_infinity()) ); assert_eq!(Some(F::zero()), F::one().checked_sub(&F::one())); assert_eq!(Some(F::new(2, 1)), F::one().checked_sub(&F::new_neg(1, 1))); assert_eq!( Some(F::new_neg(2, 1)), F::new_neg(1, 1).checked_sub(&F::one()) ); assert_eq!( Some(F::zero()), F::new_neg(1, 1).checked_sub(&F::new_neg(1, 1)) ); assert_eq!(Some(F::new_neg(1, 1)), F::one().checked_sub(&F::new(2, 1))); assert_eq!( Some(F::one()), F::new_neg(1, 1).checked_sub(&F::new_neg(2, 1)) ); } } fraction-0.15.3/src/fraction/ops/div.rs000064400000000000000000000136431046102023000160530ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio, Zero}; use std::ops::Div; impl Div for GenericFraction where T: Clone + Integer, O: Into>, { type Output = Self; fn div(self, other: O) -> Self { let other = other.into(); match self { GenericFraction::NaN => self, GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(osign, _) => { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } }, GenericFraction::Rational(sign, l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } GenericFraction::Rational(osign, r) => { if l.is_zero() && r.is_zero() { GenericFraction::NaN } else if r.is_zero() { GenericFraction::Infinity(sign) } else if l.is_zero() { GenericFraction::Rational(Sign::Plus, l) } else { GenericFraction::Rational( if sign == osign { Sign::Plus } else { Sign::Minus }, l.div(r), ) } } }, } } } impl<'a, T, O> Div for &'a GenericFraction where T: Clone + Integer, O: Into>, { type Output = GenericFraction; fn div(self, other: O) -> GenericFraction { let other = other.into(); match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(osign, _) => { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } }, GenericFraction::Rational(sign, ref l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } GenericFraction::Rational(osign, ref r) => { if l.is_zero() && r.is_zero() { GenericFraction::NaN } else if r.is_zero() { GenericFraction::Infinity(sign) } else if l.is_zero() { GenericFraction::Rational(Sign::Plus, l.clone()) } else { GenericFraction::Rational( if sign == osign { Sign::Plus } else { Sign::Minus }, l.div(r), ) } } }, } } } impl<'a, T> Div for &'a GenericFraction where T: Clone + Integer, { type Output = GenericFraction; fn div(self, other: Self) -> GenericFraction { match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(osign, _) => { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } GenericFraction::Rational(osign, ref r) => { if l.is_zero() && r.is_zero() { GenericFraction::NaN } else if r.is_zero() { GenericFraction::Infinity(sign) } else if l.is_zero() { GenericFraction::Rational(Sign::Plus, l.clone()) } else { GenericFraction::Rational( if sign == osign { Sign::Plus } else { Sign::Minus }, l.div(r), ) } } }, } } } #[cfg(test)] mod tests { use super::GenericFraction; // use crate::{One, Zero}; type F = GenericFraction; #[test] fn div_scalar() { assert_eq!(F::from(3) / 2, F::from(1.5)); assert_eq!(F::from(3) / 1.5, F::from(2)); assert_eq!(&F::from(3) / 2, F::from(1.5)); assert_eq!(&F::from(3) / 1.5, F::from(2)); assert_eq!(F::from(3) / F::from(2), F::from(1.5)); assert_eq!(F::from(3) / F::from(1.5), F::from(2)); assert_eq!(&F::from(3) / &F::from(2), F::from(1.5)); assert_eq!(&F::from(3) / &F::from(1.5), F::from(2)); } } fraction-0.15.3/src/fraction/ops/div_assign.rs000064400000000000000000000171351046102023000174170ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio, Zero}; use std::mem; use std::ops::{Div, DivAssign}; impl DivAssign for GenericFraction where T: Clone + Integer, O: Into>, { fn div_assign(&mut self, other: O) { let other = other.into(); *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(rs, _) => { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } }, GenericFraction::Rational(ls, ref mut l) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } GenericFraction::Rational(rs, r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if l_.is_zero() && r.is_zero() { GenericFraction::NaN } else if r.is_zero() { GenericFraction::Infinity(ls) } else if l_.is_zero() { GenericFraction::Rational(Sign::Plus, l_) } else { GenericFraction::Rational( if ls == rs { Sign::Plus } else { Sign::Minus }, l_.div(r), ) } } }, }; } } impl<'a, T> DivAssign<&'a GenericFraction> for GenericFraction where T: Clone + Integer, { fn div_assign(&mut self, other: &'a Self) { *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(rs, _) => { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } }, GenericFraction::Rational(ls, ref mut l) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } GenericFraction::Rational(rs, ref r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if l_.is_zero() && r.is_zero() { GenericFraction::NaN } else if r.is_zero() { GenericFraction::Infinity(ls) } else if l_.is_zero() { GenericFraction::Rational(Sign::Plus, l_) } else { GenericFraction::Rational( if ls == rs { Sign::Plus } else { Sign::Minus }, l_.div(r), ) } } }, }; } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn owned() { { let mut v = F::nan(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v /= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v /= F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= F::neg_infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= F::new_neg(1, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v /= F::new(2, 1); assert_eq!(v, F::new_neg(1, 2)); } { let mut v = F::new_neg(1, 1); v /= F::new_neg(1, 1); assert_eq!(v, F::one()); } { let mut v = F::new_neg(1, 1); v /= F::new_neg(2, 1); assert_eq!(v, F::new(1, 2)); } { let mut v = F::infinity(); v /= F::zero(); assert_eq!(v, F::infinity()); } { let mut v = F::zero(); v /= F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= F::zero(); assert_eq!(v, F::infinity()); } } #[test] fn refs() { { let mut v = F::nan(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::neg_infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v /= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v /= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v /= &F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &F::neg_infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &F::new_neg(1, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v /= &F::new(2, 1); assert_eq!(v, F::new_neg(1, 2)); } { let mut v = F::new_neg(1, 1); v /= &F::new_neg(1, 1); assert_eq!(v, F::one()); } { let mut v = F::new_neg(1, 1); v /= &F::new_neg(2, 1); assert_eq!(v, F::new(1, 2)); } { let mut v = F::infinity(); v /= &F::zero(); assert_eq!(v, F::infinity()); } { let mut v = F::zero(); v /= &F::infinity(); assert_eq!(v, F::zero()); } { let mut v = F::one(); v /= &F::zero(); assert_eq!(v, F::infinity()); } } #[test] fn cast() { { let mut v = F::from(3); v /= 1.5; assert_eq!(v, F::from(2)); } { let mut v = F::from(3); v /= 2; assert_eq!(v, F::from(1.5)); } } } fraction-0.15.3/src/fraction/ops/mod.rs000064400000000000000000000003011046102023000160330ustar 00000000000000mod add; mod add_assign; mod checked_add; mod checked_div; mod checked_mul; mod checked_sub; mod div; mod div_assign; mod mul; mod mul_assign; mod rem; mod rem_assign; mod sub; mod sub_assign; fraction-0.15.3/src/fraction/ops/mul.rs000064400000000000000000000144501046102023000160630ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Zero}; use std::ops::Mul; impl Mul for GenericFraction where T: Clone + Integer, O: Into>, { type Output = Self; fn mul(self, other: O) -> Self { let other = other.into(); match self { GenericFraction::NaN => self, GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }), GenericFraction::Rational(osign, l) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } }, GenericFraction::Rational(sign, l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } GenericFraction::Rational(osign, r) => { let s = if l.is_zero() || r.is_zero() || sign == osign { Sign::Plus } else { Sign::Minus }; GenericFraction::Rational(s, l.mul(r)) } }, } } } impl<'a, T, O> Mul for &'a GenericFraction where T: Clone + Integer, O: Into>, { type Output = GenericFraction; fn mul(self, other: O) -> GenericFraction { let other = other.into(); match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }), GenericFraction::Rational(osign, ref l) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } }, GenericFraction::Rational(sign, ref l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } GenericFraction::Rational(osign, ref r) => { let s = if l.is_zero() || r.is_zero() || sign == osign { Sign::Plus } else { Sign::Minus }; GenericFraction::Rational(s, l.mul(r)) } }, } } } impl<'a, T> Mul for &'a GenericFraction where T: Clone + Integer, { type Output = GenericFraction; fn mul(self, other: Self) -> GenericFraction { match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(osign) => GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }), GenericFraction::Rational(osign, ref l) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(osign) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if sign == osign { Sign::Plus } else { Sign::Minus }) } } GenericFraction::Rational(osign, ref r) => { let s = if l.is_zero() || r.is_zero() || sign == osign { Sign::Plus } else { Sign::Minus }; GenericFraction::Rational(s, l.mul(r)) } }, } } } #[cfg(test)] mod tests { use super::GenericFraction; type F = GenericFraction; #[test] fn mul_scalar() { assert_eq!(F::from(1.5) * 2, F::from(3)); assert_eq!(F::from(2) * 1.5, F::from(3)); assert_eq!(&F::from(1.5) * 2, F::from(3)); assert_eq!(&F::from(2) * 1.5, F::from(3)); assert_eq!(F::from(1.5) * F::from(2), F::from(3)); assert_eq!(F::from(2) * F::from(1.5), F::from(3)); assert_eq!(&F::from(1.5) * &F::from(2), F::from(3)); assert_eq!(&F::from(2) * &F::from(1.5), F::from(3)); } } fraction-0.15.3/src/fraction/ops/mul_assign.rs000064400000000000000000000171201046102023000174240ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio, Zero}; use std::mem; use std::ops::{Mul, MulAssign}; impl MulAssign for GenericFraction where T: Clone + Integer, O: Into>, { fn mul_assign(&mut self, other: O) { let other = other.into(); *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } GenericFraction::Rational(rs, r) => { if r.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } } }, GenericFraction::Rational(ls, ref mut l) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } } GenericFraction::Rational(rs, r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); let s = if l_.is_zero() || r.is_zero() || ls == rs { Sign::Plus } else { Sign::Minus }; GenericFraction::Rational(s, l_.mul(r)) } }, }; } } impl<'a, T> MulAssign<&'a GenericFraction> for GenericFraction where T: Clone + Integer, { fn mul_assign(&mut self, other: &'a Self) { *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } GenericFraction::Rational(rs, ref r) => { if r.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } } }, GenericFraction::Rational(ls, ref mut l) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { if l.is_zero() { GenericFraction::NaN } else { GenericFraction::Infinity(if ls == rs { Sign::Plus } else { Sign::Minus }) } } GenericFraction::Rational(rs, ref r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); let s = if l_.is_zero() || r.is_zero() || ls == rs { Sign::Plus } else { Sign::Minus }; GenericFraction::Rational(s, l_.mul(r)) } }, }; } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn owned() { { let mut v = F::nan(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v *= F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::infinity(); v *= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v *= F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v *= F::new_neg(1, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v *= F::new(2, 1); assert_eq!(v, F::new_neg(2, 1)); } { let mut v = F::new_neg(1, 1); v *= F::new_neg(1, 1); assert_eq!(v, F::one()); } { let mut v = F::new_neg(1, 1); v *= F::new_neg(2, 1); assert_eq!(v, F::new(2, 1)); } { let mut v = F::infinity(); v *= F::zero(); assert_eq!(v, F::nan()); } { let mut v = F::zero(); v *= F::infinity(); assert_eq!(v, F::nan()); } } #[test] fn refs() { { let mut v = F::nan(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v *= &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v *= &F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::infinity(); v *= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v *= &F::infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v *= &F::neg_infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v *= &F::new_neg(1, 1); assert_eq!(v, F::new_neg(1, 1)); } { let mut v = F::new_neg(1, 1); v *= &F::new(2, 1); assert_eq!(v, F::new_neg(2, 1)); } { let mut v = F::new_neg(1, 1); v *= &F::new_neg(1, 1); assert_eq!(v, F::one()); } { let mut v = F::new_neg(1, 1); v *= &F::new_neg(2, 1); assert_eq!(v, F::new(2, 1)); } { let mut v = F::infinity(); v *= &F::zero(); assert_eq!(v, F::nan()); } { let mut v = F::zero(); v *= &F::infinity(); assert_eq!(v, F::nan()); } } #[test] fn cast() { { let mut v = F::from(1.5); v *= 2; assert_eq!(v, F::from(3)); } { let mut v = F::from(2); v *= 1.5; assert_eq!(v, F::from(3)); } } } fraction-0.15.3/src/fraction/ops/rem.rs000064400000000000000000000101131046102023000160410ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio, Zero}; use std::ops::Rem; impl Rem for GenericFraction where T: Clone + Integer, O: Into>, { type Output = Self; fn rem(self, other: O) -> Self { let other = other.into(); match self { GenericFraction::NaN => self, GenericFraction::Infinity(_) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::NaN, }, GenericFraction::Rational(sign, l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::Rational(sign, l), GenericFraction::Rational(_, r) => { if r.is_zero() { GenericFraction::NaN } else if l == r { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } else { GenericFraction::Rational(sign, l % r) } } }, } } } impl<'a, T, O> Rem for &'a GenericFraction where T: Clone + Integer, O: Into>, { type Output = GenericFraction; fn rem(self, other: O) -> GenericFraction { let other = other.into(); match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(_) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::NaN, }, GenericFraction::Rational(sign, ref l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(_) => GenericFraction::Rational(sign, l.clone()), GenericFraction::Rational(_, ref r) => { if r.is_zero() { GenericFraction::NaN } else if l == r { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } else { GenericFraction::Rational(sign, l.rem(r)) } } }, } } } impl<'a, T> Rem for &'a GenericFraction where T: Clone + Integer, { type Output = GenericFraction; fn rem(self, other: Self) -> GenericFraction { match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(_) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::NaN, }, GenericFraction::Rational(sign, ref l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(_) => GenericFraction::Rational(sign, l.clone()), GenericFraction::Rational(_, ref r) => { if r.is_zero() { GenericFraction::NaN } else if l == r { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } else { GenericFraction::Rational(sign, l.rem(r)) } } }, } } } #[cfg(test)] mod tests { use super::GenericFraction; type F = GenericFraction; #[test] fn mul_scalar() { assert_eq!(F::from(3) % 2, F::from(1)); assert_eq!(F::from(3) % 1.5, F::from(0)); assert_eq!(&F::from(3) % 2, F::from(1)); assert_eq!(&F::from(3) % 1.5, F::from(0)); assert_eq!(F::from(3) % F::from(2), F::from(1)); assert_eq!(F::from(3) % F::from(1.5), F::from(0)); assert_eq!(&F::from(3) % &F::from(2), F::from(1)); assert_eq!(&F::from(3) % &F::from(1.5), F::from(0)); } } fraction-0.15.3/src/fraction/ops/rem_assign.rs000064400000000000000000000103501046102023000174100ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio, Zero}; use std::mem; use std::ops::{Rem, RemAssign}; impl RemAssign for GenericFraction where T: Clone + Integer, O: Into>, { fn rem_assign(&mut self, other: O) { let other = other.into(); *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::NaN, }, GenericFraction::Rational(ls, ref mut l) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::Rational( ls, mem::replace(l, Ratio::new_raw(T::zero(), T::zero())), ), GenericFraction::Rational(_, r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if r.is_zero() { GenericFraction::NaN } else if l_ == r { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } else { GenericFraction::Rational(ls, l_.rem(r)) } } }, }; } } impl<'a, T> RemAssign<&'a GenericFraction> for GenericFraction where T: Clone + Integer, { fn rem_assign(&mut self, other: &'a Self) { *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::NaN, GenericFraction::Rational(_, _) => GenericFraction::NaN, }, GenericFraction::Rational(ls, ref mut l) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(_) => GenericFraction::Rational( ls, mem::replace(l, Ratio::new_raw(T::zero(), T::zero())), ), GenericFraction::Rational(_, ref r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if r.is_zero() { GenericFraction::NaN } else if l_ == *r { GenericFraction::Rational(Sign::Plus, Ratio::zero()) } else { GenericFraction::Rational(ls, l_.rem(r)) } } }, }; } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn owned() { { let mut v = F::infinity(); v %= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= F::one(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v %= F::infinity(); assert_eq!(v, F::one()); } } #[test] fn refs() { { let mut v = F::infinity(); v %= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v %= &F::one(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v %= &F::infinity(); assert_eq!(v, F::one()); } } #[test] fn cast() { { let mut v = F::from(4); v %= 1.5; assert_eq!(v, F::one()); } { let mut v = F::from(4); v %= 2; assert_eq!(v, F::zero()); } } } fraction-0.15.3/src/fraction/ops/sub.rs000064400000000000000000000136661046102023000160670ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::Integer; use std::ops::{Add, Sub}; impl Sub for GenericFraction where T: Clone + Integer, O: Into>, { type Output = Self; fn sub(self, other: O) -> Self { let other = other.into(); match self { GenericFraction::NaN => self, GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => { if sign == osign { GenericFraction::NaN } else { self } } GenericFraction::Rational(_, _) => self, }, GenericFraction::Rational(ls, l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(sign) => GenericFraction::Infinity(-sign), GenericFraction::Rational(rs, r) => { if ls == Sign::Plus && rs == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if ls == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if rs == Sign::Plus { GenericFraction::Rational(Sign::Minus, l.add(r)) } else if l < r { GenericFraction::Rational(Sign::Plus, r.sub(l)) } else { GenericFraction::Rational(Sign::Minus, l.sub(r)) } } }, } } } impl<'a, T, O> Sub for &'a GenericFraction where T: Clone + Integer, O: Into>, { type Output = GenericFraction; fn sub(self, other: O) -> GenericFraction { let other = other.into(); match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(osign) => { if sign == osign { GenericFraction::NaN } else { self.clone() } } GenericFraction::Rational(_, _) => self.clone(), }, GenericFraction::Rational(ls, ref l) => match other { GenericFraction::NaN => other, GenericFraction::Infinity(sign) => GenericFraction::Infinity(-sign), GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if ls == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if rs == Sign::Plus { GenericFraction::Rational(Sign::Minus, l.add(r)) } else if l < r { GenericFraction::Rational(Sign::Plus, r.sub(l)) } else { GenericFraction::Rational(Sign::Minus, l.sub(r)) } } }, } } } impl<'a, T> Sub for &'a GenericFraction where T: Clone + Integer, { type Output = GenericFraction; fn sub(self, other: Self) -> GenericFraction { match *self { GenericFraction::NaN => self.clone(), GenericFraction::Infinity(sign) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(osign) => { if sign == osign { GenericFraction::NaN } else { self.clone() } } GenericFraction::Rational(_, _) => self.clone(), }, GenericFraction::Rational(ls, ref l) => match *other { GenericFraction::NaN => other.clone(), GenericFraction::Infinity(sign) => GenericFraction::Infinity(-sign), GenericFraction::Rational(rs, ref r) => { if ls == Sign::Plus && rs == Sign::Plus { if l < r { GenericFraction::Rational(Sign::Minus, r.sub(l)) } else { GenericFraction::Rational(Sign::Plus, l.sub(r)) } } else if ls == Sign::Plus { GenericFraction::Rational(Sign::Plus, l.add(r)) } else if rs == Sign::Plus { GenericFraction::Rational(Sign::Minus, l.add(r)) } else if l < r { GenericFraction::Rational(Sign::Plus, r.sub(l)) } else { GenericFraction::Rational(Sign::Minus, l.sub(r)) } } }, } } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn sub_scalar() { assert_eq!(F::one() - 1, F::zero()); assert_eq!(F::one() - 0.5, F::from(0.5)); assert_eq!(&F::one() - 1, F::zero()); assert_eq!(&F::one() - 0.5, F::from(0.5)); assert_eq!(F::one() - F::from(1), F::zero()); assert_eq!(F::one() - F::from(0.5), F::from(0.5)); assert_eq!(&F::one() - &F::from(1), F::zero()); assert_eq!(&F::one() - &F::from(0.5), F::from(0.5)); } } fraction-0.15.3/src/fraction/ops/sub_assign.rs000064400000000000000000000166311046102023000174260ustar 00000000000000use crate::fraction::{GenericFraction, Sign}; use crate::{Integer, Ratio}; use std::mem; use std::ops::{Add, Sub, SubAssign}; impl SubAssign for GenericFraction where T: Clone + Integer, O: Into>, { fn sub_assign(&mut self, other: O) { let other = other.into(); *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { if ls == rs { GenericFraction::NaN } else { GenericFraction::Infinity(ls) } } GenericFraction::Rational(_, _) => GenericFraction::Infinity(ls), }, GenericFraction::Rational(ls, ref mut l) => match other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(s) => GenericFraction::Infinity(-s), GenericFraction::Rational(rs, r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if ls == Sign::Plus && rs == Sign::Plus { if r > l_ { GenericFraction::Rational(Sign::Minus, r.sub(l_)) } else { GenericFraction::Rational(Sign::Plus, l_.sub(r)) } } else if ls == Sign::Plus { GenericFraction::Rational(Sign::Plus, l_.add(r)) } else if rs == Sign::Plus { GenericFraction::Rational(Sign::Minus, l_.add(r)) } else if l_ > r { GenericFraction::Rational(Sign::Minus, l_.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l_)) } } }, }; } } impl<'a, T> SubAssign<&'a GenericFraction> for GenericFraction where T: Clone + Integer, { fn sub_assign(&mut self, other: &'a Self) { *self = match *self { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(ls) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(rs) => { if ls == rs { GenericFraction::NaN } else { GenericFraction::Infinity(ls) } } GenericFraction::Rational(_, _) => GenericFraction::Infinity(ls), }, GenericFraction::Rational(ls, ref mut l) => match *other { GenericFraction::NaN => GenericFraction::NaN, GenericFraction::Infinity(s) => GenericFraction::Infinity(-s), GenericFraction::Rational(rs, ref r) => { let l_ = mem::replace(l, Ratio::new_raw(T::zero(), T::zero())); if ls == Sign::Plus && rs == Sign::Plus { if l_ < *r { GenericFraction::Rational(Sign::Minus, r.sub(l_)) } else { GenericFraction::Rational(Sign::Plus, l_.sub(r)) } } else if ls == Sign::Plus { GenericFraction::Rational(Sign::Plus, l_.add(r)) } else if rs == Sign::Plus { GenericFraction::Rational(Sign::Minus, l_.add(r)) } else if l_ > *r { GenericFraction::Rational(Sign::Minus, l_.sub(r)) } else { GenericFraction::Rational(Sign::Plus, r.sub(l_)) } } }, }; } } #[cfg(test)] mod tests { use super::GenericFraction; use crate::{One, Zero}; type F = GenericFraction; #[test] fn owned() { { let mut v = F::nan(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v -= F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v -= F::infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v -= F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= F::new_neg(1, 1); assert_eq!(v, F::new(2, 1)); } { let mut v = F::new_neg(1, 1); v -= F::new(2, 1); assert_eq!(v, F::new_neg(3, 1)); } { let mut v = F::new_neg(1, 1); v -= F::new_neg(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::new_neg(1, 1); v -= F::new_neg(2, 1); assert_eq!(v, F::one()); } } #[test] fn refs() { { let mut v = F::nan(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::infinity(); assert_eq!(v, F::nan()); } { let mut v = F::infinity(); v -= &F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::infinity(); v -= &F::one(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= &F::nan(); assert_eq!(v, F::nan()); } { let mut v = F::one(); v -= &F::infinity(); assert_eq!(v, F::neg_infinity()); } { let mut v = F::one(); v -= &F::neg_infinity(); assert_eq!(v, F::infinity()); } { let mut v = F::one(); v -= &F::new_neg(1, 1); assert_eq!(v, F::new(2, 1)); } { let mut v = F::new_neg(1, 1); v -= &F::new(2, 1); assert_eq!(v, F::new_neg(3, 1)); } { let mut v = F::new_neg(1, 1); v -= &F::new_neg(1, 1); assert_eq!(v, F::zero()); } { let mut v = F::new_neg(1, 1); v -= &F::new_neg(2, 1); assert_eq!(v, F::one()); } } #[test] fn cast() { { let mut v = F::one(); v -= 1; assert_eq!(v, F::zero()); } { let mut v = F::one(); v -= 1; assert_eq!(v, F::zero()); } } } fraction-0.15.3/src/fraction/postgres_support.rs000064400000000000000000000443431046102023000201330ustar 00000000000000extern crate byteorder; extern crate bytes; use self::byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use self::bytes::{BufMut, BytesMut}; use postgres_types::{FromSql, IsNull, ToSql, Type}; use crate::{GenericFraction, Sign, Zero}; use division::{divide_integral, divide_rem}; use generic::GenericInteger; use std::error::Error; use std::fmt; use std::mem; const PG_POS: u16 = 0x0000; const PG_NEG: u16 = 0x4000; const PG_NAN: u16 = 0xC000; const PG_NBASE_U: u16 = 10000; const PG_NBASE_I: i16 = 10000; pub const PG_MAX_PRECISION: usize = 16383; #[inline] pub fn read_i16(mut buf: &[u8]) -> Result> { match buf.read_i16::() { Ok(n) => Ok(n), Err(e) => Err(e.into()), } } #[inline] pub fn read_u16(mut buf: &[u8]) -> Result> { match buf.read_u16::() { Ok(n) => Ok(n), Err(e) => Err(e.into()), } } #[inline] pub fn write_i16(mut buf: &mut [u8], value: i16) -> Result<(), Box> { match buf.write_i16::(value) { Ok(()) => Ok(()), Err(e) => Err(e.into()), } } #[inline] pub fn write_u16(mut buf: &mut [u8], value: u16) -> Result<(), Box> { match buf.write_u16::(value) { Ok(()) => Ok(()), Err(e) => Err(e.into()), } } impl<'a, T> FromSql<'a> for GenericFraction where T: Clone + GenericInteger + From, { fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { if raw.len() < 8 { return Err("unexpected data package from the database".into()); } let sign: u16 = read_u16(&raw[4..6])?; let sign = match sign { PG_NEG => Sign::Minus, PG_POS => Sign::Plus, PG_NAN => return Ok(Self::nan()), _ => return Err("unexpected sign byte in the data package".into()), }; let ndigits: i16 = read_i16(&raw[0..2])?; if ndigits <= 0 { return Ok(match sign { Sign::Minus => Self::neg_zero(), Sign::Plus => Self::zero(), }); } // safe to transmute as it is > 0 let ndigits: usize = unsafe { mem::transmute::(ndigits) }.into(); if (raw.len() - 8) / 2 < ndigits { return Err("data package declares more digits than received".into()); } let weight: i16 = read_i16(&raw[2..4])?; let uweight: usize = if weight <= 0 { 0 } else { // safe to transmute as it is > 0 unsafe { mem::transmute::(weight) }.into() }; let mut num: T = 0u16.into(); let mut den: T = 1u16.into(); let mut exp: T = 1u16.into(); let nbase: T = PG_NBASE_U.into(); let overflow_message = "integer overflow during unpacking the database value (try to use bigger integer as the base for Fraction, or you may even try BigFraction to use heap memory)"; if weight < 0 { for _ in weight..0 { den = match den.checked_mul(&nbase) { Some(n) => n, None => return Err(overflow_message.into()), }; } } else { for _ in 0..weight { exp = match exp.checked_mul(&nbase) { Some(n) => n, None => return Err(overflow_message.into()), }; } } for iteration in 0..ndigits { // TODO: check if we could do GCD it within the loop let i = 8 + iteration * 2; let digits: i16 = read_i16(&raw[i..i + 2])?; let mut digits = if digits < 0 { return Err("database sent unexpected negative value".into()); } else { unsafe { mem::transmute::(digits) } }; /* Digit x000 */ let digit: u16 = digits / 1000 * 1000; digits -= digit; let d: T = digit.into(); let d = match d.checked_mul(&exp) { Some(n) => n, None => return Err(overflow_message.into()), }; num = match num.checked_add(&d) { Some(n) => n, None => return Err(overflow_message.into()), }; /* Digit 0x00 */ let digit: u16 = digits / 100 * 100; digits -= digit; let d: T = digit.into(); let d = match d.checked_mul(&exp) { Some(n) => n, None => return Err(overflow_message.into()), }; num = match num.checked_add(&d) { Some(n) => n, None => return Err(overflow_message.into()), }; /* Digit 00x0 */ let digit: u16 = digits / 10 * 10; digits -= digit; let d: T = digit.into(); let d = match d.checked_mul(&exp) { Some(n) => n, None => return Err(overflow_message.into()), }; num = match num.checked_add(&d) { Some(n) => n, None => return Err(overflow_message.into()), }; /* Digit 000x */ let d: T = digits.into(); let d = match d.checked_mul(&exp) { Some(n) => n, None => return Err(overflow_message.into()), }; num = match num.checked_add(&d) { Some(n) => n, None => return Err(overflow_message.into()), }; /* maintain the exponential growth */ if iteration >= uweight { // after the decimal point num = match num.checked_mul(&nbase) { Some(n) => n, None => return Err(overflow_message.into()), }; den = match den.checked_mul(&nbase) { Some(n) => n, None => return Err(overflow_message.into()), }; } else if uweight > 0 { // before the decimal point exp = match exp.checked_div(&nbase) { Some(n) => n, None => return Err(overflow_message.into()), }; } } Ok(match sign { Sign::Plus => Self::new(num, den), Sign::Minus => Self::new_neg(num, den), }) } accepts!(NUMERIC); } impl ToSql for GenericFraction where T: Clone + GenericInteger + From + fmt::Debug, { fn to_sql( &self, ty: &Type, buf: &mut BytesMut, ) -> Result> { fraction_to_sql_buf(self, ty, buf, PG_MAX_PRECISION) } accepts!(NUMERIC); to_sql_checked!(); } pub fn fraction_to_sql_buf( source: &GenericFraction, _ty: &Type, buf: &mut BytesMut, precision: usize, ) -> Result> where T: Clone + GenericInteger + From, { let precision = if precision <= PG_MAX_PRECISION { precision } else { PG_MAX_PRECISION }; let buffer_offset: usize = buf.len(); buf.put_u64(0); // fill in the first 8 bytes if source.is_zero() { return Ok(IsNull::No); } if source.is_nan() { write_u16(&mut buf[buffer_offset + 4..buffer_offset + 6], PG_NAN)?; return Ok(IsNull::No); } let numer: T = if let Some(n) = source.numer() { n.clone() } else { unreachable!(); }; let denom: T = if let Some(d) = source.denom() { d.clone() } else { unreachable!(); }; let mut ndigits: i16 = 0; let mut weight: i16 = -1; let mut scale: i16 = 0; let mut uscale: usize = 0; let mut ndigit: i16 = 0; let mut nptr: u32 = 0; let mut padding = true; let mut rpad = 0; let div_state = divide_integral(numer, denom, |digit: u8| { if padding && digit == 0 { return Ok(true); } else { padding = false; } let digit: i16 = digit.into(); ndigit *= 10; ndigit += digit; nptr += 1; if nptr > 3 { nptr = 0; weight += 1; if ndigit == 0 { rpad += 1; } else { rpad = 0; } ndigits += 1; buf.put_i16(ndigit); ndigit = 0; } Ok(true) })?; if nptr != 0 { let shift = 4 - nptr; ndigits += 1; weight += 1; nptr = 0; let digits = (buf.len() - buffer_offset - 8) / 2; let mut rem_: i16 = 0; for i in 0..digits { let pos = buffer_offset + 8 + i * 2; let mut digit = read_i16(&buf[pos..pos + 2])?; let mut tmp_rem: i16 = (digit - (digit / 10 * 10)) * 1000; digit /= 10; if shift > 1 { tmp_rem /= 10; tmp_rem += (digit - (digit / 10 * 10)) * 1000; digit /= 10; if shift > 2 { tmp_rem /= 10; tmp_rem += (digit - (digit / 10 * 10)) * 1000; digit /= 10; } } digit += rem_; rem_ = tmp_rem; write_i16(&mut buf[pos..pos + 2], digit)?; } ndigit += rem_; if ndigit == 0 { rpad += 1; } else { rpad = 0; } buf.put_i16(ndigit); ndigit = 0; } if div_state.remainder.is_zero() && rpad > 0 { ndigits -= rpad; buf.truncate(buffer_offset + 8 + (ndigits as usize) * 2); } if !div_state.remainder.is_zero() { padding = weight < 0; // true; divide_rem( div_state.remainder, div_state.divisor, |state, digit: u8| { let digit: i16 = digit.into(); if digit != 0 { if padding && weight > 0 { ndigits += weight; for _ in 0..weight { buf.put_i16(ndigit); } } padding = false; } nptr += 1; ndigit += digit * (PG_NBASE_I / 10i16.pow(nptr)); scale += 1; uscale += 1; if nptr > 3 { if padding && weight < 0 { weight -= 1; } else { ndigits += 1; buf.put_i16(ndigit); } nptr = 0; ndigit = 0; } Ok(if uscale < precision { Ok(state) } else { Err(state) }) }, )?; if nptr != 0 && !padding { ndigits += 1; buf.put_i16(ndigit); } } write_i16(&mut buf[buffer_offset..buffer_offset + 2], ndigits)?; write_i16(&mut buf[buffer_offset + 2..buffer_offset + 4], weight)?; write_u16( &mut buf[buffer_offset + 4..buffer_offset + 6], match source.sign() { Some(Sign::Minus) => PG_NEG, _ => PG_POS, }, )?; write_i16(&mut buf[buffer_offset + 6..buffer_offset + 8], scale)?; Ok(IsNull::No) } #[cfg(test)] mod tests { use super::*; type Fraction = GenericFraction; const NUMERIC_OID: u32 = 1700; fn get_tests() -> Vec<(Fraction, &'static [u8])> { vec![ ( Fraction::new_raw_signed(Sign::Minus, 12345678901234u128, 1u128), &[0, 4, 0, 3, 64, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210], ), ( Fraction::new_raw_signed(Sign::Minus, 12345678u128, 1u128), &[0, 2, 0, 1, 64, 0, 0, 0, 4, 210, 22, 46], ), ( Fraction::new_raw_signed(Sign::Minus, 10000000000000000001u128, 10000000000u128), &[ 0, 6, 0, 2, 64, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, ], ), ( Fraction::new_raw_signed(Sign::Minus, 1000000000u128, 1u128), &[0, 1, 0, 2, 64, 0, 0, 0, 0, 10], ), ( Fraction::new_raw_signed(Sign::Minus, 1234u128, 1u128), &[0, 1, 0, 0, 64, 0, 0, 0, 4, 210], ), ( Fraction::new_raw_signed(Sign::Minus, 256u128, 1u128), &[0, 1, 0, 0, 64, 0, 0, 0, 1, 0], ), ( Fraction::new_raw_signed(Sign::Minus, 42u128, 1u128), &[0, 1, 0, 0, 64, 0, 0, 0, 0, 42], ), ( Fraction::new_raw_signed(Sign::Minus, 1u128, 1u128), &[0, 1, 0, 0, 64, 0, 0, 0, 0, 1], ), ( Fraction::new_raw_signed(Sign::Minus, 1u128, 2u128), &[0, 1, 255, 255, 64, 0, 0, 1, 19, 136], ), ( Fraction::new_raw_signed(Sign::Minus, 1u128, 10u128), &[0, 1, 255, 255, 64, 0, 0, 1, 3, 232], ), ( Fraction::new_raw_signed(Sign::Minus, 66u128, 100u128), &[0, 1, 255, 255, 64, 0, 0, 2, 25, 200], ), ( Fraction::new_raw_signed(Sign::Minus, 6172839450617u128, 50000000000000u128), &[0, 4, 255, 255, 64, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72], ), ( Fraction::new_raw_signed(Sign::Minus, 1u128, 100u128), &[0, 1, 255, 255, 64, 0, 0, 2, 0, 100], ), ( Fraction::new_raw_signed(Sign::Minus, 601u128, 2500u128), &[0, 1, 255, 255, 64, 0, 0, 4, 9, 100], ), ( Fraction::new_raw_signed(Sign::Minus, 1u128, 1000000000u128), &[0, 1, 255, 253, 64, 0, 0, 9, 3, 232], ), ( Fraction::new_raw_signed( Sign::Minus, 617283945061706172839450617u128, 50000000000000u128, ), &[ 0, 8, 0, 3, 64, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35, 52, 13, 72, ], ), (Fraction::zero(), &[0, 0, 0, 0, 0, 0, 0, 0]), (Fraction::nan(), &[0, 0, 0, 0, 192, 0, 0, 0]), ( Fraction::new_raw(617283945061706172839450617u128, 50000000000000u128), &[ 0, 8, 0, 3, 0, 0, 0, 14, 0, 12, 13, 128, 30, 210, 4, 210, 4, 210, 22, 46, 35, 52, 13, 72, ], ), ( Fraction::new_raw(1u128, 1000000000u128), &[0, 1, 255, 253, 0, 0, 0, 9, 3, 232], ), ( Fraction::new_raw(601u128, 2500u128), &[0, 1, 255, 255, 0, 0, 0, 4, 9, 100], ), ( Fraction::new_raw(1u128, 100u128), &[0, 1, 255, 255, 0, 0, 0, 2, 0, 100], ), ( Fraction::new_raw(66u128, 100u128), &[0, 1, 255, 255, 0, 0, 0, 2, 25, 200], ), ( Fraction::new_raw(10000000000000000000001u128, 10000000000000000u128), &[ 0, 6, 0, 1, 0, 0, 0, 16, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ], ), ( Fraction::new_raw(6172839450617u128, 50000000000000u128), &[0, 4, 255, 255, 0, 0, 0, 14, 4, 210, 22, 46, 35, 52, 13, 72], ), ( Fraction::new_raw(1u128, 10u128), &[0, 1, 255, 255, 0, 0, 0, 1, 3, 232], ), ( Fraction::new_raw(1u128, 2u128), &[0, 1, 255, 255, 0, 0, 0, 1, 19, 136], ), ( Fraction::new_raw(1u128, 1u128), &[0, 1, 0, 0, 0, 0, 0, 0, 0, 1], ), ( Fraction::new_raw(42u128, 1u128), &[0, 1, 0, 0, 0, 0, 0, 0, 0, 42], ), ( Fraction::new_raw(256u128, 1u128), &[0, 1, 0, 0, 0, 0, 0, 0, 1, 0], ), ( Fraction::new_raw(1234u128, 1u128), &[0, 1, 0, 0, 0, 0, 0, 0, 4, 210], ), ( Fraction::new_raw(1000000000u128, 1u128), &[0, 1, 0, 2, 0, 0, 0, 0, 0, 10], ), ( Fraction::new_raw(10000000000000000001u128, 10000000000u128), &[ 0, 6, 0, 2, 0, 0, 0, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, ], ), ( Fraction::new_raw(12345678u128, 1u128), &[0, 2, 0, 1, 0, 0, 0, 0, 4, 210, 22, 46], ), ( Fraction::new_raw(12345678901234u128, 1u128), &[0, 4, 0, 3, 0, 0, 0, 0, 0, 12, 13, 128, 30, 210, 4, 210], ), ( Fraction::new_raw(33333333333333333333u128, 100000000000000000000u128), &[ 0, 5, 255, 255, 0, 0, 0, 20, 13, 5, 13, 5, 13, 5, 13, 5, 13, 5, ], ), ] } #[test] fn test_from_sql() { let t = Type::from_oid(NUMERIC_OID).unwrap(); for ref test in &get_tests() { assert_eq!( test.0, ::from_sql(&t, test.1).ok().unwrap() ) } } #[test] fn test_to_sql() { let t = Type::from_oid(NUMERIC_OID).unwrap(); let mut buf = BytesMut::with_capacity(1024); for ref test in &get_tests() { buf.clear(); let res = ::to_sql(&test.0, &t, &mut buf) .ok() .unwrap(); match res { IsNull::Yes => assert!(false), IsNull::No => assert!(true), }; assert_eq!(&buf, &test.1); } } } fraction-0.15.3/src/fraction/sign.rs000064400000000000000000000033731046102023000154270ustar 00000000000000use std::{ cmp::Ordering, fmt, ops::{Mul, Neg}, }; use crate::display; /// Sign representation /// /// Fractions keep the sign represented by this enum, /// so that we can use unsigned ints as base data types. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "with-serde-support", derive(Serialize, Deserialize))] pub enum Sign { Plus, Minus, } impl Sign { pub fn is_positive(self) -> bool { matches!(self, Sign::Plus) } pub fn is_negative(self) -> bool { matches!(self, Sign::Minus) } } impl Mul for Sign { type Output = Self; fn mul(self, oth: Sign) -> Self::Output { if self == oth { Sign::Plus } else { Sign::Minus } } } impl Neg for Sign { type Output = Self; fn neg(self) -> Sign { match self { Sign::Plus => Sign::Minus, Sign::Minus => Sign::Plus, } } } impl PartialOrd for Sign { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Sign { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (Sign::Minus, Sign::Minus) => Ordering::Equal, (Sign::Plus, Sign::Minus) => Ordering::Greater, (Sign::Minus, Sign::Plus) => Ordering::Less, (Sign::Plus, Sign::Plus) => Ordering::Equal, } } } impl fmt::Display for Sign { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let format = display::Format::new(f); display::format_sign(*self, f, &format) } } impl From for char { fn from(sign: Sign) -> char { match sign { Sign::Plus => '+', Sign::Minus => '-', } } } fraction-0.15.3/src/fraction/try_from.rs000064400000000000000000000762121046102023000163320ustar 00000000000000use num::ToPrimitive; use crate::{generic::GenericInteger, GenericFraction}; use std::convert::TryFrom; macro_rules! generic_fraction_into_primitive { ( $( $(#[$cfg:meta])* fn $method:ident -> $IntoT:ident ; )*) => {$( $(#[$cfg])* impl TryFrom> for $IntoT where T: Clone + GenericInteger, { type Error = (); fn try_from(value: GenericFraction) -> Result { value.$method().ok_or(()) } } )*} } generic_fraction_into_primitive!( fn to_u8 -> u8 ; fn to_i8 -> i8 ; fn to_u16 -> u16 ; fn to_i16 -> i16 ; fn to_u32 -> u32 ; fn to_i32 -> i32 ; fn to_u64 -> u64 ; fn to_i64 -> i64 ; fn to_u128 -> u128 ; fn to_i128 -> i128 ; fn to_usize -> usize ; fn to_isize -> isize ; fn to_f32 -> f32 ; fn to_f64 -> f64 ; ); #[cfg(feature = "with-bigint")] pub mod with_bigint { use num::BigInt; use crate::{ generic::{read_generic_integer, GenericInteger}, BigUint, GenericFraction, Sign, }; use std::convert::TryFrom; impl TryFrom> for BigUint where T: Clone + GenericInteger, { type Error = (); fn try_from(value: GenericFraction) -> Result { match value { GenericFraction::NaN => Err(()), GenericFraction::Infinity(_) => Err(()), GenericFraction::Rational(Sign::Plus, ref r) if *r.denom() == T::one() => { if let Some((Sign::Plus, v)) = read_generic_integer(r.numer().clone()) { Ok(v) } else { Err(()) } } _ => Err(()), } } } impl TryFrom> for BigInt where T: Clone + GenericInteger, { type Error = (); fn try_from(value: GenericFraction) -> Result { match value { GenericFraction::NaN => Err(()), GenericFraction::Infinity(_) => Err(()), GenericFraction::Rational(sign, ref r) if *r.denom() == T::one() => { if let Some((s, v)) = read_generic_integer(r.numer().clone()) { // Numerator must always be positive, but let's do this for hygiene let sign = sign * s; let result = if sign.is_negative() { v * -1 } else { v }; Ok(result) } else { Err(()) } } _ => Err(()), } } } } #[cfg(test)] mod test { use crate::prelude::Fraction; use num::{One, Zero}; use std::convert::TryInto; #[test] fn fraction_to_i() { let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into(), Ok(-1i8)); assert_eq!(n_one.try_into(), Ok(-1i16)); assert_eq!(n_one.try_into(), Ok(-1i32)); assert_eq!(n_one.try_into(), Ok(-1i64)); assert_eq!(n_one.try_into(), Ok(-1i128)); assert_eq!(n_one.try_into(), Ok(-1isize)); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(0i8)); assert_eq!(zero.try_into(), Ok(0i16)); assert_eq!(zero.try_into(), Ok(0i32)); assert_eq!(zero.try_into(), Ok(0i64)); assert_eq!(zero.try_into(), Ok(0i128)); assert_eq!(zero.try_into(), Ok(0isize)); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(1i8)); assert_eq!(p_one.try_into(), Ok(1i16)); assert_eq!(p_one.try_into(), Ok(1i32)); assert_eq!(p_one.try_into(), Ok(1i64)); assert_eq!(p_one.try_into(), Ok(1i128)); assert_eq!(p_one.try_into(), Ok(1isize)); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[test] fn fraction_to_u() { let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(0u8)); assert_eq!(zero.try_into(), Ok(0u16)); assert_eq!(zero.try_into(), Ok(0u32)); assert_eq!(zero.try_into(), Ok(0u64)); assert_eq!(zero.try_into(), Ok(0u128)); assert_eq!(zero.try_into(), Ok(0usize)); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(1u8)); assert_eq!(p_one.try_into(), Ok(1u16)); assert_eq!(p_one.try_into(), Ok(1u32)); assert_eq!(p_one.try_into(), Ok(1u64)); assert_eq!(p_one.try_into(), Ok(1u128)); assert_eq!(p_one.try_into(), Ok(1usize)); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[test] fn fraction_to_f32() { let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[test] fn fraction_to_f64() { let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn fraction_to_bigint() { use crate::BigInt; let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into(), Ok(BigInt::one() * -1)); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(BigInt::zero())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(BigInt::one())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn fraction_to_biguint() { use crate::BigUint; let n_inf = Fraction::neg_infinity(); let n_one = Fraction::one() * -1; let n_half = n_one / 2; let zero = Fraction::zero(); let p_one = Fraction::one(); let p_half = p_one / 2; let p_inf = Fraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.try_into() as Result, Err(())); assert_eq!(n_one.try_into() as Result, Err(())); assert_eq!(n_half.try_into() as Result, Err(())); assert_eq!(zero.try_into(), Ok(BigUint::zero())); assert_eq!(p_half.try_into() as Result, Err(())); assert_eq!(p_one.try_into(), Ok(BigUint::one())); assert_eq!(p_inf.try_into() as Result, Err(())); assert_eq!(nan.try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigfraction_to_i() { use crate::BigFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into(), Ok(-1i8)); assert_eq!(n_one.clone().try_into(), Ok(-1i16)); assert_eq!(n_one.clone().try_into(), Ok(-1i32)); assert_eq!(n_one.clone().try_into(), Ok(-1i64)); assert_eq!(n_one.clone().try_into(), Ok(-1i128)); assert_eq!(n_one.clone().try_into(), Ok(-1isize)); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0i8)); assert_eq!(zero.clone().try_into(), Ok(0i16)); assert_eq!(zero.clone().try_into(), Ok(0i32)); assert_eq!(zero.clone().try_into(), Ok(0i64)); assert_eq!(zero.clone().try_into(), Ok(0i128)); assert_eq!(zero.clone().try_into(), Ok(0isize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1i8)); assert_eq!(p_one.clone().try_into(), Ok(1i16)); assert_eq!(p_one.clone().try_into(), Ok(1i32)); assert_eq!(p_one.clone().try_into(), Ok(1i64)); assert_eq!(p_one.clone().try_into(), Ok(1i128)); assert_eq!(p_one.clone().try_into(), Ok(1isize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigintfraction_to_i() { use crate::GenericFraction; use num::BigInt; type BigFraction = GenericFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into(), Ok(-1i8)); assert_eq!(n_one.clone().try_into(), Ok(-1i16)); assert_eq!(n_one.clone().try_into(), Ok(-1i32)); assert_eq!(n_one.clone().try_into(), Ok(-1i64)); assert_eq!(n_one.clone().try_into(), Ok(-1i128)); assert_eq!(n_one.clone().try_into(), Ok(-1isize)); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0i8)); assert_eq!(zero.clone().try_into(), Ok(0i16)); assert_eq!(zero.clone().try_into(), Ok(0i32)); assert_eq!(zero.clone().try_into(), Ok(0i64)); assert_eq!(zero.clone().try_into(), Ok(0i128)); assert_eq!(zero.clone().try_into(), Ok(0isize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1i8)); assert_eq!(p_one.clone().try_into(), Ok(1i16)); assert_eq!(p_one.clone().try_into(), Ok(1i32)); assert_eq!(p_one.clone().try_into(), Ok(1i64)); assert_eq!(p_one.clone().try_into(), Ok(1i128)); assert_eq!(p_one.clone().try_into(), Ok(1isize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigfraction_to_u() { use crate::BigFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0u8)); assert_eq!(zero.clone().try_into(), Ok(0u16)); assert_eq!(zero.clone().try_into(), Ok(0u32)); assert_eq!(zero.clone().try_into(), Ok(0u64)); assert_eq!(zero.clone().try_into(), Ok(0u128)); assert_eq!(zero.clone().try_into(), Ok(0usize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1u8)); assert_eq!(p_one.clone().try_into(), Ok(1u16)); assert_eq!(p_one.clone().try_into(), Ok(1u32)); assert_eq!(p_one.clone().try_into(), Ok(1u64)); assert_eq!(p_one.clone().try_into(), Ok(1u128)); assert_eq!(p_one.clone().try_into(), Ok(1usize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigintfraction_to_u() { use crate::GenericFraction; use num::BigInt; type BigFraction = GenericFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = Fraction::nan(); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_inf.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_one.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(n_half.clone().try_into() as Result, Err(())); assert_eq!(zero.clone().try_into(), Ok(0u8)); assert_eq!(zero.clone().try_into(), Ok(0u16)); assert_eq!(zero.clone().try_into(), Ok(0u32)); assert_eq!(zero.clone().try_into(), Ok(0u64)); assert_eq!(zero.clone().try_into(), Ok(0u128)); assert_eq!(zero.clone().try_into(), Ok(0usize)); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_half.clone().try_into() as Result, Err(())); assert_eq!(p_one.clone().try_into(), Ok(1u8)); assert_eq!(p_one.clone().try_into(), Ok(1u16)); assert_eq!(p_one.clone().try_into(), Ok(1u32)); assert_eq!(p_one.clone().try_into(), Ok(1u64)); assert_eq!(p_one.clone().try_into(), Ok(1u128)); assert_eq!(p_one.clone().try_into(), Ok(1usize)); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(p_inf.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); assert_eq!(nan.clone().try_into() as Result, Err(())); } #[cfg(feature = "with-bigint")] #[test] fn bigfraction_to_f32() { use crate::BigFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigintfraction_to_f32() { use crate::GenericFraction; use num::BigInt; type BigFraction = GenericFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.try_into(), Ok(f32::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f32)); assert_eq!(n_half.try_into(), Ok(-0.5f32)); assert_eq!(zero.try_into(), Ok(0f32)); assert_eq!(p_half.try_into(), Ok(0.5f32)); assert_eq!(p_one.try_into(), Ok(1f32)); assert_eq!(p_inf.try_into(), Ok(f32::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f32).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigfraction_to_f64() { use crate::BigFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } #[cfg(feature = "with-bigint")] #[test] fn bigintfraction_to_f64() { use crate::GenericFraction; use num::BigInt; type BigFraction = GenericFraction; let n_inf = BigFraction::neg_infinity(); let n_one = BigFraction::one() * -1; let n_half = n_one.clone() / 2; let zero = BigFraction::zero(); let p_one = BigFraction::one(); let p_half = p_one.clone() / 2; let p_inf = BigFraction::infinity(); let nan = BigFraction::nan(); assert_eq!(n_inf.try_into(), Ok(f64::NEG_INFINITY)); assert_eq!(n_one.try_into(), Ok(-1f64)); assert_eq!(n_half.try_into(), Ok(-0.5f64)); assert_eq!(zero.try_into(), Ok(0f64)); assert_eq!(p_half.try_into(), Ok(0.5f64)); assert_eq!(p_one.try_into(), Ok(1f64)); assert_eq!(p_inf.try_into(), Ok(f64::INFINITY)); assert!((nan.try_into() as Result).unwrap_or(0f64).is_nan()); } } fraction-0.15.3/src/fraction/unicode_str_io.rs000064400000000000000000000521301046102023000174670ustar 00000000000000use crate::fraction::GenericFraction; use crate::{error::ParseError, Sign}; use num::rational::Ratio; use num::Zero; use std::{fmt, str}; use Integer; pub struct UnicodeDisplay<'a, T: Clone + Integer>(&'a GenericFraction); pub struct SupSubDisplay<'a, T: Clone + Integer>(&'a UnicodeDisplay<'a, T>); impl<'a, T> fmt::Display for UnicodeDisplay<'a, T> where T: Clone + Integer + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { GenericFraction::NaN => write!(f, "NaN"), GenericFraction::Infinity(s) => { write!(f, "{}∞", s) } GenericFraction::Rational(s, r) => { write!(f, "{}{}", s, r.numer())?; if !r.denom().is_one() { write!(f, "⁄{}", r.denom())?; } Ok(()) } } } } impl<'a, T: Clone + Integer + fmt::Display> UnicodeDisplay<'a, T> { /// Display the fraction using FRACTION SLASH '\u{2044}' '⁄' as a mixed fraction e.g. "1⁤1⁄2" /// Will put INVISIBLE PLUS '\u{2064}' as a separator '⁤' /// If you have font support, this is the way Unicode wants to display fractions /// ``` /// use fraction::Fraction; /// assert_eq!( /// format!("{}", Fraction::new(1u8,2u8).get_unicode_display().mixed()), /// "1⁄2" /// ); /// assert_eq!( /// format!("{}", Fraction::new(3u8,2u8).get_unicode_display().mixed()), /// "1⁤1⁄2" /// ); /// ``` pub fn mixed(&self) -> impl fmt::Display + '_ { struct D<'a, T: Clone + Integer + fmt::Display>(&'a UnicodeDisplay<'a, T>); impl<'a, T: Clone + Integer + fmt::Display> fmt::Display for D<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 .0 { GenericFraction::Rational(s, r) if r.fract() != *r && !r.denom().is_one() => { write!( f, "{}{}\u{2064}{}⁄{}", s, r.trunc().numer(), r.fract().numer(), r.fract().denom() ) } _ => write!(f, "{}", self.0), } } } D(self) } /// Display the fraction using super/subscript /// This will look OK without font support /// ``` /// use fraction::Fraction; /// assert_eq!( /// format!("{}", Fraction::new(1u8,2u8).get_unicode_display().supsub()), /// "¹/₂" /// ); /// assert_eq!( /// format!("{}", Fraction::new(3u8,2u8).get_unicode_display().supsub()), /// "³/₂" /// ); /// ``` pub fn supsub(&'a self) -> SupSubDisplay<'a, T> { SupSubDisplay(self) } } impl<'a, T: Clone + Integer + fmt::Display> SupSubDisplay<'a, T> { /// Display the fraction as a mixed fraction using super/subscript e.g. "1¹/₂" /// This will look OK without font support /// ``` /// use fraction::Fraction; /// assert_eq!( /// format!("{}", Fraction::new(1u8,2u8).get_unicode_display().supsub().mixed()), /// "¹/₂" /// ); /// assert_eq!( /// format!("{}", Fraction::new(3u8,2u8).get_unicode_display().supsub().mixed()), /// "1¹/₂" /// ); /// ``` pub fn mixed(&self) -> impl fmt::Display + '_ { struct D<'a, T: Clone + Integer + fmt::Display>(&'a SupSubDisplay<'a, T>); impl<'a, T: Clone + Integer + fmt::Display> fmt::Display for D<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 .0 .0 { GenericFraction::Rational(s, r) if r.fract() != *r && !r.denom().is_one() => { write!( f, "{}{}{}", s, r.trunc().numer(), GenericFraction::Rational(Sign::Plus, r.fract().clone()) .get_unicode_display() .supsub() ) } _ => write!(f, "{}", self.0), } } } D(self) } } impl<'a, T: Clone + Integer + fmt::Display> fmt::Display for SupSubDisplay<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 .0 { GenericFraction::Rational(s, r) => { write!( f, "{}{}/{}", s, r.numer() .to_string() .chars() .map(|c| match c { '1' => '¹', '2' => '²', '3' => '³', '4' => '⁴', '5' => '⁵', '6' => '⁶', '7' => '⁷', '8' => '⁸', '9' => '⁹', '0' => '⁰', _ => '?', }) .collect::(), r.denom() .to_string() .chars() .map(|c| match c { '1' => '₁', '2' => '₂', '3' => '₃', '4' => '₄', '5' => '₅', '6' => '₆', '7' => '₇', '8' => '₈', '9' => '₉', '0' => '₀', c => c, }) .collect::(), ) } _ => write!(f, "{}", self.0), } } } impl GenericFraction { /// Display the fraction using FRACTION SLASH '\u{2044}' '⁄' /// If you have font support, this is the way Unicode wants to display fractions /// ``` /// use fraction::Fraction; /// assert_eq!( /// format!("{}", Fraction::new(1u8,2u8).get_unicode_display()), /// "1⁄2" /// ); /// assert_eq!( /// format!("{}", Fraction::new(3u8,2u8).get_unicode_display()), /// "3⁄2" /// ); /// ``` pub fn get_unicode_display(&self) -> UnicodeDisplay<'_, T> { UnicodeDisplay(self) } } impl> GenericFraction { /// Parse a unicode string /// The string can be: /// - A normal fraction e.g. "1/2" /// - A vulgar fraction e.g. '½' /// - ~A mixed vulgar fraction "1½"~ /// - A unicode fraction e.g. "1⁄2" where '⁄' can be any of: /// - '/' ASCII SOLIDUS /// - '⁄' FRACTION SLASH /// - '∕' DIVISION SLASH /// - '÷' DIVISION SIGN /// - A mixed unicode fraction: "1\u{2063}1⁄2": 1⁤1⁄2 /// - '\u{2064}' INVISIBLE PLUS /// - '\u{2063}' INVISIBLE SEPARATOR /// - NOT ~'\u{2062}' INVISIBLE TIMES~ /// - A super-subscript fraction "¹/₂" /// - A mixed super-subscript fraction "1¹/₂" /// /// Focus is on being lenient towards input rather than being fast. /// ``` /// use fraction::Fraction; /// let v = vec![ /// ("1/2", Fraction::new(1u8,2u8)), /// ("-1/2", Fraction::new_neg(1u8,2u8)), /// ("½", Fraction::new(1u8,2u8)), /// // ("1½", Fraction::new(3u8,2u8)), // mixed vulgar fractions /// // ("-1½", Fraction::new_neg(3u8,2u8)), // currently not supported /// ("1⁄2", Fraction::new(1u8,2u8)), /// ("-1⁄2", Fraction::new_neg(1u8,2u8)), /// ("1⁤1⁄2", Fraction::new(3u8,2u8)), /// ("-1⁤1⁄2", Fraction::new_neg(3u8,2u8)), /// ("¹/₂", Fraction::new(1u8,2u8)), /// ("-¹/₂", Fraction::new_neg(1u8,2u8)), /// ("1¹/₂", Fraction::new(3u8,2u8)), /// ("-1¹/₂", Fraction::new_neg(3u8,2u8)), /// ]; /// for (f_str, f) in v { /// assert_eq!(Fraction::from_unicode_str(f_str), Ok(f)) /// } /// ``` pub fn from_unicode_str(input: &str) -> Result { let s: &str; let sign = if input.starts_with('-') { s = &input.strip_prefix('-').unwrap(); Sign::Minus } else if input.starts_with('+') { s = &input.strip_prefix('+').unwrap(); Sign::Plus } else { s = input; Sign::Plus }; if s.to_lowercase().starts_with("nan") { Ok(GenericFraction::nan()) } else if s.starts_with('∞') || s.starts_with("inf") || s.starts_with("infty") { Ok(GenericFraction::Infinity(sign)) // vulgar fractions } else if s.starts_with('½') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 2.into()), )) } else if s.starts_with('¼') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 4.into()), )) } else if s.starts_with('¾') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(3.into(), 4.into()), )) } else if s.starts_with('⅐') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 7.into()), )) } else if s.starts_with('⅑') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 9.into()), )) } else if s.starts_with('⅒') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 10.into()), )) } else if s.starts_with('⅓') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 3.into()), )) } else if s.starts_with('⅔') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(2.into(), 3.into()), )) } else if s.starts_with('⅕') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(1.into(), 5.into()), )) } else if s.starts_with('⅖') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(2.into(), 5.into()), )) } else if s.starts_with('⅗') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(3.into(), 5.into()), )) } else if s.starts_with('⅘') { Ok(GenericFraction::Rational( sign, Ratio::new_raw(4.into(), 5.into()), )) } else if let Some((first, denom_str)) = s.split_once(&['/', '⁄', '∕', '÷'][..]) { // allow for mixed fractions of the shape 1²/₃ if let Some(idx) = first.find(&['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '⁰'][..]) { let trunc = if idx.is_zero() { T::zero() } else { let Ok(t) = T::from_str_radix(&first[..idx], 10) else { return Err(ParseError::ParseIntError); }; t }; let Ok(numer) = T::from_str_radix( &first[idx..] .chars() .map(|c| match c { '¹' => '1', '²' => '2', '³' => '3', '⁴' => '4', '⁵' => '5', '⁶' => '6', '⁷' => '7', '⁸' => '8', '⁹' => '9', '⁰' => '0', _ => '?', }) .collect::(), 10, ) else { return Err(ParseError::ParseIntError); }; let Ok(denom) = T::from_str_radix( // let n = &denom_str .chars() .map(|c| match c { '₁' => '1', '₂' => '2', '₃' => '3', '₄' => '4', '₅' => '5', '₆' => '6', '₇' => '7', '₈' => '8', '₉' => '9', '₀' => '0', c => c, }) .collect::(), 10, ) else { return Err(ParseError::ParseIntError); }; Ok(GenericFraction::Rational( sign, Ratio::new(numer + trunc * denom.clone(), denom), )) } else if let Some((trunc_str, numer_str)) = // also allow for mixed fractions to be parsed: `1⁤1⁄2` // allowed invisible separators: \u{2064} \u{2063} // '+' is disallowed, bc it would be confusing with -1+1/2 first.split_once(&['\u{2064}', '\u{2063}'][..]) { let Ok(numer) = T::from_str_radix(numer_str, 10) else { return Err(ParseError::ParseIntError); }; let Ok(trunc) = T::from_str_radix(trunc_str, 10) else { return Err(ParseError::ParseIntError); }; let Ok(denom) = T::from_str_radix(denom_str, 10) else { return Err(ParseError::ParseIntError); }; Ok(GenericFraction::Rational( sign, Ratio::new(numer + trunc * denom.clone(), denom), )) } else { let Ok(numer) = T::from_str_radix(first, 10) else { return Err(ParseError::ParseIntError); }; let Ok(denom) = T::from_str_radix(denom_str, 10) else { return Err(ParseError::ParseIntError); }; Ok(GenericFraction::Rational(sign, Ratio::new(numer, denom))) } } else { let Ok(val) = T::from_str_radix(s, 10) else { return Err(ParseError::ParseIntError); }; Ok(GenericFraction::Rational(sign, Ratio::new(val, T::one()))) } } } #[cfg(test)] mod tests { use crate::{error::ParseError, Fraction}; use num::{One, Zero}; #[test] fn test_fromto_str() { let test_vec = vec![ ("NaN", Fraction::nan()), ("∞", Fraction::infinity()), ("-∞", Fraction::neg_infinity()), ("0", Fraction::zero()), ("1", Fraction::one()), ("-1", -Fraction::one()), ("5", Fraction::from(5)), ("1⁄2", Fraction::new(1u8, 2u8)), ("-1⁄2", Fraction::new_neg(1u8, 2u8)), ("3⁄2", Fraction::new(3u8, 2u8)), ("-3⁄2", Fraction::new_neg(3u8, 2u8)), ("12⁄23", Fraction::new(12u8, 23u8)), ]; for (string, frac) in test_vec { println!("{} ?= {}", string, frac); assert_eq!(Fraction::from_unicode_str(string), Ok(frac)); println!("{} ?= {}", string, frac); assert_eq!(format!("{}", frac.get_unicode_display()), string); } } #[test] fn test_alternate_parse() { let test_vec = vec![ ("nan", Fraction::nan()), ("+∞", Fraction::infinity()), ("+1", Fraction::one()), ("+5", Fraction::from(5)), // vulgar fractions ("½", Fraction::new(1u8, 2u8)), ("-½", Fraction::new_neg(1u8, 2u8)), ("+½", Fraction::new(1u8, 2u8)), ("¼", Fraction::new(1u8, 4u8)), ("-¼", Fraction::new_neg(1u8, 4u8)), ("+¼", Fraction::new(1u8, 4u8)), ("¾", Fraction::new(3u8, 4u8)), ("-¾", Fraction::new_neg(3u8, 4u8)), ("+¾", Fraction::new(3u8, 4u8)), ("⅐", Fraction::new(1u8, 7u8)), ("-⅐", Fraction::new_neg(1u8, 7u8)), ("+⅐", Fraction::new(1u8, 7u8)), ("⅑", Fraction::new(1u8, 9u8)), ("-⅑", Fraction::new_neg(1u8, 9u8)), ("+⅑", Fraction::new(1u8, 9u8)), ("⅒", Fraction::new(1u8, 10u8)), ("-⅒", Fraction::new_neg(1u8, 10u8)), ("+⅒", Fraction::new(1u8, 10u8)), ("⅓", Fraction::new(1u8, 3u8)), ("-⅓", Fraction::new_neg(1u8, 3u8)), ("+⅓", Fraction::new(1u8, 3u8)), ("⅔", Fraction::new(2u8, 3u8)), ("-⅔", Fraction::new_neg(2u8, 3u8)), ("+⅔", Fraction::new(2u8, 3u8)), ("⅕", Fraction::new(1u8, 5u8)), ("-⅕", Fraction::new_neg(1u8, 5u8)), ("+⅕", Fraction::new(1u8, 5u8)), ("⅖", Fraction::new(2u8, 5u8)), ("-⅖", Fraction::new_neg(2u8, 5u8)), ("+⅖", Fraction::new(2u8, 5u8)), ("⅗", Fraction::new(3u8, 5u8)), ("-⅗", Fraction::new_neg(3u8, 5u8)), ("+⅗", Fraction::new(3u8, 5u8)), ("⅘", Fraction::new(4u8, 5u8)), ("-⅘", Fraction::new_neg(4u8, 5u8)), ("+⅘", Fraction::new(4u8, 5u8)), // supsub with other delim ("¹⁄₃", Fraction::new(1u8, 3u8)), ("1¹⁄₃", Fraction::new(4u8, 3u8)), ]; for (string, frac) in test_vec { println!("{} ?= {}", string, frac); assert_eq!(Fraction::from_unicode_str(string), Ok(frac)); } } #[test] fn test_fromto_supsub() { let test_vec = vec![ // super/subscript ("¹²/₂₃", Fraction::new(12u8, 23u8)), ("¹²³⁴⁵⁶⁷⁸⁹⁰/₂₃", Fraction::new(1234567890u64, 23u8)), ("²³/₁₂₃₄₅₆₇₈₉₀", Fraction::new(23u8, 1234567890u64)), ]; for (string, frac) in test_vec { println!("{} ?= {}", string, frac); assert_eq!(Fraction::from_unicode_str(string), Ok(frac)); println!("{} ?= {}", string, frac); assert_eq!(format!("{}", frac.get_unicode_display().supsub()), string); } } #[test] fn test_fromto_supsub_mixed() { let test_vec = vec![ // super/subscript mixed ("1¹/₂", Fraction::new(3u8, 2u8)), ("¹/₂", Fraction::new(1u8, 2u8)), ]; for (string, frac) in test_vec { println!("{} ?= {}", string, frac); assert_eq!(Fraction::from_unicode_str(string), Ok(frac)); println!("{} ?= {}", string, frac); assert_eq!( format!("{}", frac.get_unicode_display().supsub().mixed()), string ); } } #[test] fn test_fromto_mixed() { let test_vec = vec![ ("NaN", Fraction::nan()), ("∞", Fraction::infinity()), ("-∞", Fraction::neg_infinity()), ("0", Fraction::zero()), ("1", Fraction::one()), ("-1", -Fraction::one()), ("5", Fraction::from(5)), ("1\u{2064}1⁄2", Fraction::new(3u8, 2u8)), ("-1\u{2064}1⁄2", Fraction::new_neg(3u8, 2u8)), ("1⁄2", Fraction::new(1u8, 2u8)), ("-1⁄2", Fraction::new_neg(1u8, 2u8)), ]; for (string, frac) in test_vec { println!("{} ?= {}", string, frac); let f_test = Fraction::from_unicode_str(string); assert_eq!(f_test, Ok(frac)); assert_eq!(format!("{}", frac.get_unicode_display().mixed()), string); } } #[test] fn test_from_fail() { // TODO: "nanBOGUS" and "∞BOGUS" will parse. // Either make that everything with BOGUS // after will parse, or make ^those fail. let test_vec = vec![ "asdf", "+1BOGUS", "+5BOGUS", "1⁤1⁄2BOGUS", "1⁣1⁄2BOGUS", "-1⁤1⁄2BOGUS", "1⁢1⁄2", // uses INVISIBLE_TIMES ]; for s in test_vec { println!("{}", s); assert_eq!( Fraction::from_unicode_str(s), Err(ParseError::ParseIntError) ) } } #[test] fn test_fromstr_fraction_ops() { let test_vec = vec!["1", "1/2", "3/2"]; for s in test_vec { let f = Fraction::from_unicode_str(s).unwrap(); assert_eq!(f * Fraction::one(), f); assert_eq!(f + Fraction::zero(), f); } } } fraction-0.15.3/src/generic.rs000064400000000000000000000232561046102023000143000ustar 00000000000000//! Integer generic traits and operations //! //! These traits and functions are for use in generic algorithms //! that work indifferently to particular types, relying on //! some common traits. use super::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Integer, ToPrimitive}; use std::{any::TypeId, cmp::PartialOrd}; use Sign; #[cfg(feature = "with-bigint")] use super::{BigInt, BigUint, One, Signed, Zero}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; /// Methods common to all integer types that /// could be used generically in abstract algorithms pub trait GenericInteger: 'static + Sized + Integer + ToPrimitive + CheckedAdd + CheckedDiv + CheckedMul + CheckedSub + Add + Div + Mul + Rem + Sub + AddAssign + DivAssign + MulAssign + RemAssign + SubAssign + for<'a> Add<&'a Self, Output = Self> + for<'a> Div<&'a Self, Output = Self> + for<'a> Mul<&'a Self, Output = Self> + for<'a> Rem<&'a Self, Output = Self> + for<'a> Sub<&'a Self, Output = Self> + for<'a> AddAssign<&'a Self> + for<'a> DivAssign<&'a Self> + for<'a> MulAssign<&'a Self> + for<'a> RemAssign<&'a Self> + for<'a> SubAssign<&'a Self> { /// Returns value 0 of the type fn _0() -> Self; /// Returns value 1 of the type fn _1() -> Self; /// Returns value 10 of the type fn _10() -> Self; /// Returns Maybe of 0 fn _0r() -> Option<&'static Self>; /// Returns Maybe of 1 fn _1r() -> Option<&'static Self>; /// Returns Maybe of 10 fn _10r() -> Option<&'static Self>; /// Returns the sign and the value itself. /// Zero values must have [Sign::Plus] fn get_signed_value(self) -> (Sign, Self); } #[cfg(feature = "with-bigint")] lazy_static! { static ref _0_BU: BigUint = BigUint::zero(); static ref _1_BU: BigUint = BigUint::one(); static ref _10_BU: BigUint = BigUint::from(10u8); static ref _0_BI: BigInt = BigInt::zero(); static ref _1_BI: BigInt = BigInt::one(); static ref _10_BI: BigInt = BigInt::from(10i8); } #[cfg(feature = "with-bigint")] impl GenericInteger for BigUint { #[inline] fn _0() -> Self { BigUint::zero() } #[inline] fn _1() -> Self { BigUint::one() } #[inline] fn _10() -> Self { _10_BU.clone() } #[inline] fn _0r() -> Option<&'static Self> { Some(&_0_BU) } #[inline] fn _1r() -> Option<&'static Self> { Some(&_1_BU) } #[inline] fn _10r() -> Option<&'static Self> { Some(&_10_BU) } #[inline] fn get_signed_value(self) -> (Sign, Self) { (Sign::Plus, self) } } #[cfg(feature = "with-bigint")] impl GenericInteger for BigInt { #[inline] fn _0() -> Self { BigInt::zero() } #[inline] fn _1() -> Self { BigInt::one() } #[inline] fn _10() -> Self { _10_BI.clone() } #[inline] fn _0r() -> Option<&'static Self> { Some(&_0_BI) } #[inline] fn _1r() -> Option<&'static Self> { Some(&_1_BI) } #[inline] fn _10r() -> Option<&'static Self> { Some(&_10_BI) } #[inline] fn get_signed_value(self) -> (Sign, Self) { ( if self.is_negative() { Sign::Minus } else { Sign::Plus }, self, ) } } macro_rules! generic_integer_for_uint { ($($t:ty),*) => { $( impl GenericInteger for $t { #[inline] fn _0() -> Self { 0 } #[inline] fn _1() -> Self { 1 } #[inline] fn _10() -> Self { 10 } #[inline] fn _0r() -> Option<&'static Self> { None } #[inline] fn _1r() -> Option<&'static Self> { None } #[inline] fn _10r() -> Option<&'static Self> { None } #[inline] fn get_signed_value(self) -> (Sign, Self) { (Sign::Plus, self) } } )* }; } generic_integer_for_uint!(u8, u16, u32, u64, u128, usize); macro_rules! generic_integer_for_int { ($($t:ty),*) => { $( impl GenericInteger for $t { #[inline] fn _0() -> Self { 0 } #[inline] fn _1() -> Self { 1 } #[inline] fn _10() -> Self { 10 } #[inline] fn _0r() -> Option<&'static Self> { None } #[inline] fn _1r() -> Option<&'static Self> { None } #[inline] fn _10r() -> Option<&'static Self> { None } #[inline] fn get_signed_value(self) -> (Sign, Self) { (if self.is_negative() { Sign::Minus } else { Sign::Plus }, self) } } )* }; } generic_integer_for_int!(i8, i16, i32, i64, i128, isize); /// Builds integer of type T from another integer of type F in a generic way. /// Guarantees to only return positive values. /// /// Allows safe runtime conversions between integer types when it's not possible /// statically. E.g: `i8 -> u8`, `u8 -> i8`, `usize -> u8` or even `BigUint -> u8` and so on. /// /// Simply reinterprets type F as T when they are the same type. /// /// # Examples /// /// ``` /// use fraction::{Sign, generic::read_generic_integer}; /// /// assert_eq!((Sign::Plus, 127i8), read_generic_integer(127u8).unwrap()); /// assert_eq!((Sign::Minus, 128u8), read_generic_integer(-128i8).unwrap()); /// assert_eq!((Sign::Minus, 255u8), read_generic_integer(-255isize).unwrap()); /// ``` pub fn read_generic_integer(val: F) -> Option<(Sign, T)> where F: GenericInteger + PartialOrd, T: GenericInteger, { let (sign, mut val) = val.get_signed_value(); if TypeId::of::() == TypeId::of::() && val >= F::zero() { let cast = Some((sign, unsafe { (&mut val as *mut F as *mut T).read() })); let _ = std::mem::ManuallyDrop::new(val); return cast; } let mut vptr: F = F::_1(); let mut rptr: T = T::_1(); let mut result: T = T::_0(); loop { vptr = match F::_10r().map_or_else( || vptr.checked_mul(&GenericInteger::_10()), |_10| vptr.checked_mul(_10), ) { Some(v) => v, None => break, }; let vdelta: F = val.checked_sub(&val.checked_div(&vptr)?.checked_mul(&vptr)?)?; let mut rdelta: T = T::_0(); let mut vldelta: F = vdelta.checked_div(&F::_10r().map_or_else( || vptr.checked_div(&GenericInteger::_10()), |_10| vptr.checked_div(_10), )?)?; loop { if F::_0r().map_or_else(|| vldelta == GenericInteger::_0(), |v| vldelta == *v) { break; } rdelta = T::_1r() .map_or_else(|| rdelta.checked_add(&T::_1()), |_1| rdelta.checked_add(_1))?; vldelta = F::_1r().map_or_else( || { if sign == Sign::Plus { vldelta.checked_sub(&GenericInteger::_1()) } else { vldelta.checked_add(&GenericInteger::_1()) } }, |_1| { if sign == Sign::Plus { vldelta.checked_sub(_1) } else { vldelta.checked_add(_1) } }, )?; } result = result.checked_add(&rdelta.checked_mul(&rptr)?)?; val = val.checked_sub(&vdelta)?; if F::_0r().map_or_else(|| val == GenericInteger::_0(), |_0| val == *_0) { break; } rptr = T::_10r().map_or_else( || rptr.checked_mul(&GenericInteger::_10()), |_10| rptr.checked_mul(_10), )?; } if F::_0r().map_or_else(|| val != GenericInteger::_0(), |_0| val != *_0) { let mut vldelta: F = val.checked_div(&vptr)?; let mut rdelta: T = T::_0(); loop { if F::_0r().map_or_else(|| vldelta == GenericInteger::_0(), |_0| vldelta == *_0) { break; } rdelta = T::_1r().map_or_else( || rdelta.checked_add(&GenericInteger::_1()), |_1| rdelta.checked_add(_1), )?; vldelta = F::_1r().map_or_else( || { if sign == Sign::Plus { vldelta.checked_sub(&GenericInteger::_1()) } else { vldelta.checked_add(&GenericInteger::_1()) } }, |_1| { if sign == Sign::Plus { vldelta.checked_sub(_1) } else { vldelta.checked_add(_1) } }, )?; } result = result.checked_add(&rdelta.checked_mul(&rptr)?)?; } Some((sign, result)) } #[cfg(test)] mod tests { // TODO: tests use super::*; #[test] fn max_to_max() { let (s, v) = read_generic_integer::(u32::max_value()).unwrap(); assert_eq!(s, Sign::Plus); assert_eq!(v, u32::max_value()); } #[test] fn sign() { let (s, _) = read_generic_integer::(0i8).unwrap(); assert_eq!(s, Sign::Plus); let (s, _) = read_generic_integer::(1i8).unwrap(); assert_eq!(s, Sign::Plus); let (s, _) = read_generic_integer::(-1i8).unwrap(); assert_eq!(s, Sign::Minus); } } fraction-0.15.3/src/lib.rs000064400000000000000000000262161046102023000134310ustar 00000000000000#![doc(test(attr(deny(warnings), allow(deprecated))))] //! Fraction is designed to be a precise lossless drop-in replacement for floating types (f32, f64). //! //! It comes with a number of predefined type aliases covering the most common use cases such as //! [Fraction], [Decimal], [BigFraction], [DynaDecimal] and so on (see [prelude] module for more examples). //! //! The public API provides you with the generic types that you may use straightforwardly to build your //! own types, suiting your needs best (see [prelude] module for the examples). //! //! # Library features //! //! - Drop in replacement for floats with the exception for NaN == NaN so that it's hashable //! - It's hashable, so may be used as values in Sets and keys in dictionaries and hash maps //! - [Display](fraction::display) implementation for fractions and decimals //! - [Fraction](GenericFraction) type, representing fractions //! - [Decimal](GenericDecimal) type, based on [Fraction](GenericFraction) type represents floats as lossless decimals //! - [DynaInt](dynaint) implements dynamically growing integer type that perfarms checked math and avoids stack overflows //! - PostgreSQL binary protocol integration for both fractions and decimals //! - Juniper support for both fractions and decimals //! - [Generic integer conversions](generic), such as `i8 -> u8`, `usize -> u8` and so on //! - [Lossless division](division) with no allocations and infinite precision //! //! # Disclaimer //! Even though we do our best to keep it well covered with tests, there may be bugs out there. //! The library API is still in flux. When it gets stable we will release the version 1.0.0. //! You may find more info about Semantic Versioning on [https://semver.org/](https://semver.org/). //! Bug reports and contributions are appreciated. //! //! # Crate features //! - `with-bigint` (default) integration with [num::BigInt] and [num::BigUint] data types //! - `with-decimal` (default) [Decimal] type implemented upon [GenericFraction] //! - `with-dynaint` (default) dynamically growing integer avoiding stack overflows //! - `with-unicode` Unicode formatting and parsing options //! - `with-approx` adds methods for approximate computations (currently `sqrt`) //! - `with-juniper-support` [Juniper](https://crates.io/crates/juniper) integration //! - `with-postgres-support` [PostgreSQL](https://crates.io/crates/postgres) integration; Numeric/Decimal type //! - `with-serde-support` [Serde](https://crates.io/crates/serde) traits implementation //! //! # Implementation //! Basic math implemented upon the [num] crate (in particular the [num::rational] module). //! The utilised traits from the [num] crate are re-exported, so you don't have to explicitly depend on that crate however, //! you may import them from either of crates if necessary. //! //! # Usage //! To start using types see the [Prelude](self::prelude) module. //! //! # Examples //! //! ## Simple use: //! //! ``` //! type F = fraction::Fraction; // choose the type accordingly to your needs (see prelude module docs) //! //! let two = F::from(0) + F::from(2); // 0 + 2 = 2 //! let two_third = two / F::from(3); // 2/3 = 0.666666[...] //! //! assert_eq!(F::from(2), two); //! assert_eq!(F::new(2u64, 3u64), two_third); //! //! assert_eq!("2/3", format!("{}", two_third)); // print as Fraction (by default) //! assert_eq!("0.6666", format!("{:.4}", two_third)); // format as decimal and print up to 4 digits after floating point //! ``` //! //! Decimal is implemented as a representation layer on top of Fraction. //! Thus, it is also lossless and may require explicit control over "precision" //! for comparison and formatting operations. //! ``` //! type D = fraction::Decimal; //! //! let result = D::from(0.5) / D::from(0.3); //! //! assert_eq!(format!("{}", result), "1.6"); // calculation result uses precision of the operands //! assert_eq!(format!("{:.4}", result), "1.6666"); // explicitly passing precision to format //! //! assert_eq!("1.6666", format!("{}", result.set_precision(4))); // the other way to set precision explicitly on Decimal //! ``` //! //! ## Construct: //! //! Fraction: //! //! ``` //! use std::str::FromStr; //! use fraction::{Fraction, Sign}; //! //! // fraction crate also re-exports num::{One, Zero} traits for convenience. //! use fraction::{One, Zero}; //! //! //! // There are several ways to construct a fraction, depending on your use case //! //! // `new` - construct with numerator/denominator and normalize the fraction. //! // "Normalization" means it will always find the least common denominator //! // and convert the input accordingly. //! let f = Fraction::new(1u8, 2u8); //! //! // `new_generic` - construct with numerator/denominator of different integer types //! assert_eq!(f, Fraction::new_generic(Sign::Plus, 1i32, 2u8).unwrap()); //! //! // `from` - converts from primitive types such as i32 and f32. //! assert_eq!(f, Fraction::from(0.5)); // convert from float (f32, f64) //! //! // `from_str` - tries parse a string fraction. Supports the usual decimal notation. //! assert_eq!(f, Fraction::from_str("0.5").unwrap()); // parse a string //! //! // `from_str` - also supports _fraction_ notation such as "numerator/denominator" delimited by slash (`/`). //! assert_eq!(f, Fraction::from_str("1/2").unwrap()); // parse a string //! //! // `new_raw` - construct with numerator/denominator but do not normalize the fraction. //! // This is the most performant constructor, but does not calculate the common denominator, //! // so may lead to unexpected results in following calculations if the fraction is not normalised. //! // WARNING: Only use if you are sure numerator/denominator are already normalized. //! assert_eq!(f, Fraction::new_raw(1u64, 2u64)); //! //! // `one` - implements num::One trait //! assert_eq!(f * 2, Fraction::one()); //! //! // `zero` - implements num::Zero trait //! assert_eq!(f - f, Fraction::zero()); //! ``` //! //! Decimal: //! ``` //! use std::str::FromStr; //! use fraction::{Decimal, Fraction}; //! //! // There are similar ways to construct Decimal. Underneath it is always represented as Fraction. //! // When constructed, Decimal preserves its precision (number of digits after floating point). //! // When two decimals are calculated, the result takes the biggest precision of both. //! // The precision is used for visual representation (formatting and printing) and for comparison of two decimals. //! // Precision is NOT used in any calculations. All calculations are lossless and implemented through Fraction. //! // To override the precision use Decimal::set_precision. //! //! let d = Decimal::from(1); // from integer, precision = 0 //! assert_eq!(d, Decimal::from_fraction(Fraction::from(1))); // from fraction, precision is calculated from fraction //! //! let d = Decimal::from(1.3); // from float (f32, f64) //! assert_eq!(d, Decimal::from_str("1.3").unwrap()); //! //! let d = Decimal::from(0.5); // from float (f32, f64) //! assert_eq!(d, Decimal::from_str("1/2").unwrap()); //! ``` //! //! ## Format (convert to string) //! Formatting works similar for both Decimal and Fraction (Decimal uses Fraction internally). //! The format implementation closely follows the rust Format trait documentation. //! //! ``` //! type F = fraction::Fraction; //! //! let result = F::from(0.7) / F::from(0.4); //! assert_eq!(format!("{}", result), "7/4"); // Printed as fraction by default //! assert_eq!(format!("{:.2}", result), "1.75"); // if precision is defined, printed as decimal //! assert_eq!(format!("{:#.3}", result), "1.750"); // to print leading zeroes, pass hash to the format //! ``` //! //! Additionally, there are [methods](GenericFraction::get_unicode_display) available for various unicode display options: //! See [this SO answer](https://stackoverflow.com/a/77861320/14681457) for a discussion. //! //! ``` //! type F = fraction::Fraction; //! //! let res = F::from(0.7) / F::from(0.4); //! assert_eq!("7⁄4",format!("{}", res.get_unicode_display())); // needs font support. Unicode way //! assert_eq!("1⁤3⁄4",format!("{}", res.get_unicode_display().mixed())); // interpreted wrongly without font support //! assert_eq!("⁷/₄",format!("{}", res.get_unicode_display().supsub())); // no need for font support //! assert_eq!("1³/₄",format!("{}", res.get_unicode_display().supsub().mixed())); //! ``` //! //! ## Convert into/from other types //! //! Both `fraction` and `decimal` types implement //! - `from` and `try_into` for all built-in primitive types. //! - `from` and `try_into` for `BigInt` and `BigUint` when `with-bigint` feature enabled. //! //! ```rust //! use fraction::{Fraction, One, BigInt, BigUint}; //! use std::convert::TryInto; //! //! // Convert from examples (from primitives always succeed) //! assert_eq!(Fraction::from(1i8), Fraction::one()); //! assert_eq!(Fraction::from(1u8), Fraction::one()); //! assert_eq!(Fraction::from(BigInt::one()), Fraction::one()); //! assert_eq!(Fraction::from(BigUint::one()), Fraction::one()); //! assert_eq!(Fraction::from(1f32), Fraction::one()); //! assert_eq!(Fraction::from(1f64), Fraction::one()); //! //! //! // Convert into examples (try_into returns Result) //! assert_eq!(Ok(1i8), Fraction::one().try_into()); //! assert_eq!(Ok(1u8), Fraction::one().try_into()); //! assert_eq!(Ok(BigInt::one()), Fraction::one().try_into()); //! assert_eq!(Ok(BigUint::one()), Fraction::one().try_into()); //! assert_eq!(Ok(1f32), Fraction::one().try_into()); //! assert_eq!(Ok(1f64), Fraction::one().try_into()); //! ``` //! //! ### Postgres usage //! Postgres uses i16 for its binary protocol, so you'll have to use at least u16 //! as the base type for fractions/decimals. //! Otherwise you may workaround with DynaInt. //! The safest way to go with would be DynaInt based types //! such as DynaFraction or DynaDecimal as they would prevent //! stack overflows for high values. //! //! Beware bad numbers such as 1/3, 1/7. //! Fraction keeps the highest achievable precision (up to 16383 digits after floating point). //! Decimal uses its own precision. //! So, if you may end up with bad numbers, it may be preferable to go with Decimals over Fractions. //! //! Both types (fractions and decimals) should work transparently //! in accordance with Postgres crate documentation extern crate num; #[cfg(feature = "with-bigint")] #[macro_use] extern crate lazy_static; #[cfg(feature = "with-bigint")] pub use num::bigint::{BigInt, BigUint}; pub use num::rational::{ParseRatioError, Ratio}; pub use num::{ traits::{ConstOne, ConstZero}, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Integer, Num, One, Signed, ToPrimitive, Zero, }; #[cfg(test)] #[macro_use] mod tests; pub mod convert; pub mod division; pub mod error; mod fraction; pub use fraction::*; pub mod generic; pub mod prelude; pub use self::prelude::*; // ====================================== FEATURES ====================================== #[cfg(feature = "with-juniper-support")] extern crate juniper; #[cfg(feature = "with-postgres-support")] #[macro_use] extern crate postgres_types; #[cfg(feature = "with-serde-support")] #[macro_use] extern crate serde_derive; #[cfg(feature = "with-serde-support")] extern crate serde; #[cfg(feature = "with-decimal")] mod decimal; #[cfg(feature = "with-dynaint")] pub mod dynaint; fraction-0.15.3/src/prelude.rs000064400000000000000000000131221046102023000143130ustar 00000000000000//! Predefines some types for the most common use cases //! //! You should consider this module as a list of shortcuts, and not //! as the list of only available types. //! The actual workhorses are a part of the Public API and you are //! encouraged to use them straightforwardly whenever you may //! feel necessary. //! //! Long story short, you may compose your own types with these: //! - [`GenericFraction`] for fractions //! - [`GenericDecimal`] for decimals //! - [`DynaInt`] integers on stack, but dynamically growing into heap when necessary //! //! [`GenericFraction`]: super::GenericFraction //! [`GenericDecimal`]: super::GenericDecimal //! [`DynaInt`]: super::dynaint pub use super::fraction::GenericFraction; #[cfg(feature = "with-bigint")] pub use super::{BigInt, BigUint}; #[cfg(feature = "with-decimal")] pub use super::decimal::GenericDecimal; #[cfg(feature = "with-dynaint")] pub use super::dynaint::DynaInt; /// Fraction consisting from two `u64` numbers /// /// Allows to keep and work with fractions on stack. /// /// Be aware of possible stack overflows that might be caused by /// exceeding `u64` limits in some math operations, which will make thread to panic. /// /// # Examples /// /// ``` /// use fraction::Fraction; /// /// let first = Fraction::new (1u8, 2u8); /// let second = Fraction::new (2u8, 8u8); /// /// assert_eq! (first + second, Fraction::new (3u8, 4u8)); /// ``` pub type Fraction = GenericFraction; /// Fraction consisting from two [`BigUint`] numbers /// /// Allows to keep and work with fractions on heap. /// /// BigUint number is based on heap and does not have any limits, which makes /// BigFraction safe from stack overflows. However, it comes with a price of /// making allocations on every math operation. /// /// # Examples /// /// ``` /// use fraction::BigFraction; /// /// let first = BigFraction::new (2u8, 3u8); /// let second = BigFraction::new (1u8, 6u8); /// /// assert_eq! (first + second, BigFraction::new (5u8, 6u8)); /// ``` /// /// [`BigUint`]: https://docs.rs/num-bigint/*/num_bigint/ #[cfg(feature = "with-bigint")] pub type BigFraction = GenericFraction; /// Stack allocated, but dynamically growing into heap if necessary /// /// Fraction using T as the base type for numerator and denominator /// Whenever possible keeps data in T, but on math overflows /// automatically casts values into BigUint, which allocates memory on heap. /// /// Allows to use fractions without memory allocations wherever possible. /// For unexpectedly big values performs on heap and doesn't suffer from stack overflows. /// Automatically uses T if an operation on two BigUint numbers produces the result /// that may fit within T. /// /// # Examples /// /// ``` /// use fraction::{DynaFraction, BigUint}; /// /// type D = DynaFraction; /// /// let max_u32 = u32::max_value(); /// /// let one = D::from (1u32); /// let mut val = D::from (max_u32); /// /// assert_eq!( // check we still use u32 for the numerator /// max_u32, /// val.numer().unwrap().clone().unpack().ok().unwrap() /// ); /// /// /// val += &one; /// assert_eq!( // BigUint allocated for the result instead of stack overflow on u32 /// BigUint::from(max_u32) + BigUint::from(1u8), /// val.numer().unwrap().clone().unpack().err().unwrap() /// ); /// /// /// val -= one; /// assert_eq!( // check we use u32 again /// max_u32, /// val.numer().unwrap().clone().unpack().ok().unwrap() /// ); /// ``` #[cfg(all(feature = "with-bigint", feature = "with-dynaint"))] pub type DynaFraction = GenericFraction>; /// Basic Decimal based on 2 u64 numbers and one u8 for precision. /// Able to keep up to 19 digits in the number (including /// both sides across the floating point). /// /// # Examples /// ``` /// use fraction::Decimal; /// /// let one = Decimal::from(152.568); /// let two = Decimal::from(328.76842); /// /// assert_eq!(one + two, Decimal::from("481.33642")); /// assert_eq!(two - one, Decimal::from("176.20042")); /// assert_eq!(one * two, Decimal::from("50159.5403")); /// assert_eq!(two / one, Decimal::from("2.15489")); /// // the result takes the max precision (between 5 and 8 it goes with 8) /// assert_eq!(two / one.set_precision(8), Decimal::from("2.15489761")) /// ``` #[cfg(feature = "with-decimal")] pub type Decimal = GenericDecimal; /// Heap allocated [`BigUint`] for numerics and `usize` for precision /// /// # Examples /// /// ``` /// use fraction::BigDecimal; /// /// let one = BigDecimal::from(11.5); /// let two = BigDecimal::from(30.5); /// /// assert_eq!(one + two, BigDecimal::from(42)); /// ``` /// /// [`BigUint`]: https://docs.rs/num-bigint/*/num_bigint/ #[cfg(all(feature = "with-decimal", feature = "with-bigint"))] pub type BigDecimal = GenericDecimal; /// Stack allocated, but dynamically growing into heap if necessary /// /// Allows to use decimals without memory allocations wherever possible. /// For unexpectedly big values performs on heap and doesn't suffer from stack overflows. /// Automatically goes back onto T if an operation with [`BigUint`] numbers produces the result /// that may fit within T. /// /// # Examples /// /// ``` /// use fraction::DynaDecimal; /// /// type D = DynaDecimal; /// /// let d1 = D::from("0.462046206206402"); /// let d2 = D::from(12042002442022044usize); /// /// let d3 = d2 / d1 * D::from(240); /// /// assert_eq!(d3, D::from("6254960104129183747.885873163232639")); /// ``` /// /// [`BigUint`]: https://docs.rs/num-bigint/*/num_bigint/ #[cfg(all( feature = "with-decimal", feature = "with-bigint", feature = "with-dynaint" ))] pub type DynaDecimal = GenericDecimal, P>; fraction-0.15.3/src/tests/mod.rs000064400000000000000000001533101046102023000146000ustar 00000000000000macro_rules! generate_ops_tests { ( Zero => $_0:block; One => $_1:block; Two => $_2:block; Three => $_3:block; Four => $_4:block; ) => { #[test] fn op_eq_i() { let nil = $_0; let one = $_1; let two = $_2; let thr = $_3; let four = $_4; assert_eq!(nil, nil); assert_eq!(nil, $_0); assert_eq!($_0, $_0); assert_eq!(one, one); assert_eq!(one, $_1); assert_eq!($_1, $_1); assert_eq!(two, two); assert_eq!(two, $_2); assert_eq!($_2, $_2); assert_eq!(thr, thr); assert_eq!(thr, $_3); assert_eq!($_3, $_3); assert_eq!(four, four); assert_eq!(four, $_4); assert_eq!($_4, $_4); } #[test] fn op_add_assign_i() { { let mut v = $_0; v += $_0; assert_eq!(v, $_0); } { let mut v = $_0; v += $_1; assert_eq!(v, $_1); } { let mut v = $_1; v += $_0; assert_eq!(v, $_1); } { let mut v = $_1; v += $_1; assert_eq!(v, $_2); } { let mut v = $_1; v += $_2; assert_eq!(v, $_3); } { let mut v = $_2; v += $_2; assert_eq!(v, $_4); } } #[test] fn op_add_assign_refs_i() { { let mut v = $_0; v += &$_0; assert_eq!(v, $_0); } { let mut v = $_0; v += &$_1; assert_eq!(v, $_1); } { let mut v = $_1; v += &$_0; assert_eq!(v, $_1); } { let mut v = $_1; v += &$_1; assert_eq!(v, $_2); } { let mut v = $_1; v += &$_2; assert_eq!(v, $_3); } { let mut v = $_2; v += &$_2; assert_eq!(v, $_4); } } #[test] fn op_checked_add_i() { assert_eq!($_0, $_0.checked_add(&$_0).unwrap()); assert_eq!($_1, $_1.checked_add(&$_0).unwrap()); assert_eq!($_1, $_0.checked_add(&$_1).unwrap()); assert_eq!($_2, $_2.checked_add(&$_0).unwrap()); assert_eq!($_2, $_0.checked_add(&$_2).unwrap()); assert_eq!($_2, $_1.checked_add(&$_1).unwrap()); assert_eq!($_3, $_3.checked_add(&$_0).unwrap()); assert_eq!($_3, $_2.checked_add(&$_1).unwrap()); assert_eq!($_3, $_1.checked_add(&$_2).unwrap()); assert_eq!($_4, $_0.checked_add(&$_4).unwrap()); assert_eq!($_4, $_4.checked_add(&$_0).unwrap()); assert_eq!($_4, $_2.checked_add(&$_2).unwrap()); assert_eq!($_4, $_1.checked_add(&$_3).unwrap()); assert_eq!($_4, $_3.checked_add(&$_1).unwrap()); } #[test] fn op_checked_div_i() { assert_eq!($_1, $_1.checked_div(&$_1).unwrap()); assert_eq!($_1, $_2.checked_div(&$_2).unwrap()); assert_eq!($_1, $_3.checked_div(&$_3).unwrap()); assert_eq!($_1, $_4.checked_div(&$_4).unwrap()); assert_eq!($_2, $_2.checked_div(&$_1).unwrap()); assert_eq!($_3, $_3.checked_div(&$_1).unwrap()); assert_eq!($_2, $_4.checked_div(&$_2).unwrap()); assert_eq!($_4, $_4.checked_div(&$_1).unwrap()); } #[test] fn op_checked_mul_i() { assert_eq!($_0, $_0.checked_mul(&$_0).unwrap()); assert_eq!($_0, $_1.checked_mul(&$_0).unwrap()); assert_eq!($_0, $_0.checked_mul(&$_1).unwrap()); assert_eq!($_0, $_2.checked_mul(&$_0).unwrap()); assert_eq!($_0, $_0.checked_mul(&$_2).unwrap()); assert_eq!($_1, $_1.checked_mul(&$_1).unwrap()); assert_eq!($_0, $_3.checked_mul(&$_0).unwrap()); assert_eq!($_2, $_2.checked_mul(&$_1).unwrap()); assert_eq!($_2, $_1.checked_mul(&$_2).unwrap()); assert_eq!($_0, $_0.checked_mul(&$_4).unwrap()); assert_eq!($_0, $_4.checked_mul(&$_0).unwrap()); assert_eq!($_4, $_2.checked_mul(&$_2).unwrap()); assert_eq!($_3, $_1.checked_mul(&$_3).unwrap()); assert_eq!($_3, $_3.checked_mul(&$_1).unwrap()); } #[test] fn op_checked_sub_i() { assert_eq!($_0, $_0.checked_sub(&$_0).unwrap()); assert_eq!($_0, $_1.checked_sub(&$_1).unwrap()); assert_eq!($_0, $_2.checked_sub(&$_2).unwrap()); assert_eq!($_0, $_3.checked_sub(&$_3).unwrap()); assert_eq!($_0, $_4.checked_sub(&$_4).unwrap()); assert_eq!($_1, $_1.checked_sub(&$_0).unwrap()); assert_eq!($_1, $_2.checked_sub(&$_1).unwrap()); assert_eq!($_1, $_3.checked_sub(&$_2).unwrap()); assert_eq!($_1, $_4.checked_sub(&$_3).unwrap()); assert_eq!($_2, $_2.checked_sub(&$_0).unwrap()); assert_eq!($_2, $_3.checked_sub(&$_1).unwrap()); assert_eq!($_2, $_4.checked_sub(&$_2).unwrap()); assert_eq!($_3, $_3.checked_sub(&$_0).unwrap()); assert_eq!($_3, $_4.checked_sub(&$_1).unwrap()); assert_eq!($_4, $_4.checked_sub(&$_0).unwrap()); } #[test] fn op_add_i() { assert_eq!($_0, &$_0 + &$_0); assert_eq!($_0, $_0 + $_0); assert_eq!($_1, &$_1 + &$_0); assert_eq!($_1, $_1 + $_0); assert_eq!($_1, &$_0 + &$_1); assert_eq!($_1, $_0 + $_1); assert_eq!($_2, &$_2 + &$_0); assert_eq!($_2, $_2 + $_0); assert_eq!($_2, &$_0 + &$_2); assert_eq!($_2, $_0 + $_2); assert_eq!($_2, &$_1 + &$_1); assert_eq!($_2, $_1 + $_1); assert_eq!($_3, &$_3 + &$_0); assert_eq!($_3, $_3 + $_0); assert_eq!($_3, &$_2 + &$_1); assert_eq!($_3, $_2 + $_1); assert_eq!($_3, &$_1 + &$_2); assert_eq!($_3, $_1 + $_2); assert_eq!($_4, &$_0 + &$_4); assert_eq!($_4, $_0 + $_4); assert_eq!($_4, &$_4 + &$_0); assert_eq!($_4, $_4 + $_0); assert_eq!($_4, &$_2 + &$_2); assert_eq!($_4, $_2 + $_2); assert_eq!($_4, &$_1 + &$_3); assert_eq!($_4, $_1 + $_3); assert_eq!($_4, &$_3 + &$_1); assert_eq!($_4, $_3 + $_1); assert_eq!($_4, $_2 + $_1 + $_1); assert_eq!($_4, $_1 + $_1 + $_1 + $_1); } #[test] fn op_sub_assign_i() { { let mut v = $_0; v -= $_0; assert_eq!(v, $_0); } { let mut v = $_1; v -= $_0; assert_eq!(v, $_1); } { let mut v = $_1; v -= $_1; assert_eq!(v, $_0); } { let mut v = $_2; v -= $_2; assert_eq!(v, $_0); } { let mut v = $_2; v -= $_1; assert_eq!(v, $_1); } { let mut v = $_2; v -= $_0; assert_eq!(v, $_2); } { let mut v = $_3; v -= $_2; assert_eq!(v, $_1); } { let mut v = $_3; v -= $_1; assert_eq!(v, $_2); } { let mut v = $_3; v -= $_0; assert_eq!(v, $_3); } { let mut v = $_4; v -= $_4; assert_eq!(v, $_0); } { let mut v = $_4; v -= $_3; assert_eq!(v, $_1); } { let mut v = $_4; v -= $_2; assert_eq!(v, $_2); } { let mut v = $_4; v -= $_1; assert_eq!(v, $_3); } { let mut v = $_4; v -= $_0; assert_eq!(v, $_4); } } #[test] fn op_sub_assign_refs_i() { { let mut v = $_0; v -= &$_0; assert_eq!(v, $_0); } { let mut v = $_1; v -= &$_0; assert_eq!(v, $_1); } { let mut v = $_1; v -= &$_1; assert_eq!(v, $_0); } { let mut v = $_2; v -= &$_2; assert_eq!(v, $_0); } { let mut v = $_2; v -= &$_1; assert_eq!(v, $_1); } { let mut v = $_2; v -= &$_0; assert_eq!(v, $_2); } { let mut v = $_3; v -= &$_2; assert_eq!(v, $_1); } { let mut v = $_3; v -= &$_1; assert_eq!(v, $_2); } { let mut v = $_3; v -= &$_0; assert_eq!(v, $_3); } { let mut v = $_4; v -= &$_4; assert_eq!(v, $_0); } { let mut v = $_4; v -= &$_3; assert_eq!(v, $_1); } { let mut v = $_4; v -= &$_2; assert_eq!(v, $_2); } { let mut v = $_4; v -= &$_1; assert_eq!(v, $_3); } { let mut v = $_4; v -= &$_0; assert_eq!(v, $_4); } } #[test] fn op_sub_i() { assert_eq!($_0, $_0 - $_0); assert_eq!($_0, &$_0 - &$_0); assert_eq!($_0, $_1 - $_1); assert_eq!($_0, &$_1 - &$_1); assert_eq!($_0, $_2 - $_2); assert_eq!($_0, &$_2 - &$_2); assert_eq!($_0, $_3 - $_3); assert_eq!($_0, &$_3 - &$_3); assert_eq!($_0, $_4 - $_4); assert_eq!($_0, &$_4 - &$_4); assert_eq!($_1, &$_1 - &$_0); assert_eq!($_1, $_1 - $_0); assert_eq!($_1, &$_2 - &$_1); assert_eq!($_1, $_2 - $_1); assert_eq!($_1, &$_3 - &$_2); assert_eq!($_1, $_3 - $_2); assert_eq!($_1, &$_4 - &$_3); assert_eq!($_1, $_4 - $_3); assert_eq!($_2, &$_2 - &$_0); assert_eq!($_2, $_2 - $_0); assert_eq!($_2, &$_3 - &$_1); assert_eq!($_2, $_3 - $_1); assert_eq!($_2, &$_4 - &$_2); assert_eq!($_2, $_4 - $_2); assert_eq!($_3, &$_3 - &$_0); assert_eq!($_3, $_3 - $_0); assert_eq!($_3, &$_4 - &$_1); assert_eq!($_3, $_4 - $_1); assert_eq!($_4, &$_4 - &$_0); assert_eq!($_4, $_4 - $_0); } #[test] fn op_mul_assign_i() { { let mut v = $_0; v *= $_0; assert_eq!(v, $_0); } { let mut v = $_0; v *= $_1; assert_eq!(v, $_0); } { let mut v = $_1; v *= $_0; assert_eq!(v, $_0); } { let mut v = $_1; v *= $_1; assert_eq!(v, $_1); } { let mut v = $_1; v *= $_2; assert_eq!(v, $_2); } { let mut v = $_1; v *= $_3; assert_eq!(v, $_3); } } #[test] fn op_mul_assign_refs_i() { { let mut v = $_0; v *= &$_0; assert_eq!(v, $_0); } { let mut v = $_0; v *= &$_1; assert_eq!(v, $_0); } { let mut v = $_1; v *= &$_0; assert_eq!(v, $_0); } { let mut v = $_1; v *= &$_1; assert_eq!(v, $_1); } { let mut v = $_1; v *= &$_2; assert_eq!(v, $_2); } { let mut v = $_1; v *= &$_3; assert_eq!(v, $_3); } } #[test] fn op_mul_i() { assert_eq!($_0, &$_0 * &$_0); assert_eq!($_0, $_0 * $_0); assert_eq!($_0, &$_1 * &$_0); assert_eq!($_0, $_1 * $_0); assert_eq!($_0, &$_0 * &$_1); assert_eq!($_0, $_0 * $_1); assert_eq!($_0, &$_2 * &$_0); assert_eq!($_0, $_2 * $_0); assert_eq!($_0, &$_0 * &$_2); assert_eq!($_0, $_0 * $_2); assert_eq!($_1, &$_1 * &$_1); assert_eq!($_1, $_1 * $_1); assert_eq!($_0, &$_3 * &$_0); assert_eq!($_0, $_3 * $_0); assert_eq!($_2, &$_2 * &$_1); assert_eq!($_2, $_2 * $_1); assert_eq!($_2, &$_1 * &$_2); assert_eq!($_2, $_1 * $_2); assert_eq!($_0, &$_0 * &$_4); assert_eq!($_0, $_0 * $_4); assert_eq!($_0, &$_4 * &$_0); assert_eq!($_0, $_4 * $_0); assert_eq!($_4, &$_2 * &$_2); assert_eq!($_4, $_2 * $_2); assert_eq!($_3, &$_1 * &$_3); assert_eq!($_3, $_1 * $_3); assert_eq!($_3, &$_3 * &$_1); assert_eq!($_3, $_3 * $_1); assert_eq!($_2, $_2 * $_1 * $_1); assert_eq!($_1, $_1 * $_1 * $_1 * $_1); } #[test] fn op_div_assign_i() { { let mut v = $_0; v /= $_1; assert_eq!(v, $_0); } { let mut v = $_1; v /= $_1; assert_eq!(v, $_1); } { let mut v = $_2; v /= $_2; assert_eq!(v, $_1); } { let mut v = $_2; v /= $_1; assert_eq!(v, $_2); } { let mut v = $_0; v /= $_2; assert_eq!(v, $_0); } { let mut v = $_3; v /= $_1; assert_eq!(v, $_3); } { let mut v = $_0; v /= $_3; assert_eq!(v, $_0); } { let mut v = $_4; v /= $_4; assert_eq!(v, $_1); } { let mut v = $_4; v /= $_2; assert_eq!(v, $_2); } { let mut v = $_4; v /= $_1; assert_eq!(v, $_4); } { let mut v = $_0; v /= $_4; assert_eq!(v, $_0); } } #[test] fn op_div_assign_refs_i() { { let mut v = $_0; v /= &$_1; assert_eq!(v, $_0); } { let mut v = $_1; v /= &$_1; assert_eq!(v, $_1); } { let mut v = $_2; v /= &$_2; assert_eq!(v, $_1); } { let mut v = $_2; v /= &$_1; assert_eq!(v, $_2); } { let mut v = $_0; v /= &$_2; assert_eq!(v, $_0); } { let mut v = $_3; v /= &$_1; assert_eq!(v, $_3); } { let mut v = $_0; v /= &$_3; assert_eq!(v, $_0); } { let mut v = $_4; v /= &$_4; assert_eq!(v, $_1); } { let mut v = $_4; v /= &$_2; assert_eq!(v, $_2); } { let mut v = $_4; v /= &$_1; assert_eq!(v, $_4); } { let mut v = $_0; v /= &$_4; assert_eq!(v, $_0); } } #[test] fn op_div_i() { assert_eq!($_1, $_1 / $_1); assert_eq!($_1, &$_1 / &$_1); assert_eq!($_1, $_2 / $_2); assert_eq!($_1, &$_2 / &$_2); assert_eq!($_1, $_3 / $_3); assert_eq!($_1, &$_3 / &$_3); assert_eq!($_1, $_4 / $_4); assert_eq!($_1, &$_4 / &$_4); assert_eq!($_2, &$_2 / &$_1); assert_eq!($_2, $_2 / $_1); assert_eq!($_3, &$_3 / &$_1); assert_eq!($_3, $_3 / $_1); assert_eq!($_2, &$_4 / &$_2); assert_eq!($_2, $_4 / $_2); assert_eq!($_4, &$_4 / &$_1); assert_eq!($_4, $_4 / $_1); } #[test] fn op_rem_assign_i() { { let mut v = $_1; v %= $_1; assert_eq!(v, $_0); } { let mut v = $_2; v %= $_2; assert_eq!(v, $_0); } { let mut v = $_2; v %= $_1; assert_eq!(v, $_0); } { let mut v = $_3; v %= $_2; assert_eq!(v, $_1); } { let mut v = $_3; v %= $_1; assert_eq!(v, $_0); } { let mut v = $_4; v %= $_4; assert_eq!(v, $_0); } { let mut v = $_4; v %= $_3; assert_eq!(v, $_1); } { let mut v = $_4; v %= $_2; assert_eq!(v, $_0); } { let mut v = $_4; v %= $_1; assert_eq!(v, $_0); } } #[test] fn op_rem_assign_refs_i() { { let mut v = $_1; v %= &$_1; assert_eq!(v, $_0); } { let mut v = $_2; v %= &$_2; assert_eq!(v, $_0); } { let mut v = $_2; v %= &$_1; assert_eq!(v, $_0); } { let mut v = $_3; v %= &$_2; assert_eq!(v, $_1); } { let mut v = $_3; v %= &$_1; assert_eq!(v, $_0); } { let mut v = $_4; v %= &$_4; assert_eq!(v, $_0); } { let mut v = $_4; v %= &$_3; assert_eq!(v, $_1); } { let mut v = $_4; v %= &$_2; assert_eq!(v, $_0); } { let mut v = $_4; v %= &$_1; assert_eq!(v, $_0); } } #[test] fn op_rem_i() { assert_eq!($_0, $_1 % $_1); assert_eq!($_0, &$_1 % &$_1); assert_eq!($_0, $_2 % $_2); assert_eq!($_0, &$_2 % &$_2); assert_eq!($_0, $_3 % $_3); assert_eq!($_0, &$_3 % &$_3); assert_eq!($_0, $_4 % $_4); assert_eq!($_0, &$_4 % &$_4); assert_eq!($_0, &$_2 % &$_1); assert_eq!($_0, $_2 % $_1); assert_eq!($_1, &$_3 % &$_2); assert_eq!($_1, $_3 % $_2); assert_eq!($_1, &$_4 % &$_3); assert_eq!($_1, $_4 % $_3); assert_eq!($_0, &$_3 % &$_1); assert_eq!($_0, $_3 % $_1); assert_eq!($_0, &$_4 % &$_2); assert_eq!($_0, $_4 % $_2); assert_eq!($_0, &$_4 % &$_1); assert_eq!($_0, $_4 % $_1); } }; ( NaN => $nan:block; NegInf => $nin:block; PosInf => $pin:block; Zero => $_0:block; Half => $half:block; One => $_1:block; Two => $_2:block; Three => $_3:block; Four => $_4:block; ) => { generate_ops_tests!( Zero => $_0; One => $_1; Two => $_2; Three => $_3; Four => $_4; ); #[test] fn op_add_assign() { let nan = $nan; let nin = $nin; let pin = $pin; let nil = $_0; let one = $_1; let mut nan_ = $nan; nan_ += nan; assert_eq!(nan, nan_); nan_ += nin; assert_eq!(nan, nan_); nan_ += pin; assert_eq!(nan, nan_); nan_ += nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ += nil_; assert_eq!(nil, nil_); nil_ += one; assert_eq!(one, nil_); } #[test] fn op_add_assign_refs() { let nan = $nan; let nin = $nin; let pin = $pin; let nil = $_0; let one = $_1; let mut nan_ = $nan; nan_ += &nan; assert_eq!(nan, nan_); nan_ += &nin; assert_eq!(nan, nan_); nan_ += &pin; assert_eq!(nan, nan_); nan_ += &nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ += &nil; assert_eq!(nil, nil_); nil_ += &one; assert_eq!(one, nil_); } #[test] fn op_add() { let nan = $nan; let ninf = $nin; let pinf = $pin; assert_eq!(nan, nan); assert_eq!(nan, &nan + &nan); assert_eq!(nan, nan + nan); assert_eq!(nan, &nan + &ninf); assert_eq!(nan, nan + ninf); assert_eq!(nan, &nan + &pinf); assert_eq!(nan, nan + pinf); assert_eq!(nan, &ninf + &nan); assert_eq!(nan, ninf + nan); assert_eq!(nan, &pinf + &nan); assert_eq!(nan, pinf + nan); assert_eq!(ninf, &ninf + &ninf); assert_eq!(ninf, ninf + ninf); assert_eq!(pinf, &pinf + &pinf); assert_eq!(pinf, pinf + pinf); assert_eq!(nan, &ninf + &pinf); assert_eq!(nan, ninf + pinf); assert_eq!(nan, &pinf + &ninf); assert_eq!(nan, pinf + ninf); let nil = $_0; assert_eq!(nil, nil); assert_eq!(nil, &nil + &nil); assert_eq!(nil, nil + nil); assert_eq!(nan, &nil + &nan); assert_eq!(nan, nil + nan); assert_eq!(nan, &nan + &nil); assert_eq!(nan, nan + nil); assert_eq!(ninf, &nil + &ninf); assert_eq!(ninf, nil + ninf); assert_eq!(ninf, &ninf + &nil); assert_eq!(ninf, ninf + nil); assert_eq!(pinf, &nil + &pinf); assert_eq!(pinf, nil + pinf); assert_eq!(pinf, &pinf + &nil); assert_eq!(pinf, pinf + nil); let one = $_1; assert_eq!(one, one); assert_eq!(one, &one + &nil); assert_eq!(one, one + nil); assert_eq!(one, &nil + &one); assert_eq!(one, nil + one); assert_eq!(nan, &one + &nan); assert_eq!(nan, one + nan); assert_eq!(nan, &nan + &one); assert_eq!(nan, nan + one); assert_eq!(ninf, &one + &ninf); assert_eq!(ninf, one + ninf); assert_eq!(ninf, &ninf + &one); assert_eq!(ninf, ninf + one); assert_eq!(pinf, &one + &pinf); assert_eq!(pinf, one + pinf); assert_eq!(pinf, &pinf + &one); assert_eq!(pinf, pinf + one); let two = $_2; assert_eq!(two, two); assert_eq!(two, &two + &nil); assert_eq!(two, two + nil); assert_eq!(two, &nil + &two); assert_eq!(two, nil + two); assert_eq!(two, &one + &one); assert_eq!(two, one + one); assert_eq!(nan, &two + &nan); assert_eq!(nan, two + nan); assert_eq!(nan, &nan + &two); assert_eq!(nan, nan + two); assert_eq!(ninf, &two + &ninf); assert_eq!(ninf, two + ninf); assert_eq!(ninf, &ninf + &two); assert_eq!(ninf, ninf + two); assert_eq!(pinf, &two + &pinf); assert_eq!(pinf, two + pinf); assert_eq!(pinf, &pinf + &two); assert_eq!(pinf, pinf + two); let mnil = -nil; assert_eq!(mnil, mnil); assert_eq!(mnil, nil); assert_eq!(mnil, &nil + &nil); assert_eq!(mnil, nil + nil); assert_eq!(mnil, &mnil + &mnil); assert_eq!(mnil, mnil + mnil); assert_eq!(mnil, &mnil + &nil); assert_eq!(mnil, mnil + nil); assert_eq!(mnil, &nil + &mnil); assert_eq!(mnil, nil + mnil); assert_eq!(nan, &mnil + &nan); assert_eq!(nan, mnil + nan); assert_eq!(nan, &nan + &mnil); assert_eq!(nan, nan + mnil); assert_eq!(ninf, &mnil + &ninf); assert_eq!(ninf, mnil + ninf); assert_eq!(ninf, &ninf + &mnil); assert_eq!(ninf, ninf + mnil); assert_eq!(pinf, &mnil + &pinf); assert_eq!(pinf, mnil + pinf); assert_eq!(pinf, &pinf + &mnil); assert_eq!(pinf, pinf + mnil); let mone = -one; assert_eq!(mone, mone); assert_eq!(mone, &mone + &nil); assert_eq!(mone, mone + nil); assert_eq!(mone, &nil + &mone); assert_eq!(mone, nil + mone); assert_eq!(nan, &mone + &nan); assert_eq!(nan, mone + nan); assert_eq!(nan, &nan + &mone); assert_eq!(nan, nan + mone); assert_eq!(nil, &mone + &one); assert_eq!(nil, mone + one); assert_eq!(nil, &one + &mone); assert_eq!(nil, one + mone); assert_eq!(one, &mone + &two); assert_eq!(one, mone + two); assert_eq!(one, &two + &mone); assert_eq!(one, two + mone); assert_eq!(nan, &mone + &nan); assert_eq!(nan, mone + nan); assert_eq!(nan, &nan + &mone); assert_eq!(nan, nan + mone); assert_eq!(ninf, &mone + &ninf); assert_eq!(ninf, mone + ninf); assert_eq!(ninf, &ninf + &mone); assert_eq!(ninf, ninf + mone); assert_eq!(pinf, &mone + &pinf); assert_eq!(pinf, mone + pinf); assert_eq!(pinf, &pinf + &mone); assert_eq!(pinf, pinf + mone); let mtwo = -two; assert_eq!(mtwo, mtwo); assert_eq!(mtwo, &mtwo + &nil); assert_eq!(mtwo, mtwo + nil); assert_eq!(mtwo, &nil + &mtwo); assert_eq!(mtwo, nil + mtwo); assert_eq!(mtwo, &mtwo + &mnil); assert_eq!(mtwo, mtwo + mnil); assert_eq!(mtwo, &mnil + &mtwo); assert_eq!(mtwo, mnil + mtwo); assert_eq!(mone, &mtwo + &one); assert_eq!(mone, mtwo + one); assert_eq!(mone, &one + &mtwo); assert_eq!(mone, one + mtwo); assert_eq!(nil, &mtwo + &two); assert_eq!(nil, mtwo + two); assert_eq!(nil, &two + &mtwo); assert_eq!(nil, two + mtwo); assert_eq!(nan, &mtwo + &nan); assert_eq!(nan, mtwo + nan); assert_eq!(nan, &nan + &mtwo); assert_eq!(nan, nan + mtwo); assert_eq!(ninf, &mtwo + &ninf); assert_eq!(ninf, mtwo + ninf); assert_eq!(ninf, &ninf + &mtwo); assert_eq!(ninf, ninf + mtwo); assert_eq!(pinf, &mtwo + &pinf); assert_eq!(pinf, mtwo + pinf); assert_eq!(pinf, &pinf + &mtwo); assert_eq!(pinf, pinf + mtwo); } #[test] fn op_sub_assign() { let nan = $nan; let nin = $nin; let pin = $pin; let nil = $_0; let one = $_1; let mut nan_ = $nan; nan_ -= nan; assert_eq!(nan, nan_); nan_ -= nin; assert_eq!(nan, nan_); nan_ -= pin; assert_eq!(nan, nan_); nan_ -= nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ -= nil_; assert_eq!(nil, nil_); nil_ -= one; assert_eq!(-one, nil_); } #[test] fn op_sub_assign_refs() { let nan = $nan; let nin = $nin; let pin = $pin; let nil = $_0; let one = $_1; let mut nan_ = $nan; nan_ -= &nan; assert_eq!(nan, nan_); nan_ -= &nin; assert_eq!(nan, nan_); nan_ -= &pin; assert_eq!(nan, nan_); nan_ -= &nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ -= &nil; assert_eq!(nil, nil_); nil_ -= &one; assert_eq!(-one, nil_); } #[test] fn op_sub() { let nan = $nan; let ninf = $nin; let pinf = $pin; assert_eq!(nan, nan); assert_eq!(nan, &nan - &nan); assert_eq!(nan, nan - nan); assert_eq!(nan, &nan - &ninf); assert_eq!(nan, nan - ninf); assert_eq!(nan, &nan - &pinf); assert_eq!(nan, nan - pinf); assert_eq!(nan, &ninf - &nan); assert_eq!(nan, ninf - nan); assert_eq!(nan, &pinf - &nan); assert_eq!(nan, pinf - nan); assert_eq!(nan, &ninf - &ninf); assert_eq!(nan, ninf - ninf); assert_eq!(nan, &pinf - &pinf); assert_eq!(nan, pinf - pinf); assert_eq!(pinf, &pinf - &ninf); assert_eq!(pinf, pinf - ninf); assert_eq!(ninf, &ninf - &pinf); assert_eq!(ninf, ninf - pinf); let nil = $_0; assert_eq!(nil, nil); assert_eq!(nil, &nil - &nil); assert_eq!(nil, nil - nil); assert_eq!(nan, &nil - &nan); assert_eq!(nan, nil - nan); assert_eq!(nan, &nan - &nil); assert_eq!(nan, nan - nil); assert_eq!(pinf, &nil - &ninf); assert_eq!(pinf, nil - ninf); assert_eq!(ninf, &ninf - &nil); assert_eq!(ninf, ninf - nil); assert_eq!(ninf, &nil - &pinf); assert_eq!(ninf, nil - pinf); assert_eq!(pinf, &pinf - &nil); assert_eq!(pinf, pinf - nil); let one = $_1; let two = $_2; let mone = -one; let mtwo = -two; assert_eq!(one, one); assert_eq!(one, &one - &nil); assert_eq!(one, one - nil); assert_eq!(mone, &nil - &one); assert_eq!(mone, nil - one); assert_eq!(nan, &one - &nan); assert_eq!(nan, one - nan); assert_eq!(nan, &nan - &one); assert_eq!(nan, nan - one); assert_eq!(pinf, &one - &ninf); assert_eq!(pinf, one - ninf); assert_eq!(ninf, &ninf - &one); assert_eq!(ninf, ninf - one); assert_eq!(ninf, &one - &pinf); assert_eq!(ninf, one - pinf); assert_eq!(pinf, &pinf - &one); assert_eq!(pinf, pinf - one); assert_eq!(two, &one - &mone); assert_eq!(two, one - mone); assert_eq!(mtwo, &mone - &one); assert_eq!(mtwo, mone - one); } #[test] fn op_mul() { // let nan: Frac = GenericFraction::NaN; // let ninf: Frac = GenericFraction::Infinity(Sign::Minus); // let pinf: Frac = GenericFraction::Infinity(Sign::Plus); let nan = $nan; let ninf = $nin; let pinf = $pin; assert_eq!(nan, nan); assert_eq!(nan, &nan * &nan); assert_eq!(nan, nan * nan); assert_eq!(nan, &nan * &ninf); assert_eq!(nan, nan * ninf); assert_eq!(nan, &nan * &pinf); assert_eq!(nan, nan * pinf); assert_eq!(nan, &ninf * &nan); assert_eq!(nan, ninf * nan); assert_eq!(nan, &pinf * &nan); assert_eq!(nan, pinf * nan); assert_eq!(pinf, &ninf * &ninf); assert_eq!(pinf, ninf * ninf); assert_eq!(pinf, &pinf * &pinf); assert_eq!(pinf, pinf * pinf); assert_eq!(ninf, &pinf * &ninf); assert_eq!(ninf, pinf * ninf); assert_eq!(ninf, &ninf * &pinf); assert_eq!(ninf, ninf * pinf); // let nil = Frac::new(0, 1); let nil = $_0; assert_eq!(nil, nil); assert_eq!(nil, &nil * &nil); assert_eq!(nil, nil * nil); assert_eq!(nan, &nil * &nan); assert_eq!(nan, nil * nan); assert_eq!(nan, &nan * &nil); assert_eq!(nan, nan * nil); assert_eq!(nan, &nil * &ninf); assert_eq!(nan, nil * ninf); assert_eq!(nan, &ninf * &nil); assert_eq!(nan, ninf * nil); assert_eq!(nan, &nil * &pinf); assert_eq!(nan, nil * pinf); assert_eq!(nan, &pinf * &nil); assert_eq!(nan, pinf * nil); // let one = Frac::new(1, 1); let one = $_1; assert_eq!(one, one); assert_eq!(nil, &one * &nil); assert_eq!(nil, one * nil); assert_eq!(nil, &nil * &one); assert_eq!(nil, nil * one); assert_eq!(nan, &one * &nan); assert_eq!(nan, one * nan); assert_eq!(nan, &nan * &one); assert_eq!(nan, nan * one); assert_eq!(ninf, &one * &ninf); assert_eq!(ninf, one * ninf); assert_eq!(ninf, &ninf * &one); assert_eq!(ninf, ninf * one); assert_eq!(pinf, &one * &pinf); assert_eq!(pinf, one * pinf); assert_eq!(pinf, &pinf * &one); assert_eq!(pinf, pinf * one); // let two = Frac::new(2, 1); let two = $_2; assert_eq!(two, two); assert_eq!(nil, &two * &nil); assert_eq!(nil, two * nil); assert_eq!(nil, &nil * &two); assert_eq!(nil, nil * two); assert_eq!(one, &one * &one); assert_eq!(one, one * one); assert_eq!(nan, &two * &nan); assert_eq!(nan, two * nan); assert_eq!(nan, &nan * &two); assert_eq!(nan, nan * two); assert_eq!(ninf, &two * &ninf); assert_eq!(ninf, two * ninf); assert_eq!(ninf, &ninf * &two); assert_eq!(ninf, ninf * two); assert_eq!(pinf, &two * &pinf); assert_eq!(pinf, two * pinf); assert_eq!(pinf, &pinf * &two); assert_eq!(pinf, pinf * two); let mone = -one; let mtwo = -two; assert_eq!(mone, mone); assert_eq!(nil, &mone * &nil); assert_eq!(nil, mone * nil); assert_eq!(nil, &nil * &mone); assert_eq!(nil, nil * mone); assert_eq!(nan, &mone * &nan); assert_eq!(nan, mone * nan); assert_eq!(nan, &nan * &mone); assert_eq!(nan, nan * mone); assert_eq!(mone, &mone * &one); assert_eq!(mone, mone * one); assert_eq!(mone, &one * &mone); assert_eq!(mone, one * mone); assert_eq!(mtwo, &mone * &two); assert_eq!(mtwo, mone * two); assert_eq!(mtwo, &two * &mone); assert_eq!(mtwo, two * mone); assert_eq!(nan, &mtwo * &nan); assert_eq!(nan, mtwo * nan); assert_eq!(nan, &nan * &mtwo); assert_eq!(nan, nan * mtwo); assert_eq!(pinf, &mone * &ninf); assert_eq!(pinf, mone * ninf); assert_eq!(pinf, &ninf * &mone); assert_eq!(pinf, ninf * mone); assert_eq!(ninf, &mone * &pinf); assert_eq!(ninf, mone * pinf); assert_eq!(ninf, &pinf * &mone); assert_eq!(ninf, pinf * mone); } #[test] fn op_mul_assign() { let nan = $nan; // Frac::nan(); let nin = $nin; // Frac::neg_infinity(); let pin = $pin; // Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; // Frac::new(2, 1); let mut nan_ = $nan; // Frac::nan(); nan_ *= nan; assert_eq!(nan, nan_); nan_ *= nin; assert_eq!(nan, nan_); nan_ *= pin; assert_eq!(nan, nan_); nan_ *= nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ *= nil_; assert_eq!(nil, nil_); nil_ *= one; assert_eq!(nil_, nil); let mut one_ = one; one_ *= two; assert_eq!(one_, two); let mut one_ = one; one_ *= -two; assert_eq!(one_, -two); } #[test] fn op_mul_assign_refs() { let nan = $nan; // Frac::nan(); let nin = $nin; // Frac::neg_infinity(); let pin = $pin; // Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; // Frac::new(2, 1); let mut nan_ = $nan; // Frac::nan(); nan_ *= &nan; assert_eq!(nan, nan_); nan_ *= &nin; assert_eq!(nan, nan_); nan_ *= &pin; assert_eq!(nan, nan_); nan_ *= &nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ *= &nil; assert_eq!(nil, nil_); nil_ *= &one; assert_eq!(nil_, nil); let mut one_ = one; one_ *= &two; assert_eq!(one_, two); let mut one_ = one; one_ *= &-two; assert_eq!(one_, -two); } #[test] fn op_div() { // let nan: Frac = GenericFraction::NaN; // let ninf: Frac = GenericFraction::Infinity(Sign::Minus); // let pinf: Frac = GenericFraction::Infinity(Sign::Plus); let nan = $nan; let ninf = $nin; let pinf = $pin; assert_eq!(nan, nan); assert_eq!(nan, &nan / &nan); assert_eq!(nan, nan / nan); assert_eq!(nan, &nan / &ninf); assert_eq!(nan, nan / ninf); assert_eq!(nan, &nan / &pinf); assert_eq!(nan, nan / pinf); assert_eq!(nan, &ninf / &nan); assert_eq!(nan, ninf / nan); assert_eq!(nan, &pinf / &nan); assert_eq!(nan, pinf / nan); assert_eq!(nan, &ninf / &ninf); assert_eq!(nan, ninf / ninf); assert_eq!(nan, &pinf / &pinf); assert_eq!(nan, pinf / pinf); assert_eq!(nan, &pinf / &ninf); assert_eq!(nan, pinf / ninf); assert_eq!(nan, &ninf / &pinf); assert_eq!(nan, ninf / pinf); // let nil = Frac::new(0, 1); let nil = $_0; assert_eq!(nil, nil); assert_eq!(nan, &nil / &nil); assert_eq!(nan, nil / nil); assert_eq!(nan, &nil / &nan); assert_eq!(nan, nil / nan); assert_eq!(nan, &nan / &nil); assert_eq!(nan, nan / nil); assert_eq!(nil, &nil / &ninf); assert_eq!(nil, nil / ninf); assert_eq!(ninf, &ninf / &nil); assert_eq!(ninf, ninf / nil); assert_eq!(nil, &nil / &pinf); assert_eq!(nil, nil / pinf); assert_eq!(pinf, &pinf / &nil); assert_eq!(pinf, pinf / nil); // let one = Frac::new(1, 1); let one = $_1; assert_eq!(one, one); assert_eq!(one, &one / &one); assert_eq!(one, one / one); assert_eq!(pinf, &one / &nil); assert_eq!(pinf, one / nil); assert_eq!(nil, &nil / &one); assert_eq!(nil, nil / one); assert_eq!(nan, &one / &nan); assert_eq!(nan, one / nan); assert_eq!(nan, &nan / &one); assert_eq!(nan, nan / one); assert_eq!(nil, &one / &ninf); assert_eq!(nil, one / ninf); assert_eq!(ninf, &ninf / &one); assert_eq!(ninf, ninf / one); assert_eq!(nil, &one / &pinf); assert_eq!(nil, one / pinf); assert_eq!(pinf, &pinf / &one); assert_eq!(pinf, pinf / one); // let two = Frac::new(2, 1); let two = $_2; assert_eq!(two, two); assert_eq!(pinf, &two / &nil); assert_eq!(pinf, two / nil); assert_eq!(nil, &nil / &two); assert_eq!(nil, nil / two); assert_eq!(nan, &two / &nan); assert_eq!(nan, two / nan); assert_eq!(nan, &nan / &two); assert_eq!(nan, nan / two); assert_eq!(nil, &two / &ninf); assert_eq!(nil, two / ninf); assert_eq!(ninf, &ninf / &two); assert_eq!(ninf, ninf / two); assert_eq!(nil, &two / &pinf); assert_eq!(nil, two / pinf); assert_eq!(pinf, &pinf / &two); assert_eq!(pinf, pinf / two); let mone = -one; let mtwo = -two; assert_eq!(mone, mone); assert_eq!(ninf, &mone / &nil); assert_eq!(ninf, mone / nil); assert_eq!(nil, &nil / &mone); assert_eq!(nil, nil / mone); assert_eq!(nan, &mone / &nan); assert_eq!(nan, mone / nan); assert_eq!(nan, &nan / &mone); assert_eq!(nan, nan / mone); assert_eq!(mone, &mone / &one); assert_eq!(mone, mone / one); assert_eq!(mone, &one / &mone); assert_eq!(mone, one / mone); assert_eq!(format!("{:.1}", -$half), format!("{:.1}", &mone / &two)); assert_eq!(format!("{:.1}", -$half), format!("{:.1}", mone / two)); assert_eq!(mtwo, &two / &mone); assert_eq!(mtwo, two / mone); assert_eq!(nan, &mtwo / &nan); assert_eq!(nan, mtwo / nan); assert_eq!(nan, &nan / &mtwo); assert_eq!(nan, nan / mtwo); assert_eq!(nil, &mone / &ninf); assert_eq!(nil, mone / ninf); assert_eq!(pinf, &ninf / &mone); assert_eq!(pinf, ninf / mone); assert_eq!(nil, &mone / &pinf); assert_eq!(nil, mone / pinf); assert_eq!(ninf, &pinf / &mone); assert_eq!(ninf, pinf / mone); } #[test] fn op_div_assign() { let nan = $nan; // Frac::nan(); let nin = $nin; // Frac::neg_infinity(); let pin = $pin; // Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; // Frac::new(2, 1); let mut nan_ = $nan; // Frac::nan(); nan_ /= nan; assert_eq!(nan, nan_); nan_ /= nin; assert_eq!(nan, nan_); nan_ /= pin; assert_eq!(nan, nan_); nan_ /= nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ /= nil_; assert_eq!(nan, nil_); let mut nil_ = nil; nil_ /= one; assert_eq!(nil_, nil); let mut one_ = one; one_ /= two; assert_eq!(format!("{:.1}", one_), format!("{:.1}", $half)); let mut one_ = one; one_ /= -two; assert_eq!(format!("{:.1}", one_), format!("{:.1}", -$half)); } #[test] fn op_div_assign_refs() { let nan = $nan; // Frac::nan(); let nin = $nin; // Frac::neg_infinity(); let pin = $pin; // Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; // Frac::new(2, 1); let mut nan_ = $nan; // Frac::nan(); nan_ /= &nan; assert_eq!(nan, nan_); nan_ /= &nin; assert_eq!(nan, nan_); nan_ /= &pin; assert_eq!(nan, nan_); nan_ /= &nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ /= &nil; assert_eq!(nan, nil_); let mut nil_ = nil; nil_ /= &one; assert_eq!(nil_, nil); let mut one_ = one; one_ /= &two; assert_eq!(format!("{:.1}", one_), format!("{:.1}", $half)); let mut one_ = one; one_ /= &-two; assert_eq!(format!("{:.1}", one_), format!("{:.1}", -$half)); } #[test] fn op_rem() { //let nan: Frac = GenericFraction::NaN; //let ninf: Frac = GenericFraction::Infinity(Sign::Minus); //let pinf: Frac = GenericFraction::Infinity(Sign::Plus); let nan = $nan; let ninf = $nin; let pinf = $pin; assert_eq!(nan, nan); assert_eq!(nan, &nan % &nan); assert_eq!(nan, nan % nan); assert_eq!(nan, &nan % &ninf); assert_eq!(nan, nan % ninf); assert_eq!(nan, &nan % &pinf); assert_eq!(nan, nan % pinf); assert_eq!(nan, &ninf % &nan); assert_eq!(nan, ninf % nan); assert_eq!(nan, &pinf % &nan); assert_eq!(nan, pinf % nan); assert_eq!(nan, &ninf % &ninf); assert_eq!(nan, ninf % ninf); assert_eq!(nan, &pinf % &pinf); assert_eq!(nan, pinf % pinf); assert_eq!(nan, &pinf % &ninf); assert_eq!(nan, pinf % ninf); assert_eq!(nan, &ninf % &pinf); assert_eq!(nan, ninf % pinf); // let nil = Frac::new(0, 1); let nil = $_0; assert_eq!(nil, nil); assert_eq!(nan, &nil % &nil); assert_eq!(nan, nil % nil); assert_eq!(nan, &nil % &nan); assert_eq!(nan, nil % nan); assert_eq!(nan, &nan % &nil); assert_eq!(nan, nan % nil); assert_eq!(nil, &nil % &ninf); assert_eq!(nil, nil % ninf); assert_eq!(nan, &ninf % &nil); assert_eq!(nan, ninf % nil); assert_eq!(nil, &nil % &pinf); assert_eq!(nil, nil % pinf); assert_eq!(nan, &pinf % &nil); assert_eq!(nan, pinf % nil); // let one = Frac::new(1, 1); let one = $_1; assert_eq!(one, one); assert_eq!(nil, &one % &one); assert_eq!(nil, one % one); assert_eq!(nan, &one % &nil); assert_eq!(nan, one % nil); assert_eq!(nil, &nil % &one); assert_eq!(nil, nil % one); assert_eq!(nan, &one % &nan); assert_eq!(nan, one % nan); assert_eq!(nan, &nan % &one); assert_eq!(nan, nan % one); assert_eq!(one, &one % &ninf); assert_eq!(one, one % ninf); assert_eq!(nan, &ninf % &one); assert_eq!(nan, ninf % one); assert_eq!(one, &one % &pinf); assert_eq!(one, one % pinf); assert_eq!(nan, &pinf % &one); assert_eq!(nan, pinf % one); // let two = Frac::new(2, 1); let two = $_2; assert_eq!(two, two); assert_eq!(nan, &two % &nil); assert_eq!(nan, two % nil); assert_eq!(nil, &nil % &two); assert_eq!(nil, nil % two); assert_eq!(nan, &two % &nan); assert_eq!(nan, two % nan); assert_eq!(nan, &nan % &two); assert_eq!(nan, nan % two); assert_eq!(two, &two % &ninf); assert_eq!(two, two % ninf); assert_eq!(nan, &ninf % &two); assert_eq!(nan, ninf % two); assert_eq!(two, &two % &pinf); assert_eq!(two, two % pinf); assert_eq!(nan, &pinf % &two); assert_eq!(nan, pinf % two); let mone = -one; let mtwo = -two; assert_eq!(mone, mone); assert_eq!(nan, &mone % &nil); assert_eq!(nan, mone % nil); assert_eq!(nil, &nil % &mone); assert_eq!(nil, nil % mone); assert_eq!(nan, &mone % &nan); assert_eq!(nan, mone % nan); assert_eq!(nan, &nan % &mone); assert_eq!(nan, nan % mone); assert_eq!(nil, &mone % &one); assert_eq!(nil, mone % one); assert_eq!(nil, &one % &mone); assert_eq!(nil, one % mone); assert_eq!(mone, &mone % &two); assert_eq!(mone, mone % two); assert_eq!(nil, &two % &mone); assert_eq!(nil, two % mone); assert_eq!(nan, &mtwo % &nan); assert_eq!(nan, mtwo % nan); assert_eq!(nan, &nan % &mtwo); assert_eq!(nan, nan % mtwo); assert_eq!(mone, &mone % &ninf); assert_eq!(mone, mone % ninf); assert_eq!(nan, &ninf % &mone); assert_eq!(nan, ninf % mone); assert_eq!(mone, &mone % &pinf); assert_eq!(mone, mone % pinf); assert_eq!(nan, &pinf % &mone); assert_eq!(nan, pinf % mone); } #[test] fn op_rem_assign() { let nan = $nan; // Frac::nan(); let nin = $nin; // Frac::neg_infinity(); let pin = $pin; // Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; // Frac::new(2, 1); let thr = $_3; // Frac::new(3, 1); let mut nan_ = $nan; // Frac::nan(); nan_ %= nan; assert_eq!(nan, nan_); nan_ %= nin; assert_eq!(nan, nan_); nan_ %= pin; assert_eq!(nan, nan_); nan_ %= nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ %= nil_; assert_eq!(nan, nil_); let mut nil_ = nil; nil_ %= one; assert_eq!(nil_, nil); let mut one_ = one; one_ %= two; assert_eq!(one_, $_1); let mut one_ = one; one_ %= -two; assert_eq!(one_, $_1); let mut two_ = two; two_ %= two; assert_eq!(two_, nil); let mut thr_ = thr; thr_ %= two; assert_eq!(thr_, one); } #[test] fn op_rem_assign_refs() { let nan = $nan; //Frac::nan(); let nin = $nin; //Frac::neg_infinity(); let pin = $pin; //Frac::infinity(); let nil = $_0; // Frac::new(0, 1); let one = $_1; // Frac::new(1, 1); let two = $_2; //Frac::new(2, 1); let thr = $_3; // Frac::new(3, 1); let mut nan_ = $nan; //Frac::nan(); nan_ %= &nan; assert_eq!(nan, nan_); nan_ %= &nin; assert_eq!(nan, nan_); nan_ %= &pin; assert_eq!(nan, nan_); nan_ %= &nil; assert_eq!(nan, nan_); let mut nil_ = nil; nil_ %= &nil; assert_eq!(nan, nil_); let mut nil_ = nil; nil_ %= &one; assert_eq!(nil_, nil); let mut one_ = one; one_ %= &two; assert_eq!(one_, $_1); let mut one_ = one; one_ %= &-two; assert_eq!(one_, $_1); let mut two_ = two; two_ %= &two; assert_eq!(two_, nil); let mut thr_ = thr; thr_ %= &two; assert_eq!(thr_, one); } }; }