fn char_value(c: &u8) -> u8 {
if c.is_ascii_digit() {
c - b'0'
} else if c.is_ascii_uppercase() {
c - b'A' + 10
} else {
panic!("Non-ASCII-alphanumeric characters should be impossible here!");
}
}
#[allow(dead_code)]
pub fn checksum_functional(s: &[u8]) -> u8 {
fn digits_of(x: u8) -> Vec<u8> {
if x >= 10 {
vec![x / 10, x % 10]
} else {
vec![x]
}
}
let sum: u32 = s
.iter()
.map(char_value)
.flat_map(digits_of)
.rev()
.enumerate()
.flat_map(|(i, x)| {
if (i % 2) == 0 {
digits_of(x * 2)
} else {
digits_of(x)
}
})
.map(|x| x as u32)
.sum();
let sum = (sum % 10) as u8;
let diff = 10 - sum;
if diff == 10 {
0
} else {
diff
}
}
#[rustfmt::skip]
const WIDTHS: [u8; 36] = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2,
];
#[rustfmt::skip]
const ODDS: [u8; 36] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
6, 7, 8, 9, 0, 1,
];
#[rustfmt::skip]
const EVENS: [u8; 36] = [
0, 2, 4, 6, 8,
1, 3, 5, 7, 9,
1, 3, 5, 7, 9,
2, 4, 6, 8, 0,
2, 4, 6, 8, 0,
3, 5, 7, 9, 1,
3, 5, 7, 9, 1,
4,
];
const MAX_ACCUM: u8 = u8::MAX - 9;
pub fn checksum_table(s: &[u8]) -> u8 {
let mut sum: u8 = 0;
let mut idx: usize = 0;
for c in s.iter().rev() {
let v = char_value(c);
let w = WIDTHS[v as usize];
let x = if (idx & 0x1) == 0 {
EVENS[v as usize]
} else {
ODDS[v as usize]
};
if sum > MAX_ACCUM {
sum %= 10
}
sum += x;
idx += w as usize;
}
sum %= 10;
let diff = 10 - sum;
if diff == 10 {
0
} else {
diff
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn single_chars() {
for c in ('0'..='9').chain('A'..='Z') {
let s = c.to_string();
let ss = s.as_bytes();
let a = checksum_functional(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from library {} should equal that from functional style {} for \"{}\"",
b, a, s
);
}
}
#[test]
fn single_chars_left_of_zero() {
for c in ('0'..='9').chain('A'..='Z') {
let s = format!("{}0", c);
let ss = s.as_bytes();
let a = checksum_functional(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from functional style {} for \"{}\"",
b, a, s
);
}
}
#[test]
fn nine_left_of_single_chars() {
for c in ('0'..='9').chain('A'..='Z') {
let s = format!("9{}", c);
let ss = s.as_bytes();
let a = checksum_functional(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from functional style {} for \"{}\"",
b, a, s
);
}
}
proptest! {
#[test]
fn processes_all_valid_strings(s in "[A-Z]{2}[0-9A-Z]{9}") {
let ss = s.as_bytes();
let a = checksum_functional(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from functional style {} for \"{}\"",
b, a, s
);
}
}
}