#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use crate::ported::init::tccolours;
use crate::ported::zsh_h::{color_rgb, features, hookdef, module};
use std::sync::atomic::Ordering;
use std::sync::{Mutex, OnceLock};
#[derive(Debug, Clone, Copy, Default)]
pub struct cielab {
pub L: f64, pub a: f64, pub b: f64, }
pub type Cielab = Box<cielab>;
pub fn deltae(lab1: &cielab, lab2: &cielab) -> f64 {
(lab1.L - lab2.L).powi(2) + (lab1.a - lab2.a).powi(2) + (lab1.b - lab2.b).powi(2) }
pub fn RGBtoLAB(red: i32, green: i32, blue: i32, lab: &mut cielab) {
let mut R: f64 = red as f64 / 255.0; let mut G: f64 = green as f64 / 255.0; let mut B: f64 = blue as f64 / 255.0; R = 100.0
* if R > 0.04045 {
((R + 0.055) / 1.055).powf(2.4)
}
else {
R / 12.92
};
G = 100.0
* if G > 0.04045 {
((G + 0.055) / 1.055).powf(2.4)
}
else {
G / 12.92
};
B = 100.0
* if B > 0.04045 {
((B + 0.055) / 1.055).powf(2.4)
}
else {
B / 12.92
};
let mut X: f64 = (R * 0.4124 + G * 0.3576 + B * 0.1805) / 95.047; let mut Y: f64 = (R * 0.2126 + G * 0.7152 + B * 0.0722) / 100.0; let mut Z: f64 = (R * 0.0193 + G * 0.1192 + B * 0.9505) / 108.883;
X = if X > 0.008856 {
X.powf(1.0 / 3.0)
}
else {
7.787 * X + 16.0 / 116.0
};
Y = if Y > 0.008856 {
Y.powf(1.0 / 3.0)
}
else {
7.787 * Y + 16.0 / 116.0
};
Z = if Z > 0.008856 {
Z.powf(1.0 / 3.0)
}
else {
7.787 * Z + 16.0 / 116.0
};
lab.L = 116.0 * Y - 16.0; lab.a = 500.0 * (X - Y); lab.b = 200.0 * (Y - Z); }
pub fn mapRGBto88(red: i32, green: i32, blue: i32) -> i32 {
let component: [i32; 11] = [
0, 0x8b, 0xcd, 0xff, 0x2e, 0x5c, 0x8b, 0xa2, 0xb9, 0xd0, 0xe7,
]; let mut orig = cielab::default(); let mut next = cielab::default(); let mut nextl: f64; let mut bestl: f64 = -1.0; let mut r: i32; let mut g: i32; let mut b: i32; let mut comp_r: i32 = 0; let mut comp_g: i32 = 0; let mut comp_b: i32 = 0;
RGBtoLAB(red, green, blue, &mut orig);
r = 0; while r < 11 {
g = 0; while g <= 3 {
b = 0; while b <= 3 {
if r > 3 {
g = r;
b = r;
} RGBtoLAB(
component[r as usize], component[g as usize],
component[b as usize],
&mut next,
);
nextl = deltae(&orig, &next); if nextl < bestl || bestl < 0.0 {
bestl = nextl; comp_r = r; comp_g = g; comp_b = b; }
b += 1; }
g += 1; }
r += 1; }
if comp_r > 3 {
77 + comp_r } else {
16 + (comp_r * 16) + (comp_g * 4) + comp_b }
}
pub fn mapRGBto256(red: i32, green: i32, blue: i32) -> i32 {
let component: [i32; 30] = [
0, 0x5f, 0x87, 0xaf, 0xd7, 0xff, 0x8, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e, 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee, ];
let mut orig = cielab::default(); let mut next = cielab::default(); let mut nextl: f64; let mut bestl: f64 = -1.0; let mut r: i32; let mut g: i32; let mut b: i32; let mut comp_r: i32 = 0; let mut comp_g: i32 = 0; let mut comp_b: i32 = 0;
RGBtoLAB(red, green, blue, &mut orig);
let len: i32 = component.len() as i32; r = 0; while r < len {
g = 0; while g <= 5 {
b = 0; while b <= 5 {
if r > 5 {
g = r;
b = r;
} RGBtoLAB(
component[r as usize], component[g as usize],
component[b as usize],
&mut next,
);
nextl = deltae(&orig, &next); if nextl < bestl || bestl < 0.0 {
bestl = nextl; comp_r = r; comp_g = g; comp_b = b; }
b += 1; }
g += 1; }
r += 1; }
if comp_r > 5 {
226 + comp_r } else {
16 + (comp_r * 36) + (comp_g * 6) + comp_b }
}
#[allow(unused_variables)]
pub fn getnearestcolor(dummy: *const hookdef, col: *const color_rgb) -> i32 {
let red: i32;
let green: i32;
let blue: i32;
unsafe {
red = (*col).red as i32;
green = (*col).green as i32;
blue = (*col).blue as i32;
}
if tccolours.load(Ordering::Relaxed) == 256 {
return mapRGBto256(red, green, blue) + 1; }
if tccolours.load(Ordering::Relaxed) == 88 {
return mapRGBto88(red, green, blue) + 1; }
-1 }
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 {
0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 {
addhookfunc("get_color_attr", getnearestcolor); 0 }
pub fn cleanup_(m: *const module) -> i32 {
deletehookfunc("get_color_attr", getnearestcolor); setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0 }
fn addhookfunc(n: &str, f: fn(*const hookdef, *const color_rgb) -> i32) -> i32 {
let h = gethookdef(n);
if let Some(h) = h {
return addhookdeffunc(h, f); }
1 }
fn deletehookfunc(n: &str, f: fn(*const hookdef, *const color_rgb) -> i32) {
let h = gethookdef(n); if let Some(h) = h {
let _ = deletehookdeffunc(h, f); }
}
fn gethookdef(_n: &str) -> Option<*const hookdef> {
None
}
#[allow(unused_variables)]
fn addhookdeffunc(h: *const hookdef, f: fn(*const hookdef, *const color_rgb) -> i32) -> i32 {
0 }
fn deletehookdeffunc(_h: *const hookdef, _f: fn(*const hookdef, *const color_rgb) -> i32) -> i32 {
1 }
static MODULE_FEATURES: OnceLock<Mutex<features>> = OnceLock::new();
fn featuresarray(_m: *const module, _f: &Mutex<features>) -> Vec<String> {
vec![]
}
fn handlefeatures(_m: *const module, _f: &Mutex<features>, enables: &mut Option<Vec<i32>>) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 0]);
}
0
}
fn setfeatureenables(_m: *const module, _f: &Mutex<features>, _e: Option<&[i32]>) -> i32 {
0
}
fn module_features() -> &'static Mutex<features> {
MODULE_FEATURES.get_or_init(|| {
Mutex::new(features {
bn_list: None,
bn_size: 0,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rgb_to_lab_black_is_zero() {
let _g = crate::test_util::global_state_lock();
let mut lab = cielab::default();
RGBtoLAB(0, 0, 0, &mut lab);
assert!(lab.L.abs() < 0.5);
assert!(lab.a.abs() < 0.5);
assert!(lab.b.abs() < 0.5);
}
#[test]
fn deltae_self_is_zero() {
let _g = crate::test_util::global_state_lock();
let mut lab = cielab::default();
RGBtoLAB(123, 45, 67, &mut lab);
assert!(deltae(&lab, &lab).abs() < 1e-9);
}
#[test]
fn map_rgb_to_256_white_is_15_or_higher() {
let _g = crate::test_util::global_state_lock();
let idx = mapRGBto256(0xff, 0xff, 0xff);
assert!(idx >= 15);
}
#[test]
fn map_rgb_to_88_white_is_in_range() {
let _g = crate::test_util::global_state_lock();
let idx = mapRGBto88(0xff, 0xff, 0xff);
assert!((16..=87).contains(&idx) || idx >= 77);
}
#[test]
fn getnearestcolor_dispatches_on_tccolours() {
let _g = crate::test_util::global_state_lock();
let saved = tccolours.load(Ordering::SeqCst);
let col = color_rgb {
red: 0xff,
green: 0xff,
blue: 0xff,
};
tccolours.store(256, Ordering::SeqCst);
let r256 = getnearestcolor(std::ptr::null(), &col);
assert_eq!(r256, mapRGBto256(0xff, 0xff, 0xff) + 1);
tccolours.store(88, Ordering::SeqCst);
let r88 = getnearestcolor(std::ptr::null(), &col);
assert_eq!(r88, mapRGBto88(0xff, 0xff, 0xff) + 1);
tccolours.store(16, Ordering::SeqCst);
assert_eq!(getnearestcolor(std::ptr::null(), &col), -1);
tccolours.store(saved, Ordering::SeqCst);
}
#[test]
fn deltae_is_symmetric() {
let _g = crate::test_util::global_state_lock();
let mut a = cielab::default();
let mut b = cielab::default();
RGBtoLAB(200, 50, 25, &mut a);
RGBtoLAB(25, 200, 50, &mut b);
let d_ab = deltae(&a, &b);
let d_ba = deltae(&b, &a);
assert!(
(d_ab - d_ba).abs() < 1e-9,
"deltae symmetry broken: {} vs {}",
d_ab,
d_ba
);
}
#[test]
fn deltae_distinct_colors_is_positive() {
let _g = crate::test_util::global_state_lock();
let mut white = cielab::default();
let mut black = cielab::default();
RGBtoLAB(0xff, 0xff, 0xff, &mut white);
RGBtoLAB(0, 0, 0, &mut black);
let d = deltae(&white, &black);
assert!(
d > 0.0 && d.is_finite(),
"deltae(white, black) = {} — must be a finite positive value",
d
);
}
#[test]
fn rgb_to_lab_pure_black_yields_zero() {
let _g = crate::test_util::global_state_lock();
let mut lab = cielab::default();
RGBtoLAB(0, 0, 0, &mut lab);
assert!(lab.L.abs() < 1e-9, "L for black = {}; should be 0", lab.L);
assert!(lab.a.abs() < 1e-9, "a for black = {}; should be 0", lab.a);
assert!(lab.b.abs() < 1e-9, "b for black = {}; should be 0", lab.b);
}
#[test]
fn rgb_to_lab_pure_white_has_lightness_near_100() {
let _g = crate::test_util::global_state_lock();
let mut lab = cielab::default();
RGBtoLAB(0xff, 0xff, 0xff, &mut lab);
assert!(
(lab.L - 100.0).abs() < 1.0,
"L for white = {}, expected ≈ 100",
lab.L
);
}
#[test]
fn map_rgb_to_256_black_is_16() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto256(0, 0, 0), 16);
}
#[test]
fn map_rgb_to_256_pure_red_is_196() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto256(0xff, 0, 0), 196);
}
#[test]
fn map_rgb_to_256_pure_green_is_46() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto256(0, 0xff, 0), 46);
}
#[test]
fn map_rgb_to_256_pure_blue_is_21() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto256(0, 0, 0xff), 21);
}
#[test]
fn map_rgb_to_88_black_is_16() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto88(0, 0, 0), 16);
}
#[test]
fn map_rgb_to_88_pure_red_is_64() {
let _g = crate::test_util::global_state_lock();
assert_eq!(mapRGBto88(0xff, 0, 0), 64);
}
#[test]
fn getnearestcolor_unknown_palette_returns_neg_one() {
let _g = crate::test_util::global_state_lock();
let saved = tccolours.load(Ordering::SeqCst);
tccolours.store(0, Ordering::SeqCst);
let col = color_rgb {
red: 100,
green: 100,
blue: 100,
};
let r = getnearestcolor(std::ptr::null(), &col);
tccolours.store(saved, Ordering::SeqCst);
assert_eq!(r, -1);
}
#[test]
fn module_lifecycle_shims_all_return_zero() {
let _g = crate::test_util::global_state_lock();
let m: *const module = std::ptr::null();
assert_eq!(setup_(m), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
#[test]
fn nearcolor_corpus_map256_black_is_16() {
let r = mapRGBto256(0, 0, 0);
assert_eq!(r, 16, "black = palette index 16");
}
#[test]
fn nearcolor_corpus_map256_white_is_231() {
let r = mapRGBto256(255, 255, 255);
assert_eq!(r, 231, "white = palette index 231");
}
#[test]
fn nearcolor_corpus_map256_is_deterministic() {
let a = mapRGBto256(128, 64, 200);
let b = mapRGBto256(128, 64, 200);
assert_eq!(a, b);
}
#[test]
fn nearcolor_corpus_map256_within_palette_range() {
for r in [0, 64, 128, 192, 255] {
for g in [0, 128, 255] {
for b in [0, 128, 255] {
let idx = mapRGBto256(r, g, b);
assert!(
(16..=255).contains(&idx),
"({r},{g},{b}) → {idx} out of [16,255]"
);
}
}
}
}
#[test]
fn nearcolor_corpus_map88_black_in_palette() {
let r = mapRGBto88(0, 0, 0);
assert!(r >= 0 && r < 88, "88-palette index in [0,88), got {r}");
}
#[test]
fn nearcolor_corpus_map88_white_in_palette() {
let r = mapRGBto88(255, 255, 255);
assert!(r >= 0 && r < 88, "88-palette index in [0,88), got {r}");
}
#[test]
fn nearcolor_corpus_deltae_self_is_zero() {
let lab = cielab {
L: 50.0,
a: 25.0,
b: 30.0,
};
let d = deltae(&lab, &lab);
assert!(d.abs() < 1e-9, "deltae(x,x)=0, got {d}");
}
#[test]
fn nearcolor_corpus_deltae_symmetric() {
let a = cielab {
L: 50.0,
a: 25.0,
b: 30.0,
};
let b = cielab {
L: 60.0,
a: 15.0,
b: 35.0,
};
let d1 = deltae(&a, &b);
let d2 = deltae(&b, &a);
assert!((d1 - d2).abs() < 1e-9);
}
#[test]
fn deltae_always_non_negative() {
let a = cielab {
L: -100.0,
a: -50.0,
b: 200.0,
};
let b = cielab {
L: 100.0,
a: 50.0,
b: -200.0,
};
let d = deltae(&a, &b);
assert!(
d >= 0.0,
"delta-E is sum of squares, must be ≥ 0, got {}",
d
);
}
#[test]
fn deltae_matches_sum_of_squares_formula() {
let a = cielab {
L: 50.0,
a: 0.0,
b: 0.0,
};
let b = cielab {
L: 53.0,
a: 4.0,
b: 12.0,
};
let r = deltae(&a, &b);
let expected = 9.0 + 16.0 + 144.0; assert!((r - expected).abs() < 1e-9, "{} != {}", r, expected);
}
#[test]
fn rgb_to_lab_black_origin() {
let mut lab = cielab {
L: 99.0,
a: 99.0,
b: 99.0,
};
RGBtoLAB(0, 0, 0, &mut lab);
assert!(lab.L.abs() < 1e-3, "black L≈0, got {}", lab.L);
assert!(lab.a.abs() < 1e-3, "black a≈0, got {}", lab.a);
assert!(lab.b.abs() < 1e-3, "black b≈0, got {}", lab.b);
}
#[test]
fn rgb_to_lab_white_has_l_near_100() {
let mut lab = cielab {
L: 0.0,
a: 0.0,
b: 0.0,
};
RGBtoLAB(255, 255, 255, &mut lab);
assert!((lab.L - 100.0).abs() < 0.5, "white L≈100, got {}", lab.L);
}
#[test]
fn rgb_to_lab_gray_has_zero_chroma() {
let mut lab = cielab {
L: 0.0,
a: 99.0,
b: 99.0,
};
RGBtoLAB(128, 128, 128, &mut lab);
assert!(lab.a.abs() < 1.0, "gray a≈0, got {}", lab.a);
assert!(lab.b.abs() < 1.0, "gray b≈0, got {}", lab.b);
}
#[test]
fn rgb_to_lab_is_deterministic() {
let mut lab1 = cielab {
L: 0.0,
a: 0.0,
b: 0.0,
};
let mut lab2 = cielab {
L: 0.0,
a: 0.0,
b: 0.0,
};
RGBtoLAB(100, 150, 200, &mut lab1);
RGBtoLAB(100, 150, 200, &mut lab2);
assert_eq!(lab1.L, lab2.L);
assert_eq!(lab1.a, lab2.a);
assert_eq!(lab1.b, lab2.b);
}
#[test]
fn mapRGBto88_returns_valid_palette_index() {
let r = mapRGBto88(0, 0, 0);
assert!(
r >= 0 && r < 88,
"88-color palette index in [0,88), got {}",
r
);
}
#[test]
fn mapRGBto88_is_deterministic() {
let a = mapRGBto88(255, 128, 64);
let b = mapRGBto88(255, 128, 64);
assert_eq!(a, b);
}
#[test]
fn mapRGBto256_returns_valid_palette_index() {
let r = mapRGBto256(0, 0, 0);
assert!(
r >= 0 && r < 256,
"256-color palette index in [0,256), got {}",
r
);
}
#[test]
fn mapRGBto256_is_deterministic() {
let a = mapRGBto256(200, 100, 50);
let b = mapRGBto256(200, 100, 50);
assert_eq!(a, b);
}
#[test]
fn nearcolor_setup_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(setup_(std::ptr::null()), 0);
}
#[test]
fn nearcolor_cleanup_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(cleanup_(std::ptr::null()), 0);
}
#[test]
fn deltae_reflexive_for_arbitrary_colors() {
for (r, g, b) in [(0, 0, 0), (255, 0, 0), (128, 128, 128), (255, 255, 255)] {
let mut lab = cielab::default();
RGBtoLAB(r, g, b, &mut lab);
assert_eq!(
deltae(&lab, &lab),
0.0,
"deltae({:?}, {:?}) must be 0",
(r, g, b),
(r, g, b)
);
}
}
#[test]
fn deltae_symmetric_full_sweep() {
let pairs = [
((255, 0, 0), (0, 255, 0)),
((0, 0, 0), (128, 128, 128)),
((255, 255, 255), (0, 0, 0)),
];
for ((r1, g1, b1), (r2, g2, b2)) in pairs {
let mut a = cielab::default();
let mut b = cielab::default();
RGBtoLAB(r1, g1, b1, &mut a);
RGBtoLAB(r2, g2, b2, &mut b);
let ab = deltae(&a, &b);
let ba = deltae(&b, &a);
assert_eq!(ab, ba, "deltae must be symmetric");
}
}
#[test]
fn mapRGBto88_full_sweep_pure() {
for (r, g, b) in [(0, 0, 0), (127, 127, 127), (255, 0, 0), (255, 255, 255)] {
let first = mapRGBto88(r, g, b);
for _ in 0..3 {
assert_eq!(
mapRGBto88(r, g, b),
first,
"mapRGBto88({},{},{}) must be pure",
r,
g,
b
);
}
}
}
#[test]
fn mapRGBto256_full_sweep_pure() {
for (r, g, b) in [(0, 0, 0), (127, 127, 127), (255, 0, 0), (255, 255, 255)] {
let first = mapRGBto256(r, g, b);
for _ in 0..3 {
assert_eq!(
mapRGBto256(r, g, b),
first,
"mapRGBto256({},{},{}) must be pure",
r,
g,
b
);
}
}
}
#[test]
fn rgb_to_lab_lightness_in_zero_to_hundred() {
for (r, g, b) in [
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(255, 255, 255),
] {
let mut lab = cielab::default();
RGBtoLAB(r, g, b, &mut lab);
assert!(
(0.0..=100.5).contains(&lab.L),
"L value {} out of [0, 100] for ({},{},{})",
lab.L,
r,
g,
b
);
}
}
#[test]
fn mapRGBto88_output_in_palette_range() {
for (r, g, b) in [(0, 0, 0), (255, 0, 0), (128, 128, 128), (255, 255, 255)] {
let idx = mapRGBto88(r, g, b);
assert!(
(16..88).contains(&idx),
"mapRGBto88({},{},{}) = {} out of [16, 88)",
r,
g,
b,
idx
);
}
}
#[test]
fn mapRGBto256_output_in_palette_range() {
for (r, g, b) in [(0, 0, 0), (255, 0, 0), (128, 128, 128), (255, 255, 255)] {
let idx = mapRGBto256(r, g, b);
assert!(
(16..256).contains(&idx),
"mapRGBto256({},{},{}) = {} out of [16, 256)",
r,
g,
b,
idx
);
}
}
#[test]
fn rgb_to_lab_full_sweep_is_pure() {
for (r, g, b) in [(0, 0, 0), (255, 128, 64), (50, 50, 50)] {
let mut a = cielab::default();
let mut b_lab = cielab::default();
RGBtoLAB(r, g, b, &mut a);
RGBtoLAB(r, g, b, &mut b_lab);
assert_eq!(a.L, b_lab.L, "L must be pure");
assert_eq!(a.a, b_lab.a, "a must be pure");
assert_eq!(a.b, b_lab.b, "b must be pure");
}
}
#[test]
fn nearcolor_full_lifecycle_returns_zero_for_all() {
let _g = crate::test_util::global_state_lock();
let null = std::ptr::null();
assert_eq!(setup_(null), 0);
let mut feats = Vec::new();
let _ = features_(null, &mut feats);
let mut enables: Option<Vec<i32>> = None;
let _ = enables_(null, &mut enables);
assert_eq!(boot_(null), 0);
assert_eq!(cleanup_(null), 0);
assert_eq!(finish_(null), 0);
}
#[test]
fn nearcolor_setup_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(setup_(std::ptr::null()), 0);
}
}
#[test]
fn deltae_returns_f64_type() {
let a = cielab::default();
let b = cielab::default();
let _: f64 = deltae(&a, &b);
}
#[test]
fn deltae_identical_labs_is_zero() {
let lab = cielab {
L: 50.0,
a: 25.0,
b: -10.0,
};
let d = deltae(&lab, &lab);
assert_eq!(d, 0.0, "deltae(x, x) = 0; got {}", d);
}
#[test]
fn deltae_symmetric_alt_pin() {
let p = cielab {
L: 30.0,
a: 10.0,
b: 20.0,
};
let q = cielab {
L: 60.0,
a: -5.0,
b: 40.0,
};
let d_pq = deltae(&p, &q);
let d_qp = deltae(&q, &p);
assert!(
(d_pq - d_qp).abs() < 1e-9,
"CIE76 ΔE must be symmetric; got {} vs {}",
d_pq,
d_qp
);
}
#[test]
fn deltae_non_negative_invariant() {
for (l, a, b) in [(0.0, 0.0, 0.0), (100.0, 50.0, -50.0), (25.0, -30.0, 10.0)] {
let x = cielab { L: l, a, b };
let y = cielab {
L: 50.0,
a: 0.0,
b: 0.0,
};
let d = deltae(&x, &y);
assert!(
d >= 0.0,
"ΔE must be ≥ 0 for any inputs; got {} from L={}/a={}/b={}",
d,
l,
a,
b
);
}
}
#[test]
fn rgb_to_lab_black_has_zero_lightness() {
let mut lab = cielab::default();
RGBtoLAB(0, 0, 0, &mut lab);
assert!(
lab.L.abs() < 0.5,
"black should have L ≈ 0; got L={}",
lab.L
);
}
#[test]
fn rgb_to_lab_white_has_lightness_near_hundred() {
let mut lab = cielab::default();
RGBtoLAB(255, 255, 255, &mut lab);
assert!(
(lab.L - 100.0).abs() < 0.5,
"white should have L ≈ 100; got L={}",
lab.L
);
}
#[test]
fn mapRGBto88_returns_i32_type() {
let _: i32 = mapRGBto88(0, 0, 0);
}
#[test]
fn mapRGBto256_returns_i32_type() {
let _: i32 = mapRGBto256(0, 0, 0);
}
#[test]
fn mapRGBto88_black_in_palette_range() {
let idx = mapRGBto88(0, 0, 0);
assert!(
idx >= 16 && idx < 88,
"mapRGBto88(black) = {} must be in [16, 88)",
idx
);
}
#[test]
fn mapRGBto256_black_and_white_distinct() {
let black_idx = mapRGBto256(0, 0, 0);
let white_idx = mapRGBto256(255, 255, 255);
assert_ne!(
black_idx, white_idx,
"black and white must map to different 256-palette entries"
);
}
#[test]
fn deltae_returns_squared_distance_no_sqrt() {
let a = cielab {
L: 0.0,
a: 0.0,
b: 0.0,
};
let c = cielab {
L: 100.0,
a: 0.0,
b: 0.0,
};
let d = deltae(&a, &c);
assert!(
(d - 10000.0).abs() < 0.5,
"deltae must return Σ(diff²) = 10000 (NOT sqrt'd 100); got {}",
d
);
}
#[test]
fn deltae_zero_for_identical_inputs() {
let a = cielab {
L: 50.0,
a: 10.0,
b: -5.0,
};
let b = a.clone();
assert_eq!(
deltae(&a, &b),
0.0,
"deltae(x, x.clone()) must be exactly 0"
);
}
#[test]
fn deltae_is_symmetric_alt() {
let a = cielab {
L: 10.0,
a: 20.0,
b: 30.0,
};
let b = cielab {
L: 40.0,
a: 50.0,
b: 60.0,
};
let d1 = deltae(&a, &b);
let d2 = deltae(&b, &a);
assert!(
(d1 - d2).abs() < 1e-9,
"deltae must be symmetric, got {} vs {}",
d1,
d2
);
}
#[test]
fn deltae_is_non_negative() {
for ((l1, a1, b1), (l2, a2, b2)) in [
((0.0, 0.0, 0.0), (100.0, 100.0, 100.0)),
((50.0, -50.0, 50.0), (0.0, 0.0, 0.0)),
((1.0, 2.0, 3.0), (4.0, 5.0, 6.0)),
] {
let p = cielab {
L: l1,
a: a1,
b: b1,
};
let q = cielab {
L: l2,
a: a2,
b: b2,
};
assert!(deltae(&p, &q) >= 0.0, "deltae must be ≥ 0");
}
}
#[test]
fn rgb_to_lab_black_is_l_zero() {
let mut lab = cielab {
L: -1.0,
a: -1.0,
b: -1.0,
};
RGBtoLAB(0, 0, 0, &mut lab);
assert!(lab.L.abs() < 1.0, "black must produce L≈0, got {}", lab.L);
}
#[test]
fn rgb_to_lab_white_is_l_one_hundred() {
let mut lab = cielab {
L: 0.0,
a: 0.0,
b: 0.0,
};
RGBtoLAB(255, 255, 255, &mut lab);
assert!(
(lab.L - 100.0).abs() < 1.0,
"white must produce L≈100, got {}",
lab.L
);
}
#[test]
fn map_rgb_to_88_black_in_range() {
let i = mapRGBto88(0, 0, 0);
assert!(
(0..88).contains(&i),
"mapRGBto88(0,0,0) = {} must be in 0..88",
i
);
}
#[test]
fn map_rgb_to_256_black_in_range() {
let i = mapRGBto256(0, 0, 0);
assert!(
(0..256).contains(&i),
"mapRGBto256(0,0,0) = {} must be in 0..256",
i
);
}
#[test]
fn map_rgb_functions_deterministic() {
for (r, g, b) in [
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(128, 128, 128),
] {
let a1 = mapRGBto88(r, g, b);
let a2 = mapRGBto88(r, g, b);
assert_eq!(a1, a2);
let b1 = mapRGBto256(r, g, b);
let b2 = mapRGBto256(r, g, b);
assert_eq!(b1, b2);
}
}
#[test]
fn nearcolor_setup_idempotent_alt_pin() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(setup_(std::ptr::null()), 0);
}
}
#[test]
fn nearcolor_cleanup_idempotent_alt_pin() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(cleanup_(std::ptr::null()), 0);
}
}
#[test]
fn nearcolor_finish_idempotent_alt_pin() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(finish_(std::ptr::null()), 0);
}
}
}