#[must_use]
pub fn digital_root(n: u64) -> u64 {
if n == 0 {
0
} else {
1 + (n - 1) % 9
}
}
#[must_use]
pub fn sum_digits(mut n: u64) -> u64 {
let mut s = 0;
while n > 0 {
s += n % 10;
n /= 10;
}
s
}
#[must_use]
pub fn string_sum(s: &str, value: impl Fn(char) -> Option<u64>) -> u64 {
s.chars().filter_map(value).sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn digital_roots() {
assert_eq!(digital_root(0), 0);
assert_eq!(digital_root(9), 9);
assert_eq!(digital_root(18), 9);
assert_eq!(digital_root(31), 4); }
#[test]
fn string_sum_skips_unmapped_and_is_additive() {
let v = |c: char| c.to_digit(10).map(u64::from);
assert_eq!(string_sum("a1b2c3", v), 6);
assert_eq!(string_sum("", v), 0);
assert_eq!(string_sum("12", v) + string_sum("34", v), string_sum("1234", v));
}
#[test]
fn sum_digits_and_zero() {
assert_eq!(sum_digits(0), 0);
assert_eq!(sum_digits(999), 27);
}
use proptest::prelude::*;
proptest! {
#[test]
fn prop_digital_root_is_mod9_variant(n in any::<u64>()) {
let dr = digital_root(n);
if n == 0 {
prop_assert_eq!(dr, 0);
} else {
prop_assert!((1..=9).contains(&dr));
prop_assert_eq!(dr, 1 + (n - 1) % 9);
}
}
#[test]
fn prop_sum_digits_never_exceeds_input(n in 1..u64::MAX) {
prop_assert!(sum_digits(n) <= n);
prop_assert_eq!(sum_digits(n) % 9, n % 9);
}
}
}