diff --git a/Cargo.toml b/Cargo.toml index c0946e9..d3b00c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fixed-bigint" -version = "0.1.5" +version = "0.1.6" authors = ["kaidokert "] documentation = "https://docs.rs/fixed-bigint" edition = "2018" @@ -20,6 +20,10 @@ exclude = ["/.github/*"] version = "0.2.14" default-features = false +[dependencies.num-integer] +version = "0.1.44" +default-features = false + [profile.dev] opt-level = 0 debug = true diff --git a/README.md b/README.md index 44d6080..a4f7fea 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ The crate is written for `no_std` and `no_alloc` environments with option for pa The arithmetic operands ( +, -, .add() ) panic on overflow, just like native integer types. Panic-free alternatives like `overlowing_add` and `wrapping_add` are supported. +In addition to basic arithmetic, two main traits are implemented: [num_traits::PrimInt](https://docs.rs/num-traits/latest/num_traits/int/trait.PrimInt.html) and [num_integer::Integer](https://docs.rs/num/latest/num/integer/trait.Integer.html). + _TODO list_: * Implement experimental `unchecked_math` operands, unchecked_mul, unchecked_div etc. * Probably needs its own error structs instead of reusing core::fmt::Error and core::num::ParseIntError diff --git a/src/fixeduint.rs b/src/fixeduint.rs index f31576a..5b6076f 100644 --- a/src/fixeduint.rs +++ b/src/fixeduint.rs @@ -15,6 +15,8 @@ use num_traits::ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub}; use num_traits::{Bounded, One, PrimInt, ToPrimitive, Zero}; +use num_integer; + use core::convert::TryFrom; use core::fmt::Write; @@ -1248,6 +1250,70 @@ impl num_traits::PrimInt for FixedUInt { // #endregion unimplemented +// #region num-integer::Integer + +// Most code here from num_integer crate, unsigned implementation + +impl num_integer::Integer for FixedUInt { + fn div_floor(&self, other: &Self) -> Self { + *self / *other + } + fn mod_floor(&self, other: &Self) -> Self { + *self % *other + } + fn gcd(&self, other: &Self) -> Self { + // Use Stein's algorithm + let mut m = *self; + let mut n = *other; + let zero = Self::zero(); + if m == zero || n == zero { + return m | n; + } + + // find common factors of 2 + let shift = (m | n).trailing_zeros(); + + // divide n and m by 2 until odd + m = m >> m.trailing_zeros(); + n = n >> n.trailing_zeros(); + + while m != n { + if m > n { + m -= n; + m = m >> m.trailing_zeros(); + } else { + n -= m; + n = n >> n.trailing_zeros(); + } + } + m << shift + } + fn lcm(&self, other: &Self) -> Self { + if self.is_zero() && other.is_zero() { + return Self::zero(); + } + let gcd = self.gcd(other); + *self * (*other / gcd) + } + fn divides(&self, other: &Self) -> bool { + self.is_multiple_of(other) + } + fn is_multiple_of(&self, other: &Self) -> bool { + (*self) % other == Self::zero() + } + fn is_even(&self) -> bool { + (*self) & Self::one() == Self::zero() + } + fn is_odd(&self) -> bool { + !self.is_even() + } + fn div_rem(&self, other: &Self) -> (Self, Self) { + self.div_rem(other) + } +} + +// #endregion num-integer::Integer + #[cfg(test)] mod tests { use super::FixedUInt as Bn; diff --git a/tests/integer.rs b/tests/integer.rs new file mode 100644 index 0000000..c0c7542 --- /dev/null +++ b/tests/integer.rs @@ -0,0 +1,94 @@ +// Copyright 2021 Google LLC +// +// 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. + +#[cfg(test)] +use fixed_bigint::FixedUInt; +use num_integer::Integer; + +#[test] +fn test_evenodd() { + fn even_odd>() { + let tests = [(0u8, true), (1u8, false), (2u8, true), (255u8, false)]; + for &(num, even) in &tests { + let f: T = num.into(); + assert_eq!(f.is_even(), even); + assert_eq!(f.is_odd(), !even); + } + } + even_odd::(); + even_odd::>(); + even_odd::>(); + even_odd::>(); +} + +#[test] +fn test_divides() { + fn divides>() { + let tests = [(6u8, 3u8, true), (8, 2, true), (8, 1, true), (17, 2, false)]; + for &(multiple, multiplier, expected) in &tests { + assert_eq!(multiple.is_multiple_of(&multiplier), expected); + assert_eq!(multiple.divides(&multiplier), expected); + } + let divrem = [ + (6u8, 3u8, 2u8, 0u8), + (8, 2, 4, 0), + (7, 2, 3, 1), + (89, 13, 6, 11), + ]; + for &(multiple, divider, div, rem) in &divrem { + let (divres, remres) = multiple.div_rem(÷r); + assert_eq!(divres, div); + assert_eq!(remres, rem); + + assert_eq!(multiple.div_floor(÷r), divres); + assert_eq!(multiple.mod_floor(÷r), remres); + } + } + divides::(); + divides::>(); + divides::>(); + divides::>(); +} + +// TODO: Test GCD / LCM +#[test] +fn test_gcd_lcm() { + fn gcd_lcm>() { + let gcd_tests = [ + (8u8, 12u8, 4u8), + (1, 1, 1), + (100, 100, 100), + (99, 98, 1), + (99, 90, 9), + ]; + for &(a, b, expected) in &gcd_tests { + assert_eq!(a.gcd(&b), expected); + } + let lcm_tests = [ + (10u8, 2u8, 10u8), + (1, 1, 1), + (4, 6, 12), + (7, 12, 84), + (14, 12, 84), + (255, 255, 255), + ]; + for &(a, b, expected) in &lcm_tests { + assert_eq!(a.lcm(&b), expected); + } + } + gcd_lcm::(); + gcd_lcm::>(); + gcd_lcm::>(); + gcd_lcm::>(); +}