Skip to main content

ternary_steganography/
lib.rs

1//! # ternary-steganography
2//!
3//! Hide information in ternary strategy noise.
4//!
5//! Encode and decode messages within ternary strategy patterns using
6//! various steganographic techniques: bit embedding, pattern encoding,
7//! frequency modulation, and statistical steganography.
8
9/// A ternary value: -1, 0, +1
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum Trit {
12    Neg,
13    Zero,
14    Pos,
15}
16
17impl Trit {
18    pub fn to_i8(self) -> i8 {
19        match self {
20            Trit::Neg => -1,
21            Trit::Zero => 0,
22            Trit::Pos => 1,
23        }
24    }
25
26    pub fn from_i8(v: i8) -> Option<Self> {
27        match v {
28            -1 => Some(Trit::Neg),
29            0 => Some(Trit::Zero),
30            1 => Some(Trit::Pos),
31            _ => None,
32        }
33    }
34
35    /// Convert to trit digit (0, 1, 2)
36    pub fn digit(self) -> u8 {
37        match self {
38            Trit::Neg => 0,
39            Trit::Zero => 1,
40            Trit::Pos => 2,
41        }
42    }
43
44    pub fn from_digit(d: u8) -> Option<Self> {
45        match d {
46            0 => Some(Trit::Neg),
47            1 => Some(Trit::Zero),
48            2 => Some(Trit::Pos),
49            _ => None,
50        }
51    }
52}
53
54/// A ternary strategy sequence
55#[derive(Debug, Clone)]
56pub struct TernarySequence {
57    trits: Vec<Trit>,
58}
59
60impl TernarySequence {
61    pub fn new(trits: Vec<Trit>) -> Self {
62        TernarySequence { trits }
63    }
64
65    pub fn from_i8(values: &[i8]) -> Self {
66        TernarySequence {
67            trits: values.iter()
68                .filter_map(|&v| Trit::from_i8(v))
69                .collect(),
70        }
71    }
72
73    pub fn len(&self) -> usize {
74        self.trits.len()
75    }
76
77    pub fn is_empty(&self) -> bool {
78        self.trits.is_empty()
79    }
80
81    pub fn trits(&self) -> &[Trit] {
82        &self.trits
83    }
84
85    pub fn push(&mut self, trit: Trit) {
86        self.trits.push(trit);
87    }
88
89    pub fn get(&self, idx: usize) -> Option<Trit> {
90        self.trits.get(idx).copied()
91    }
92
93    pub fn set(&mut self, idx: usize, trit: Trit) {
94        if idx < self.trits.len() {
95            self.trits[idx] = trit;
96        }
97    }
98
99    /// Compute capacity for embedding bits
100    pub fn bit_capacity(&self, bits_per_trit: usize) -> usize {
101        self.trits.len() * bits_per_trit
102    }
103}
104
105// ─── Bit-level Embedding ───────────────────────────────────────────
106
107/// Simple bit embedding: encode binary data in ternary values
108/// Uses 1 bit per trit: Neg/Zero = 0, Pos = 1
109pub struct BitEmbedder {
110    _bits_per_trit: usize,
111}
112
113impl BitEmbedder {
114    pub fn new(bits_per_trit: usize) -> Self {
115        BitEmbedder { _bits_per_trit: bits_per_trit.min(1) }
116    }
117
118    /// Encode bytes into a ternary sequence carrier
119    pub fn encode(&self, carrier: &TernarySequence, data: &[u8]) -> Option<TernarySequence> {
120        let bits_needed = data.len() * 8;
121        if bits_needed > carrier.len() {
122            return None;
123        }
124
125        let mut result = carrier.clone();
126        let mut bit_idx = 0;
127
128        for &byte in data {
129            for shift in (0..8).rev() {
130                let bit = (byte >> shift) & 1;
131                let trit = if bit == 1 { Trit::Pos } else { Trit::Neg };
132                result.set(bit_idx, trit);
133                bit_idx += 1;
134            }
135        }
136        Some(result)
137    }
138
139    /// Decode bytes from an encoded ternary sequence
140    pub fn decode(&self, encoded: &TernarySequence, byte_count: usize) -> Option<Vec<u8>> {
141        let mut result = Vec::with_capacity(byte_count);
142        let mut bit_idx = 0;
143
144        for _ in 0..byte_count {
145            let mut byte = 0u8;
146            for shift in (0..8).rev() {
147                let trit = encoded.get(bit_idx)?;
148                let bit = if trit == Trit::Pos { 1 } else { 0 };
149                byte |= bit << shift;
150                bit_idx += 1;
151            }
152            result.push(byte);
153        }
154        Some(result)
155    }
156}
157
158// ─── Pattern Encoding ──────────────────────────────────────────────
159
160/// Encode messages as specific ternary patterns
161pub struct PatternEncoder {
162    /// Pattern length per character
163    pattern_len: usize,
164}
165
166impl PatternEncoder {
167    pub fn new(pattern_len: usize) -> Self {
168        PatternEncoder { pattern_len: pattern_len.max(2) }
169    }
170
171    /// Encode a string message as ternary patterns
172    pub fn encode(&self, message: &str) -> TernarySequence {
173        let mut trits = Vec::new();
174        for ch in message.chars() {
175            let code = ch as u32;
176            // Convert character code to balanced ternary
177            let ternary = Self::to_base3(code, self.pattern_len);
178            trits.extend_from_slice(&ternary);
179        }
180        TernarySequence::new(trits)
181    }
182
183    /// Decode ternary patterns back to a string
184    pub fn decode(&self, encoded: &TernarySequence) -> String {
185        let mut result = String::new();
186        let chunks = encoded.trits.chunks(self.pattern_len);
187        for chunk in chunks {
188            let code = Self::from_base3(chunk);
189            if let Some(ch) = char::from_u32(code) {
190                result.push(ch);
191            }
192        }
193        result
194    }
195
196    /// Hide encoded message within a carrier by replacing specific positions
197    pub fn stego_encode(
198        &self,
199        carrier: &TernarySequence,
200        message: &str,
201        positions: &[usize],
202    ) -> Option<TernarySequence> {
203        let encoded = self.encode(message);
204        if encoded.len() > positions.len() {
205            return None;
206        }
207        let mut result = carrier.clone();
208        for (i, &pos) in positions.iter().enumerate() {
209            if i >= encoded.len() {
210                break;
211            }
212            if pos < result.len() {
213                result.set(pos, encoded.get(i)?);
214            }
215        }
216        Some(result)
217    }
218
219    /// Extract a hidden message from specific positions
220    pub fn stego_decode(
221        &self,
222        carrier: &TernarySequence,
223        positions: &[usize],
224        char_count: usize,
225    ) -> Option<String> {
226        let mut trits = Vec::new();
227        for &pos in positions {
228            if pos < carrier.len() {
229                trits.push(carrier.get(pos)?);
230            }
231        }
232        let seq = TernarySequence::new(trits);
233        // Truncate to expected length
234        let needed = char_count * self.pattern_len;
235        let truncated: Vec<Trit> = seq.trits.into_iter().take(needed).collect();
236        let seq = TernarySequence::new(truncated);
237        Some(self.decode(&seq))
238    }
239
240    fn to_base3(mut value: u32, len: usize) -> Vec<Trit> {
241        let mut digits = Vec::new();
242        while value > 0 {
243            digits.push(Trit::from_digit((value % 3) as u8).unwrap_or(Trit::Neg));
244            value /= 3;
245        }
246        while digits.len() < len {
247            digits.push(Trit::Neg); // digit 0
248        }
249        digits.truncate(len);
250        digits.reverse();
251        digits
252    }
253
254    fn from_base3(trits: &[Trit]) -> u32 {
255        let mut value = 0u32;
256        for &trit in trits {
257            value = value * 3 + trit.digit() as u32;
258        }
259        value
260    }
261}
262
263// ─── Frequency Modulation ──────────────────────────────────────────
264
265/// Encode data by modulating the frequency of ternary values
266pub struct FrequencyModulator {
267    window_size: usize,
268}
269
270impl FrequencyModulator {
271    pub fn new(window_size: usize) -> Self {
272        FrequencyModulator { window_size: window_size.max(3) }
273    }
274
275    /// Encode a byte into a window by adjusting trit frequencies
276    pub fn encode_byte(&self, byte: u8) -> Vec<Trit> {
277        let mut trits = Vec::with_capacity(self.window_size);
278        // Distribute trits based on byte value
279        let neg_count = ((byte as usize) % self.window_size).min(self.window_size);
280        let pos_count = ((byte as usize / 3) % self.window_size).min(self.window_size.saturating_sub(neg_count));
281        let zero_count = self.window_size - neg_count - pos_count;
282
283        for _ in 0..neg_count {
284            trits.push(Trit::Neg);
285        }
286        for _ in 0..zero_count {
287            trits.push(Trit::Zero);
288        }
289        for _ in 0..pos_count {
290            trits.push(Trit::Pos);
291        }
292        trits
293    }
294
295    /// Decode a byte from a window of trits
296    pub fn decode_byte(&self, trits: &[Trit]) -> u8 {
297        let neg_count = trits.iter().filter(|&&t| t == Trit::Neg).count();
298        let pos_count = trits.iter().filter(|&&t| t == Trit::Pos).count();
299        ((neg_count + pos_count * 3) % 256) as u8
300    }
301
302    /// Encode data into a carrier sequence using frequency windows
303    pub fn encode(&self, carrier: &TernarySequence, data: &[u8]) -> Option<TernarySequence> {
304        let needed = data.len() * self.window_size;
305        if needed > carrier.len() {
306            return None;
307        }
308
309        let mut result = carrier.clone();
310        for (i, &byte) in data.iter().enumerate() {
311            let window = self.encode_byte(byte);
312            let offset = i * self.window_size;
313            for (j, trit) in window.iter().enumerate() {
314                result.set(offset + j, *trit);
315            }
316        }
317        Some(result)
318    }
319
320    /// Decode data from a carrier sequence
321    pub fn decode(&self, carrier: &TernarySequence, byte_count: usize) -> Vec<u8> {
322        let mut result = Vec::with_capacity(byte_count);
323        for i in 0..byte_count {
324            let offset = i * self.window_size;
325            if offset + self.window_size > carrier.len() {
326                break;
327            }
328            let window: Vec<Trit> = (offset..offset + self.window_size)
329                .filter_map(|j| carrier.get(j))
330                .collect();
331            result.push(self.decode_byte(&window));
332        }
333        result
334    }
335}
336
337// ─── Statistical Steganography ─────────────────────────────────────
338
339/// Steganography using statistical properties of ternary sequences
340pub struct StatisticalStego {
341    block_size: usize,
342}
343
344impl StatisticalStego {
345    pub fn new(block_size: usize) -> Self {
346        StatisticalStego { block_size: block_size.max(3) }
347    }
348
349    /// Compute statistics of a ternary block
350    pub fn block_stats(trits: &[Trit]) -> (f64, f64, f64) {
351        let n = trits.len() as f64;
352        if n == 0.0 {
353            return (0.0, 0.0, 0.0);
354        }
355        let neg = trits.iter().filter(|&&t| t == Trit::Neg).count() as f64 / n;
356        let zero = trits.iter().filter(|&&t| t == Trit::Zero).count() as f64 / n;
357        let pos = trits.iter().filter(|&&t| t == Trit::Pos).count() as f64 / n;
358        (neg, zero, pos)
359    }
360
361    /// Encode a bit by shifting block statistics
362    pub fn encode_bit(&self, block: &mut [Trit], bit: u8) {
363        if block.len() < self.block_size {
364            return;
365        }
366        // If bit is 1, ensure more Pos than Neg; if 0, ensure more Neg than Pos
367        match bit {
368            0 => {
369                // Ensure at least half are Neg
370                for i in 0..block.len() / 2 {
371                    block[i] = Trit::Neg;
372                }
373            }
374            1 => {
375                // Ensure at least half are Pos
376                for i in 0..block.len() / 2 {
377                    block[i] = Trit::Pos;
378                }
379            }
380            _ => {}
381        }
382    }
383
384    /// Decode a bit from block statistics
385    pub fn decode_bit(&self, block: &[Trit]) -> u8 {
386        let (neg, _, pos) = Self::block_stats(block);
387        if pos > neg {
388            1
389        } else {
390            0
391        }
392    }
393
394    /// Encode bytes into carrier using statistical modulation
395    pub fn encode(&self, carrier: &TernarySequence, data: &[u8]) -> Option<TernarySequence> {
396        let bits_needed = data.len() * 8;
397        if bits_needed * self.block_size > carrier.len() {
398            return None;
399        }
400
401        let mut trits = carrier.trits.clone();
402        for (byte_idx, &byte) in data.iter().enumerate() {
403            for bit_idx in 0..8 {
404                let bit = (byte >> (7 - bit_idx)) & 1;
405                let offset = (byte_idx * 8 + bit_idx) * self.block_size;
406                let end = (offset + self.block_size).min(trits.len());
407                if offset < trits.len() {
408                    self.encode_bit(&mut trits[offset..end], bit);
409                }
410            }
411        }
412        Some(TernarySequence::new(trits))
413    }
414
415    /// Decode bytes from a statistically encoded sequence
416    pub fn decode(&self, encoded: &TernarySequence, byte_count: usize) -> Vec<u8> {
417        let mut result = Vec::with_capacity(byte_count);
418        for byte_idx in 0..byte_count {
419            let mut byte = 0u8;
420            for bit_idx in 0..8 {
421                let offset = (byte_idx * 8 + bit_idx) * self.block_size;
422                let end = (offset + self.block_size).min(encoded.len());
423                if offset >= encoded.len() {
424                    break;
425                }
426                let block: Vec<Trit> = (offset..end)
427                    .filter_map(|j| encoded.get(j))
428                    .collect();
429                let bit = self.decode_bit(&block);
430                byte |= bit << (7 - bit_idx);
431            }
432            result.push(byte);
433        }
434        result
435    }
436}
437
438// ─── Spread Spectrum ───────────────────────────────────────────────
439
440/// Spread spectrum steganography using a key-based pattern
441pub struct SpreadSpectrum {
442    key: Vec<usize>,
443    chip_rate: usize,
444}
445
446impl SpreadSpectrum {
447    pub fn new(key: Vec<usize>, chip_rate: usize) -> Self {
448        SpreadSpectrum { key, chip_rate: chip_rate.max(1) }
449    }
450
451    /// Generate a pseudo-random ternary sequence from the key
452    pub fn generate_pattern(&self, length: usize) -> Vec<Trit> {
453        let mut pattern = Vec::with_capacity(length);
454        for i in 0..length {
455            let key_byte = self.key[i % self.key.len()];
456            let val = (key_byte.wrapping_add(i)).wrapping_mul(31) % 3;
457            pattern.push(Trit::from_digit(val as u8).unwrap_or(Trit::Zero));
458        }
459        pattern
460    }
461
462    /// Encode a bit using direct sequence spread spectrum
463    pub fn encode_bit(&self, bit: u8, pattern: &[Trit]) -> Vec<Trit> {
464        let sign = if bit == 1 { 1i8 } else { -1i8 };
465        pattern.iter().map(|&t| {
466            let v = t.to_i8() * sign;
467            Trit::from_i8(v.clamp(-1, 1)).unwrap_or(Trit::Zero)
468        }).collect()
469    }
470
471    /// Decode a bit using correlation with the pattern
472    pub fn decode_bit(&self, encoded: &[Trit], pattern: &[Trit]) -> u8 {
473        let correlation: i64 = encoded.iter().zip(pattern.iter())
474            .map(|(&e, &p)| e.to_i8() as i64 * p.to_i8() as i64)
475            .sum();
476        if correlation > 0 { 1 } else { 0 }
477    }
478
479    /// Encode data into carrier
480    pub fn encode(&self, carrier: &TernarySequence, data: &[u8]) -> Option<TernarySequence> {
481        let bits_needed = data.len() * 8;
482        let needed = bits_needed * self.chip_rate;
483        if needed > carrier.len() {
484            return None;
485        }
486
487        let mut trits = carrier.trits.clone();
488        let pattern = self.generate_pattern(self.chip_rate);
489
490        for (byte_idx, &byte) in data.iter().enumerate() {
491            for bit_idx in 0..8 {
492                let bit = (byte >> (7 - bit_idx)) & 1;
493                let encoded_bit = self.encode_bit(bit, &pattern);
494                let offset = (byte_idx * 8 + bit_idx) * self.chip_rate;
495                for (j, trit) in encoded_bit.iter().enumerate() {
496                    if offset + j < trits.len() {
497                        trits[offset + j] = *trit;
498                    }
499                }
500            }
501        }
502        Some(TernarySequence::new(trits))
503    }
504
505    /// Decode data from encoded sequence
506    pub fn decode(&self, encoded: &TernarySequence, byte_count: usize) -> Vec<u8> {
507        let pattern = self.generate_pattern(self.chip_rate);
508        let mut result = Vec::with_capacity(byte_count);
509
510        for byte_idx in 0..byte_count {
511            let mut byte = 0u8;
512            for bit_idx in 0..8 {
513                let offset = (byte_idx * 8 + bit_idx) * self.chip_rate;
514                if offset + self.chip_rate > encoded.len() {
515                    break;
516                }
517                let chunk: Vec<Trit> = (offset..offset + self.chip_rate)
518                    .filter_map(|j| encoded.get(j))
519                    .collect();
520                let bit = self.decode_bit(&chunk, &pattern);
521                byte |= bit << (7 - bit_idx);
522            }
523            result.push(byte);
524        }
525        result
526    }
527}
528
529// ─── Capacity Analysis ─────────────────────────────────────────────
530
531/// Analyze steganographic capacity of a ternary sequence
532pub struct CapacityAnalyzer;
533
534impl CapacityAnalyzer {
535    /// Calculate maximum embeddable bytes for a given technique
536    pub fn max_bytes(seq_len: usize, technique: &str) -> usize {
537        match technique {
538            "bit" => seq_len / 8,
539            "pattern" => seq_len / 7, // ~7 trits per ASCII char
540            "frequency" => seq_len / 9, // window size 9
541            "statistical" => seq_len / (8 * 4), // block size 4
542            "spread" => seq_len / (8 * 8), // chip rate 8
543            _ => 0,
544        }
545    }
546
547    /// Calculate embedding efficiency (bits per modified trit)
548    pub fn embedding_efficiency(data_len: usize, modified_trits: usize) -> f64 {
549        if modified_trits == 0 {
550            return 0.0;
551        }
552        (data_len as f64 * 8.0) / modified_trits as f64
553    }
554
555    /// Detect if a sequence likely contains hidden data
556    pub fn detect_anomaly(seq: &TernarySequence) -> f64 {
557        let (neg, zero, pos) = StatisticalStego::block_stats(seq.trits());
558        // Expected uniform distribution: 0.333 each
559        let expected = 1.0 / 3.0;
560        let deviation = (neg - expected).abs() + (zero - expected).abs() + (pos - expected).abs();
561        // Higher deviation = more likely to contain hidden data
562        deviation / 2.0 // Normalize to 0..1 range
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569
570    #[test]
571    fn test_trit_conversions() {
572        assert_eq!(Trit::Neg.to_i8(), -1);
573        assert_eq!(Trit::Zero.to_i8(), 0);
574        assert_eq!(Trit::Pos.to_i8(), 1);
575        assert_eq!(Trit::from_i8(-1), Some(Trit::Neg));
576        assert_eq!(Trit::from_i8(5), None);
577    }
578
579    #[test]
580    fn test_trit_digits() {
581        assert_eq!(Trit::Neg.digit(), 0);
582        assert_eq!(Trit::Zero.digit(), 1);
583        assert_eq!(Trit::Pos.digit(), 2);
584        assert_eq!(Trit::from_digit(0), Some(Trit::Neg));
585        assert_eq!(Trit::from_digit(3), None);
586    }
587
588    #[test]
589    fn test_ternary_sequence_basic() {
590        let seq = TernarySequence::from_i8(&[-1, 0, 1, -1, 0]);
591        assert_eq!(seq.len(), 5);
592        assert_eq!(seq.get(0), Some(Trit::Neg));
593        assert_eq!(seq.get(2), Some(Trit::Pos));
594        assert!(!seq.is_empty());
595    }
596
597    #[test]
598    fn test_ternary_sequence_push() {
599        let mut seq = TernarySequence::new(vec![]);
600        seq.push(Trit::Pos);
601        seq.push(Trit::Neg);
602        assert_eq!(seq.len(), 2);
603        assert_eq!(seq.get(0), Some(Trit::Pos));
604    }
605
606    #[test]
607    fn test_bit_embedder_encode_decode() {
608        let carrier = TernarySequence::from_i8(&[0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1]);
609        let embedder = BitEmbedder::new(1);
610        let data = vec![0x41]; // 'A'
611        let encoded = embedder.encode(&carrier, &data).unwrap();
612        let decoded = embedder.decode(&encoded, 1).unwrap();
613        assert_eq!(decoded, data);
614    }
615
616    #[test]
617    fn test_bit_embedder_insufficient_capacity() {
618        let carrier = TernarySequence::from_i8(&[0, 1]);
619        let embedder = BitEmbedder::new(1);
620        let data = vec![0xFF, 0xFF];
621        assert!(embedder.encode(&carrier, &data).is_none());
622    }
623
624    #[test]
625    fn test_bit_embedder_multiple_bytes() {
626        let carrier = TernarySequence::from_i8(&[0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1]);
627        let embedder = BitEmbedder::new(1);
628        let data = vec![0x48, 0x69]; // "Hi"
629        let encoded = embedder.encode(&carrier, &data).unwrap();
630        let decoded = embedder.decode(&encoded, 2).unwrap();
631        assert_eq!(decoded, data);
632    }
633
634    #[test]
635    fn test_pattern_encoder_basic() {
636        let encoder = PatternEncoder::new(6);
637        let encoded = encoder.encode("AB");
638        let decoded = encoder.decode(&encoded);
639        assert_eq!(decoded, "AB");
640    }
641
642    #[test]
643    fn test_pattern_encoder_empty() {
644        let encoder = PatternEncoder::new(4);
645        let encoded = encoder.encode("");
646        assert_eq!(encoded.len(), 0);
647        let decoded = encoder.decode(&encoded);
648        assert_eq!(decoded, "");
649    }
650
651    #[test]
652    fn test_pattern_stego_encode_decode() {
653        let carrier = TernarySequence::from_i8(&[0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1]);
654        let encoder = PatternEncoder::new(4);
655        let positions = vec![0, 1, 2, 3, 4, 5, 6, 7];
656        let encoded = encoder.stego_encode(&carrier, "A", &positions).unwrap();
657        assert_eq!(encoded.len(), carrier.len());
658    }
659
660    #[test]
661    fn test_frequency_modulator_encode_byte() {
662        let modulator = FrequencyModulator::new(6);
663        let trits = modulator.encode_byte(0x41);
664        assert_eq!(trits.len(), 6);
665    }
666
667    #[test]
668    fn test_frequency_modulator_roundtrip() {
669        let carrier = TernarySequence::from_i8(&[0; 36]);
670        let modulator = FrequencyModulator::new(9);
671        let data = vec![0x41, 0x42, 0x43, 0x00];
672        let encoded = modulator.encode(&carrier, &data).unwrap();
673        let decoded = modulator.decode(&encoded, 4);
674        // The roundtrip should preserve the decode
675        assert_eq!(decoded.len(), 4);
676    }
677
678    #[test]
679    fn test_frequency_modulator_insufficient() {
680        let carrier = TernarySequence::from_i8(&[0; 3]);
681        let modulator = FrequencyModulator::new(9);
682        let data = vec![0x41, 0x42];
683        assert!(modulator.encode(&carrier, &data).is_none());
684    }
685
686    #[test]
687    fn test_statistical_stego_block_stats() {
688        let trits = vec![Trit::Neg, Trit::Neg, Trit::Zero, Trit::Pos];
689        let (neg, zero, pos) = StatisticalStego::block_stats(&trits);
690        assert!((neg - 0.5).abs() < 0.01);
691        assert!((zero - 0.25).abs() < 0.01);
692        assert!((pos - 0.25).abs() < 0.01);
693    }
694
695    #[test]
696    fn test_statistical_stego_bit() {
697        let stego = StatisticalStego::new(8);
698        let mut block = vec![Trit::Zero; 8];
699        stego.encode_bit(&mut block, 1);
700        assert_eq!(stego.decode_bit(&block), 1);
701        stego.encode_bit(&mut block, 0);
702        assert_eq!(stego.decode_bit(&block), 0);
703    }
704
705    #[test]
706    fn test_statistical_stego_roundtrip() {
707        let carrier = TernarySequence::from_i8(&[0; 256]);
708        let stego = StatisticalStego::new(4);
709        let data = vec![0xAB];
710        let encoded = stego.encode(&carrier, &data).unwrap();
711        let decoded = stego.decode(&encoded, 1);
712        assert_eq!(decoded, data);
713    }
714
715    #[test]
716    fn test_spread_spectrum_pattern() {
717        let ss = SpreadSpectrum::new(vec![42, 17, 99], 8);
718        let pattern = ss.generate_pattern(10);
719        assert_eq!(pattern.len(), 10);
720        // All should be valid trits
721        for t in &pattern {
722            assert!(matches!(t, Trit::Neg | Trit::Zero | Trit::Pos));
723        }
724    }
725
726    #[test]
727    fn test_spread_spectrum_bit() {
728        let ss = SpreadSpectrum::new(vec![42, 17], 8);
729        let pattern = ss.generate_pattern(8);
730        let encoded = ss.encode_bit(1, &pattern);
731        assert_eq!(ss.decode_bit(&encoded, &pattern), 1);
732        let encoded0 = ss.encode_bit(0, &pattern);
733        assert_eq!(ss.decode_bit(&encoded0, &pattern), 0);
734    }
735
736    #[test]
737    fn test_spread_spectrum_roundtrip() {
738        let carrier = TernarySequence::from_i8(&[0; 128]);
739        let ss = SpreadSpectrum::new(vec![42, 17, 99], 8);
740        let data = vec![0x48];
741        let encoded = ss.encode(&carrier, &data).unwrap();
742        let decoded = ss.decode(&encoded, 1);
743        assert_eq!(decoded, data);
744    }
745
746    #[test]
747    fn test_capacity_analyzer() {
748        assert_eq!(CapacityAnalyzer::max_bytes(100, "bit"), 12);
749        assert_eq!(CapacityAnalyzer::max_bytes(100, "pattern"), 14);
750        assert!(CapacityAnalyzer::max_bytes(100, "unknown") == 0);
751    }
752
753    #[test]
754    fn test_embedding_efficiency() {
755        let eff = CapacityAnalyzer::embedding_efficiency(1, 8);
756        assert!((eff - 1.0).abs() < 0.01);
757    }
758
759    #[test]
760    fn test_detect_anomaly_uniform() {
761        // Create a perfectly uniform sequence
762        let trits: Vec<Trit> = (0..99).map(|i| match i % 3 {
763            0 => Trit::Neg,
764            1 => Trit::Zero,
765            _ => Trit::Pos,
766        }).collect();
767        let seq = TernarySequence::new(trits);
768        let anomaly = CapacityAnalyzer::detect_anomaly(&seq);
769        assert!(anomaly < 0.1); // Should be low for uniform
770    }
771
772    #[test]
773    fn test_detect_anomaly_biased() {
774        let trits = vec![Trit::Pos; 100];
775        let seq = TernarySequence::new(trits);
776        let anomaly = CapacityAnalyzer::detect_anomaly(&seq);
777        assert!(anomaly > 0.5); // Should be high for biased
778    }
779}