Skip to content

Commit 8a1389e

Browse files
authored
Implement num_integer::Integer (#20)
* 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
1 parent 87f0efb commit 8a1389e

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fixed-bigint"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
authors = ["kaidokert <[email protected]>"]
55
documentation = "https://docs.rs/fixed-bigint"
66
edition = "2018"
@@ -20,6 +20,10 @@ exclude = ["/.github/*"]
2020
version = "0.2.14"
2121
default-features = false
2222

23+
[dependencies.num-integer]
24+
version = "0.1.44"
25+
default-features = false
26+
2327
[profile.dev]
2428
opt-level = 0
2529
debug = true

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ The crate is written for `no_std` and `no_alloc` environments with option for pa
1717

1818
The arithmetic operands ( +, -, .add() ) panic on overflow, just like native integer types. Panic-free alternatives like `overlowing_add` and `wrapping_add` are supported.
1919

20+
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).
21+
2022
_TODO list_:
2123
* Implement experimental `unchecked_math` operands, unchecked_mul, unchecked_div etc.
2224
* Probably needs its own error structs instead of reusing core::fmt::Error and core::num::ParseIntError

src/fixeduint.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use num_traits::ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub};
1616
use num_traits::{Bounded, One, PrimInt, ToPrimitive, Zero};
1717

18+
use num_integer;
19+
1820
use core::convert::TryFrom;
1921
use core::fmt::Write;
2022

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

12491251
// #endregion unimplemented
12501252

1253+
// #region num-integer::Integer
1254+
1255+
// Most code here from num_integer crate, unsigned implementation
1256+
1257+
impl<T: MachineWord, const N: usize> num_integer::Integer for FixedUInt<T, N> {
1258+
fn div_floor(&self, other: &Self) -> Self {
1259+
*self / *other
1260+
}
1261+
fn mod_floor(&self, other: &Self) -> Self {
1262+
*self % *other
1263+
}
1264+
fn gcd(&self, other: &Self) -> Self {
1265+
// Use Stein's algorithm
1266+
let mut m = *self;
1267+
let mut n = *other;
1268+
let zero = Self::zero();
1269+
if m == zero || n == zero {
1270+
return m | n;
1271+
}
1272+
1273+
// find common factors of 2
1274+
let shift = (m | n).trailing_zeros();
1275+
1276+
// divide n and m by 2 until odd
1277+
m = m >> m.trailing_zeros();
1278+
n = n >> n.trailing_zeros();
1279+
1280+
while m != n {
1281+
if m > n {
1282+
m -= n;
1283+
m = m >> m.trailing_zeros();
1284+
} else {
1285+
n -= m;
1286+
n = n >> n.trailing_zeros();
1287+
}
1288+
}
1289+
m << shift
1290+
}
1291+
fn lcm(&self, other: &Self) -> Self {
1292+
if self.is_zero() && other.is_zero() {
1293+
return Self::zero();
1294+
}
1295+
let gcd = self.gcd(other);
1296+
*self * (*other / gcd)
1297+
}
1298+
fn divides(&self, other: &Self) -> bool {
1299+
self.is_multiple_of(other)
1300+
}
1301+
fn is_multiple_of(&self, other: &Self) -> bool {
1302+
(*self) % other == Self::zero()
1303+
}
1304+
fn is_even(&self) -> bool {
1305+
(*self) & Self::one() == Self::zero()
1306+
}
1307+
fn is_odd(&self) -> bool {
1308+
!self.is_even()
1309+
}
1310+
fn div_rem(&self, other: &Self) -> (Self, Self) {
1311+
self.div_rem(other)
1312+
}
1313+
}
1314+
1315+
// #endregion num-integer::Integer
1316+
12511317
#[cfg(test)]
12521318
mod tests {
12531319
use super::FixedUInt as Bn;

tests/integer.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#[cfg(test)]
16+
use fixed_bigint::FixedUInt;
17+
use num_integer::Integer;
18+
19+
#[test]
20+
fn test_evenodd() {
21+
fn even_odd<T: num_integer::Integer + From<u8>>() {
22+
let tests = [(0u8, true), (1u8, false), (2u8, true), (255u8, false)];
23+
for &(num, even) in &tests {
24+
let f: T = num.into();
25+
assert_eq!(f.is_even(), even);
26+
assert_eq!(f.is_odd(), !even);
27+
}
28+
}
29+
even_odd::<u8>();
30+
even_odd::<FixedUInt<u8, 1>>();
31+
even_odd::<FixedUInt<u8, 2>>();
32+
even_odd::<FixedUInt<u16, 1>>();
33+
}
34+
35+
#[test]
36+
fn test_divides() {
37+
fn divides<T: num_integer::Integer + From<u8>>() {
38+
let tests = [(6u8, 3u8, true), (8, 2, true), (8, 1, true), (17, 2, false)];
39+
for &(multiple, multiplier, expected) in &tests {
40+
assert_eq!(multiple.is_multiple_of(&multiplier), expected);
41+
assert_eq!(multiple.divides(&multiplier), expected);
42+
}
43+
let divrem = [
44+
(6u8, 3u8, 2u8, 0u8),
45+
(8, 2, 4, 0),
46+
(7, 2, 3, 1),
47+
(89, 13, 6, 11),
48+
];
49+
for &(multiple, divider, div, rem) in &divrem {
50+
let (divres, remres) = multiple.div_rem(&divider);
51+
assert_eq!(divres, div);
52+
assert_eq!(remres, rem);
53+
54+
assert_eq!(multiple.div_floor(&divider), divres);
55+
assert_eq!(multiple.mod_floor(&divider), remres);
56+
}
57+
}
58+
divides::<u8>();
59+
divides::<FixedUInt<u8, 1>>();
60+
divides::<FixedUInt<u8, 2>>();
61+
divides::<FixedUInt<u16, 1>>();
62+
}
63+
64+
// TODO: Test GCD / LCM
65+
#[test]
66+
fn test_gcd_lcm() {
67+
fn gcd_lcm<T: num_integer::Integer + From<u8>>() {
68+
let gcd_tests = [
69+
(8u8, 12u8, 4u8),
70+
(1, 1, 1),
71+
(100, 100, 100),
72+
(99, 98, 1),
73+
(99, 90, 9),
74+
];
75+
for &(a, b, expected) in &gcd_tests {
76+
assert_eq!(a.gcd(&b), expected);
77+
}
78+
let lcm_tests = [
79+
(10u8, 2u8, 10u8),
80+
(1, 1, 1),
81+
(4, 6, 12),
82+
(7, 12, 84),
83+
(14, 12, 84),
84+
(255, 255, 255),
85+
];
86+
for &(a, b, expected) in &lcm_tests {
87+
assert_eq!(a.lcm(&b), expected);
88+
}
89+
}
90+
gcd_lcm::<u8>();
91+
gcd_lcm::<FixedUInt<u8, 1>>();
92+
gcd_lcm::<FixedUInt<u8, 2>>();
93+
gcd_lcm::<FixedUInt<u16, 1>>();
94+
}

0 commit comments

Comments
 (0)