use crate::backend::native::types::NativeNodeId;
use std::time::Instant;
pub fn are_clusters_contiguous(offsets: &[(u64, u32)]) -> bool {
if offsets.len() < 2 {
return false;
}
for i in 0..offsets.len() - 1 {
let (current_offset, current_size) = offsets[i];
let (next_offset, _) = offsets[i + 1];
let expected_next = current_offset.saturating_add(current_size as u64);
if next_offset != expected_next {
return false;
}
}
true
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalPattern {
Unknown,
Linear,
Branching,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DetectorState {
Unknown,
Accumulating,
Linear,
Branching,
}
pub struct LinearDetector {
state: DetectorState,
consecutive_linear: u32,
threshold: u32,
cluster_offsets: Vec<(u64, u32)>,
chains_detected: u64,
total_chain_length: u64,
time_linear_detection_ns: u64,
time_contiguity_validation_ns: u64,
}
impl LinearDetector {
#[inline]
pub fn new() -> Self {
Self {
state: DetectorState::Unknown,
consecutive_linear: 0,
threshold: 3,
cluster_offsets: Vec::new(),
chains_detected: 0,
total_chain_length: 0,
time_linear_detection_ns: 0,
time_contiguity_validation_ns: 0,
}
}
#[inline]
pub fn with_threshold(threshold: u32) -> Self {
Self {
state: DetectorState::Unknown,
consecutive_linear: 0,
threshold,
cluster_offsets: Vec::new(),
chains_detected: 0,
total_chain_length: 0,
time_linear_detection_ns: 0,
time_contiguity_validation_ns: 0,
}
}
#[inline]
pub fn observe(&mut self, _node_id: NativeNodeId, degree: u32) -> TraversalPattern {
let start = Instant::now();
let result = match self.state {
DetectorState::Branching => {
return TraversalPattern::Branching;
}
DetectorState::Linear => {
if degree > 1 {
self.state = DetectorState::Branching;
return TraversalPattern::Branching;
}
return TraversalPattern::Linear;
}
DetectorState::Unknown | DetectorState::Accumulating => {
if degree > 1 {
self.state = DetectorState::Branching;
return TraversalPattern::Branching;
} else if degree == 1 {
self.consecutive_linear += 1;
if self.consecutive_linear >= self.threshold {
self.state = DetectorState::Linear;
return TraversalPattern::Linear;
} else {
self.state = DetectorState::Accumulating;
return TraversalPattern::Unknown;
}
}
TraversalPattern::Unknown
}
};
self.time_linear_detection_ns += start.elapsed().as_nanos() as u64;
result
}
#[inline]
pub fn observe_with_cluster(
&mut self,
node_id: NativeNodeId,
degree: u32,
cluster_offset: u64,
cluster_size: u32,
) -> TraversalPattern {
self.cluster_offsets.push((cluster_offset, cluster_size));
self.observe(node_id, degree)
}
#[inline]
pub fn cluster_offsets(&self) -> &[(u64, u32)] {
&self.cluster_offsets
}
#[inline]
pub fn record_chain(&mut self, length: u32) {
self.chains_detected += 1;
self.total_chain_length += length as u64;
}
#[inline]
pub fn confidence(&self) -> f64 {
match self.state {
DetectorState::Linear => 1.0,
DetectorState::Accumulating => {
if self.threshold > 0 {
(self.consecutive_linear as f64) / (self.threshold as f64)
} else {
0.0
}
}
DetectorState::Unknown | DetectorState::Branching => 0.0,
}
}
#[inline]
pub fn reset(&mut self) {
self.state = DetectorState::Unknown;
self.consecutive_linear = 0;
self.cluster_offsets.clear();
self.chains_detected = 0;
self.total_chain_length = 0;
self.time_linear_detection_ns = 0;
self.time_contiguity_validation_ns = 0;
}
#[inline]
pub fn current_pattern(&self) -> TraversalPattern {
match self.state {
DetectorState::Linear => TraversalPattern::Linear,
DetectorState::Branching => TraversalPattern::Branching,
DetectorState::Unknown | DetectorState::Accumulating => TraversalPattern::Unknown,
}
}
#[inline]
pub fn is_linear_confirmed(&self) -> bool {
self.state == DetectorState::Linear
}
#[inline]
pub fn chain_count(&self) -> u64 {
self.chains_detected
}
#[inline]
pub fn total_chain_length(&self) -> u64 {
self.total_chain_length
}
#[inline]
pub fn average_chain_length(&self) -> f64 {
if self.chains_detected == 0 {
0.0
} else {
self.total_chain_length as f64 / self.chains_detected as f64
}
}
#[inline]
pub fn validate_contiguity(&mut self) -> bool {
let start = Instant::now();
let result = are_clusters_contiguous(&self.cluster_offsets);
self.time_contiguity_validation_ns += start.elapsed().as_nanos() as u64;
result
}
#[inline]
pub fn time_linear_detection_ms(&self) -> f64 {
self.time_linear_detection_ns as f64 / 1_000_000.0
}
#[inline]
pub fn time_contiguity_validation_ms(&self) -> f64 {
self.time_contiguity_validation_ns as f64 / 1_000_000.0
}
#[inline]
pub fn should_use_sequential_read(&mut self) -> bool {
self.is_linear_confirmed() && {
let start = Instant::now();
let result = are_clusters_contiguous(&self.cluster_offsets);
self.time_contiguity_validation_ns += start.elapsed().as_nanos() as u64;
result
}
}
#[inline]
pub fn predicted_chain_length(&self) -> usize {
match self.state {
DetectorState::Linear => {
self.cluster_offsets
.len()
.max(self.consecutive_linear as usize)
}
DetectorState::Accumulating => {
self.consecutive_linear as usize
}
DetectorState::Unknown | DetectorState::Branching => {
0
}
}
}
#[inline]
pub fn observed_length(&self) -> usize {
self.consecutive_linear as usize
}
}
impl Default for LinearDetector {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linear_detector_new() {
let detector = LinearDetector::new();
assert_eq!(detector.confidence(), 0.0);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
}
#[test]
fn test_linear_detector_default() {
let detector = LinearDetector::default();
assert_eq!(detector.confidence(), 0.0);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_with_threshold() {
let detector = LinearDetector::with_threshold(5);
assert_eq!(detector.confidence(), 0.0);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_chain_confirms_after_three() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(2, 1), TraversalPattern::Unknown);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(3, 1), TraversalPattern::Linear);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Linear);
assert_eq!(detector.confidence(), 1.0);
}
#[test]
fn test_linear_detector_star_immediate_branching() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 3), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert_eq!(detector.confidence(), 0.0);
assert_eq!(detector.observe(2, 1), TraversalPattern::Branching);
assert_eq!(detector.observe(3, 1), TraversalPattern::Branching);
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_diamond_transitions_to_branching() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert_eq!(detector.observe(2, 2), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_linear_then_branching() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert_eq!(detector.observe(2, 1), TraversalPattern::Unknown);
assert_eq!(detector.observe(3, 1), TraversalPattern::Linear);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.observe(4, 2), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_dead_end_stays_unknown() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 0), TraversalPattern::Unknown);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
assert_eq!(detector.observe(2, 0), TraversalPattern::Unknown);
assert_eq!(detector.confidence(), 0.0);
assert_eq!(detector.observe(3, 1), TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
}
#[test]
fn test_linear_detector_reset() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 1.0);
detector.reset();
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
detector.observe(1, 1);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
}
#[test]
fn test_linear_detector_custom_threshold() {
let mut detector = LinearDetector::with_threshold(5);
detector.observe(1, 1);
assert!((detector.confidence() - 1.0 / 5.0).abs() < f64::EPSILON);
detector.observe(2, 1);
assert!((detector.confidence() - 2.0 / 5.0).abs() < f64::EPSILON);
detector.observe(3, 1);
assert!((detector.confidence() - 3.0 / 5.0).abs() < f64::EPSILON);
detector.observe(4, 1);
assert!((detector.confidence() - 4.0 / 5.0).abs() < f64::EPSILON);
assert!(!detector.is_linear_confirmed());
detector.observe(5, 1);
assert_eq!(detector.confidence(), 1.0);
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_confidence_progression() {
let mut detector = LinearDetector::new();
assert_eq!(detector.confidence(), 0.0);
detector.observe(1, 1);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
detector.observe(2, 1);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
detector.observe(3, 1);
assert_eq!(detector.confidence(), 1.0);
}
#[test]
fn test_linear_detector_current_pattern() {
let mut detector = LinearDetector::new();
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
detector.observe(1, 1);
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
detector.observe(2, 1);
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
detector.observe(3, 1);
assert_eq!(detector.current_pattern(), TraversalPattern::Linear);
detector.observe(4, 2);
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
}
#[test]
fn test_traversal_pattern_traits() {
let pattern = TraversalPattern::Linear;
let _ = pattern; let _clone = pattern; let _format = format!("{:?}", pattern); let _eq = pattern == TraversalPattern::Linear; }
#[test]
fn test_cluster_offsets_initially_empty() {
let detector = LinearDetector::new();
assert_eq!(detector.cluster_offsets().len(), 0);
assert!(detector.cluster_offsets().is_empty());
}
#[test]
fn test_cluster_offsets_single_observation() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 1);
assert_eq!(offsets[0], (1024, 4096));
}
#[test]
fn test_cluster_offsets_multiple_observations() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
detector.observe_with_cluster(4, 1, 13312, 4096);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 4);
assert_eq!(offsets[0], (1024, 4096));
assert_eq!(offsets[1], (5120, 4096));
assert_eq!(offsets[2], (9216, 4096));
assert_eq!(offsets[3], (13312, 4096));
}
#[test]
fn test_cluster_offsets_recorded_in_order() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 100, 100);
detector.observe_with_cluster(2, 1, 5000, 200);
detector.observe_with_cluster(3, 1, 10000, 150);
detector.observe_with_cluster(4, 1, 2000, 300);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 4);
assert_eq!(offsets[0], (100, 100));
assert_eq!(offsets[1], (5000, 200));
assert_eq!(offsets[2], (10000, 150));
assert_eq!(offsets[3], (2000, 300));
}
#[test]
fn test_cluster_offsets_reset_clears_history() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert_eq!(detector.cluster_offsets().len(), 3);
detector.reset();
assert_eq!(detector.cluster_offsets().len(), 0);
assert!(detector.cluster_offsets().is_empty());
}
#[test]
fn test_cluster_offsets_with_pattern_detection() {
let mut detector = LinearDetector::new();
assert_eq!(
detector.observe_with_cluster(1, 1, 1024, 4096),
TraversalPattern::Unknown
);
assert_eq!(detector.cluster_offsets().len(), 1);
assert_eq!(
detector.observe_with_cluster(2, 1, 5120, 4096),
TraversalPattern::Unknown
);
assert_eq!(detector.cluster_offsets().len(), 2);
assert_eq!(
detector.observe_with_cluster(3, 1, 9216, 4096),
TraversalPattern::Linear
);
assert_eq!(detector.cluster_offsets().len(), 3);
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_cluster_offsets_after_branching() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert_eq!(
detector.observe_with_cluster(4, 2, 13312, 8192),
TraversalPattern::Branching
);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 4);
assert_eq!(offsets[3], (13312, 8192));
}
#[test]
fn test_cluster_offsets_different_sizes() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 0, 100);
detector.observe_with_cluster(2, 1, 100, 200);
detector.observe_with_cluster(3, 1, 300, 150);
detector.observe_with_cluster(4, 1, 450, 4000);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 4);
assert_eq!(offsets[0].1, 100);
assert_eq!(offsets[1].1, 200);
assert_eq!(offsets[2].1, 150);
assert_eq!(offsets[3].1, 4000);
}
#[test]
fn test_cluster_offsets_empty_using_observe() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert_eq!(detector.cluster_offsets().len(), 0);
}
#[test]
fn test_cluster_offsets_mixed_observe_methods() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe(3, 1);
detector.observe_with_cluster(4, 1, 13312, 4096);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 2);
assert_eq!(offsets[0], (5120, 4096));
assert_eq!(offsets[1], (13312, 4096));
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_cluster_offsets_large_offsets() {
let mut detector = LinearDetector::new();
let large_offset: u64 = 4_000_000_000;
let large_size: u32 = 4096;
detector.observe_with_cluster(1, 1, large_offset, large_size);
let offsets = detector.cluster_offsets();
assert_eq!(offsets.len(), 1);
assert_eq!(offsets[0], (large_offset, large_size));
}
#[test]
fn test_are_clusters_contiguous_empty_returns_false() {
let offsets: &[(u64, u32)] = &[];
assert!(!are_clusters_contiguous(offsets));
}
#[test]
fn test_are_clusters_contiguous_single_returns_false() {
let offsets = [(1024, 4096)];
assert!(!are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_two_contiguous_returns_true() {
let offsets = [(1024, 4096), (5120, 4096)];
assert!(are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_multiple_contiguous_returns_true() {
let offsets = [(1024, 4096), (5120, 4096), (9216, 4096), (13312, 4096)];
assert!(are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_gap_returns_false() {
let offsets = [(1024, 4096), (5120, 4096), (10000, 4096)];
assert!(!are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_overlap_returns_false() {
let offsets = [(1024, 4096), (4000, 4096)];
assert!(!are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_different_sizes() {
let offsets = [(0, 100), (100, 200), (300, 150)];
assert!(are_clusters_contiguous(&offsets));
}
#[test]
fn test_are_clusters_contiguous_non_contiguous_different_sizes() {
let offsets = [(0, 100), (150, 200)];
assert!(!are_clusters_contiguous(&offsets));
}
#[test]
fn test_validate_contiguity_empty_returns_false() {
let mut detector = LinearDetector::new();
assert!(!detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_single_cluster_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
assert!(!detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_contiguous_returns_true() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
assert!(detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_gap_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 10000, 4096); assert!(!detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_overlap_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 4000, 4096); assert!(!detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_after_reset_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
assert!(detector.validate_contiguity());
detector.reset();
assert!(!detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_with_branching() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert!(detector.validate_contiguity());
detector.observe_with_cluster(4, 2, 13312, 8192);
assert!(detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_variable_sizes() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 0, 100);
detector.observe_with_cluster(2, 1, 100, 200);
detector.observe_with_cluster(3, 1, 300, 150);
detector.observe_with_cluster(4, 1, 450, 4000);
assert!(detector.validate_contiguity());
}
#[test]
fn test_validate_contiguity_large_offsets() {
let mut detector = LinearDetector::new();
let large_offset: u64 = 4_000_000_000;
let large_size: u32 = 4096;
let next_offset = large_offset + large_size as u64;
detector.observe_with_cluster(1, 1, large_offset, large_size);
detector.observe_with_cluster(2, 1, next_offset, large_size);
assert!(detector.validate_contiguity());
}
#[test]
fn test_chain_instrumentation_initial_state() {
let detector = LinearDetector::new();
assert_eq!(detector.chain_count(), 0);
assert_eq!(detector.total_chain_length(), 0);
assert_eq!(detector.average_chain_length(), 0.0);
}
#[test]
fn test_chain_instrumentation_single_chain() {
let mut detector = LinearDetector::new();
detector.record_chain(10);
assert_eq!(detector.chain_count(), 1);
assert_eq!(detector.total_chain_length(), 10);
assert_eq!(detector.average_chain_length(), 10.0);
}
#[test]
fn test_chain_instrumentation_multiple_chains() {
let mut detector = LinearDetector::new();
detector.record_chain(10);
detector.record_chain(20);
detector.record_chain(30);
assert_eq!(detector.chain_count(), 3);
assert_eq!(detector.total_chain_length(), 60);
assert_eq!(detector.average_chain_length(), 20.0);
}
#[test]
fn test_chain_instrumentation_average_calculation() {
let mut detector = LinearDetector::new();
detector.record_chain(5);
assert_eq!(detector.average_chain_length(), 5.0);
detector.record_chain(15);
assert_eq!(detector.average_chain_length(), 10.0);
detector.record_chain(10);
assert_eq!(detector.average_chain_length(), 10.0); }
#[test]
fn test_chain_instrumentation_accumulation() {
let mut detector = LinearDetector::new();
let mut total = 0u64;
for i in 1u32..=10 {
detector.record_chain(i * 5);
total += (i * 5) as u64;
assert_eq!(detector.chain_count(), i as u64);
assert_eq!(detector.total_chain_length(), total);
}
assert_eq!(detector.chain_count(), 10);
assert_eq!(detector.total_chain_length(), 275);
assert!((detector.average_chain_length() - 27.5).abs() < f64::EPSILON);
}
#[test]
fn test_chain_instrumentation_zero_length_chain() {
let mut detector = LinearDetector::new();
detector.record_chain(0);
assert_eq!(detector.chain_count(), 1);
assert_eq!(detector.total_chain_length(), 0);
assert_eq!(detector.average_chain_length(), 0.0);
}
#[test]
fn test_chain_instrumentation_large_chain() {
let mut detector = LinearDetector::new();
let large_length: u32 = 1_000_000;
detector.record_chain(large_length);
assert_eq!(detector.chain_count(), 1);
assert_eq!(detector.total_chain_length(), 1_000_000);
assert_eq!(detector.average_chain_length(), 1_000_000.0);
}
#[test]
fn test_chain_instrumentation_reset_clears_metrics() {
let mut detector = LinearDetector::new();
detector.record_chain(10);
detector.record_chain(20);
detector.record_chain(30);
assert_eq!(detector.chain_count(), 3);
assert_eq!(detector.total_chain_length(), 60);
assert_eq!(detector.average_chain_length(), 20.0);
detector.reset();
assert_eq!(detector.chain_count(), 0);
assert_eq!(detector.total_chain_length(), 0);
assert_eq!(detector.average_chain_length(), 0.0);
}
#[test]
fn test_chain_instrumentation_with_pattern_detection() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert!(detector.is_linear_confirmed());
detector.record_chain(3);
assert_eq!(detector.chain_count(), 1);
assert_eq!(detector.total_chain_length(), 3);
assert_eq!(detector.average_chain_length(), 3.0);
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_chain_instrumentation_with_threshold() {
let mut detector = LinearDetector::with_threshold(5);
detector.record_chain(7);
detector.record_chain(13);
assert_eq!(detector.chain_count(), 2);
assert_eq!(detector.total_chain_length(), 20);
assert_eq!(detector.average_chain_length(), 10.0);
}
#[test]
fn test_should_use_sequential_read_returns_false_before_threshold() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
assert!(!detector.should_use_sequential_read());
detector.observe_with_cluster(2, 1, 5120, 4096);
assert!(!detector.should_use_sequential_read());
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_should_use_sequential_read_returns_false_for_non_contiguous() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 10000, 4096);
assert!(detector.is_linear_confirmed());
assert!(!detector.validate_contiguity());
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_returns_true_for_linear_and_contiguous() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
assert!(detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_returns_false_for_branching() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 2, 1024, 4096);
assert!(!detector.is_linear_confirmed());
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_linear_then_branching() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert!(detector.should_use_sequential_read());
detector.observe_with_cluster(4, 2, 13312, 8192);
assert!(!detector.should_use_sequential_read());
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_should_use_sequential_read_single_cluster_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
assert!(!detector.should_use_sequential_read());
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert!(detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_reset_clears_state() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 1024, 4096);
detector.observe_with_cluster(2, 1, 5120, 4096);
detector.observe_with_cluster(3, 1, 9216, 4096);
assert!(detector.should_use_sequential_read());
detector.reset();
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_with_custom_threshold() {
let mut detector = LinearDetector::with_threshold(5);
for i in 0..5 {
let offset = (i as u64) * 4096;
detector.observe_with_cluster(i, 1, offset, 4096);
}
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
assert!(detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_variable_cluster_sizes() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 0, 100);
detector.observe_with_cluster(2, 1, 100, 200);
detector.observe_with_cluster(3, 1, 300, 150);
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
assert!(detector.should_use_sequential_read());
}
#[test]
fn test_should_use_sequential_read_dead_end_returns_false() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 0, 1024, 4096);
assert!(!detector.should_use_sequential_read());
detector.observe_with_cluster(2, 0, 5120, 4096);
assert!(!detector.should_use_sequential_read());
assert!(!detector.is_linear_confirmed());
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_chain_detection_on_linear_graph() {
let mut detector = LinearDetector::new();
let cluster_size = 4096u64;
let mut current_offset = 0u64;
for node_id in 1..=100 {
detector.observe_with_cluster(node_id, 1, current_offset, cluster_size as u32);
current_offset += cluster_size;
}
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
assert!(detector.should_use_sequential_read());
assert_eq!(detector.cluster_offsets().len(), 100);
assert_eq!(detector.cluster_offsets()[0], (0, 4096));
assert_eq!(detector.cluster_offsets()[99], (99 * 4096, 4096));
}
#[test]
fn test_no_false_positive_on_tree() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 2, 0, 4096);
assert!(!detector.is_linear_confirmed());
assert!(!detector.should_use_sequential_read());
detector.observe_with_cluster(2, 3, 4096, 4096);
detector.observe_with_cluster(3, 3, 8192, 4096);
detector.observe_with_cluster(4, 1, 12288, 4096);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_no_false_positive_on_diamond() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 2, 0, 4096);
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert!(!detector.should_use_sequential_read());
detector.observe_with_cluster(2, 2, 4096, 4096); detector.observe_with_cluster(3, 2, 8192, 4096); detector.observe_with_cluster(4, 2, 12288, 4096);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_mixed_pattern_detection() {
let mut detector = LinearDetector::new();
for i in 1..=5 {
let offset = ((i - 1) * 4096) as u64;
detector.observe_with_cluster(i, 1, offset, 4096);
}
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
assert!(detector.should_use_sequential_read());
detector.observe_with_cluster(6, 2, 5 * 4096, 4096);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_non_contiguous_linear_chain() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 0, 4096);
detector.observe_with_cluster(2, 1, 4096, 4096);
detector.observe_with_cluster(3, 1, 8192, 4096);
assert!(detector.is_linear_confirmed());
assert!(detector.validate_contiguity());
detector.observe_with_cluster(4, 1, 20000, 4096);
assert!(detector.is_linear_confirmed());
assert!(!detector.validate_contiguity());
assert!(!detector.should_use_sequential_read());
}
#[test]
fn test_predicted_chain_length_initial_state() {
let detector = LinearDetector::new();
assert_eq!(detector.predicted_chain_length(), 0);
assert_eq!(detector.observed_length(), 0);
}
#[test]
fn test_predicted_chain_length_accumulating() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
assert_eq!(detector.predicted_chain_length(), 1);
detector.observe(2, 1);
assert_eq!(detector.predicted_chain_length(), 2);
}
#[test]
fn test_predicted_chain_length_linear_confirmed() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.predicted_chain_length(), 3);
}
#[test]
fn test_predicted_chain_length_with_clusters() {
let mut detector = LinearDetector::new();
detector.observe_with_cluster(1, 1, 0, 4096);
detector.observe_with_cluster(2, 1, 4096, 4096);
detector.observe_with_cluster(3, 1, 8192, 4096);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.predicted_chain_length(), 3);
assert_eq!(detector.cluster_offsets().len(), 3);
}
#[test]
fn test_predicted_chain_length_branching() {
let mut detector = LinearDetector::new();
detector.observe(1, 2);
assert_eq!(detector.predicted_chain_length(), 0);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_predicted_chain_length_dead_end() {
let mut detector = LinearDetector::new();
detector.observe(1, 0);
assert_eq!(detector.predicted_chain_length(), 0);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_predicted_chain_length_long_chain() {
let mut detector = LinearDetector::new();
for i in 0..15 {
let offset = (i * 4096) as u64;
detector.observe_with_cluster(i, 1, offset, 4096);
}
assert_eq!(detector.predicted_chain_length(), 15);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.cluster_offsets().len(), 15);
}
#[test]
fn test_predicted_chain_length_long_chain_with_observe_only() {
let mut detector = LinearDetector::new();
for i in 0..15 {
detector.observe(i, 1);
}
assert_eq!(detector.predicted_chain_length(), 3); assert!(detector.is_linear_confirmed());
assert_eq!(detector.cluster_offsets().len(), 0); }
#[test]
fn test_predicted_chain_length_linear_then_branching() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert_eq!(detector.predicted_chain_length(), 3);
detector.observe(4, 2);
assert_eq!(detector.predicted_chain_length(), 0);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_predicted_chain_length_reset() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
assert_eq!(detector.predicted_chain_length(), 2);
detector.reset();
assert_eq!(detector.predicted_chain_length(), 0);
}
#[test]
fn test_predicted_chain_length_with_custom_threshold() {
let mut detector = LinearDetector::with_threshold(5);
for i in 0..3 {
detector.observe(i, 1);
}
assert_eq!(detector.predicted_chain_length(), 3);
assert!(!detector.is_linear_confirmed());
detector.observe(3, 1);
detector.observe(4, 1);
assert!(detector.is_linear_confirmed());
assert_eq!(detector.predicted_chain_length(), 5);
}
#[test]
fn test_observed_length_helper() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observed_length(), 0);
detector.observe(1, 1);
assert_eq!(detector.observed_length(), 1);
detector.observe(2, 1);
assert_eq!(detector.observed_length(), 2);
detector.observe(3, 1);
assert_eq!(detector.observed_length(), 3);
detector.observe(4, 2);
assert_eq!(detector.observed_length(), 3);
assert!(!detector.is_linear_confirmed());
}
}