#![warn(missing_docs)]
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!");
}
}
const MAX_ACCUM_SIMPLE: u8 = u8::MAX - 14;
const MAX_ACCUM_TABLE: u8 = u8::MAX - 9;
#[rustfmt::skip]
const ODDS: [u8; 36] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
3, 4, 5, 6, 7, 8
];
#[rustfmt::skip]
const EVENS: [u8; 36] = [
0, 2, 4, 6, 8,
1, 3, 5, 7, 9,
2, 4, 6, 8, 0,
3, 5, 7, 9, 1,
4, 6, 8, 0, 2,
5, 7, 9, 1, 3,
6, 8, 0, 2, 4,
7
];
pub fn checksum_simple(s: &[u8]) -> u8 {
let mut sum: u8 = 0;
for (i, c) in s.iter().enumerate() {
let v = char_value(c);
let vv = if ((i + 1) % 2) == 0 { v * 2 } else { v };
if sum > MAX_ACCUM_SIMPLE {
sum %= 10
}
sum += (vv / 10) + (vv % 10)
}
sum %= 10;
(10 - sum) % 10
}
pub fn checksum_table(s: &[u8]) -> u8 {
let mut sum: u8 = 0;
for (i, c) in s.iter().rev().enumerate() {
let v = char_value(c);
let v = if (i & 0x1) == 0 {
EVENS[v as usize]
} else {
ODDS[v as usize]
};
if sum > MAX_ACCUM_TABLE {
sum %= 10
}
sum += v
}
sum %= 10;
(10 - sum) % 10
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn single_chars_right_of_zero() {
for c in ('0'..='9').chain('A'..='Z') {
let s = format!("0{}", c);
let ss = s.as_bytes();
let a = checksum_simple(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from simple 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_simple(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from simple style {} for \"{}\"",
b, a, s
);
}
}
proptest! {
#[test]
fn processes_all_valid_strings(s in "[0-9A-Z]{8}") {
let ss = s.as_bytes();
let a = checksum_simple(ss);
let b = checksum_table(ss);
assert_eq!(
a, b,
"checksum from table style {} should equal that from simple style {} for \"{}\"",
b, a, s
);
}
}
}