bcd 0.1.0

Binary coded decimal library
use bcd::{Bcd, BcdType};
use std::fmt;
use std::ops::AddAssign;

#[derive(Clone, PartialEq, Eq)]
pub struct BigBcd<T: BcdType>(Vec<Bcd<T>>);

impl<'a, T: BcdType> AddAssign<&'a Self> for BigBcd<T> {
    #[inline]
    fn add_assign(&mut self, other: &'a Self) {
        if self.0.len() < other.0.len() {
            self.0.resize(other.0.len(), Bcd(0.into()));
        }

        let mut overflow = false;
        for (a, b) in self.0.iter_mut().zip(other.0.iter()) {
            if a.0 == 0.into() && b.0 == 0.into() && !overflow {
                break;
            }
            let (r, o) = a.add_with_overflow(*b, overflow);
            *a = r;
            overflow = o;
        }
        if overflow {
            self.0.push(Bcd(1.into()));
        }
    }
}

impl<T: BcdType> BigBcd<T> {
    pub fn iter<'a>(&'a self) -> BigBcdDigitIter<'a, T> {
        BigBcdDigitIter {
            data: &self.0,
            index: self.0.len() * Bcd::<T>::digits(),
        }
    }
}

impl<T: BcdType> From<Vec<T>> for BigBcd<T> {
    fn from(v: Vec<T>) -> BigBcd<T> {
        BigBcd(v.iter().map(|&n| Bcd(n)).collect())
    }
}

impl<T: BcdType> Into<Vec<T>> for BigBcd<T> {
    fn into(self) -> Vec<T> {
        self.0.iter().map(|&bcd| bcd.0).collect()
    }
}

impl<T: BcdType> fmt::Display for BigBcd<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for d in self.iter().skip_while(|d| *d == 0) {
            write!(f, "{}", d)?;
        }
        Ok(())
    }
}

pub struct BigBcdDigitIter<'a, T: BcdType + 'a> {
    data: &'a Vec<Bcd<T>>,
    index: usize,
}

impl<'a, T: BcdType + 'a> Iterator for BigBcdDigitIter<'a, T> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        self.index = self
            .index
            .checked_sub(if self.index % Bcd::<T>::digits() == 0 {
                2
            } else {
                1
            })
            .unwrap_or(usize::max_value());
        self.data
            .get(self.index / Bcd::<T>::digits())
            .map(|d| d.digit((self.index % Bcd::<T>::digits()) as u8))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn correct_digits() {
        let num: BigBcd<u32> = vec![0x01234467, 0x01235567].into();
        let digits: Vec<_> = num.iter().collect();
        assert_eq!(digits, vec![1, 2, 3, 5, 5, 6, 7, 1, 2, 3, 4, 4, 6, 7]);
    }
}