Skip to main content

lzcomplexity_core/
sequence.rs

1//! Byte-sequence type mirroring the C++ `lz::sequence` class.
2
3use rand::Rng;
4use rand::SeedableRng;
5use rand_chacha::ChaCha8Rng;
6
7use crate::ALPHABET_SIZE;
8
9/// A symbolic sequence stored as raw bytes, plus a cached alphabet.
10#[derive(Clone, Debug)]
11pub struct Sequence {
12    pub(crate) data: Vec<u8>,
13    pub(crate) alphabet: Vec<u8>,
14    pub(crate) alphabet_size: u32,
15}
16
17impl Sequence {
18    pub fn new() -> Self {
19        Self {
20            data: Vec::new(),
21            alphabet: Vec::new(),
22            alphabet_size: ALPHABET_SIZE,
23        }
24    }
25
26    pub fn with_capacity(cap: usize) -> Self {
27        Self {
28            data: Vec::with_capacity(cap),
29            alphabet: Vec::new(),
30            alphabet_size: ALPHABET_SIZE,
31        }
32    }
33
34    // Inherent constructor mirroring the C++ `sequence(string)`; not the
35    // `FromStr` trait (it is infallible and part of the public port surface).
36    #[allow(clippy::should_implement_trait)]
37    pub fn from_str(s: &str) -> Self {
38        let mut seq = Self {
39            data: s.as_bytes().to_vec(),
40            alphabet: Vec::new(),
41            alphabet_size: ALPHABET_SIZE,
42        };
43        seq.determine_alphabet();
44        seq
45    }
46
47    pub fn from_bytes(b: Vec<u8>) -> Self {
48        let mut seq = Self {
49            data: b,
50            alphabet: Vec::new(),
51            alphabet_size: ALPHABET_SIZE,
52        };
53        seq.determine_alphabet();
54        seq
55    }
56
57    pub fn from_bytes_with_alphabet(b: Vec<u8>, alphabet_size: u32) -> Self {
58        let mut seq = Self {
59            data: b,
60            alphabet: Vec::new(),
61            alphabet_size,
62        };
63        seq.determine_alphabet();
64        seq
65    }
66
67    pub fn with_alphabet_size(alphabet_size: u32) -> Self {
68        Self {
69            data: Vec::new(),
70            alphabet: Vec::new(),
71            alphabet_size,
72        }
73    }
74
75    pub fn as_bytes(&self) -> &[u8] {
76        &self.data
77    }
78
79    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
80        &mut self.data
81    }
82
83    pub fn to_string_lossy(&self) -> String {
84        String::from_utf8_lossy(&self.data).into_owned()
85    }
86
87    pub fn alphabet(&self) -> &[u8] {
88        &self.alphabet
89    }
90
91    pub fn alphabet_size(&self) -> u32 {
92        self.alphabet_size
93    }
94
95    pub fn set_alphabet_size(&mut self, s: u32) {
96        self.alphabet_size = s;
97    }
98
99    pub fn len(&self) -> usize {
100        self.data.len()
101    }
102
103    pub fn is_empty(&self) -> bool {
104        self.data.is_empty()
105    }
106
107    /// Scan the data and rebuild the alphabet (sorted descending), updating
108    /// `alphabet_size` if needed. Mirrors the C++ `DetermineAlphabet()`.
109    pub fn determine_alphabet(&mut self) -> Vec<u8> {
110        let mut seen = [false; 256];
111        for &c in &self.data {
112            seen[c as usize] = true;
113        }
114        let mut alph = Vec::with_capacity(64);
115        for i in (0..=255u16).rev() {
116            if seen[i as usize] {
117                alph.push(i as u8);
118            }
119        }
120        if alph.len() as u32 != self.alphabet_size {
121            self.alphabet_size = if alph.len() < 2 { 2 } else { alph.len() as u32 };
122        }
123        self.alphabet = alph.clone();
124        alph
125    }
126
127    pub fn push(&mut self, c: u8) -> usize {
128        self.data.push(c);
129        self.data.len()
130    }
131
132    pub fn pop(&mut self) -> usize {
133        self.data.pop();
134        self.data.len()
135    }
136
137    pub fn clear(&mut self) {
138        self.alphabet_size = ALPHABET_SIZE;
139        self.data.clear();
140    }
141
142    pub fn get(&self, idx: usize) -> Option<u8> {
143        self.data.get(idx).copied()
144    }
145
146    pub fn first(&self) -> Option<u8> {
147        self.data.first().copied()
148    }
149
150    pub fn last(&self) -> Option<u8> {
151        self.data.last().copied()
152    }
153
154    pub fn min_byte(&self) -> Option<u8> {
155        self.data.iter().copied().min()
156    }
157
158    pub fn max_byte(&self) -> Option<u8> {
159        self.data.iter().copied().max()
160    }
161
162    pub fn min_range(&self, start: usize, end: usize) -> Option<u8> {
163        self.data
164            .get(start..end)
165            .and_then(|s| s.iter().copied().min())
166    }
167
168    pub fn max_range(&self, start: usize, end: usize) -> Option<u8> {
169        self.data
170            .get(start..end)
171            .and_then(|s| s.iter().copied().max())
172    }
173
174    /// Mirror of `seq.Take(l)` — first l characters, clamped.
175    pub fn take(&self, l: usize) -> Self {
176        let n = l.min(self.data.len());
177        Sequence::from_bytes_with_alphabet(self.data[..n].to_vec(), self.alphabet_size)
178    }
179
180    /// Mirror of `seq.Drop(l)` — skip first l characters.
181    pub fn drop(&self, l: usize) -> Self {
182        if l >= self.data.len() {
183            return Sequence::with_alphabet_size(self.alphabet_size);
184        }
185        Sequence::from_bytes_with_alphabet(self.data[l..].to_vec(), self.alphabet_size)
186    }
187
188    /// Mirror of `seq.Split(l)`.
189    pub fn split_at(&self, l: usize) -> (Self, Self) {
190        let n = l.min(self.data.len());
191        (
192            Sequence::from_bytes_with_alphabet(self.data[..n].to_vec(), self.alphabet_size),
193            Sequence::from_bytes_with_alphabet(self.data[n..].to_vec(), self.alphabet_size),
194        )
195    }
196
197    /// Mirror of `seq.Granularity(gr)`.
198    pub fn granularity(&self, gr: u32) -> Self {
199        if gr == 0 {
200            return Self::new();
201        }
202        let mut buf = Vec::with_capacity(self.data.len() / gr as usize);
203        let mut seen = [false; 256];
204        let mut temp: u8 = 0;
205        let mut count: u32 = 0;
206        for &c in &self.data {
207            temp = temp.wrapping_add(c);
208            count += 1;
209            if count == gr {
210                buf.push(temp);
211                seen[temp as usize] = true;
212                temp = 0;
213                count = 0;
214            }
215        }
216        let unique = seen.iter().filter(|b| **b).count() as u32;
217        Sequence::from_bytes_with_alphabet(buf, unique)
218    }
219
220    pub fn reverse(&mut self) -> &mut Self {
221        self.data.reverse();
222        self
223    }
224
225    pub fn reverse_copy(&self) -> Self {
226        let mut s = self.clone();
227        s.data.reverse();
228        s
229    }
230
231    pub fn pi(&mut self) -> &mut Self {
232        self.data.pop();
233        self
234    }
235
236    pub fn right_shift(&mut self, ls: u32) -> &mut Self {
237        if self.data.is_empty() {
238            return self;
239        }
240        let shift = (ls as usize) % self.data.len();
241        if shift == 0 {
242            return self;
243        }
244        // Equivalent to std::rotate(begin, begin+shift, end), which rotates LEFT by `shift`.
245        // That's what the C++ rightShift does (rotates so beginning becomes [shift..]).
246        self.data.rotate_left(shift);
247        self
248    }
249
250    pub fn left_shift(&mut self, ls: u32) -> &mut Self {
251        if self.data.is_empty() {
252            return self;
253        }
254        let n = self.data.len();
255        let shift = n - ((ls as usize) % n);
256        if shift == n {
257            return self;
258        }
259        self.data.rotate_left(shift);
260        self
261    }
262
263    /// Mirror of `seq.charDensity()`.
264    pub fn char_density(&self) -> std::collections::BTreeMap<u8, u32> {
265        let mut m = std::collections::BTreeMap::new();
266        for &c in &self.data {
267            *m.entry(c).or_insert(0u32) += 1;
268        }
269        m
270    }
271
272    /// Mirror of `seq.map(f)` — returns new sequence with `f` applied to each byte.
273    pub fn map<F: Fn(u8) -> u8>(&self, f: F) -> Self {
274        let mut data = Vec::with_capacity(self.data.len());
275        for &c in &self.data {
276            data.push(f(c));
277        }
278        Self {
279            data,
280            alphabet: Vec::new(),
281            alphabet_size: self.alphabet_size,
282        }
283    }
284
285    pub fn extend_from_slice(&mut self, other: &[u8]) {
286        self.data.extend_from_slice(other);
287    }
288
289    /// In-place XOR: 0 where bytes match, 1 otherwise.
290    pub fn xor_with(&mut self, other: &Sequence) -> Result<(), &'static str> {
291        if self.data.len() != other.data.len() {
292            return Err("SequenceNoMatchSize");
293        }
294        for i in 0..self.data.len() {
295            self.data[i] = if self.data[i] == other.data[i] { 0 } else { 1 };
296        }
297        Ok(())
298    }
299}
300
301impl Default for Sequence {
302    fn default() -> Self {
303        Self::new()
304    }
305}
306
307impl std::cmp::PartialEq for Sequence {
308    fn eq(&self, other: &Self) -> bool {
309        self.data == other.data
310    }
311}
312
313impl std::cmp::Eq for Sequence {}
314
315impl std::cmp::PartialOrd for Sequence {
316    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
317        Some(self.cmp(other))
318    }
319}
320
321impl std::cmp::Ord for Sequence {
322    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
323        self.data.cmp(&other.data)
324    }
325}
326
327impl std::ops::Add for &Sequence {
328    type Output = Sequence;
329    fn add(self, rhs: &Sequence) -> Sequence {
330        let mut data = Vec::with_capacity(self.data.len() + rhs.data.len());
331        data.extend_from_slice(&self.data);
332        data.extend_from_slice(&rhs.data);
333        Sequence {
334            data,
335            alphabet: Vec::new(),
336            alphabet_size: self.alphabet_size.max(rhs.alphabet_size),
337        }
338    }
339}
340
341/// In-place block shuffle of `s`. Picks two non-overlapping aligned blocks of
342/// `block_size` and swaps them. Deterministic when called via [`Sequence::shuffle_seeded`].
343///
344/// Mirrors `lz::Shuffle(sequence&, lz_uint)` from the C++.
345pub fn shuffle_in_place(s: &mut Sequence, block_size: u32, rng: &mut ChaCha8Rng) {
346    let seq_size = s.len();
347    let bs = block_size as usize;
348    if bs == 0 || seq_size <= bs + 1 {
349        return;
350    }
351    let max_block_idx = (seq_size - bs - 1) / bs;
352    if max_block_idx == 0 {
353        return;
354    }
355    // Find first valid block
356    let mut op1 = bs * rng.gen_range(0..=max_block_idx);
357    while op1 + bs > seq_size.saturating_sub(1) {
358        op1 = bs * rng.gen_range(0..=max_block_idx);
359    }
360    if seq_size <= 10 {
361        return;
362    }
363    // Find second non-overlapping block
364    loop {
365        let op2 = bs * rng.gen_range(0..=max_block_idx);
366        let no_overlap = op2 + bs <= op1 || op2 >= op1 + bs;
367        let in_bounds = op2 + bs <= seq_size.saturating_sub(1);
368        if no_overlap && in_bounds {
369            // swap blocks [op1..op1+bs] and [op2..op2+bs]
370            for i in 0..bs {
371                s.data.swap(op1 + i, op2 + i);
372            }
373            return;
374        }
375    }
376}
377
378/// Returns a shuffled copy after `times` iterations, with a deterministic seed.
379pub fn shuffle_copy(s: &Sequence, block_size: u32, times: u32) -> Sequence {
380    let mut out = s.clone();
381    let mut rng = ChaCha8Rng::seed_from_u64(deterministic_seed(s, block_size));
382    for _ in 0..times {
383        shuffle_in_place(&mut out, block_size, &mut rng);
384    }
385    out
386}
387
388/// Same as [`shuffle_copy`] but with an explicit seed, so callers (LZ76 random
389/// shuffle complexity) can vary the seed per inner block size.
390pub fn shuffle_copy_seeded(s: &Sequence, block_size: u32, times: u32, seed: u64) -> Sequence {
391    let mut out = s.clone();
392    let mut rng = ChaCha8Rng::seed_from_u64(seed);
393    for _ in 0..times {
394        shuffle_in_place(&mut out, block_size, &mut rng);
395    }
396    out
397}
398
399fn deterministic_seed(s: &Sequence, block_size: u32) -> u64 {
400    let mut hash: u64 = 5381;
401    for &c in &s.data {
402        hash = hash.wrapping_mul(33).wrapping_add(c as u64);
403    }
404    hash ^ (block_size as u64).wrapping_mul(0x9E3779B97F4A7C15)
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410
411    #[test]
412    fn from_str_and_take() {
413        let s = Sequence::from_str("hello");
414        assert_eq!(s.len(), 5);
415        assert_eq!(&s.take(3).data[..], b"hel");
416    }
417
418    #[test]
419    fn split_drop_takes_match() {
420        let s = Sequence::from_str("ABRACADABRA");
421        let (l, r) = s.split_at(5);
422        assert_eq!(&l.data[..], b"ABRAC");
423        assert_eq!(&r.data[..], b"ADABRA");
424        assert_eq!(&s.drop(5).data[..], b"ADABRA");
425        assert_eq!(&s.take(5).data[..], b"ABRAC");
426    }
427
428    #[test]
429    fn shifts() {
430        let mut s = Sequence::from_str("ABCDE");
431        s.right_shift(2);
432        assert_eq!(&s.data[..], b"CDEAB");
433        let mut s = Sequence::from_str("ABCDE");
434        s.left_shift(1);
435        assert_eq!(&s.data[..], b"EABCD");
436    }
437
438    #[test]
439    fn alphabet_min_two() {
440        let s = Sequence::from_str("aaaa");
441        assert_eq!(s.alphabet_size(), 2);
442    }
443
444    #[test]
445    fn shuffle_deterministic() {
446        let s = Sequence::from_str(&"01".repeat(200));
447        let a = shuffle_copy_seeded(&s, 3, 5, 42);
448        let b = shuffle_copy_seeded(&s, 3, 5, 42);
449        assert_eq!(a.data, b.data);
450    }
451}