assay_sim/mutators/
bitflip.rs1use super::Mutator;
2use anyhow::Result;
3use rand::rngs::StdRng;
4use rand::{Rng, SeedableRng};
5
6pub struct BitFlip {
7 pub count: usize,
8 pub seed: Option<u64>,
10}
11
12impl Mutator for BitFlip {
13 fn mutate(&self, data: &[u8]) -> Result<Vec<u8>> {
14 let mut corrupted = data.to_vec();
15
16 let flip = |rng: &mut dyn rand::RngCore, buf: &mut Vec<u8>, count: usize| {
17 for _ in 0..count {
18 if buf.is_empty() {
19 break;
20 }
21 let idx = rng.gen_range(0..buf.len());
22 buf[idx] ^= 1 << rng.gen_range(0..8u32);
23 }
24 };
25
26 if let Some(seed) = self.seed {
27 let mut rng = StdRng::seed_from_u64(seed);
28 flip(&mut rng, &mut corrupted, self.count);
29 } else {
30 let mut rng = rand::thread_rng();
31 flip(&mut rng, &mut corrupted, self.count);
32 }
33
34 Ok(corrupted)
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn test_seeded_bitflip_is_deterministic() {
44 let data = vec![0u8; 100];
45 let bf = BitFlip {
46 count: 5,
47 seed: Some(42),
48 };
49
50 let result1 = bf.mutate(&data).unwrap();
51 let result2 = bf.mutate(&data).unwrap();
52
53 assert_eq!(
54 result1, result2,
55 "same seed must produce identical mutations"
56 );
57 assert_ne!(result1, data, "mutations should change the data");
58 }
59
60 #[test]
61 fn test_different_seeds_produce_different_mutations() {
62 let data = vec![0u8; 100];
63 let bf1 = BitFlip {
64 count: 5,
65 seed: Some(42),
66 };
67 let bf2 = BitFlip {
68 count: 5,
69 seed: Some(99),
70 };
71
72 let result1 = bf1.mutate(&data).unwrap();
73 let result2 = bf2.mutate(&data).unwrap();
74
75 assert_ne!(
76 result1, result2,
77 "different seeds should produce different mutations"
78 );
79 }
80
81 #[test]
82 fn test_seeded_bitflip_exact_bytes() {
83 let data = vec![0xAA; 50];
85 let bf = BitFlip {
86 count: 3,
87 seed: Some(12345),
88 };
89
90 let result1 = bf.mutate(&data).unwrap();
91 let result2 = bf.mutate(&data).unwrap();
92
93 let diffs1: Vec<usize> = result1
95 .iter()
96 .enumerate()
97 .filter(|(i, b)| **b != data[*i])
98 .map(|(i, _)| i)
99 .collect();
100 let diffs2: Vec<usize> = result2
101 .iter()
102 .enumerate()
103 .filter(|(i, b)| **b != data[*i])
104 .map(|(i, _)| i)
105 .collect();
106
107 assert_eq!(diffs1, diffs2, "exact same positions must be flipped");
108 assert!(!diffs1.is_empty(), "at least one bit should be flipped");
109 }
110}