#[derive(Debug, Clone)]
pub struct MinimizedTrace {
pub original_ticks: u64,
pub minimized_ticks: u64,
pub original_faults: usize,
pub minimized_faults: usize,
pub essential_fault_indices: Vec<usize>,
}
pub fn minimize_ticks<F>(reproduce_fn: F, max_ticks: u64, min_ticks: u64) -> MinimizedTrace
where
F: Fn(u64) -> bool,
{
if !reproduce_fn(max_ticks) {
return MinimizedTrace {
original_ticks: max_ticks,
minimized_ticks: max_ticks,
original_faults: 0,
minimized_faults: 0,
essential_fault_indices: Vec::new(),
};
}
let mut lo = min_ticks;
let mut hi = max_ticks;
while lo + 1 < hi {
let mid = lo + (hi - lo) / 2;
if reproduce_fn(mid) {
hi = mid;
} else {
lo = mid;
}
}
MinimizedTrace {
original_ticks: max_ticks,
minimized_ticks: hi,
original_faults: 0,
minimized_faults: 0,
essential_fault_indices: Vec::new(),
}
}
pub fn minimize_faults<P, R>(num_faults: usize, is_paired: P, reproduce_with: R) -> MinimizedTrace
where
P: Fn(usize) -> Option<usize>,
R: Fn(&[usize]) -> bool,
{
let mut active: Vec<bool> = vec![true; num_faults];
for idx in 0..num_faults {
if !active[idx] {
continue;
}
let paired = is_paired(idx);
active[idx] = false;
if let Some(pair_idx) = paired {
active[pair_idx] = false;
}
let active_indices: Vec<usize> = active
.iter()
.enumerate()
.filter(|(_, a)| **a)
.map(|(i, _)| i)
.collect();
if !reproduce_with(&active_indices) {
active[idx] = true;
if let Some(pair_idx) = paired {
active[pair_idx] = true;
}
}
}
let essential: Vec<usize> = active
.iter()
.enumerate()
.filter(|(_, a)| **a)
.map(|(i, _)| i)
.collect();
MinimizedTrace {
original_ticks: 0,
minimized_ticks: 0,
original_faults: num_faults,
minimized_faults: essential.len(),
essential_fault_indices: essential,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_minimize_ticks_binary_search() {
let result = minimize_ticks(|ticks| ticks >= 500, 2000, 1);
assert_eq!(result.minimized_ticks, 500);
assert_eq!(result.original_ticks, 2000);
}
#[test]
fn test_minimize_ticks_no_reproduction() {
let result = minimize_ticks(|_| false, 1000, 1);
assert_eq!(result.minimized_ticks, 1000);
}
#[test]
fn test_minimize_ticks_always_reproduces() {
let result = minimize_ticks(|_| true, 1000, 1);
assert_eq!(result.minimized_ticks, 2);
let result = minimize_ticks(|_| true, 1000, 0);
assert_eq!(result.minimized_ticks, 1);
}
#[test]
fn test_minimize_faults_single_essential() {
let result = minimize_faults(10, |_| None, |active| active.contains(&5));
assert_eq!(result.minimized_faults, 1);
assert_eq!(result.essential_fault_indices, vec![5]);
}
#[test]
fn test_minimize_faults_multiple_essential() {
let result = minimize_faults(
10,
|_| None,
|active| active.contains(&2) && active.contains(&7),
);
assert_eq!(result.minimized_faults, 2);
assert!(result.essential_fault_indices.contains(&2));
assert!(result.essential_fault_indices.contains(&7));
}
#[test]
fn test_minimize_faults_with_pairs() {
let result = minimize_faults(
4,
|idx| match idx {
0 => Some(1), 1 => Some(0), _ => None,
},
|active| active.contains(&2),
);
assert!(result.essential_fault_indices.contains(&2));
assert!(!result.essential_fault_indices.contains(&0));
assert!(!result.essential_fault_indices.contains(&1));
}
#[test]
fn test_minimize_faults_all_essential() {
let result = minimize_faults(3, |_| None, |active| active.len() == 3);
assert_eq!(result.minimized_faults, 3);
}
#[test]
fn test_minimize_faults_none_essential() {
let result = minimize_faults(5, |_| None, |_| true);
assert_eq!(result.minimized_faults, 0);
assert!(result.essential_fault_indices.is_empty());
}
}