const L_PATTERNS: [u8; 10] = [
0b0001101, 0b0011001, 0b0010011, 0b0111101, 0b0100011, 0b0110001, 0b0101111, 0b0111011, 0b0110111, 0b0001011, ];
fn bits7(pattern: u8) -> [bool; 7] {
let mut out = [false; 7];
for (i, slot) in out.iter_mut().enumerate() {
*slot = (pattern >> (6 - i)) & 1 == 1;
}
out
}
pub fn l_code(d: u8) -> [bool; 7] {
bits7(L_PATTERNS[d as usize])
}
pub fn r_code(d: u8) -> [bool; 7] {
bits7((!L_PATTERNS[d as usize]) & 0x7F)
}
pub fn g_code(d: u8) -> [bool; 7] {
let r = r_code(d);
let mut g = [false; 7];
for i in 0..7 {
g[i] = r[6 - i];
}
g
}
pub fn left_code(d: u8, even: bool) -> [bool; 7] {
if even { g_code(d) } else { l_code(d) }
}
pub fn match_left(m: &[bool]) -> Option<(u8, bool)> {
for d in 0..10u8 {
if m == l_code(d) {
return Some((d, false));
}
if m == g_code(d) {
return Some((d, true));
}
}
None
}
pub fn match_right(m: &[bool]) -> Option<u8> {
(0..10u8).find(|&d| m == r_code(d))
}
pub const NORMAL_GUARD: [bool; 3] = [true, false, true];
pub const CENTER_GUARD: [bool; 5] = [false, true, false, true, false];
pub const UPCE_END_GUARD: [bool; 6] = [false, true, false, true, false, true];
pub const ADDON_LEAD: [bool; 4] = [true, false, true, true];
pub const ADDON_SEP: [bool; 2] = [false, true];
pub const EAN13_FIRST_PARITY: [[bool; 6]; 10] = [
[false, false, false, false, false, false], [false, false, true, false, true, true], [false, false, true, true, false, true], [false, false, true, true, true, false], [false, true, false, false, true, true], [false, true, true, false, false, true], [false, true, true, true, false, false], [false, true, false, true, false, true], [false, true, false, true, true, false], [false, true, true, false, true, false], ];
pub const UPCE_PARITY_NS0: [[bool; 6]; 10] = [
[true, true, true, false, false, false], [true, true, false, true, false, false], [true, true, false, false, true, false], [true, true, false, false, false, true], [true, false, true, true, false, false], [true, false, false, true, true, false], [true, false, false, false, true, true], [true, false, true, false, true, false], [true, false, true, false, false, true], [true, false, false, true, false, true], ];
pub fn upce_parity(ns: u8, check: u8) -> [bool; 6] {
let base = UPCE_PARITY_NS0[check as usize];
if ns == 1 {
let mut inv = [false; 6];
for i in 0..6 {
inv[i] = !base[i];
}
inv
} else {
base
}
}
pub fn upce_from_parity(parity: &[bool; 6]) -> Option<(u8, u8)> {
for ns in 0..2u8 {
for check in 0..10u8 {
if upce_parity(ns, check) == *parity {
return Some((ns, check));
}
}
}
None
}
pub const EAN5_PARITY: [[bool; 5]; 10] = [
[true, true, false, false, false], [true, false, true, false, false], [true, false, false, true, false], [true, false, false, false, true], [false, true, true, false, false], [false, false, true, true, false], [false, false, false, true, true], [false, true, false, true, false], [false, true, false, false, true], [false, false, true, false, true], ];
pub const EAN2_PARITY: [[bool; 2]; 4] = [
[false, false], [false, true], [true, false], [true, true], ];
pub fn check_digit(data: &[u8]) -> u8 {
let mut sum = 0u32;
for (i, &d) in data.iter().rev().enumerate() {
let w = if i % 2 == 0 { 3 } else { 1 };
sum += w * u32::from(d);
}
((10 - (sum % 10)) % 10) as u8
}
pub fn ean5_checksum(digits: &[u8]) -> u8 {
let mut sum = 0u32;
for (i, &d) in digits.iter().enumerate() {
let w = if i % 2 == 0 { 3 } else { 9 };
sum += w * u32::from(d);
}
(sum % 10) as u8
}
pub fn upce_expand(ns: u8, payload: &[u8; 6]) -> [u8; 11] {
let d = payload;
let z = 0u8;
match d[5] {
0..=2 => [ns, d[0], d[1], d[5], z, z, z, z, d[2], d[3], d[4]],
3 => [ns, d[0], d[1], d[2], z, z, z, z, z, d[3], d[4]],
4 => [ns, d[0], d[1], d[2], d[3], z, z, z, z, z, d[4]],
_ => [ns, d[0], d[1], d[2], d[3], d[4], z, z, z, z, d[5]],
}
}
#[cfg(test)]
mod tests {
use super::*;
fn digits(s: &str) -> Vec<u8> {
s.bytes().map(|b| b - b'0').collect()
}
#[test]
fn ean13_check_reference() {
assert_eq!(check_digit(&digits("590123412345")), 7);
}
#[test]
fn upca_check_reference() {
assert_eq!(check_digit(&digits("03600029145")), 2);
}
#[test]
fn upce_expand_reference() {
let expanded = upce_expand(0, &[4, 2, 5, 2, 6, 1]);
assert_eq!(expanded, [0, 4, 2, 1, 0, 0, 0, 0, 5, 2, 6]);
assert_eq!(check_digit(&expanded), 4);
}
#[test]
fn ean5_checksum_reference() {
assert_eq!(ean5_checksum(&digits("52495")), 1);
}
#[test]
fn element_relations() {
for d in 0..10u8 {
let l = l_code(d);
let r = r_code(d);
let g = g_code(d);
for i in 0..7 {
assert_eq!(l[i], !r[i], "L/R complement, digit {d}");
assert_eq!(g[i], r[6 - i], "G is R reversed, digit {d}");
}
}
}
}