use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use super::types::{
BlahutArimoto, ChannelSimulator, ChernoffInformationCalc, EntropyEstimator, HuffmanCode,
InfoTheoreticSecurity, KLDivergenceCalc, KolmogorovComplexity, LempelZivComplexity,
MutualInformation, RateDistortion, RenyiEntropyComputer, SlepianWolfCoder,
TsallisEntropyComputer, WiretapChannel,
};
pub fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Box::new(f), Box::new(a))
}
pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
app(app(f, a), b)
}
pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
app(app2(f, a, b), c)
}
pub fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
pub fn prop() -> Expr {
Expr::Sort(Level::zero())
}
pub fn type0() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
}
pub fn arrow(a: Expr, b: Expr) -> Expr {
pi(BinderInfo::Default, "_", a, b)
}
pub fn bvar(n: u32) -> Expr {
Expr::BVar(n)
}
pub fn nat_ty() -> Expr {
cst("Nat")
}
pub fn real_ty() -> Expr {
cst("Real")
}
pub fn list_ty(elem: Expr) -> Expr {
app(cst("List"), elem)
}
pub fn bool_ty() -> Expr {
cst("Bool")
}
pub fn entropy_ty() -> Expr {
arrow(list_ty(real_ty()), real_ty())
}
pub fn mutual_information_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn channel_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn channel_coding_ty() -> Expr {
prop()
}
pub fn source_coding_ty() -> Expr {
prop()
}
pub fn kolmogorov_complexity_ty() -> Expr {
arrow(nat_ty(), nat_ty())
}
pub fn shannon_source_coding_ty() -> Expr {
pi(BinderInfo::Default, "dist", list_ty(real_ty()), prop())
}
pub fn noisy_channel_theorem_ty() -> Expr {
pi(
BinderInfo::Default,
"channel",
list_ty(list_ty(real_ty())),
prop(),
)
}
pub fn data_processing_inequality_ty() -> Expr {
prop()
}
pub fn fano_inequality_ty() -> Expr {
pi(
BinderInfo::Default,
"pe",
real_ty(),
pi(BinderInfo::Default, "alphabet_size", nat_ty(), prop()),
)
}
pub fn conditional_mutual_information_ty() -> Expr {
arrow(list_ty(list_ty(list_ty(real_ty()))), real_ty())
}
pub fn awgn_capacity_ty() -> Expr {
arrow(real_ty(), real_ty())
}
pub fn dmc_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn rate_distortion_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(real_ty(), real_ty()))
}
pub fn berger_theorem_ty() -> Expr {
prop()
}
pub fn typical_set_ty() -> Expr {
pi(
BinderInfo::Default,
"dist",
list_ty(real_ty()),
pi(
BinderInfo::Default,
"eps",
real_ty(),
pi(BinderInfo::Default, "n", nat_ty(), prop()),
),
)
}
pub fn aep_ty() -> Expr {
pi(BinderInfo::Default, "dist", list_ty(real_ty()), prop())
}
pub fn entropy_rate_ty() -> Expr {
arrow(arrow(nat_ty(), list_ty(real_ty())), real_ty())
}
pub fn differential_entropy_ty() -> Expr {
arrow(arrow(real_ty(), real_ty()), real_ty())
}
pub fn fisher_information_ty() -> Expr {
arrow(
arrow(real_ty(), arrow(real_ty(), real_ty())),
arrow(real_ty(), real_ty()),
)
}
pub fn cramer_rao_bound_ty() -> Expr {
prop()
}
pub fn secrecy_capacity_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), real_ty()),
)
}
pub fn mac_capacity_region_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), prop()),
)
}
pub fn broadcast_channel_ty() -> Expr {
pi(
BinderInfo::Default,
"channel",
list_ty(list_ty(real_ty())),
prop(),
)
}
pub fn wyner_ziv_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), real_ty()))
}
pub fn slepian_wolf_ty() -> Expr {
pi(
BinderInfo::Default,
"joint",
list_ty(list_ty(real_ty())),
prop(),
)
}
pub fn mdl_criterion_ty() -> Expr {
arrow(nat_ty(), real_ty())
}
pub fn shannon_fano_ty() -> Expr {
arrow(list_ty(real_ty()), list_ty(nat_ty()))
}
pub fn lz_compression_ty() -> Expr {
prop()
}
pub fn entropy_power_inequality_ty() -> Expr {
prop()
}
pub fn log_sum_inequality_ty() -> Expr {
prop()
}
pub fn blahut_arimoto_convergence_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(nat_ty(), real_ty()))
}
pub fn perfect_secrecy_ty() -> Expr {
prop()
}
pub fn interference_channel_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), real_ty()),
)
}
pub fn capacity_per_unit_cost_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(real_ty()), real_ty()),
)
}
pub fn channel_dispersion_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn typical_set_size_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(nat_ty(), real_ty()))
}
pub fn renyi_entropy_ty() -> Expr {
arrow(real_ty(), arrow(list_ty(real_ty()), real_ty()))
}
pub fn min_entropy_ty() -> Expr {
arrow(list_ty(real_ty()), real_ty())
}
pub fn marton_region_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), prop()),
)
}
pub fn degraded_broadcast_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn relay_channel_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn mac_sum_capacity_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), real_ty()),
)
}
pub fn han_kobayashi_bound_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), prop()),
)
}
pub fn gray_wyner_region_ty() -> Expr {
pi(
BinderInfo::Default,
"joint",
list_ty(list_ty(real_ty())),
prop(),
)
}
pub fn slepian_wolf_region_ty() -> Expr {
pi(
BinderInfo::Default,
"joint",
list_ty(list_ty(real_ty())),
pi(
BinderInfo::Default,
"r1",
real_ty(),
pi(BinderInfo::Default, "r2", real_ty(), prop()),
),
)
}
pub fn wyner_ziv_rate_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), real_ty()))
}
pub fn broadcast_confidential_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), real_ty()),
)
}
pub fn gacs_korner_common_info_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn wyner_common_info_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn secret_key_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn tsallis_entropy_ty() -> Expr {
arrow(real_ty(), arrow(list_ty(real_ty()), real_ty()))
}
pub fn sibson_mutual_info_ty() -> Expr {
arrow(real_ty(), arrow(list_ty(list_ty(real_ty())), real_ty()))
}
pub fn lautum_information_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn arimoto_mutual_info_ty() -> Expr {
arrow(real_ty(), arrow(list_ty(list_ty(real_ty())), real_ty()))
}
pub fn f_divergence_ty() -> Expr {
arrow(
arrow(real_ty(), real_ty()),
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty())),
)
}
pub fn sanov_theorem_ty() -> Expr {
pi(BinderInfo::Default, "dist", list_ty(real_ty()), prop())
}
pub fn chernoff_information_ty() -> Expr {
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty()))
}
pub fn gallager_exponent_ty() -> Expr {
arrow(
real_ty(),
arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), real_ty())),
)
}
pub fn expurgated_exponent_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), real_ty()))
}
pub fn sphere_packing_exponent_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), real_ty()))
}
pub fn neyman_pearson_ty() -> Expr {
pi(
BinderInfo::Default,
"p",
list_ty(real_ty()),
pi(
BinderInfo::Default,
"q",
list_ty(real_ty()),
pi(BinderInfo::Default, "threshold", real_ty(), prop()),
),
)
}
pub fn stein_lemma_ty() -> Expr {
pi(
BinderInfo::Default,
"p",
list_ty(real_ty()),
pi(BinderInfo::Default, "q", list_ty(real_ty()), prop()),
)
}
pub fn chernoff_stein_ty() -> Expr {
pi(
BinderInfo::Default,
"p",
list_ty(real_ty()),
pi(BinderInfo::Default, "q", list_ty(real_ty()), prop()),
)
}
pub fn lz_complexity_ty() -> Expr {
arrow(list_ty(bool_ty()), nat_ty())
}
pub fn lz78_universal_ty() -> Expr {
prop()
}
pub fn mdl_model_selection_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), real_ty()))
}
pub fn fisher_matrix_ty() -> Expr {
arrow(
arrow(list_ty(real_ty()), list_ty(real_ty())),
arrow(list_ty(real_ty()), list_ty(list_ty(real_ty()))),
)
}
pub fn cramer_rao_matrix_ty() -> Expr {
prop()
}
pub fn jeffreys_prior_ty() -> Expr {
arrow(
arrow(list_ty(real_ty()), list_ty(real_ty())),
arrow(list_ty(real_ty()), real_ty()),
)
}
pub fn natural_gradient_ty() -> Expr {
arrow(
arrow(list_ty(real_ty()), real_ty()),
arrow(
list_ty(real_ty()),
arrow(list_ty(list_ty(real_ty())), list_ty(real_ty())),
),
)
}
pub fn von_neumann_entropy_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn quantum_mutual_info_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn holevo_bound_ty() -> Expr {
arrow(
list_ty(real_ty()),
arrow(list_ty(list_ty(list_ty(real_ty()))), real_ty()),
)
}
pub fn quantum_channel_capacity_ty() -> Expr {
arrow(list_ty(list_ty(list_ty(real_ty()))), real_ty())
}
pub fn quantum_relative_entropy_ty() -> Expr {
arrow(
list_ty(list_ty(real_ty())),
arrow(list_ty(list_ty(real_ty())), real_ty()),
)
}
pub fn schumacher_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn functional_compression_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), real_ty())
}
pub fn computing_capacity_ty() -> Expr {
arrow(list_ty(list_ty(real_ty())), arrow(nat_ty(), real_ty()))
}
pub fn korner_graph_entropy_ty() -> Expr {
arrow(nat_ty(), arrow(list_ty(list_ty(bool_ty())), real_ty()))
}
pub fn build_information_theory_env(env: &mut Environment) -> Result<(), String> {
let axioms: &[(&str, Expr)] = &[
("Entropy", entropy_ty()),
("MutualInformation", mutual_information_ty()),
("ChannelCapacity", channel_capacity_ty()),
("ChannelCoding", channel_coding_ty()),
("SourceCoding", source_coding_ty()),
("KolmogorovComplexity", kolmogorov_complexity_ty()),
(
"JointEntropy",
arrow(list_ty(list_ty(real_ty())), real_ty()),
),
(
"ConditionalEntropy",
arrow(list_ty(list_ty(real_ty())), real_ty()),
),
(
"KLDivergence",
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty())),
),
(
"CrossEntropy",
arrow(list_ty(real_ty()), arrow(list_ty(real_ty()), real_ty())),
),
("BinaryEntropy", arrow(real_ty(), real_ty())),
("BSCCapacity", arrow(real_ty(), real_ty())),
("BECCapacity", arrow(real_ty(), real_ty())),
("shannon_source_coding", shannon_source_coding_ty()),
("noisy_channel_theorem", noisy_channel_theorem_ty()),
(
"data_processing_inequality",
data_processing_inequality_ty(),
),
("fano_inequality", fano_inequality_ty()),
(
"entropy_nonneg",
pi(BinderInfo::Default, "dist", list_ty(real_ty()), prop()),
),
(
"entropy_max_uniform",
pi(BinderInfo::Default, "n", nat_ty(), prop()),
),
(
"mi_nonneg",
pi(
BinderInfo::Default,
"joint",
list_ty(list_ty(real_ty())),
prop(),
),
),
("kl_nonneg", prop()),
("chain_rule_entropy", prop()),
(
"ConditionalMutualInformation",
conditional_mutual_information_ty(),
),
("AWGNCapacity", awgn_capacity_ty()),
("DMCCapacity", dmc_capacity_ty()),
("RateDistortion", rate_distortion_ty()),
("berger_theorem", berger_theorem_ty()),
("TypicalSet", typical_set_ty()),
("aep", aep_ty()),
("TypicalSetSize", typical_set_size_ty()),
("EntropyRate", entropy_rate_ty()),
("DifferentialEntropy", differential_entropy_ty()),
("FisherInformation", fisher_information_ty()),
("cramer_rao_bound", cramer_rao_bound_ty()),
("SecrecyCapacity", secrecy_capacity_ty()),
("MACCapacityRegion", mac_capacity_region_ty()),
("BroadcastChannel", broadcast_channel_ty()),
("WynerZiv", wyner_ziv_ty()),
("SlepianWolf", slepian_wolf_ty()),
("InterferenceChannel", interference_channel_ty()),
("MDLCriterion", mdl_criterion_ty()),
("ShannonFano", shannon_fano_ty()),
("lz_compression", lz_compression_ty()),
("entropy_power_inequality", entropy_power_inequality_ty()),
("log_sum_inequality", log_sum_inequality_ty()),
("BlahutArimotoConvergence", blahut_arimoto_convergence_ty()),
("perfect_secrecy", perfect_secrecy_ty()),
("CapacityPerUnitCost", capacity_per_unit_cost_ty()),
("ChannelDispersion", channel_dispersion_ty()),
("RenyiEntropy", renyi_entropy_ty()),
("MinEntropy", min_entropy_ty()),
("MartonRegion", marton_region_ty()),
(
"DegradedBroadcastCapacity",
degraded_broadcast_capacity_ty(),
),
("RelayChannelCapacity", relay_channel_capacity_ty()),
("MACsumCapacity", mac_sum_capacity_ty()),
("HanKobayashiBound", han_kobayashi_bound_ty()),
("GrayWynerRegion", gray_wyner_region_ty()),
("SlepianWolfRegion", slepian_wolf_region_ty()),
("WynerZivRate", wyner_ziv_rate_ty()),
("BroadcastConfidential", broadcast_confidential_ty()),
("GacsKornerCommonInfo", gacs_korner_common_info_ty()),
("WynerCommonInfo", wyner_common_info_ty()),
("SecretKeyCapacity", secret_key_capacity_ty()),
("TsallisEntropy", tsallis_entropy_ty()),
("SibsonMutualInfo", sibson_mutual_info_ty()),
("LautumInformation", lautum_information_ty()),
("ArimotoMutualInfo", arimoto_mutual_info_ty()),
("FDivergence", f_divergence_ty()),
("sanov_theorem", sanov_theorem_ty()),
("ChernoffInformation", chernoff_information_ty()),
("GallagerExponent", gallager_exponent_ty()),
("ExpurgatedExponent", expurgated_exponent_ty()),
("SpherePackingExponent", sphere_packing_exponent_ty()),
("neyman_pearson", neyman_pearson_ty()),
("stein_lemma", stein_lemma_ty()),
("chernoff_stein", chernoff_stein_ty()),
("LZComplexity", lz_complexity_ty()),
("lz78_universal", lz78_universal_ty()),
("MDLModelSelection", mdl_model_selection_ty()),
("FisherMatrix", fisher_matrix_ty()),
("cramer_rao_matrix", cramer_rao_matrix_ty()),
("JeffreysPrior", jeffreys_prior_ty()),
("NaturalGradient", natural_gradient_ty()),
("VonNeumannEntropy", von_neumann_entropy_ty()),
("QuantumMutualInfo", quantum_mutual_info_ty()),
("HolevoBound", holevo_bound_ty()),
("QuantumChannelCapacity", quantum_channel_capacity_ty()),
("QuantumRelativeEntropy", quantum_relative_entropy_ty()),
("SchumacherCapacity", schumacher_capacity_ty()),
("FunctionalCompression", functional_compression_ty()),
("ComputingCapacity", computing_capacity_ty()),
("KornerGraphEntropy", korner_graph_entropy_ty()),
];
for (name, ty) in axioms {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
Ok(())
}
pub fn entropy(probs: &[f64]) -> f64 {
probs
.iter()
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum()
}
pub fn joint_entropy(joint: &[Vec<f64>]) -> f64 {
joint
.iter()
.flat_map(|row| row.iter())
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum()
}
pub fn marginal_x(joint: &[Vec<f64>]) -> Vec<f64> {
joint.iter().map(|row| row.iter().sum::<f64>()).collect()
}
pub fn marginal_y(joint: &[Vec<f64>]) -> Vec<f64> {
if joint.is_empty() {
return vec![];
}
let cols = joint[0].len();
(0..cols)
.map(|j| {
joint
.iter()
.map(|row| row.get(j).copied().unwrap_or(0.0))
.sum()
})
.collect()
}
pub fn conditional_entropy(joint: &[Vec<f64>]) -> f64 {
let h_xy = joint_entropy(joint);
let px = marginal_x(joint);
let h_x = entropy(&px);
h_xy - h_x
}
pub fn mutual_information(joint: &[Vec<f64>]) -> f64 {
let px = marginal_x(joint);
let py = marginal_y(joint);
let h_x = entropy(&px);
let h_y = entropy(&py);
let h_xy = joint_entropy(joint);
h_x + h_y - h_xy
}
pub fn kl_divergence(p: &[f64], q: &[f64]) -> f64 {
p.iter()
.zip(q.iter())
.filter(|(&pi, _)| pi > 0.0)
.map(|(&pi, &qi)| {
if qi == 0.0 {
f64::INFINITY
} else {
pi * (pi / qi).log2()
}
})
.sum()
}
pub fn cross_entropy(p: &[f64], q: &[f64]) -> f64 {
p.iter()
.zip(q.iter())
.filter(|(&pi, _)| pi > 0.0)
.map(|(&pi, &qi)| {
if qi == 0.0 {
f64::INFINITY
} else {
-pi * qi.log2()
}
})
.sum()
}
pub fn binary_entropy(p: f64) -> f64 {
if p <= 0.0 || p >= 1.0 {
return 0.0;
}
-p * p.log2() - (1.0 - p) * (1.0 - p).log2()
}
pub fn bsc_capacity(p: f64) -> f64 {
1.0 - binary_entropy(p)
}
pub fn bec_capacity(epsilon: f64) -> f64 {
1.0 - epsilon
}
pub fn huffman_code_lengths(probs: &[f64]) -> (Vec<u32>, f64) {
let n = probs.len();
if n == 0 {
return (vec![], 0.0);
}
if n == 1 {
return (vec![1], probs[0]);
}
let mut heap: Vec<(f64, Vec<usize>)> = probs
.iter()
.enumerate()
.map(|(i, &p)| (p, vec![i]))
.collect();
let mut lengths = vec![0u32; n];
while heap.len() > 1 {
heap.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
let (p1, idx1) = heap.remove(0);
let (p2, idx2) = heap.remove(0);
for i in &idx1 {
lengths[*i] += 1;
}
for i in &idx2 {
lengths[*i] += 1;
}
let mut merged = idx1;
merged.extend(idx2);
heap.push((p1 + p2, merged));
}
let avg: f64 = probs
.iter()
.zip(lengths.iter())
.map(|(&p, &l)| p * l as f64)
.sum();
(lengths, avg)
}
pub fn renyi_entropy(alpha: f64, probs: &[f64]) -> f64 {
if probs.is_empty() {
return 0.0;
}
if (alpha - 1.0).abs() < 1e-10 {
return entropy(probs);
}
let sum: f64 = probs
.iter()
.filter(|&&p| p > 0.0)
.map(|&p| p.powf(alpha))
.sum();
if sum <= 0.0 {
return f64::INFINITY;
}
sum.log2() / (1.0 - alpha)
}
pub fn min_entropy(probs: &[f64]) -> f64 {
let max_p = probs.iter().cloned().fold(0.0f64, f64::max);
if max_p <= 0.0 {
return f64::INFINITY;
}
-max_p.log2()
}
pub fn awgn_capacity(snr: f64) -> f64 {
0.5 * (1.0 + snr).log2()
}
pub fn gaussian_differential_entropy(sigma_sq: f64) -> f64 {
use std::f64::consts::{E, PI};
0.5 * (2.0 * PI * E * sigma_sq).log2()
}
pub fn shannon_fano_lengths(probs: &[f64]) -> Vec<u32> {
probs
.iter()
.map(|&p| {
if p <= 0.0 {
0u32
} else {
(-p.log2()).ceil() as u32
}
})
.collect()
}
pub fn is_typical(probs: &[f64], sequence: &[usize], eps: f64) -> bool {
if sequence.is_empty() {
return true;
}
let h = entropy(probs);
let log_prob: f64 = sequence
.iter()
.map(|&sym| {
let p = probs.get(sym).copied().unwrap_or(0.0);
if p <= 0.0 {
f64::NEG_INFINITY
} else {
p.log2()
}
})
.sum();
let empirical_rate = -log_prob / sequence.len() as f64;
(empirical_rate - h).abs() <= eps
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-9;
#[test]
fn test_entropy_uniform() {
let probs = [0.25, 0.25, 0.25, 0.25];
let h = entropy(&probs);
assert!((h - 2.0).abs() < EPS, "expected 2.0, got {h}");
}
#[test]
fn test_entropy_certain() {
let probs = [0.0, 0.0, 1.0, 0.0];
let h = entropy(&probs);
assert!(h.abs() < EPS, "expected 0.0, got {h}");
}
#[test]
fn test_binary_entropy() {
assert!((binary_entropy(0.5) - 1.0).abs() < EPS);
assert!(binary_entropy(0.0).abs() < EPS);
assert!(binary_entropy(1.0).abs() < EPS);
}
#[test]
fn test_kl_divergence_zero() {
let p = [0.1, 0.4, 0.3, 0.2];
let d = kl_divergence(&p, &p);
assert!(d.abs() < EPS, "expected 0.0, got {d}");
}
#[test]
fn test_mutual_information_independent() {
let joint = vec![
vec![0.0625, 0.0625, 0.0625, 0.0625],
vec![0.0625, 0.0625, 0.0625, 0.0625],
vec![0.0625, 0.0625, 0.0625, 0.0625],
vec![0.0625, 0.0625, 0.0625, 0.0625],
];
let mi = mutual_information(&joint);
assert!(mi.abs() < EPS, "expected 0.0, got {mi}");
}
#[test]
fn test_bsc_capacity() {
assert!((bsc_capacity(0.0) - 1.0).abs() < EPS);
assert!(bsc_capacity(0.5).abs() < EPS);
}
#[test]
fn test_bec_capacity() {
assert!((bec_capacity(0.0) - 1.0).abs() < EPS);
assert!(bec_capacity(1.0).abs() < EPS);
}
#[test]
fn test_huffman_code_lengths() {
let probs = [0.5, 0.5];
let (lengths, avg) = huffman_code_lengths(&probs);
assert_eq!(lengths, vec![1, 1]);
assert!((avg - 1.0).abs() < EPS, "avg expected 1.0, got {avg}");
}
#[test]
fn test_build_information_theory_env() {
let mut env = oxilean_kernel::Environment::new();
let result = build_information_theory_env(&mut env);
assert!(
result.is_ok(),
"build_information_theory_env failed: {:?}",
result.err()
);
}
#[test]
fn test_renyi_entropy_limit() {
let probs = [0.25, 0.25, 0.25, 0.25];
let r1 = renyi_entropy(1.0, &probs);
let h = entropy(&probs);
assert!(
(r1 - h).abs() < EPS,
"Rényi(1) should equal Shannon entropy"
);
}
#[test]
fn test_renyi_entropy_order2() {
let probs = [0.25, 0.25, 0.25, 0.25];
let r2 = renyi_entropy(2.0, &probs);
assert!(
(r2 - 2.0).abs() < EPS,
"Rényi(2) for uniform-4 should be 2.0"
);
}
#[test]
fn test_min_entropy() {
let probs = [0.5, 0.25, 0.25];
let h_min = min_entropy(&probs);
assert!((h_min - 1.0).abs() < EPS, "expected 1.0, got {h_min}");
}
#[test]
fn test_awgn_capacity() {
assert!((awgn_capacity(1.0) - 0.5).abs() < EPS);
assert!((awgn_capacity(3.0) - 1.0).abs() < EPS);
}
#[test]
fn test_gaussian_differential_entropy() {
use std::f64::consts::{E, PI};
let sigma_sq = 1.0;
let h = gaussian_differential_entropy(sigma_sq);
let expected = 0.5 * (2.0 * PI * E).log2();
assert!((h - expected).abs() < EPS);
}
#[test]
fn test_entropy_estimator() {
let est = EntropyEstimator::new(vec![10, 10, 10, 10]);
let h = est.estimate_entropy();
assert!((h - 2.0).abs() < EPS, "expected 2.0, got {h}");
}
#[test]
fn test_entropy_estimator_min_entropy() {
let est = EntropyEstimator::new(vec![40, 20, 20, 20]);
let h_min = est.estimate_min_entropy();
let expected = -(0.4f64).log2();
assert!((h_min - expected).abs() < EPS);
}
#[test]
fn test_huffman_code_build() {
let probs = [0.5, 0.25, 0.25];
let hc = HuffmanCode::build(&probs);
assert_eq!(hc.codewords.len(), 3);
let symbols = vec![0, 1, 2, 0, 1];
let bits = hc.encode(&symbols);
let decoded = hc.decode(&bits).expect("decode failed");
assert_eq!(decoded, symbols);
}
#[test]
fn test_huffman_code_avg_bits() {
let probs = [0.5, 0.5];
let hc = HuffmanCode::build(&probs);
assert!((hc.avg_bits - 1.0).abs() < EPS);
}
#[test]
fn test_channel_simulator_capacity() {
let bsc = ChannelSimulator::new_bsc(0.0);
assert!((bsc.capacity() - 1.0).abs() < EPS);
let bec = ChannelSimulator::new_bec(0.5);
assert!((bec.capacity() - 0.5).abs() < EPS);
}
#[test]
fn test_blahut_arimoto_bsc() {
let p = 0.1;
let channel = vec![vec![1.0 - p, p], vec![p, 1.0 - p]];
let ba = BlahutArimoto::new(channel, 200, 1e-9);
let (cap, q) = ba.run();
let expected = bsc_capacity(p);
assert!(
(cap - expected).abs() < 1e-5,
"BA BSC capacity: expected {expected}, got {cap}"
);
assert!((q[0] - 0.5).abs() < 1e-4);
}
#[test]
fn test_kl_divergence_calc_jensen_shannon() {
let p = [0.5, 0.5];
let js = KLDivergenceCalc::jensen_shannon(&p, &p);
assert!(js.abs() < EPS, "JS(p,p) should be 0, got {js}");
}
#[test]
fn test_kl_divergence_calc_symmetrized() {
let p = [0.3, 0.4, 0.3];
let sym = KLDivergenceCalc::symmetrized(&p, &p);
assert!(
sym.abs() < EPS,
"Symmetrized KL(p,p) should be 0, got {sym}"
);
}
#[test]
fn test_shannon_fano_lengths() {
let probs = [0.5, 0.25, 0.25];
let lengths = shannon_fano_lengths(&probs);
assert_eq!(lengths[0], 1);
assert_eq!(lengths[1], 2);
assert_eq!(lengths[2], 2);
}
#[test]
fn test_is_typical() {
let probs = [0.5, 0.5];
let seq: Vec<usize> = vec![0, 0, 0, 0, 1, 1, 1, 1];
assert!(is_typical(&probs, &seq, 1e-9));
}
#[test]
fn test_slepian_wolf_achievable() {
let joint = vec![vec![0.25, 0.25], vec![0.25, 0.25]];
let coder = SlepianWolfCoder::new(joint);
let h_xy = coder.h_xy();
assert!(
(h_xy - 2.0).abs() < EPS,
"H(X,Y) = 2 for uniform 2x2, got {h_xy}"
);
assert!(coder.is_achievable(1.0, 1.0));
assert!(!coder.is_achievable(0.0, 0.0));
}
#[test]
fn test_slepian_wolf_corner_rates() {
let joint = vec![vec![0.5, 0.0], vec![0.0, 0.5]];
let coder = SlepianWolfCoder::new(joint);
let h_x_y = coder.h_x_given_y();
let h_y_x = coder.h_y_given_x();
assert!(
h_x_y.abs() < EPS,
"H(X|Y) = 0 for perfectly correlated, got {h_x_y}"
);
assert!(
h_y_x.abs() < EPS,
"H(Y|X) = 0 for perfectly correlated, got {h_y_x}"
);
assert!(coder.is_achievable(0.5, 0.5));
}
#[test]
fn test_wiretap_channel_secrecy_rate() {
let p = 0.1;
let wy = vec![vec![1.0 - p, p], vec![p, 1.0 - p]];
let wz = wy.clone();
let wt = WiretapChannel::new(wy, wz);
let q = vec![0.5, 0.5];
let sr = wt.secrecy_rate(&q);
assert!(
sr.abs() < EPS,
"secrecy rate should be 0 when WY=WZ, got {sr}"
);
}
#[test]
fn test_wiretap_channel_positive_secrecy() {
let wy = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
let wz = vec![vec![0.5, 0.5], vec![0.5, 0.5]];
let wt = WiretapChannel::new(wy, wz);
let q = vec![0.5, 0.5];
let sr = wt.secrecy_rate(&q);
assert!(
(sr - 1.0).abs() < EPS,
"expected secrecy rate = 1, got {sr}"
);
}
#[test]
fn test_lempel_ziv_complexity_constant() {
let bits = vec![false; 8];
let c = LempelZivComplexity::compute(&bits);
assert!(
c >= 1,
"LZ complexity of constant string should be ≥ 1, got {c}"
);
}
#[test]
fn test_lempel_ziv_complexity_alternating() {
let bits: Vec<bool> = (0..8).map(|i| i % 2 == 1).collect();
let c_alt = LempelZivComplexity::compute(&bits);
let bits_const = vec![false; 8];
let c_const = LempelZivComplexity::compute(&bits_const);
assert!(
c_alt >= c_const,
"alternating complexity {c_alt} should be ≥ constant complexity {c_const}"
);
}
#[test]
fn test_chernoff_information_identical() {
let p = vec![0.3, 0.4, 0.3];
let c = ChernoffInformationCalc::compute(&p, &p);
assert!(c.abs() < 1e-6, "C(P,P) should be 0, got {c}");
}
#[test]
fn test_chernoff_information_orthogonal() {
let p = vec![1.0, 0.0];
let q = vec![0.0, 1.0];
let c = ChernoffInformationCalc::compute(&p, &q);
assert!(
c.is_infinite(),
"C(P,Q) for orthogonal distributions should be inf, got {c}"
);
}
#[test]
fn test_chernoff_bhattacharyya_distance() {
let p = vec![0.5, 0.5];
let d = ChernoffInformationCalc::bhattacharyya_distance(&p, &p);
assert!(
d.abs() < EPS,
"Bhattacharyya distance P=Q should be 0, got {d}"
);
}
#[test]
fn test_renyi_entropy_computer_order2() {
let comp = RenyiEntropyComputer::new(2.0);
let probs = vec![0.25, 0.25, 0.25, 0.25];
let h = comp.compute(&probs);
assert!(
(h - 2.0).abs() < EPS,
"H_2 for uniform-4 should be 2.0, got {h}"
);
}
#[test]
fn test_renyi_entropy_computer_divergence_self() {
let comp = RenyiEntropyComputer::new(2.0);
let p = vec![0.3, 0.4, 0.3];
let d = comp.renyi_divergence(&p, &p);
assert!(d.abs() < EPS, "D_2(P||P) should be 0, got {d}");
}
#[test]
fn test_renyi_hartley_entropy() {
let probs = vec![0.5, 0.25, 0.25];
let h0 = RenyiEntropyComputer::hartley_entropy(&probs);
let expected = 3.0f64.log2();
assert!(
(h0 - expected).abs() < EPS,
"Hartley entropy should be log2(3), got {h0}"
);
}
#[test]
fn test_tsallis_entropy_limit() {
let comp = TsallisEntropyComputer::new(1.0);
let probs = vec![0.25, 0.25, 0.25, 0.25];
let s1 = comp.compute(&probs);
let h = entropy(&probs);
assert!(
(s1 - h).abs() < EPS,
"Tsallis(1) should equal Shannon entropy, got {s1}"
);
}
#[test]
fn test_tsallis_entropy_order2() {
let comp = TsallisEntropyComputer::new(2.0);
let probs = vec![0.25, 0.25, 0.25, 0.25];
let s2 = comp.compute(&probs);
assert!(
(s2 - 0.75).abs() < EPS,
"S_2(uniform-4) should be 0.75, got {s2}"
);
}
#[test]
fn test_tsallis_divergence_self() {
let comp = TsallisEntropyComputer::new(2.0);
let p = vec![0.3, 0.4, 0.3];
let d = comp.tsallis_divergence(&p, &p);
assert!(d.abs() < EPS, "Tsallis D_q(P||P) should be 0, got {d}");
}
#[test]
fn test_build_information_theory_env_extended() {
let mut env = oxilean_kernel::Environment::new();
let result = build_information_theory_env(&mut env);
assert!(
result.is_ok(),
"build_information_theory_env failed: {:?}",
result.err()
);
assert!(
env.get(&oxilean_kernel::Name::str("MartonRegion"))
.is_some(),
"MartonRegion axiom should be registered"
);
assert!(
env.get(&oxilean_kernel::Name::str("VonNeumannEntropy"))
.is_some(),
"VonNeumannEntropy axiom should be registered"
);
assert!(
env.get(&oxilean_kernel::Name::str("LZComplexity"))
.is_some(),
"LZComplexity axiom should be registered"
);
}
}
pub fn entropy_dist(dist: &super::types::Distribution) -> f64 {
dist.entropy()
}
pub fn joint_entropy_triples(joint: &[(f64, usize, usize)]) -> f64 {
joint
.iter()
.filter(|(p, _, _)| *p > 0.0)
.map(|(p, _, _)| -p * p.log2())
.sum()
}
fn marginal_x_from_triples(joint: &[(f64, usize, usize)]) -> Vec<f64> {
if joint.is_empty() {
return vec![];
}
let max_x = joint.iter().map(|(_, x, _)| x).max().copied().unwrap_or(0);
let mut px = vec![0.0f64; max_x + 1];
for &(p, x, _) in joint {
px[x] += p;
}
px
}
fn marginal_y_from_triples(joint: &[(f64, usize, usize)]) -> Vec<f64> {
if joint.is_empty() {
return vec![];
}
let max_y = joint.iter().map(|(_, _, y)| y).max().copied().unwrap_or(0);
let mut py = vec![0.0f64; max_y + 1];
for &(p, _, y) in joint {
py[y] += p;
}
py
}
pub fn conditional_entropy_triples(joint: &[(f64, usize, usize)]) -> f64 {
let h_xy = joint_entropy_triples(joint);
let px = marginal_x_from_triples(joint);
let h_x: f64 = px
.iter()
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum();
h_xy - h_x
}
pub fn mutual_information_triples(joint: &[(f64, usize, usize)]) -> f64 {
let px = marginal_x_from_triples(joint);
let py = marginal_y_from_triples(joint);
let h_x: f64 = px
.iter()
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum();
let h_y: f64 = py
.iter()
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum();
let h_xy = joint_entropy_triples(joint);
h_x + h_y - h_xy
}
pub fn kl_divergence_dist(p: &super::types::Distribution, q: &super::types::Distribution) -> f64 {
kl_divergence(&p.probs, &q.probs)
}
pub fn channel_capacity(channel: &super::types::Channel) -> f64 {
let ba = BlahutArimoto::new(channel.matrix.clone(), 500, 1e-9);
let (cap, _) = ba.run();
cap
}
pub fn build_huffman_code(dist: &super::types::Distribution) -> HuffmanCode {
HuffmanCode::build(&dist.probs)
}
pub fn huffman_encode(data: &[usize], code: &HuffmanCode) -> Vec<bool> {
code.encode(data)
}
pub fn huffman_decode(bits: &[bool], code: &HuffmanCode) -> Option<Vec<usize>> {
code.decode(bits)
}
pub fn arithmetic_encode(symbols: &[usize], dist: &super::types::Distribution) -> Vec<bool> {
use super::types::ArithmeticCodeState;
if symbols.is_empty() || dist.probs.is_empty() {
return vec![];
}
let mut state = ArithmeticCodeState::new(dist);
for &s in symbols {
if s < dist.probs.len() {
state.encode_symbol(s);
}
}
let mid = (state.low + state.high) / 2.0;
let mut bits = Vec::new();
let mut val = mid;
for _ in 0..64 {
val *= 2.0;
if val >= 1.0 {
bits.push(true);
val -= 1.0;
} else {
bits.push(false);
}
}
while bits.len() > 1 && !bits[bits.len() - 1] {
bits.pop();
}
bits
}
pub fn arithmetic_decode(
bits: &[bool],
dist: &super::types::Distribution,
len: usize,
) -> Vec<usize> {
use super::types::ArithmeticCodeState;
if bits.is_empty() || dist.probs.is_empty() || len == 0 {
return vec![];
}
let mut val = 0.0f64;
let mut place = 0.5f64;
for &b in bits {
if b {
val += place;
}
place *= 0.5;
}
let mut state = ArithmeticCodeState::new(dist);
let mut result = Vec::with_capacity(len);
for _ in 0..len {
match state.decode_symbol(val) {
Some(s) => {
result.push(s);
let range = state.high - state.low;
let lo = state.low + range * state.cdf[s];
let hi = state.low + range * state.cdf[s + 1];
state.low = lo;
state.high = hi;
}
None => break,
}
}
result
}
#[allow(dead_code)]
pub fn shannon_entropy(pmf: &[f64]) -> f64 {
pmf.iter()
.filter(|&&p| p > 0.0)
.map(|&p| -p * p.log2())
.sum()
}
#[allow(dead_code)]
pub fn h_b(p: f64) -> f64 {
if p <= 0.0 || p >= 1.0 {
return 0.0;
}
-p * p.log2() - (1.0 - p) * (1.0 - p).log2()
}
#[cfg(test)]
mod tests_it_extra {
use super::*;
#[test]
fn test_mutual_information() {
let joint = vec![vec![0.25, 0.25], vec![0.25, 0.25]];
let mi = MutualInformation::new(joint);
assert!(mi.compute().abs() < 1e-9, "Independent vars: MI=0");
let joint2 = vec![vec![0.5, 0.0], vec![0.0, 0.5]];
let mi2 = MutualInformation::new(joint2);
let result = mi2.compute();
assert!((result - 1.0).abs() < 1e-9, "Deterministic: MI=H(X)=1");
}
#[test]
fn test_kolmogorov_complexity() {
let compressible = KolmogorovComplexity::new(10000, 1000);
assert!(!compressible.is_random());
assert!(compressible.redundancy() > 0.5);
let random = KolmogorovComplexity::new(1000, 990);
assert!(random.is_random());
}
#[test]
fn test_rate_distortion() {
let r = RateDistortion::binary_rd_function(0.3, 0.0);
let hb = h_b(0.3);
assert!((r - hb).abs() < 1e-9);
let r2 = RateDistortion::binary_rd_function(0.3, 0.35);
assert_eq!(r2, 0.0);
}
#[test]
fn test_info_theoretic_security() {
let otp = InfoTheoreticSecurity::new(128, 128, 128);
assert!(otp.is_perfectly_secret());
assert!(otp.is_one_time_pad());
assert_eq!(otp.leakage_bits(), 0.0);
let weak = InfoTheoreticSecurity::new(256, 128, 256);
assert!(!weak.is_perfectly_secret());
assert_eq!(weak.leakage_bits(), 128.0);
}
}
#[cfg(test)]
mod tests_new_api {
use super::super::types::{ArithmeticCodeState, Channel, Distribution, InformationMeasure};
use super::*;
const EPS: f64 = 1e-9;
#[test]
fn test_distribution_new_uniform() {
let d = Distribution::new(vec![0.25, 0.25, 0.25, 0.25]).expect("valid dist");
assert_eq!(d.alphabet_size(), 4);
assert!((d.entropy() - 2.0).abs() < EPS);
}
#[test]
fn test_distribution_new_normalizes() {
let d = Distribution::new(vec![1.0, 1.0, 2.0]).expect("valid dist");
assert!((d.probs[0] - 0.25).abs() < EPS);
assert!((d.probs[2] - 0.5).abs() < EPS);
}
#[test]
fn test_distribution_new_invalid() {
assert!(Distribution::new(vec![]).is_none());
assert!(Distribution::new(vec![-0.1, 0.5]).is_none());
}
#[test]
fn test_entropy_dist() {
let d = Distribution::new(vec![0.5, 0.5]).expect("valid");
assert!((entropy_dist(&d) - 1.0).abs() < EPS);
}
#[test]
fn test_joint_entropy_triples() {
let joint = vec![(0.25, 0, 0), (0.25, 0, 1), (0.25, 1, 0), (0.25, 1, 1)];
let h = joint_entropy_triples(&joint);
assert!((h - 2.0).abs() < EPS, "H(X,Y)=2 for uniform 2x2, got {h}");
}
#[test]
fn test_conditional_entropy_triples() {
let joint = vec![(0.25, 0, 0), (0.25, 0, 1), (0.25, 1, 0), (0.25, 1, 1)];
let h_yx = conditional_entropy_triples(&joint);
assert!(
(h_yx - 1.0).abs() < EPS,
"H(Y|X)=1 for independent uniform, got {h_yx}"
);
}
#[test]
fn test_conditional_entropy_deterministic() {
let joint = vec![(0.5, 0, 0), (0.5, 1, 1)];
let h_yx = conditional_entropy_triples(&joint);
assert!(h_yx.abs() < EPS, "H(Y|X)=0 for deterministic, got {h_yx}");
}
#[test]
fn test_mutual_information_triples_independent() {
let joint = vec![(0.25, 0, 0), (0.25, 0, 1), (0.25, 1, 0), (0.25, 1, 1)];
let mi = mutual_information_triples(&joint);
assert!(mi.abs() < EPS, "MI=0 for independent vars, got {mi}");
}
#[test]
fn test_mutual_information_triples_deterministic() {
let joint = vec![(0.5, 0, 0), (0.5, 1, 1)];
let mi = mutual_information_triples(&joint);
assert!(
(mi - 1.0).abs() < EPS,
"MI=1 for deterministic binary, got {mi}"
);
}
#[test]
fn test_kl_divergence_dist_self() {
let p = Distribution::new(vec![0.3, 0.4, 0.3]).expect("valid");
let kl = kl_divergence_dist(&p, &p);
assert!(kl.abs() < EPS, "D_KL(P||P)=0, got {kl}");
}
#[test]
fn test_kl_divergence_dist_known() {
let p = Distribution::new(vec![0.5, 0.5]).expect("valid");
let q = Distribution::new(vec![0.25, 0.75]).expect("valid");
let kl = kl_divergence_dist(&p, &q);
assert!(kl > 0.0, "KL divergence should be positive, got {kl}");
}
#[test]
fn test_channel_capacity_bsc() {
let matrix = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
let ch = Channel::new(matrix).expect("valid channel");
let cap = channel_capacity(&ch);
assert!((cap - 1.0).abs() < 1e-5, "BSC(p=0) capacity=1, got {cap}");
}
#[test]
fn test_channel_capacity_noisy() {
let p = 0.1;
let matrix = vec![vec![1.0 - p, p], vec![p, 1.0 - p]];
let ch = Channel::new(matrix).expect("valid channel");
let cap = channel_capacity(&ch);
let expected = bsc_capacity(p);
assert!(
(cap - expected).abs() < 1e-5,
"BSC({p}) capacity: expected {expected}, got {cap}"
);
}
#[test]
fn test_build_huffman_code_and_encode_decode() {
let dist = Distribution::new(vec![0.5, 0.25, 0.25]).expect("valid");
let code = build_huffman_code(&dist);
assert_eq!(code.codewords.len(), 3);
let symbols = vec![0, 1, 2, 0, 0, 1];
let bits = huffman_encode(&symbols, &code);
let decoded = huffman_decode(&bits, &code).expect("decode failed");
assert_eq!(decoded, symbols);
}
#[test]
fn test_build_huffman_code_average_bits() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let code = build_huffman_code(&dist);
assert!(
(code.avg_bits - 1.0).abs() < EPS,
"avg_bits should be 1.0, got {}",
code.avg_bits
);
}
#[test]
fn test_huffman_decode_invalid() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let code = build_huffman_code(&dist);
let decoded = huffman_decode(&[], &code);
assert_eq!(decoded, Some(vec![]));
}
#[test]
fn test_arithmetic_encode_decode_roundtrip() {
let dist = Distribution::new(vec![0.5, 0.25, 0.25]).expect("valid");
let symbols = vec![0, 1, 2];
let bits = arithmetic_encode(&symbols, &dist);
assert!(!bits.is_empty(), "encoded bits should not be empty");
let decoded = arithmetic_decode(&bits, &dist, symbols.len());
assert_eq!(decoded, symbols, "arithmetic codec roundtrip failed");
}
#[test]
fn test_arithmetic_encode_single_symbol() {
let dist = Distribution::new(vec![0.7, 0.3]).expect("valid");
let symbols = vec![0];
let bits = arithmetic_encode(&symbols, &dist);
let decoded = arithmetic_decode(&bits, &dist, 1);
assert_eq!(decoded, symbols);
}
#[test]
fn test_arithmetic_encode_empty() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let bits = arithmetic_encode(&[], &dist);
assert!(bits.is_empty());
}
#[test]
fn test_arithmetic_code_state_encode_symbol() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let mut state = ArithmeticCodeState::new(&dist);
state.encode_symbol(0);
assert!(
state.low < state.high,
"interval should be valid after encoding"
);
assert!(
state.high <= 0.5 + EPS,
"high should be <= 0.5 for symbol 0"
);
}
#[test]
fn test_information_measure_enum() {
assert_eq!(InformationMeasure::Entropy, InformationMeasure::Entropy);
assert_ne!(InformationMeasure::Entropy, InformationMeasure::MutualInfo);
assert_ne!(
InformationMeasure::RelativeEntropy,
InformationMeasure::ChannelCapacity
);
}
#[test]
fn test_channel_output_distribution() {
let matrix = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
let ch = Channel::new(matrix).expect("valid");
let px = Distribution::new(vec![0.3, 0.7]).expect("valid");
let py = ch.output_distribution(&px);
assert!((py.probs[0] - 0.3).abs() < EPS);
assert!((py.probs[1] - 0.7).abs() < EPS);
}
#[test]
fn test_channel_new_invalid() {
let matrix = vec![vec![0.5, 0.5], vec![0.3]];
assert!(Channel::new(matrix).is_none());
assert!(Channel::new(vec![]).is_none());
}
#[test]
fn test_huffman_node_collect_codewords() {
use super::super::types::HuffmanNode;
let leaf0 = HuffmanNode::Leaf {
symbol: 0,
prob: 0.5,
};
let leaf1 = HuffmanNode::Leaf {
symbol: 1,
prob: 0.5,
};
let root = HuffmanNode::Internal {
prob: 1.0,
left: Box::new(leaf0),
right: Box::new(leaf1),
};
let mut codes = Vec::new();
root.collect_codewords(vec![], &mut codes);
assert_eq!(codes.len(), 2);
let code0 = codes
.iter()
.find(|(s, _)| *s == 0)
.map(|(_, c)| c.clone())
.expect("sym 0");
let code1 = codes
.iter()
.find(|(s, _)| *s == 1)
.map(|(_, c)| c.clone())
.expect("sym 1");
assert_eq!(code0, vec![false]);
assert_eq!(code1, vec![true]);
}
#[test]
fn test_arithmetic_decode_empty_bits() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let result = arithmetic_decode(&[], &dist, 3);
assert!(result.is_empty());
}
#[test]
fn test_arithmetic_decode_zero_len() {
let dist = Distribution::new(vec![0.5, 0.5]).expect("valid");
let bits = vec![false, true];
let result = arithmetic_decode(&bits, &dist, 0);
assert!(result.is_empty());
}
}