include!(concat!(env!("OUT_DIR"), "/decimal_digit_zeros.rs"));
#[must_use]
pub(crate) fn decimal_system(ch: char) -> Option<u32> {
let cp = ch as u32;
let idx = match DECIMAL_DIGIT_ZEROS.binary_search(&cp) {
Ok(i) => i,
Err(0) => return None,
Err(i) => i - 1,
};
let zero = DECIMAL_DIGIT_ZEROS[idx];
(cp - zero < 10).then_some(zero)
}
#[must_use]
pub(crate) fn system_count(text: &str) -> usize {
let mut seen: [u32; 8] = [0; 8];
let mut n = 0usize;
for ch in text.chars() {
let Some(zero) = decimal_system(ch) else {
continue;
};
if seen[..n].contains(&zero) {
continue;
}
if n == seen.len() {
return seen.len();
}
seen[n] = zero;
n += 1;
}
n
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_non_digit_belongs_to_no_system() {
for ch in ['a', 'Ω', '中', ' ', '\u{0301}', '\u{200b}'] {
assert_eq!(decimal_system(ch), None, "{ch:?}");
}
}
#[test]
fn every_digit_of_a_system_reports_the_same_zero() {
for zero in DECIMAL_DIGIT_ZEROS {
for offset in 0..10u32 {
let ch = char::from_u32(zero + offset).expect("digits are scalar values");
assert_eq!(decimal_system(ch), Some(zero), "U+{:04X}", zero + offset);
}
}
}
#[test]
fn the_code_point_after_a_system_is_not_in_it() {
for zero in DECIMAL_DIGIT_ZEROS {
let Some(ch) = char::from_u32(zero + 10) else {
continue;
};
assert_ne!(decimal_system(ch), Some(zero), "U+{:04X}", zero + 10);
}
}
#[test]
fn system_count_is_the_number_of_systems_not_of_digits() {
assert_eq!(system_count(""), 0);
assert_eq!(system_count("hello"), 0);
assert_eq!(system_count("2024"), 1);
assert_eq!(system_count("\u{0662}\u{0660}\u{0662}\u{0664}"), 1); assert_eq!(system_count("12\u{0663}"), 2); assert_eq!(system_count("1\u{0662}\u{06F3}\u{FF14}\u{0968}"), 5);
}
}