use num_bigint::BigUint;
use num_traits::One;
mod bbbd;
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidChain,
}
pub fn find_shortest_chain(n: BigUint) -> Vec<BigUint> {
bbbd::find_shortest_chain(n)
}
#[derive(Debug, PartialEq)]
pub enum Step {
Double { index: usize },
Add { left: usize, right: usize },
}
pub fn build_steps(chain: &[BigUint]) -> Result<Vec<Step>, Error> {
match chain.first() {
Some(n) if n.is_one() => (),
_ => return Err(Error::InvalidChain),
}
let mut steps = vec![];
for (i, val) in chain.iter().enumerate().skip(1) {
'search: for (j, left) in chain[..i].iter().enumerate() {
for (k, right) in chain[..=j].iter().enumerate() {
if val == &(left + right) {
if j == k {
steps.push(Step::Double { index: j })
} else {
steps.push(Step::Add { left: j, right: k });
}
break 'search;
}
}
}
if steps.len() != i {
return Err(Error::InvalidChain);
}
}
Ok(steps)
}
pub fn build_addition_chain(n: BigUint) -> Vec<Step> {
build_steps(&find_shortest_chain(n)).expect("chain is valid")
}
#[cfg(test)]
mod tests {
use num_bigint::BigUint;
use super::{build_steps, Error, Step};
#[test]
fn steps_from_valid_chains() {
assert_eq!(
build_steps(&[
BigUint::from(1u32),
BigUint::from(2u32),
BigUint::from(3u32),
]),
Ok(vec![
Step::Double { index: 0 },
Step::Add { left: 1, right: 0 }
]),
);
assert_eq!(
build_steps(&[
BigUint::from(1u32),
BigUint::from(2u32),
BigUint::from(4u32),
BigUint::from(8u32),
]),
Ok(vec![
Step::Double { index: 0 },
Step::Double { index: 1 },
Step::Double { index: 2 },
]),
);
assert_eq!(
build_steps(&[
BigUint::from(1u32),
BigUint::from(2u32),
BigUint::from(3u32),
BigUint::from(6u32),
BigUint::from(7u32),
BigUint::from(10u32),
BigUint::from(20u32),
BigUint::from(40u32),
BigUint::from(80u32),
BigUint::from(87u32),
]),
Ok(vec![
Step::Double { index: 0 },
Step::Add { left: 1, right: 0 },
Step::Double { index: 2 },
Step::Add { left: 3, right: 0 },
Step::Add { left: 4, right: 2 },
Step::Double { index: 5 },
Step::Double { index: 6 },
Step::Double { index: 7 },
Step::Add { left: 8, right: 4 },
]),
);
}
#[test]
fn invalid_chains() {
assert_eq!(
build_steps(&[BigUint::from(2u32), BigUint::from(3u32),]),
Err(Error::InvalidChain),
);
assert_eq!(
build_steps(&[
BigUint::from(1u32),
BigUint::from(4u32),
BigUint::from(8u32),
]),
Err(Error::InvalidChain),
);
}
}