#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "large-numbers")]
use num_bigint::BigUint;
use core::fmt;
#[cfg(all(not(feature = "checked-overflow"), not(feature = "std")))]
use core::num::Wrapping;
#[cfg(all(not(feature = "checked-overflow"), feature = "std"))]
use std::num::Wrapping;
#[derive(Debug, Clone, PartialEq)]
pub enum ArithmeticError {
Overflow,
}
impl fmt::Display for ArithmeticError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ArithmeticError::Overflow => write!(f, "Arithmetic operation overflowed"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ArithmeticError {}
pub trait UnsignedInteger: Clone + core::ops::Add<Output = Self> {
fn zero() -> Self;
fn one() -> Self;
#[cfg(feature = "checked-overflow")]
fn safe_add(self, rhs: Self) -> Result<Self, ArithmeticError>;
#[cfg(all(not(feature = "checked-overflow"), feature = "std"))]
fn unchecked_add(self, rhs: Self) -> Self;
#[cfg(all(not(feature = "checked-overflow"), not(feature = "std")))]
fn unchecked_add(self, rhs: Self) -> Self;
}
macro_rules! impl_unsigned_integer {
($($t:ty)*) => ($(impl UnsignedInteger for $t {
fn zero() -> Self { 0 }
fn one() -> Self { 1 }
#[cfg(feature = "checked-overflow")]
fn safe_add(self, rhs: Self) -> Result<Self, ArithmeticError> {
self.checked_add(rhs).ok_or(ArithmeticError::Overflow)
}
#[cfg(all(not(feature = "checked-overflow"), feature = "std"))]
fn unchecked_add(self, rhs: Self) -> Self {
Wrapping(self).0.wrapping_add(Wrapping(rhs).0)
}
})*)
}
impl_unsigned_integer! { u8 u16 u32 u64 u128 }
#[cfg(feature = "large-numbers")]
impl UnsignedInteger for BigUint {
fn zero() -> Self {
BigUint::from(0u32)
}
fn one() -> Self {
BigUint::from(1u32)
}
#[cfg(feature = "checked-overflow")]
fn safe_add(self, rhs: Self) -> Result<Self, ArithmeticError> {
Ok(self + rhs) }
#[cfg(all(not(feature = "checked-overflow"), feature = "std"))]
fn unchecked_add(self, rhs: Self) -> Self {
self + rhs }
}
pub struct Fibonacci<T: UnsignedInteger> {
current: T,
next: T,
}
impl<T: UnsignedInteger> Fibonacci<T> {
pub fn new() -> Fibonacci<T> {
Fibonacci {
current: T::zero(),
next: T::one(),
}
}
}
impl<T: UnsignedInteger> Default for Fibonacci<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "iterator")]
impl<T: UnsignedInteger> Iterator for Fibonacci<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
#[cfg(feature = "checked-overflow")]
{
let next = match self.current.clone().safe_add(self.next.clone()) {
Ok(value) => value,
Err(ArithmeticError::Overflow) => return None,
};
let current = self.current.clone();
self.current = self.next.clone();
self.next = next;
return Some(current);
}
#[cfg(all(not(feature = "checked-overflow"), feature = "std"))]
{
let current = self.current.clone();
self.next = self.current.unchecked_add(self.next.clone());
self.current = current;
return Some(self.current.clone());
}
#[cfg(all(not(feature = "checked-overflow"), not(feature = "std")))]
{
let current = self.current.clone();
self.next = self.current + self.next;
self.current = current;
return Some(self.current.clone());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "std")]
fn test_fibonacci<T: UnsignedInteger + std::cmp::PartialEq + std::fmt::Debug>() {
let mut fib = Fibonacci::<T>::new();
assert_eq!(fib.current, T::zero());
assert_eq!(fib.next, T::one());
assert_eq!(fib.next(), Some(T::zero())); assert_eq!(fib.next(), Some(T::one())); assert_eq!(fib.next(), Some(T::one())); assert_eq!(fib.next(), Some(T::one() + T::one())); assert_eq!(fib.next(), Some(T::one() + T::one() + T::one())); assert_eq!(
fib.next(),
Some(T::one() + T::one() + T::one() + T::one() + T::one())
);
}
#[cfg(feature = "std")]
#[test]
fn test_fibonacci_u8() {
test_fibonacci::<u8>();
}
#[cfg(feature = "std")]
#[test]
fn test_fibonacci_u16() {
test_fibonacci::<u16>();
}
#[cfg(feature = "std")]
#[test]
fn test_fibonacci_u32() {
test_fibonacci::<u32>();
}
#[cfg(feature = "std")]
#[test]
fn test_fibonacci_u64() {
test_fibonacci::<u64>();
}
#[cfg(feature = "std")]
#[test]
fn test_fibonacci_u128() {
test_fibonacci::<u128>();
}
#[cfg(all(feature = "std", feature = "large-numbers"))]
#[test]
fn test_fibonacci_big_uint() {
test_fibonacci::<BigUint>();
}
#[cfg(feature = "checked-overflow")]
#[test]
fn test_unsigned_integer() {
let a: u8 = 1;
let b: u8 = 1;
let c: u8 = 255;
assert_eq!(a.safe_add(b), Ok(2u8)); assert_eq!(c.safe_add(b), Err(ArithmeticError::Overflow)); }
#[test]
fn test_fibonacci_new() {
let fib: Fibonacci<u8> = Fibonacci::new();
assert_eq!(fib.current, 0);
assert_eq!(fib.next, 1);
}
#[test]
fn test_fibonacci_default() {
let fib: Fibonacci<u8> = Default::default();
assert_eq!(fib.current, 0);
assert_eq!(fib.next, 1);
}
#[cfg(feature = "iterator")]
#[test]
fn test_fibonacci_iterator() {
let mut fib = Fibonacci::<u8>::new();
assert_eq!(fib.next(), Some(0));
assert_eq!(fib.next(), Some(1));
assert_eq!(fib.next(), Some(1));
assert_eq!(fib.next(), Some(2));
assert_eq!(fib.next(), Some(3));
assert_eq!(fib.next(), Some(5));
}
#[test]
fn test_fibonacci_overflow() {
let mut fib = Fibonacci::<u8>::new();
for _ in 0..255 {
fib.next(); }
assert_eq!(fib.next(), None);
}
}