pub const ACHROMATIC_BOUNDARIES: &[(f64, f64, u16, &str)] = &[
(0.0, 2.5, 267, "black"),
(2.5, 4.5, 266, "dark gray"),
(4.5, 6.5, 265, "medium gray"),
(6.5, 8.5, 264, "light gray"),
(8.5, 10.0, 263, "white"),
];
pub fn get_achromatic_color_number(value: f64) -> Option<u16> {
if value < 0.0 || value > 10.0 {
return None;
}
for &(lower, upper, color_number, _) in ACHROMATIC_BOUNDARIES {
if value > lower && value <= upper {
return Some(color_number);
}
}
None
}
pub fn get_achromatic_color_name(value: f64) -> Option<&'static str> {
if value < 0.0 || value > 10.0 {
return None;
}
for &(lower, upper, _, name) in ACHROMATIC_BOUNDARIES {
if value > lower && value <= upper {
return Some(name);
}
}
None
}
pub fn is_achromatic_hue(hue: &str) -> bool {
let hue = hue.trim().to_uppercase();
if hue == "N" {
return true;
}
if let Some(n_pos) = hue.rfind('N') {
let prefix = &hue[..n_pos];
return prefix.is_empty() || prefix.chars().all(|c| c.is_ascii_digit() || c == '.');
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_achromatic_value_boundaries() {
assert_eq!(get_achromatic_color_number(0.0), None); assert_eq!(get_achromatic_color_number(0.1), Some(267)); assert_eq!(get_achromatic_color_number(2.5), Some(267)); assert_eq!(get_achromatic_color_number(2.6), Some(266)); assert_eq!(get_achromatic_color_number(4.5), Some(266)); assert_eq!(get_achromatic_color_number(4.6), Some(265)); assert_eq!(get_achromatic_color_number(6.5), Some(265)); assert_eq!(get_achromatic_color_number(6.6), Some(264)); assert_eq!(get_achromatic_color_number(8.5), Some(264)); assert_eq!(get_achromatic_color_number(8.6), Some(263)); assert_eq!(get_achromatic_color_number(10.0), Some(263)); assert_eq!(get_achromatic_color_number(10.1), None); }
#[test]
fn test_achromatic_color_names() {
assert_eq!(get_achromatic_color_name(1.0), Some("black"));
assert_eq!(get_achromatic_color_name(3.0), Some("dark gray"));
assert_eq!(get_achromatic_color_name(5.0), Some("medium gray"));
assert_eq!(get_achromatic_color_name(7.0), Some("light gray"));
assert_eq!(get_achromatic_color_name(9.0), Some("white"));
assert_eq!(get_achromatic_color_name(-1.0), None);
assert_eq!(get_achromatic_color_name(11.0), None);
}
#[test]
fn test_achromatic_hue_recognition() {
assert!(is_achromatic_hue("N"));
assert!(is_achromatic_hue("n"));
assert!(is_achromatic_hue(" N "));
assert!(is_achromatic_hue("0N"));
assert!(is_achromatic_hue("0.0N"));
assert!(is_achromatic_hue("5.5N"));
assert!(is_achromatic_hue("10N"));
assert!(!is_achromatic_hue("R"));
assert!(!is_achromatic_hue("5R"));
assert!(!is_achromatic_hue("5.0R"));
assert!(!is_achromatic_hue("YR"));
assert!(!is_achromatic_hue(""));
assert!(!is_achromatic_hue("5RN")); }
#[test]
fn test_boundary_consistency() {
let mut boundaries = ACHROMATIC_BOUNDARIES.to_vec();
boundaries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
assert_eq!(boundaries[0].0, 0.0);
assert_eq!(boundaries.last().unwrap().1, 10.0);
for i in 1..boundaries.len() {
assert_eq!(boundaries[i-1].1, boundaries[i].0,
"Gap between boundaries {} and {}", i-1, i);
}
}
#[test]
fn test_all_color_numbers_unique() {
let mut color_numbers: Vec<u16> = ACHROMATIC_BOUNDARIES.iter().map(|(_, _, num, _)| *num).collect();
color_numbers.sort();
color_numbers.dedup();
assert_eq!(color_numbers.len(), ACHROMATIC_BOUNDARIES.len(),
"Duplicate color numbers found in achromatic boundaries");
}
}