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]);
}
}