sc_neurocore_engine/
fault.rs1use rand::{RngExt, SeedableRng};
11use rand_xoshiro::Xoshiro256PlusPlus;
12
13pub fn inject_bitflips(data: &mut [u64], rate: f64, seed: u64) {
15 if rate <= 0.0 {
16 return;
17 }
18 let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed);
19 for word in data.iter_mut() {
20 let mut flip_mask = 0u64;
21 for bit in 0..64 {
22 if rng.random::<f64>() < rate {
23 flip_mask |= 1u64 << bit;
24 }
25 }
26 *word ^= flip_mask;
27 }
28}
29
30pub fn inject_stuck_at(data: &mut [u64], rate: f64, value: bool, seed: u64) {
32 if rate <= 0.0 {
33 return;
34 }
35 let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed);
36 for word in data.iter_mut() {
37 for bit in 0..64 {
38 if rng.random::<f64>() < rate {
39 if value {
40 *word |= 1u64 << bit;
41 } else {
42 *word &= !(1u64 << bit);
43 }
44 }
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn zero_rate_no_change() {
55 let mut data = vec![0xDEAD_BEEF_CAFE_BABEu64; 4];
56 let original = data.clone();
57 inject_bitflips(&mut data, 0.0, 42);
58 assert_eq!(data, original);
59 }
60
61 #[test]
62 fn full_rate_flips_all() {
63 let mut data = vec![0u64; 2];
64 inject_bitflips(&mut data, 1.0, 42);
65 assert_eq!(data, vec![u64::MAX; 2]);
66 }
67
68 #[test]
69 fn stuck_at_zero() {
70 let mut data = vec![u64::MAX; 2];
71 inject_stuck_at(&mut data, 1.0, false, 42);
72 assert_eq!(data, vec![0u64; 2]);
73 }
74
75 #[test]
76 fn stuck_at_one() {
77 let mut data = vec![0u64; 2];
78 inject_stuck_at(&mut data, 1.0, true, 42);
79 assert_eq!(data, vec![u64::MAX; 2]);
80 }
81
82 #[test]
83 fn partial_rate_changes_some() {
84 let mut data = vec![0u64; 8];
85 inject_bitflips(&mut data, 0.5, 99);
86 let total_set: u32 = data.iter().map(|w| w.count_ones()).sum();
87 assert!(total_set > 100 && total_set < 400);
89 }
90}