Reorganize somewhat, add more fraction tests
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
This commit is contained in:
parent
44467df38f
commit
cbf66916f9
@ -1,4 +1,4 @@
|
||||
//! Arbitrarily large integers
|
||||
//! \[WIP\] Arbitrarily large integers
|
||||
//!
|
||||
//! Traits to implement:
|
||||
//! * Add
|
||||
@ -14,7 +14,7 @@
|
||||
//! * SubAssign
|
||||
use crate::num::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BigInt {
|
||||
inner: Vec<usize>,
|
||||
sign: Sign,
|
||||
@ -41,6 +41,18 @@ impl<T: Unsigned> From<T> for BigInt {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for BigInt {
|
||||
fn from(_: &str) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for BigInt {
|
||||
fn from(_: String) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl BigInt {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
|
123
src/lib.rs
123
src/lib.rs
@ -6,4 +6,125 @@
|
||||
pub mod bigint;
|
||||
pub mod num;
|
||||
pub mod rational;
|
||||
pub mod seq;
|
||||
|
||||
/// Calculate a number in the fibonacci sequence,
|
||||
/// using a lookup table for better worst-case performance.
|
||||
///
|
||||
/// Can calculate up to 186 using native unsigned 128 bit integers.
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use rusty_numbers::fibonacci;
|
||||
///
|
||||
/// let valid = fibonacci(45); // Some(1134903170)
|
||||
/// # assert_eq!(1134903170, fibonacci(45).unwrap());
|
||||
/// # assert!(valid.is_some());
|
||||
///
|
||||
/// let invalid = fibonacci(187); // None
|
||||
/// # assert!(invalid.is_none());
|
||||
/// ```
|
||||
pub fn fibonacci(n: usize) -> Option<u128> {
|
||||
let mut table: Vec<u128> = vec![0, 1, 1, 2, 3, 5];
|
||||
|
||||
_fibonacci(n, &mut table)
|
||||
}
|
||||
|
||||
/// Actual calculating function for `fibonacci`
|
||||
#[inline]
|
||||
fn _fibonacci(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
||||
match table.get(n) {
|
||||
Some(x) => Some(*x),
|
||||
None => {
|
||||
let a = _fibonacci(n - 1, table)?;
|
||||
let b = _fibonacci(n - 2, table)?;
|
||||
|
||||
// Check for overflow when adding
|
||||
let attempt = a.checked_add(b);
|
||||
|
||||
if let Some(current) = attempt {
|
||||
table.insert(n, current);
|
||||
}
|
||||
|
||||
attempt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the value of a factorial,
|
||||
/// using a lookup table for better worst-case performance.
|
||||
///
|
||||
/// Can calculate up to 34! using native unsigned 128 bit integers.
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use rusty_numbers::factorial;
|
||||
///
|
||||
/// let valid = factorial(3); // Some(6)
|
||||
/// # assert_eq!(6, valid.unwrap());
|
||||
///
|
||||
/// let invalid = factorial(35); // None
|
||||
/// # assert!(invalid.is_none());
|
||||
/// ```
|
||||
pub fn factorial(n: usize) -> Option<u128> {
|
||||
let mut table: Vec<u128> = vec![1, 1, 2];
|
||||
|
||||
_factorial(n, &mut table)
|
||||
}
|
||||
|
||||
/// Actual Calculation function for factoral
|
||||
#[inline]
|
||||
fn _factorial(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
||||
match table.get(n) {
|
||||
// Vec<T>.get returns a Option with a reference to the value,
|
||||
// so deref and wrap in Some() for proper return type
|
||||
Some(x) => Some(*x),
|
||||
None => {
|
||||
// The ? suffix passes along the Option value
|
||||
// to be handled later
|
||||
// See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
||||
let prev = _factorial(n - 1, table)?;
|
||||
|
||||
// Do an overflow-checked multiply
|
||||
let attempt = (n as u128).checked_mul(prev);
|
||||
|
||||
// If there isn't an overflow, add the result
|
||||
// to the calculation table
|
||||
if let Some(current) = attempt {
|
||||
table.insert(n, current);
|
||||
}
|
||||
|
||||
attempt // Some(x) if no overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_factorial() {
|
||||
assert_eq!(1, factorial(0).unwrap());
|
||||
assert_eq!(1, factorial(1).unwrap());
|
||||
assert_eq!(6, factorial(3).unwrap());
|
||||
|
||||
let res = factorial(34);
|
||||
assert!(res.is_some());
|
||||
|
||||
let res = factorial(35);
|
||||
assert!(res.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fibonacci() {
|
||||
assert_eq!(0, fibonacci(0).unwrap());
|
||||
assert_eq!(1, fibonacci(1).unwrap());
|
||||
assert_eq!(1, fibonacci(2).unwrap());
|
||||
|
||||
let res = fibonacci(186);
|
||||
assert!(res.is_some());
|
||||
|
||||
let res = fibonacci(187);
|
||||
assert!(res.is_none());
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use core::ops::{
|
||||
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
|
||||
};
|
||||
|
||||
/// Represents the sign of a rational number
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Sign {
|
||||
/// Greater than zero, or zero
|
||||
@ -285,6 +286,12 @@ impl_signed!(i8, i16, i32, i64, i128, isize);
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_los() {
|
||||
assert_eq!(4u8.left_overflowing_sub(2).0, 2u8);
|
||||
assert_eq!(0u8.left_overflowing_sub(2).0, 2u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd() {
|
||||
assert_eq!(u8::gcd(2, 3), 1);
|
||||
|
@ -6,6 +6,23 @@ use std::cmp::{Ord, Ordering, PartialOrd};
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
/// Type representing a fraction
|
||||
///
|
||||
/// There are three basic constructors:
|
||||
///
|
||||
/// ```
|
||||
/// use rusty_numbers::frac;
|
||||
/// use rusty_numbers::rational::Frac;
|
||||
///
|
||||
/// // Macro
|
||||
/// let reduced_macro = frac!(3 / 4);
|
||||
///
|
||||
/// // Frac::new (called by the macro)
|
||||
/// let reduced = Frac::new(3, 4);
|
||||
/// # assert_eq!(reduced_macro, reduced);
|
||||
///
|
||||
/// // Frac::new_unreduced
|
||||
/// let unreduced = Frac::new_unreduced(4, 16);
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Frac<T: Unsigned = usize> {
|
||||
numer: T,
|
||||
@ -16,12 +33,16 @@ pub struct Frac<T: Unsigned = usize> {
|
||||
#[macro_export]
|
||||
/// Create a [Frac](rational/struct.Frac.html) type with signed or unsigned number literals
|
||||
///
|
||||
/// Accepts:
|
||||
/// Example:
|
||||
/// ```
|
||||
/// use rusty_numbers::frac;
|
||||
///
|
||||
/// ```no-run
|
||||
/// // Fractions
|
||||
/// // Proper fractions
|
||||
/// frac!(1 / 3);
|
||||
///
|
||||
/// // Improper fractions
|
||||
/// frac!(4 / 3);
|
||||
///
|
||||
/// // Whole numbers
|
||||
/// frac!(5u8);
|
||||
///
|
||||
@ -116,7 +137,7 @@ impl<T: Unsigned> Frac<T> {
|
||||
}
|
||||
|
||||
/// Convert the fraction to its simplest form
|
||||
fn reduce(mut self) -> Self {
|
||||
pub fn reduce(mut self) -> Self {
|
||||
let gcd = T::gcd(self.numer, self.denom);
|
||||
self.numer /= gcd;
|
||||
self.denom /= gcd;
|
||||
@ -198,7 +219,7 @@ where
|
||||
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
|
||||
{
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = self.clone() * rhs
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,7 +243,7 @@ where
|
||||
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
|
||||
{
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
*self = self.clone() / rhs
|
||||
*self = *self / rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +289,7 @@ where
|
||||
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = self.clone() + rhs
|
||||
*self = *self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +324,11 @@ where
|
||||
let sign = Self::get_sign(a, b, FracOp::Subtraction);
|
||||
let res = Self::raw(numer, denom, sign);
|
||||
|
||||
if overflowed { -res } else { res }
|
||||
if overflowed {
|
||||
-res
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,7 +337,7 @@ where
|
||||
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = self.clone() - rhs
|
||||
*self = *self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,47 +354,6 @@ impl<T: Unsigned> Neg for Frac<T> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mul_test() {
|
||||
let frac1 = frac!(1 / 3u8);
|
||||
let frac2 = frac!(2u8 / 3);
|
||||
|
||||
let expected = frac!(2u8 / 9);
|
||||
|
||||
assert_eq!(frac1 * frac2, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_test() {
|
||||
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2), "1/3 + 1/2");
|
||||
assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_test() {
|
||||
assert_eq!(4u8.left_overflowing_sub(2).0, 2u8);
|
||||
assert_eq!(0u8.left_overflowing_sub(2).0, 2u8);
|
||||
assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3), "1/2 - 1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_test() {
|
||||
assert!(-frac!(5 / 3) < frac!(1 / 10_000));
|
||||
assert!(frac!(1 / 10_000) > -frac!(10));
|
||||
assert!(frac!(1 / 3) < frac!(1 / 2));
|
||||
assert_eq!(frac!(1 / 2), frac!(3 / 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_fractions() {
|
||||
assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3");
|
||||
assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3");
|
||||
assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - +1/3");
|
||||
assert_eq!(-frac!(1), -frac!(2 / 3) + -frac!(1 / 3), "-2/3 + -1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_test() {
|
||||
let frac1 = frac!(1 / 3);
|
||||
|
123
src/seq.rs
123
src/seq.rs
@ -1,123 +0,0 @@
|
||||
//! Sequences and other 'stock' math functions
|
||||
|
||||
/// Calculate a number in the fibonacci sequence,
|
||||
/// using a lookup table for better worst-case performance.
|
||||
///
|
||||
/// Can calculate up to 186 using native unsigned 128 bit integers.
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use rusty_numbers::seq::fibonacci;
|
||||
///
|
||||
/// let valid = fibonacci(45); // Some(1134903170)
|
||||
/// # assert_eq!(1134903170, fibonacci(45).unwrap());
|
||||
/// # assert!(valid.is_some());
|
||||
///
|
||||
/// let invalid = fibonacci(187); // None
|
||||
/// # assert!(invalid.is_none());
|
||||
/// ```
|
||||
pub fn fibonacci(n: usize) -> Option<u128> {
|
||||
let mut table: Vec<u128> = vec![0, 1, 1, 2, 3, 5];
|
||||
|
||||
_fibonacci(n, &mut table)
|
||||
}
|
||||
|
||||
/// Actual calculating function for `fibonacci`
|
||||
#[inline]
|
||||
fn _fibonacci(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
||||
match table.get(n) {
|
||||
Some(x) => Some(*x),
|
||||
None => {
|
||||
let a = _fibonacci(n - 1, table)?;
|
||||
let b = _fibonacci(n - 2, table)?;
|
||||
|
||||
// Check for overflow when adding
|
||||
let attempt = a.checked_add(b);
|
||||
|
||||
if let Some(current) = attempt {
|
||||
table.insert(n, current);
|
||||
}
|
||||
|
||||
attempt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the value of a factorial,
|
||||
/// using a lookup table for better worst-case performance.
|
||||
///
|
||||
/// Can calculate up to 34! using native unsigned 128 bit integers.
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use rusty_numbers::seq::factorial;
|
||||
///
|
||||
/// let valid = factorial(3); // Some(6)
|
||||
/// # assert_eq!(6, valid.unwrap());
|
||||
///
|
||||
/// let invalid = factorial(35); // None
|
||||
/// # assert!(invalid.is_none());
|
||||
/// ```
|
||||
pub fn factorial(n: usize) -> Option<u128> {
|
||||
let mut table: Vec<u128> = vec![1, 1, 2];
|
||||
|
||||
_factorial(n, &mut table)
|
||||
}
|
||||
|
||||
/// Actual Calculation function for factoral
|
||||
#[inline]
|
||||
fn _factorial(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
||||
match table.get(n) {
|
||||
// Vec<T>.get returns a Option with a reference to the value,
|
||||
// so deref and wrap in Some() for proper return type
|
||||
Some(x) => Some(*x),
|
||||
None => {
|
||||
// The ? suffix passes along the Option value
|
||||
// to be handled later
|
||||
// See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
||||
let prev = _factorial(n - 1, table)?;
|
||||
|
||||
// Do an overflow-checked multiply
|
||||
let attempt = (n as u128).checked_mul(prev);
|
||||
|
||||
// If there isn't an overflow, add the result
|
||||
// to the calculation table
|
||||
if let Some(current) = attempt {
|
||||
table.insert(n, current);
|
||||
}
|
||||
|
||||
attempt // Some(x) if no overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_factorial() {
|
||||
assert_eq!(1, factorial(0).unwrap());
|
||||
assert_eq!(1, factorial(1).unwrap());
|
||||
assert_eq!(6, factorial(3).unwrap());
|
||||
|
||||
let res = factorial(34);
|
||||
assert!(res.is_some());
|
||||
|
||||
let res = factorial(35);
|
||||
assert!(res.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fibonacci() {
|
||||
assert_eq!(0, fibonacci(0).unwrap());
|
||||
assert_eq!(1, fibonacci(1).unwrap());
|
||||
assert_eq!(1, fibonacci(2).unwrap());
|
||||
|
||||
let res = fibonacci(186);
|
||||
assert!(res.is_some());
|
||||
|
||||
let res = fibonacci(187);
|
||||
assert!(res.is_none());
|
||||
}
|
||||
}
|
56
tests/fractions.rs
Normal file
56
tests/fractions.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use rusty_numbers::frac;
|
||||
|
||||
#[test]
|
||||
fn mul_test() {
|
||||
let frac1 = frac!(1 / 3u8);
|
||||
let frac2 = frac!(2u8 / 3);
|
||||
|
||||
let expected = frac!(2u8 / 9);
|
||||
|
||||
assert_eq!(frac1 * frac2, expected);
|
||||
|
||||
assert_eq!(frac!(0), frac!(0) * frac!(5 / 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_test() {
|
||||
assert_eq!(frac!(1), frac!(1 / 3) / frac!(1 / 3));
|
||||
assert_eq!(frac!(1 / 9), frac!(1 / 3) / frac!(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_test() {
|
||||
assert_eq!(frac!(5 / 8), frac!(5 / 8) + frac!(0));
|
||||
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2), "1/3 + 1/2");
|
||||
assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_test() {
|
||||
assert_eq!(frac!(5 / 8), frac!(5 / 8) - frac!(0));
|
||||
assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3), "1/2 - 1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_test() {
|
||||
assert!(frac!(0) < frac!(1));
|
||||
assert!(-frac!(5 / 3) < frac!(1 / 10_000));
|
||||
assert!(frac!(1 / 10_000) > -frac!(10));
|
||||
assert!(frac!(1 / 3) < frac!(1 / 2));
|
||||
assert_eq!(frac!(1 / 2), frac!(3 / 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_fractions() {
|
||||
assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3");
|
||||
assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3");
|
||||
assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - +1/3");
|
||||
assert_eq!(-frac!(1), -frac!(2 / 3) + -frac!(1 / 3), "-2/3 + -1/3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_mul_div() {
|
||||
assert_eq!(-frac!(1 / 12), -frac!(1 / 3) * frac!(1 / 4));
|
||||
assert_eq!(-frac!(1 / 12), frac!(1 / 3) * -frac!(1 / 4));
|
||||
assert_eq!(frac!(1 / 12), -frac!(1 / 3) * -frac!(1 / 4));
|
||||
}
|
Loading…
Reference in New Issue
Block a user