Skip to content

Commit

Permalink
Implement num_integer::Integer (#20)
Browse files Browse the repository at this point in the history
* Add num_integer dep #19

* Quick implementation of Integer easy parts #19

* Add gcd/lcm, tests for Integer divisions

* Add basic gcd/lcm tests

* Bump version number
  • Loading branch information
kaidokert authored Oct 12, 2021
1 parent 87f0efb commit 8a1389e
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 1 deletion.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fixed-bigint"
version = "0.1.5"
version = "0.1.6"
authors = ["kaidokert <[email protected]>"]
documentation = "https://docs.rs/fixed-bigint"
edition = "2018"
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 66 additions & 0 deletions src/fixeduint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -1248,6 +1250,70 @@ impl<T: MachineWord, const N: usize> num_traits::PrimInt for FixedUInt<T, N> {

// #endregion unimplemented

// #region num-integer::Integer

// Most code here from num_integer crate, unsigned implementation

impl<T: MachineWord, const N: usize> num_integer::Integer for FixedUInt<T, N> {
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;
Expand Down
94 changes: 94 additions & 0 deletions tests/integer.rs
Original file line number Diff line number Diff line change
@@ -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<T: num_integer::Integer + From<u8>>() {
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::<u8>();
even_odd::<FixedUInt<u8, 1>>();
even_odd::<FixedUInt<u8, 2>>();
even_odd::<FixedUInt<u16, 1>>();
}

#[test]
fn test_divides() {
fn divides<T: num_integer::Integer + From<u8>>() {
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(&divider);
assert_eq!(divres, div);
assert_eq!(remres, rem);

assert_eq!(multiple.div_floor(&divider), divres);
assert_eq!(multiple.mod_floor(&divider), remres);
}
}
divides::<u8>();
divides::<FixedUInt<u8, 1>>();
divides::<FixedUInt<u8, 2>>();
divides::<FixedUInt<u16, 1>>();
}

// TODO: Test GCD / LCM
#[test]
fn test_gcd_lcm() {
fn gcd_lcm<T: num_integer::Integer + From<u8>>() {
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::<u8>();
gcd_lcm::<FixedUInt<u8, 1>>();
gcd_lcm::<FixedUInt<u8, 2>>();
gcd_lcm::<FixedUInt<u16, 1>>();
}

0 comments on commit 8a1389e

Please sign in to comment.