1use rayon::prelude::*;
4
5use crate::lz76::{lz76_factorization, LzArgs, LzShuffle};
6use crate::sequence::{shuffle_copy_seeded, Sequence};
7use crate::NO_ALPHABET;
8
9pub fn max_block_size(size: usize) -> usize {
11 if size < 10 {
12 return 1;
13 }
14 let mut m: usize = 10;
15 let mut m_old: usize = 1;
16 let mut count: u32 = 0;
17 while count < 100 {
18 let est = ((size as f64) / (m_old as f64)).ln() / 2f64.ln();
19 m = est.round() as usize;
20 count += 1;
21 if m == m_old {
22 break;
23 }
24 m_old = m;
25 }
26 m
27}
28
29pub fn shuffle_factorization(seq: &Sequence, args: &LzArgs) -> (Vec<i32>, usize) {
32 let mut mm: i64 = args.block_size as i64;
33 if mm <= 0 {
34 mm = max_block_size(seq.len()) as i64;
35 if seq.len() > 50 {
36 mm += 10;
37 }
38 }
39 let mm = mm as usize;
40
41 let mut res: Vec<i32> = vec![0; mm + 3];
43
44 let base = fnv1a(seq.as_bytes());
47 let computed: Vec<(usize, i32)> = (1..=mm)
48 .into_par_iter()
49 .map(|idx| {
50 let seed = seed_from_base(base, idx);
51 let rand_seq = shuffle_copy_seeded(seq, idx as u32, (seq.len() / 2) as u32, seed);
52 let c = lz76_factorization(&rand_seq, args);
53 (idx, c as i32)
54 })
55 .collect();
56 for (idx, c) in computed {
57 res[idx] = c;
58 }
59 (res, mm)
60}
61
62pub fn shuffle_entropy_calculation(
108 seq: &Sequence,
109 args: &LzArgs,
110 complexity: i32,
111 h_rand: &[i32],
112 mm: usize,
113) -> LzShuffle {
114 let mut result = LzShuffle {
115 max_block_size: mm as i32,
116 ..Default::default()
117 };
118 if args.get_shuffle_terms {
119 result.summands = vec![0.0; mm];
120 }
121 let log_base = if args.log_base == NO_ALPHABET {
122 seq.alphabet_size().max(2)
123 } else {
124 args.log_base.max(2)
125 } as f64;
126
127 let n = seq.len() as f64;
128 if n <= 1.0 || mm == 0 {
129 return result;
130 }
131 let g = (n.ln() / log_base.ln()) / n;
133 let h_hat = complexity as f64 * g; let len = mm.min(h_rand.len().saturating_sub(1));
138 if len == 0 {
139 return result;
140 }
141 let raw: Vec<f64> = (1..=len)
142 .map(|l| l as f64 * (h_rand[l] as f64 * g - h_hat))
143 .collect();
144
145 result.multi_information = raw[0];
147
148 let fitted = non_negative_isotonic(&raw);
150 let mut prev = 0.0f64; for l in 1..=len {
152 let term = fitted[l - 1] - prev;
153 if args.get_shuffle_terms {
154 result.summands[l - 1] = term;
155 }
156 prev = fitted[l - 1];
157 }
158 result.emc_value = prev; result
160}
161
162fn non_negative_isotonic(y: &[f64]) -> Vec<f64> {
171 let mut sums: Vec<f64> = Vec::with_capacity(y.len());
172 let mut counts: Vec<usize> = Vec::with_capacity(y.len());
173 for &v in y {
174 sums.push(v);
175 counts.push(1);
176 while sums.len() > 1 {
177 let k = sums.len();
178 let last = sums[k - 1] / counts[k - 1] as f64;
179 let prev = sums[k - 2] / counts[k - 2] as f64;
180 if prev <= last {
181 break;
182 }
183 sums[k - 2] += sums[k - 1];
184 counts[k - 2] += counts[k - 1];
185 sums.pop();
186 counts.pop();
187 }
188 }
189 let mut out = Vec::with_capacity(y.len());
190 for (s, c) in sums.iter().zip(counts.iter()) {
191 let level = (s / *c as f64).max(0.0);
192 out.extend(std::iter::repeat(level).take(*c));
193 }
194 out
195}
196
197pub fn lz76_random_shuffle_complexity(seq: &Sequence, args: &LzArgs) -> LzShuffle {
199 lz76_random_shuffle_complexity_with(seq, args, lz76_factorization(seq, args) as i32)
200}
201
202pub fn lz76_random_shuffle_complexity_with(
205 seq: &Sequence,
206 args: &LzArgs,
207 complexity: i32,
208) -> LzShuffle {
209 let (h_rand, mm) = shuffle_factorization(seq, args);
210 shuffle_entropy_calculation(seq, args, complexity, &h_rand, mm)
211}
212
213pub fn lz76_paired_shuffle_complexity(seq: &Sequence, args: &LzArgs) -> LzShuffle {
215 let mid = seq.len() / 2;
216 let (past, future) = seq.split_at(mid);
217 let merged = merge_sequences(&past, &future);
218 let complexity = lz76_factorization(&merged, args) as i32;
219 let (h_rand, mm) = shuffle_factorization(&merged, args);
220 shuffle_entropy_calculation(&merged, args, complexity, &h_rand, mm)
221}
222
223pub fn merge_sequences(s1: &Sequence, s2: &Sequence) -> Sequence {
225 let max_iter = s1.len().min(s2.len());
226 let mut buf = Vec::with_capacity(max_iter);
227 let mut lookup = vec![0u8; 65536];
228 let mut next_symbol: u8 = b'0';
229 let a = s1.as_bytes();
230 let b = s2.as_bytes();
231 for i in 0..max_iter {
232 let key = ((a[i] as u16) << 8) | (b[i] as u16);
233 let entry = &mut lookup[key as usize];
234 if *entry == 0 {
235 *entry = next_symbol;
236 next_symbol = next_symbol.wrapping_add(1);
237 }
238 buf.push(*entry);
239 }
240 Sequence::from_bytes_with_alphabet(buf, s1.alphabet_size().saturating_mul(s2.alphabet_size()))
241}
242
243fn fnv1a(bytes: &[u8]) -> u64 {
245 let mut h: u64 = 14695981039346656037; for &c in bytes {
247 h ^= c as u64;
248 h = h.wrapping_mul(1099511628211);
249 }
250 h
251}
252
253fn seed_from_base(base: u64, idx: usize) -> u64 {
255 base ^ ((idx as u64).wrapping_mul(0x9E3779B97F4A7C15))
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 fn fixture() -> (Sequence, LzArgs, f64) {
266 let mut data = vec![b'0'; 50];
267 data.extend(std::iter::repeat(b'1').take(50));
268 let seq = Sequence::from_bytes(data);
269 let mut args = LzArgs::new();
270 args.get_shuffle_terms = true;
271 let n = 100.0f64;
272 (seq, args, (n.ln() / 2f64.ln()) / n)
273 }
274
275 #[test]
276 fn emc_projects_the_excess_ladder() {
277 let (seq, args, g) = fixture();
278 let r = shuffle_entropy_calculation(&seq, &args, 10, &[0, 20, 15, 12], 3);
281
282 let level = (26.0 / 3.0) * g;
284 assert!((r.emc_value - level).abs() < 1e-12, "{}", r.emc_value);
285 assert!((r.summands[0] - level).abs() < 1e-12);
286 assert_eq!(r.summands[1], 0.0);
287 assert_eq!(r.summands[2], 0.0);
288 assert!((r.multi_information - 10.0 * g).abs() < 1e-12);
290 }
291
292 #[test]
293 fn emc_keeps_the_closed_form_when_the_ladder_is_monotone() {
294 let (seq, args, g) = fixture();
295 let r = shuffle_entropy_calculation(&seq, &args, 10, &[0, 11, 11, 11], 3);
299
300 assert!((r.emc_value - 3.0 * g).abs() < 1e-12, "{}", r.emc_value);
301 for s in &r.summands {
302 assert!((s - g).abs() < 1e-12, "{s}");
303 }
304 }
305
306 #[test]
307 fn emc_is_never_negative() {
308 let (seq, args, g) = fixture();
309 let r = shuffle_entropy_calculation(&seq, &args, 20, &[0, 10, 10, 10], 3);
312
313 assert_eq!(r.emc_value, 0.0);
314 assert!(r.summands.iter().all(|&s| s == 0.0), "{:?}", r.summands);
315 assert!((r.multi_information + 10.0 * g).abs() < 1e-12);
317 }
318
319 #[test]
320 fn isotonic_pools_adjacent_violators() {
321 assert_eq!(
323 non_negative_isotonic(&[1.0, 3.0, 2.0, 4.0]),
324 vec![1.0, 2.5, 2.5, 4.0]
325 );
326 assert_eq!(non_negative_isotonic(&[-1.0, 0.5]), vec![0.0, 0.5]);
328 assert_eq!(non_negative_isotonic(&[6.0, 3.0, 0.0]), vec![3.0, 3.0, 3.0]);
330 assert_eq!(non_negative_isotonic(&[]), Vec::<f64>::new());
331 }
332
333 #[test]
334 fn summands_are_non_negative_and_sum_to_the_value() {
335 let data: Vec<u8> = b"00010111".iter().copied().cycle().take(2000).collect();
337 let seq = Sequence::from_bytes(data);
338 let mut args = LzArgs::new();
339 args.get_shuffle_terms = true;
340 let r = lz76_random_shuffle_complexity(&seq, &args);
341
342 assert_eq!(r.summands.len(), r.max_block_size as usize);
343 assert!(r.emc_value > 0.0, "expected structure, got {}", r.emc_value);
344 assert!(r.summands.iter().all(|&s| s >= 0.0), "{:?}", r.summands);
345 let total: f64 = r.summands.iter().sum();
346 assert!(
347 (total - r.emc_value).abs() < 1e-12,
348 "{total} vs {}",
349 r.emc_value
350 );
351 let mut acc = 0.0;
353 for &s in &r.summands {
354 acc += s;
355 assert!(acc <= r.emc_value + 1e-12);
356 }
357 }
358}