use super::types::WriteSet;
pub fn have_write_conflict(ws1: &WriteSet, ws2: &WriteSet) -> bool {
ws1.has_conflict_with(ws2)
}
pub struct WriteSetAnalyzer;
impl WriteSetAnalyzer {
pub fn conflict_intensity(ws1: &WriteSet, ws2: &WriteSet) -> f64 {
if !have_write_conflict(ws1, ws2) {
return 0.0;
}
let vertex_overlap = ws1.vertices.intersection(&ws2.vertices).count();
let edge_overlap = ws1.edges.intersection(&ws2.edges).count();
let max_size = ws1.size().max(ws2.size());
if max_size == 0 {
return 0.0;
}
let overlap_count = vertex_overlap + edge_overlap;
(overlap_count as f64) / (max_size as f64)
}
pub fn conflicts_on_vertex(ws1: &WriteSet, ws2: &WriteSet) -> bool {
!ws1.vertices.is_disjoint(&ws2.vertices)
}
pub fn conflicts_on_edge(ws1: &WriteSet, ws2: &WriteSet) -> bool {
!ws1.edges.is_disjoint(&ws2.edges)
}
pub fn has_local_write(writer: &WriteSet, vid: &linkrs_core::types::VertexId) -> bool {
writer.covers_vertex(vid)
}
pub fn has_local_edge_write(
writer: &WriteSet,
edge: &linkrs_core::types::EdgeIdentifier,
) -> bool {
writer.covers_edge(edge)
}
pub fn analyze_conflict(ws1: &WriteSet, ws2: &WriteSet) -> ConflictReport {
ConflictReport {
has_conflict: have_write_conflict(ws1, ws2),
vertex_conflict: Self::conflicts_on_vertex(ws1, ws2),
edge_conflict: Self::conflicts_on_edge(ws1, ws2),
intensity: Self::conflict_intensity(ws1, ws2),
}
}
}
#[derive(Debug, Clone)]
pub struct ConflictReport {
pub has_conflict: bool,
pub vertex_conflict: bool,
pub edge_conflict: bool,
pub intensity: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use linkrs_core::types::VertexId;
#[test]
fn test_have_write_conflict_no_conflict() {
let ws1 = WriteSet::new();
let ws2 = WriteSet::new();
assert!(!have_write_conflict(&ws1, &ws2));
}
#[test]
fn test_conflict_intensity_no_conflict() {
let ws1 = WriteSet::new();
let ws2 = WriteSet::new();
assert_eq!(WriteSetAnalyzer::conflict_intensity(&ws1, &ws2), 0.0);
}
#[test]
fn test_conflict_intensity_full_conflict() {
let vid = VertexId::try_from_int64(1).expect("test vertex id");
let mut ws1 = WriteSet::new();
ws1.record_vertex(vid);
let mut ws2 = WriteSet::new();
ws2.record_vertex(vid);
assert_eq!(WriteSetAnalyzer::conflict_intensity(&ws1, &ws2), 1.0);
}
#[test]
fn test_analyze_conflict_vertex() {
let vid = VertexId::try_from_int64(1).expect("test vertex id");
let mut ws1 = WriteSet::new();
ws1.record_vertex(vid);
let mut ws2 = WriteSet::new();
ws2.record_vertex(vid);
let report = WriteSetAnalyzer::analyze_conflict(&ws1, &ws2);
assert!(report.has_conflict);
assert!(report.vertex_conflict);
assert!(!report.edge_conflict);
}
#[test]
fn test_analyze_conflict_different_vertices() {
let vid1 = VertexId::try_from_int64(1).expect("test vertex id");
let vid2 = VertexId::try_from_int64(2).expect("test vertex id");
let mut ws1 = WriteSet::new();
ws1.record_vertex(vid1);
let mut ws2 = WriteSet::new();
ws2.record_vertex(vid2);
let report = WriteSetAnalyzer::analyze_conflict(&ws1, &ws2);
assert!(!report.has_conflict);
assert!(!report.vertex_conflict);
}
#[test]
fn test_shared_endpoint_not_conflict() {
let vid1 = VertexId::try_from_int64(1).expect("test vertex id");
let vid2 = VertexId::try_from_int64(2).expect("test vertex id");
let vid3 = VertexId::try_from_int64(3).expect("test vertex id");
let mut ws1 = WriteSet::new();
let edge1 = linkrs_core::types::EdgeIdentifier::new(1, vid1, 1, vid2, 1, 0);
ws1.record_edge(edge1);
let mut ws2 = WriteSet::new();
let edge2 = linkrs_core::types::EdgeIdentifier::new(1, vid1, 1, vid3, 1, 0);
ws2.record_edge(edge2);
assert!(
!ws1.has_conflict_with(&ws2),
"edges sharing a source vertex should not conflict"
);
}
#[test]
fn test_has_local_write_covers_recorded_vertices() {
let vid = VertexId::try_from_int64(7).expect("test vertex id");
let other = VertexId::try_from_int64(8).expect("test vertex id");
let mut ws = WriteSet::new();
assert!(!WriteSetAnalyzer::has_local_write(&ws, &vid));
ws.record_vertex(vid);
assert!(WriteSetAnalyzer::has_local_write(&ws, &vid));
assert!(!WriteSetAnalyzer::has_local_write(&ws, &other));
let mut deleted = WriteSet::new();
deleted.record_vertex_delete(vid);
assert!(WriteSetAnalyzer::has_local_write(&deleted, &vid));
}
#[test]
fn test_has_local_edge_write_covers_recorded_edges() {
use linkrs_core::types::EdgeIdentifier;
let vid1 = VertexId::try_from_int64(1).expect("test vertex id");
let vid2 = VertexId::try_from_int64(2).expect("test vertex id");
let vid3 = VertexId::try_from_int64(3).expect("test vertex id");
let edge = EdgeIdentifier::new(1, vid1, 1, vid2, 1, 0);
let other = EdgeIdentifier::new(1, vid1, 1, vid3, 1, 0);
let mut ws = WriteSet::new();
assert!(!WriteSetAnalyzer::has_local_edge_write(&ws, &edge));
ws.record_edge(edge);
assert!(WriteSetAnalyzer::has_local_edge_write(&ws, &edge));
assert!(!WriteSetAnalyzer::has_local_edge_write(&ws, &other));
}
}