#![deny(missing_docs, unused_imports)]
use std::fmt::{Debug, Display};
use no_panic::no_panic;
use num::{FromPrimitive, Integer, PrimInt, Unsigned};
pub trait Collatz: Unsigned + Integer + PrimInt + FromPrimitive + Debug + Display {}
impl<T> Collatz for T where T: Unsigned + Integer + PrimInt + FromPrimitive + Debug + Display {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct NonZero<T: Collatz>(T);
impl<T: Collatz> NonZero<T> {
pub fn try_new(n: T) -> Option<Self> {
if n.is_zero() {
None
} else {
Some(Self(n))
}
}
}
pub fn divide_while_even<T: Collatz>(n: NonZero<T>) -> Option<NonZero<T>> {
let n = n.0;
let trailing_zeros: usize = n.trailing_zeros().try_into().ok()?;
Some(NonZero(n >> trailing_zeros))
}
#[no_panic]
pub fn transformations<T: Collatz>(n: NonZero<T>) -> Option<Vec<T>> {
let mut n = n;
let mut trans: Vec<T> = vec![n.0];
while !n.0.is_one() {
n = rules::basic(n)?;
trans.push(n.0);
}
Some(trans)
}
pub mod bouncy_numbers;
pub mod check_range;
pub mod fall;
pub mod rules;
pub mod steps;
pub mod steps_range;
mod tests {
#[test]
fn steps_range_conforms_to_oeis() {
use crate::NonZero;
let oeis_steps: Vec<u32> = vec![
0, 1, 7, 2, 5, 8, 16, 3, 19, 6, 14, 9, 9, 17, 17, 4, 12, 20, 20, 7, 7, 15, 15, 10, 23,
10, 111, 18, 18, 18, 106, 5, 26, 13, 13, 21, 21, 21, 34, 8, 109, 8, 29, 16, 16, 16,
104, 11, 24, 24, 24, 11, 11, 112, 112, 19, 32, 19, 32, 19, 19, 107, 107, 6, 27, 27, 27,
14, 14, 14, 102, 22,
];
let r = 1..(oeis_steps.len() + 1);
let step_counts: Vec<u32> = r
.map(|n| crate::steps::omega(NonZero(n)).unwrap())
.collect();
for i in 0..oeis_steps.len() {
println!(
"{} => OEIS: {}, LIB: {}",
i + 1,
oeis_steps[i],
step_counts[i]
);
assert_eq!(oeis_steps[i], step_counts[i]);
}
}
}