#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn shannon_entropy(data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let mut freq = [0u64; 256];
for &b in data {
freq[b as usize] += 1;
}
let n = data.len() as f64;
let mut entropy = 0.0;
for &f in &freq {
if f > 0 {
let p = f as f64 / n;
entropy -= p * log2_approx(p);
}
}
entropy
}
#[allow(clippy::cast_precision_loss)]
fn log2_approx(x: f64) -> f64 {
if x <= 0.0 {
return -100.0;
}
let y = (x - 1.0) / (x + 1.0);
let y2 = y * y;
let mut sum = y;
let mut term = y;
for k in 1..20 {
term *= y2;
sum += term / f64::from(2 * k + 1);
}
2.0 * sum / core::f64::consts::LN_2
}
#[must_use]
#[allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
pub fn theoretical_min_size(data: &[u8]) -> usize {
let entropy = shannon_entropy(data);
let bits = entropy * data.len() as f64;
let v = bits / 8.0;
let i = v as usize;
if (i as f64) < v {
i + 1
} else {
i
}
}
#[cfg(test)]
#[allow(
clippy::doc_markdown,
clippy::unreadable_literal,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::uninlined_format_args
)]
mod tests {
use super::*;
use alloc::vec::Vec;
#[test]
fn entropy_uniform() {
let data: Vec<u8> = (0..=255).collect();
let e = shannon_entropy(&data);
assert!(e > 5.0); }
#[test]
fn entropy_single() {
let data = alloc::vec![42u8; 1000];
let e = shannon_entropy(&data);
assert!(e < 0.01);
}
#[test]
fn entropy_empty() {
assert!((shannon_entropy(b"") - 0.0).abs() < f64::EPSILON);
}
#[test]
fn entropy_one_byte() {
assert!((shannon_entropy(&[77]) - 0.0).abs() < f64::EPSILON);
}
#[test]
fn entropy_two_values_equal() {
let mut data = alloc::vec![];
for _ in 0..500 {
data.push(0u8);
data.push(1u8);
}
let e = shannon_entropy(&data);
assert!(
(e - 1.0).abs() < 0.01,
"2値等確率のエントロピーは1.0付近: {e}"
);
}
#[test]
fn entropy_two_values_unequal() {
let mut data = alloc::vec![0u8; 900];
data.extend_from_slice(&alloc::vec![1u8; 100]);
let e = shannon_entropy(&data);
assert!(e > 0.0 && e < 1.0, "不等確率のエントロピーは0<e<1: {e}");
}
#[test]
fn entropy_max_value() {
let data: Vec<u8> = (0..=255).collect();
let e = shannon_entropy(&data);
assert!(e > 5.0, "最大エントロピーは十分高い: {e}");
}
#[test]
fn entropy_three_values() {
let mut data = alloc::vec![];
for _ in 0..300 {
data.push(10u8);
data.push(20u8);
data.push(30u8);
}
let e = shannon_entropy(&data);
let expected = core::f64::consts::LN_2.recip() * 3.0_f64.ln(); assert!(
(e - expected).abs() < 0.1,
"3値等確率のエントロピー: {e} vs {expected}"
);
}
#[test]
fn entropy_non_negative() {
for v in 0..=255u8 {
let data = alloc::vec![v; 100];
assert!(shannon_entropy(&data) >= 0.0);
}
}
#[test]
fn entropy_large_data() {
let data: Vec<u8> = (0..10000).map(|i| (i % 256) as u8).collect();
let e = shannon_entropy(&data);
assert!(e > 5.0, "大量一様データのエントロピーは高い: {e}");
}
#[test]
fn theoretical_min() {
let data = alloc::vec![42u8; 1000];
let min = theoretical_min_size(&data);
assert!(min < 10); }
#[test]
fn theoretical_min_empty() {
assert_eq!(theoretical_min_size(b""), 0);
}
#[test]
fn theoretical_min_single() {
assert_eq!(theoretical_min_size(&[99]), 0);
}
#[test]
fn theoretical_min_uniform() {
let data: Vec<u8> = (0..=255).collect();
let min = theoretical_min_size(&data);
assert!(min > 200, "一様分布の最小サイズは元に近い: {min}");
}
#[test]
fn theoretical_min_two_values() {
let mut data = alloc::vec![];
for _ in 0..4000 {
data.push(0u8);
data.push(1u8);
}
let min = theoretical_min_size(&data);
assert!(min > 800 && min < 1200, "2値のmin: {min}");
}
#[test]
fn theoretical_min_constant() {
let data = alloc::vec![0u8; 10000];
let min = theoretical_min_size(&data);
assert!(min < 10, "定数データのmin: {min}");
}
#[test]
fn entropy_four_values() {
let mut data = alloc::vec![];
for _ in 0..1000 {
data.push(0u8);
data.push(1u8);
data.push(2u8);
data.push(3u8);
}
let e = shannon_entropy(&data);
assert!((e - 2.0).abs() < 0.1, "4値等確率のエントロピーは約2.0: {e}");
}
#[test]
fn entropy_eight_values() {
let mut data = alloc::vec![];
for _ in 0..1000 {
for v in 0..8u8 {
data.push(v);
}
}
let e = shannon_entropy(&data);
assert!((e - 3.0).abs() < 0.1, "8値等確率のエントロピーは約3.0: {e}");
}
#[test]
fn entropy_sixteen_values() {
let mut data = alloc::vec![];
for _ in 0..500 {
for v in 0..16u8 {
data.push(v);
}
}
let e = shannon_entropy(&data);
assert!(
(e - 4.0).abs() < 0.1,
"16値等確率のエントロピーは約4.0: {e}"
);
}
}