1use crate::error::{InfotheoryError, InfotheoryResult};
15use crate::spec::CompiledRateBackend;
16
17use crate::{aligned_prefix, with_default_ctx};
18
19#[inline(always)]
20pub fn try_entropy_rate_bytes(data: &[u8]) -> InfotheoryResult<f64> {
22 with_default_ctx(|ctx| ctx.try_entropy_rate_bytes(data))
23}
24
25#[inline(always)]
26pub fn try_biased_entropy_rate_bytes(data: &[u8]) -> InfotheoryResult<f64> {
28 with_default_ctx(|ctx| ctx.try_biased_entropy_rate_bytes(data))
29}
30
31pub fn try_mutual_information_rate_backend(
35 x: &[u8],
36 y: &[u8],
37 backend: &CompiledRateBackend,
38) -> InfotheoryResult<f64> {
39 let (x, y) = aligned_prefix(x, y);
40 if x.is_empty() {
41 return Ok(0.0);
42 }
43 let h_x = try_entropy_rate_backend(x, backend)?;
44 let h_y = try_entropy_rate_backend(y, backend)?;
45 let h_xy = try_joint_entropy_rate_backend(x, y, backend)?;
46 Ok((h_x + h_y - h_xy).max(0.0))
47}
48
49pub fn try_ned_rate_backend(
53 x: &[u8],
54 y: &[u8],
55 backend: &CompiledRateBackend,
56) -> InfotheoryResult<f64> {
57 let (x, y) = aligned_prefix(x, y);
58 if x.is_empty() {
59 return Ok(0.0);
60 }
61 let h_x = try_entropy_rate_backend(x, backend)?;
62 let h_y = try_entropy_rate_backend(y, backend)?;
63 let h_xy = try_joint_entropy_rate_backend(x, y, backend)?;
64 let min_h = h_x.min(h_y);
65 let max_h = h_x.max(h_y);
66 if max_h == 0.0 {
67 Ok(0.0)
68 } else {
69 Ok(((h_xy - min_h) / max_h).clamp(0.0, 1.0))
70 }
71}
72
73pub fn try_nte_rate_backend(
77 x: &[u8],
78 y: &[u8],
79 backend: &CompiledRateBackend,
80) -> InfotheoryResult<f64> {
81 let (x, y) = aligned_prefix(x, y);
82 if x.is_empty() {
83 return Ok(0.0);
84 }
85 let h_x = try_entropy_rate_backend(x, backend)?;
86 let h_y = try_entropy_rate_backend(y, backend)?;
87 let h_xy = try_joint_entropy_rate_backend(x, y, backend)?;
88 let max_h = h_x.max(h_y);
89 if max_h == 0.0 {
90 Ok(0.0)
91 } else {
92 let vi = (h_xy - h_x).max(0.0) + (h_xy - h_y).max(0.0);
93 Ok((vi / max_h).clamp(0.0, 2.0))
94 }
95}
96
97pub fn try_entropy_rate_backend(
99 data: &[u8],
100 backend: &CompiledRateBackend,
101) -> InfotheoryResult<f64> {
102 crate::runtime::try_entropy_rate_backend_direct(data, backend)
103}
104
105pub fn try_biased_entropy_rate_backend(
107 data: &[u8],
108 backend: &CompiledRateBackend,
109) -> InfotheoryResult<f64> {
110 if !backend.capabilities().supports_biased_entropy {
111 Err(InfotheoryError::unsupported(
112 "biased/plugin entropy is not supported for zpaq rate backends",
113 ))
114 } else {
115 crate::try_frozen_plugin_rate_backend(data, &[data], backend)
116 }
117}
118
119pub fn try_cross_entropy_rate_backend(
121 test_data: &[u8],
122 train_data: &[u8],
123 backend: &CompiledRateBackend,
124) -> InfotheoryResult<f64> {
125 crate::runtime::try_cross_entropy_rate_backend_direct(test_data, train_data, backend)
126}
127
128pub fn try_joint_entropy_rate_backend(
130 x: &[u8],
131 y: &[u8],
132 backend: &CompiledRateBackend,
133) -> InfotheoryResult<f64> {
134 crate::runtime::try_joint_entropy_rate_backend_direct(x, y, backend)
135}
136
137#[inline(always)]
143pub fn empirical_entropy_bytes(data: &[u8]) -> f64 {
144 if data.is_empty() {
145 return 0.0;
146 }
147
148 let mut counts = [0u64; 256];
149 for &b in data {
150 counts[b as usize] += 1;
151 }
152
153 let n = data.len() as f64;
154 let mut h = 0.0f64;
155 for &count in &counts {
156 if count > 0 {
157 let p = count as f64 / n;
158 h -= p * p.log2();
159 }
160 }
161 h
162}
163
164#[inline(always)]
169pub fn empirical_joint_entropy_bytes(x: &[u8], y: &[u8]) -> f64 {
170 let (x, y) = aligned_prefix(x, y);
171 let n = x.len();
172 if n == 0 {
173 return 0.0;
174 }
175
176 let mut counts = vec![0u64; 256 * 256];
177 for i in 0..n {
178 let pair_idx = (x[i] as usize) * 256 + (y[i] as usize);
179 counts[pair_idx] += 1;
180 }
181
182 let n_f64 = n as f64;
183 let mut h = 0.0f64;
184 for &c in &counts {
185 if c > 0 {
186 let p = c as f64 / n_f64;
187 h -= p * p.log2();
188 }
189 }
190 h
191}
192
193#[inline(always)]
194pub fn try_joint_entropy_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
196 with_default_ctx(|ctx| ctx.try_joint_entropy_rate_bytes(x, y))
197}
198
199#[inline(always)]
200pub fn try_conditional_entropy_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
202 with_default_ctx(|ctx| ctx.try_conditional_entropy_rate_bytes(x, y))
203}
204
205#[inline(always)]
206pub fn try_conditional_entropy_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
208 with_default_ctx(|ctx| ctx.try_conditional_entropy_bytes(x, y))
209}
210
211#[inline(always)]
212pub fn try_mutual_information_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
214 with_default_ctx(|ctx| ctx.try_mutual_information_bytes(x, y))
215}
216
217pub fn empirical_mutual_information_bytes(x: &[u8], y: &[u8]) -> f64 {
219 let (x, y) = aligned_prefix(x, y);
220 let h_x = empirical_entropy_bytes(x);
221 let h_y = empirical_entropy_bytes(y);
222 let h_xy = empirical_joint_entropy_bytes(x, y);
223 (h_x + h_y - h_xy).max(0.0)
224}
225
226#[inline(always)]
227pub fn try_mutual_information_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
229 with_default_ctx(|ctx| ctx.try_mutual_information_rate_bytes(x, y))
230}
231
232#[inline(always)]
233pub fn try_ned_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
235 with_default_ctx(|ctx| ctx.try_ned_bytes(x, y))
236}
237
238pub fn empirical_ned_bytes(x: &[u8], y: &[u8]) -> f64 {
241 let (x, y) = aligned_prefix(x, y);
242 let h_x = empirical_entropy_bytes(x);
243 let h_y = empirical_entropy_bytes(y);
244 let h_xy = empirical_joint_entropy_bytes(x, y);
245 let min_h = h_x.min(h_y);
246 let max_h = h_x.max(h_y);
247 if max_h == 0.0 {
248 0.0
249 } else {
250 ((h_xy - min_h) / max_h).clamp(0.0, 1.0)
251 }
252}
253
254#[inline(always)]
255pub fn try_ned_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
257 with_default_ctx(|ctx| ctx.try_ned_bytes(x, y))
258}
259
260#[inline(always)]
261pub fn try_ned_cons_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
263 with_default_ctx(|ctx| ctx.try_ned_cons_bytes(x, y))
264}
265
266pub fn empirical_ned_cons_bytes(x: &[u8], y: &[u8]) -> f64 {
269 let (x, y) = aligned_prefix(x, y);
270 let h_x = empirical_entropy_bytes(x);
271 let h_y = empirical_entropy_bytes(y);
272 let h_xy = empirical_joint_entropy_bytes(x, y);
273 let min_h = h_x.min(h_y);
274 if h_xy == 0.0 {
275 0.0
276 } else {
277 ((h_xy - min_h) / h_xy).clamp(0.0, 1.0)
278 }
279}
280
281#[inline(always)]
282pub fn try_ned_cons_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
284 with_default_ctx(|ctx| ctx.try_ned_cons_bytes(x, y))
285}
286
287#[inline(always)]
288pub fn try_nte_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
290 with_default_ctx(|ctx| ctx.try_nte_bytes(x, y))
291}
292
293pub fn empirical_nte_bytes(x: &[u8], y: &[u8]) -> f64 {
296 let (x, y) = aligned_prefix(x, y);
297 let h_x = empirical_entropy_bytes(x);
298 let h_y = empirical_entropy_bytes(y);
299 let h_xy = empirical_joint_entropy_bytes(x, y);
300 let vi = 2.0 * h_xy - h_x - h_y;
301 let max_h = h_x.max(h_y);
302 if max_h == 0.0 {
303 0.0
304 } else {
305 (vi / max_h).clamp(0.0, 2.0)
306 }
307}
308
309#[inline(always)]
310pub fn try_nte_rate_bytes(x: &[u8], y: &[u8]) -> InfotheoryResult<f64> {
312 with_default_ctx(|ctx| ctx.try_nte_bytes(x, y))
313}
314
315#[inline(always)]
316pub(crate) fn byte_histogram(data: &[u8]) -> [f64; 256] {
317 let mut counts = [0u64; 256];
318 for &b in data {
319 counts[b as usize] += 1;
320 }
321 let n = data.len() as f64;
322 let mut probs = [0.0f64; 256];
323 if n == 0.0 {
324 return probs;
325 }
326 for i in 0..256 {
327 probs[i] = counts[i] as f64 / n;
328 }
329 probs
330}
331
332#[inline(always)]
333pub fn tvd_bytes(x: &[u8], y: &[u8]) -> f64 {
337 if x.is_empty() || y.is_empty() {
338 return 0.0;
339 }
340 let p_x = byte_histogram(x);
341 let p_y = byte_histogram(y);
342
343 let mut sum = 0.0f64;
344 for i in 0..256 {
345 sum += (p_x[i] - p_y[i]).abs();
346 }
347
348 (sum / 2.0).clamp(0.0, 1.0)
349}
350
351#[inline(always)]
352pub fn nhd_bytes(x: &[u8], y: &[u8]) -> f64 {
356 if x.is_empty() || y.is_empty() {
357 return 0.0;
358 }
359 let p_x = byte_histogram(x);
360 let p_y = byte_histogram(y);
361
362 let mut bc = 0.0f64;
363 for i in 0..256 {
364 bc += (p_x[i] * p_y[i]).sqrt();
365 }
366
367 (1.0 - bc).max(0.0).sqrt()
368}
369
370#[inline(always)]
371pub fn try_cross_entropy_bytes(test_data: &[u8], train_data: &[u8]) -> InfotheoryResult<f64> {
373 with_default_ctx(|ctx| ctx.try_cross_entropy_bytes(test_data, train_data))
374}
375
376pub fn empirical_cross_entropy_bytes(test_data: &[u8], train_data: &[u8]) -> f64 {
379 if test_data.is_empty() {
380 return 0.0;
381 }
382 let p_x = byte_histogram(test_data);
383 let p_y = byte_histogram(train_data);
384 let mut h = 0.0f64;
385 for i in 0..256 {
386 if p_x[i] > 0.0 {
387 let q_y = p_y[i].max(1e-12);
388 h -= p_x[i] * q_y.log2();
389 }
390 }
391 h
392}
393
394#[inline(always)]
395pub fn try_cross_entropy_rate_bytes(test_data: &[u8], train_data: &[u8]) -> InfotheoryResult<f64> {
397 with_default_ctx(|ctx| ctx.try_cross_entropy_rate_bytes(test_data, train_data))
398}
399
400pub fn d_kl_bytes(x: &[u8], y: &[u8]) -> f64 {
404 if x.is_empty() || y.is_empty() {
405 return 0.0;
406 }
407 let p_x = byte_histogram(x);
408 let p_y = byte_histogram(y);
409 let mut d_kl = 0.0f64;
410 for i in 0..256 {
411 if p_x[i] > 0.0 {
412 let q_y = p_y[i].max(1e-12);
413 d_kl += p_x[i] * (p_x[i] / q_y).log2();
414 }
415 }
416 d_kl.max(0.0)
417}
418
419pub fn js_div_bytes(x: &[u8], y: &[u8]) -> f64 {
423 if x.is_empty() || y.is_empty() {
424 return 0.0;
425 }
426 let p_x = byte_histogram(x);
427 let p_y = byte_histogram(y);
428 let mut m = [0.0f64; 256];
429 for i in 0..256 {
430 m[i] = 0.5 * (p_x[i] + p_y[i]);
431 }
432
433 let mut kl_pm = 0.0f64;
434 let mut kl_qm = 0.0f64;
435 for i in 0..256 {
436 if p_x[i] > 0.0 {
437 kl_pm += p_x[i] * (p_x[i] / m[i]).log2();
438 }
439 if p_y[i] > 0.0 {
440 kl_qm += p_y[i] * (p_y[i] / m[i]).log2();
441 }
442 }
443 (0.5 * kl_pm + 0.5 * kl_qm).max(0.0)
444}
445
446#[inline(always)]
447pub fn try_intrinsic_dependence_bytes(data: &[u8]) -> InfotheoryResult<f64> {
451 with_default_ctx(|ctx| ctx.try_intrinsic_dependence_bytes(data))
452}
453
454#[inline(always)]
455pub fn try_resistance_to_transformation_bytes(x: &[u8], tx: &[u8]) -> InfotheoryResult<f64> {
458 with_default_ctx(|ctx| ctx.try_resistance_to_transformation_bytes(x, tx))
459}
460
461pub fn empirical_resistance_to_transformation_bytes(x: &[u8], tx: &[u8]) -> f64 {
464 let (x, tx) = aligned_prefix(x, tx);
465 let h_x = empirical_entropy_bytes(x);
466 if h_x < 1e-9 {
467 0.0
468 } else {
469 (empirical_mutual_information_bytes(x, tx) / h_x).clamp(0.0, 1.0)
470 }
471}