use crate::algorithms::astar::CfgGraphNode;
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Clone)]
pub struct SliceResult {
pub nodes: HashSet<u64>,
pub edges: Vec<(u64, u64)>,
pub direction: SliceDirection,
pub criterion: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SliceDirection {
Backward,
Forward,
}
pub fn backward_slice(nodes: &[CfgGraphNode], criterion: u64) -> SliceResult {
let mut predecessors: HashMap<u64, Vec<u64>> = HashMap::new();
let mut all_nodes: HashSet<u64> = HashSet::new();
for node in nodes {
all_nodes.insert(node.id);
for &succ in &node.successors {
predecessors.entry(succ).or_default().push(node.id);
}
}
let mut slice_nodes: HashSet<u64> = HashSet::new();
let mut worklist: VecDeque<u64> = VecDeque::new();
if all_nodes.contains(&criterion) {
slice_nodes.insert(criterion);
worklist.push_back(criterion);
}
while let Some(node_id) = worklist.pop_front() {
if let Some(preds) = predecessors.get(&node_id) {
for &pred in preds {
if !slice_nodes.contains(&pred) {
slice_nodes.insert(pred);
worklist.push_back(pred);
}
}
}
}
let mut edges = Vec::new();
for node in nodes {
if slice_nodes.contains(&node.id) {
for &succ in &node.successors {
if slice_nodes.contains(&succ) {
edges.push((node.id, succ));
}
}
}
}
SliceResult {
nodes: slice_nodes,
edges,
direction: SliceDirection::Backward,
criterion,
}
}
pub fn forward_slice(nodes: &[CfgGraphNode], criterion: u64) -> SliceResult {
let node_map: HashMap<u64, &CfgGraphNode> = nodes.iter().map(|n| (n.id, n)).collect();
let mut slice_nodes: HashSet<u64> = HashSet::new();
let mut worklist: VecDeque<u64> = VecDeque::new();
if node_map.contains_key(&criterion) {
slice_nodes.insert(criterion);
worklist.push_back(criterion);
}
while let Some(node_id) = worklist.pop_front() {
if let Some(node) = node_map.get(&node_id) {
for &succ in &node.successors {
if !slice_nodes.contains(&succ) {
slice_nodes.insert(succ);
worklist.push_back(succ);
}
}
}
}
let mut edges = Vec::new();
for node in nodes {
if slice_nodes.contains(&node.id) {
for &succ in &node.successors {
if slice_nodes.contains(&succ) {
edges.push((node.id, succ));
}
}
}
}
SliceResult {
nodes: slice_nodes,
edges,
direction: SliceDirection::Forward,
criterion,
}
}
pub fn full_slice(nodes: &[CfgGraphNode], criterion: u64) -> SliceResult {
let backward = backward_slice(nodes, criterion);
let forward = forward_slice(nodes, criterion);
let mut nodes = backward.nodes;
nodes.extend(forward.nodes);
let mut edges = backward.edges;
edges.extend(forward.edges);
SliceResult {
nodes,
edges,
direction: SliceDirection::Backward, criterion,
}
}
pub fn slice_size(slice: &SliceResult) -> usize {
slice.nodes.len()
}
pub fn node_in_slice(node_id: u64, slice: &SliceResult) -> bool {
slice.nodes.contains(&node_id)
}
pub fn slice_coverage(slice: &SliceResult, total_nodes: usize) -> f32 {
if total_nodes == 0 {
return 0.0;
}
(slice.nodes.len() as f32) / (total_nodes as f32) * 100.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backward_slice_linear() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![2],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 3,
x: 3.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 3);
assert_eq!(slice.nodes.len(), 4);
assert!(slice.nodes.contains(&0));
assert!(slice.nodes.contains(&3));
assert_eq!(slice.direction, SliceDirection::Backward);
}
#[test]
fn test_backward_slice_partial() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![2],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 3,
x: 3.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 1);
assert_eq!(slice.nodes.len(), 2);
assert!(slice.nodes.contains(&0));
assert!(slice.nodes.contains(&1));
}
#[test]
fn test_forward_slice_linear() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![2],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 3,
x: 3.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = forward_slice(&nodes, 0);
assert_eq!(slice.nodes.len(), 4);
assert!(slice.nodes.contains(&0));
assert!(slice.nodes.contains(&3));
assert_eq!(slice.direction, SliceDirection::Forward);
}
#[test]
fn test_forward_slice_partial() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![2],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 3,
x: 3.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = forward_slice(&nodes, 2);
assert_eq!(slice.nodes.len(), 2);
assert!(slice.nodes.contains(&2));
assert!(slice.nodes.contains(&3));
}
#[test]
fn test_slice_coverage() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 1);
let coverage = slice_coverage(&slice, 2);
assert!((coverage - 100.0).abs() < 0.001);
}
#[test]
fn test_node_in_slice() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 1);
assert!(node_in_slice(0, &slice));
assert!(node_in_slice(1, &slice));
}
#[test]
fn test_backward_slice_branching() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1, 2],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![3],
},
CfgGraphNode {
id: 3,
x: 3.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 3);
assert_eq!(slice.nodes.len(), 4);
}
#[test]
fn test_slice_edges() {
let nodes = vec![
CfgGraphNode {
id: 0,
x: 0.0,
y: 0.0,
z: 0.0,
successors: vec![1],
},
CfgGraphNode {
id: 1,
x: 1.0,
y: 0.0,
z: 0.0,
successors: vec![2],
},
CfgGraphNode {
id: 2,
x: 2.0,
y: 0.0,
z: 0.0,
successors: vec![],
},
];
let slice = backward_slice(&nodes, 2);
assert_eq!(slice.edges.len(), 2);
assert!(slice.edges.contains(&(0, 1)));
assert!(slice.edges.contains(&(1, 2)));
}
}