use ruvix_types::CoherenceMeta;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct CoherenceConfig {
pub min_coherence_threshold: u16,
pub enable_scheduler_hints: bool,
pub track_deltas: bool,
pub decay_rate: u16,
pub initial_coherence: u16,
}
impl Default for CoherenceConfig {
fn default() -> Self {
Self {
min_coherence_threshold: 5000, enable_scheduler_hints: true,
track_deltas: false,
decay_rate: 0,
initial_coherence: 10000, }
}
}
impl CoherenceConfig {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
min_coherence_threshold: 5000,
enable_scheduler_hints: true,
track_deltas: false,
decay_rate: 0,
initial_coherence: 10000,
}
}
#[inline]
#[must_use]
pub const fn with_min_threshold(mut self, threshold: f32) -> Self {
self.min_coherence_threshold = (threshold.clamp(0.0, 1.0) * 10000.0) as u16;
self
}
#[inline]
#[must_use]
pub const fn with_scheduler_hints(mut self, enabled: bool) -> Self {
self.enable_scheduler_hints = enabled;
self
}
#[inline]
#[must_use]
pub const fn with_delta_tracking(mut self, enabled: bool) -> Self {
self.track_deltas = enabled;
self
}
#[inline]
#[must_use]
pub const fn with_decay_rate(mut self, rate: f32) -> Self {
self.decay_rate = (rate.clamp(0.0, 1.0) * 10000.0) as u16;
self
}
#[inline]
#[must_use]
pub fn min_threshold_f32(&self) -> f32 {
self.min_coherence_threshold as f32 / 10000.0
}
}
#[derive(Debug, Clone)]
pub struct CoherenceTracker {
config: CoherenceConfig,
current_epoch: u64,
average_coherence: u16,
entry_count: u32,
coherence_sum: u64,
low_coherence_mutations: u32,
}
impl CoherenceTracker {
#[inline]
#[must_use]
pub const fn new(config: CoherenceConfig) -> Self {
Self {
config,
current_epoch: 0,
average_coherence: 10000,
entry_count: 0,
coherence_sum: 0,
low_coherence_mutations: 0,
}
}
#[inline]
#[must_use]
pub const fn current_epoch(&self) -> u64 {
self.current_epoch
}
#[inline]
pub fn advance_epoch(&mut self) -> u64 {
self.current_epoch = self.current_epoch.wrapping_add(1);
self.current_epoch
}
#[inline]
#[must_use]
pub const fn config(&self) -> &CoherenceConfig {
&self.config
}
#[inline]
#[must_use]
pub fn create_initial_meta(&mut self, proof_attestation_hash: [u8; 32]) -> CoherenceMeta {
CoherenceMeta::new(
self.config.initial_coherence,
self.advance_epoch(),
proof_attestation_hash,
)
}
pub fn on_entry_added(&mut self, coherence_score: u16) {
self.entry_count = self.entry_count.saturating_add(1);
self.coherence_sum = self.coherence_sum.saturating_add(coherence_score as u64);
self.update_average();
}
pub fn on_entry_removed(&mut self, coherence_score: u16) {
self.entry_count = self.entry_count.saturating_sub(1);
self.coherence_sum = self.coherence_sum.saturating_sub(coherence_score as u64);
self.update_average();
}
pub fn on_entry_mutated(
&mut self,
old_meta: &CoherenceMeta,
new_coherence: u16,
proof_attestation_hash: [u8; 32],
) -> CoherenceMeta {
self.coherence_sum = self
.coherence_sum
.saturating_sub(old_meta.coherence_score as u64)
.saturating_add(new_coherence as u64);
self.update_average();
if new_coherence < self.config.min_coherence_threshold {
self.low_coherence_mutations = self.low_coherence_mutations.saturating_add(1);
}
CoherenceMeta::new(new_coherence, self.advance_epoch(), proof_attestation_hash)
}
#[inline]
#[must_use]
pub fn would_violate_threshold(&self, proposed_coherence: u16) -> bool {
proposed_coherence < self.config.min_coherence_threshold
}
#[inline]
#[must_use]
pub fn average_coherence_f32(&self) -> f32 {
self.average_coherence as f32 / 10000.0
}
#[inline]
#[must_use]
pub const fn entry_count(&self) -> u32 {
self.entry_count
}
#[inline]
#[must_use]
pub const fn low_coherence_mutations(&self) -> u32 {
self.low_coherence_mutations
}
fn update_average(&mut self) {
if self.entry_count > 0 {
self.average_coherence = (self.coherence_sum / self.entry_count as u64) as u16;
} else {
self.average_coherence = self.config.initial_coherence;
}
}
pub fn apply_decay(&mut self, epochs_elapsed: u64) {
if self.config.decay_rate == 0 || epochs_elapsed == 0 {
return;
}
let decay_amount =
((self.config.decay_rate as u64) * epochs_elapsed).min(self.average_coherence as u64);
self.average_coherence = self.average_coherence.saturating_sub(decay_amount as u16);
if self.entry_count > 0 {
let sum_decay = decay_amount * self.entry_count as u64;
self.coherence_sum = self.coherence_sum.saturating_sub(sum_decay);
}
}
}
impl Default for CoherenceTracker {
fn default() -> Self {
Self::new(CoherenceConfig::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coherence_config_defaults() {
let config = CoherenceConfig::default();
assert_eq!(config.initial_coherence, 10000);
assert_eq!(config.min_coherence_threshold, 5000);
assert!(config.enable_scheduler_hints);
}
#[test]
fn test_coherence_config_builder() {
let config = CoherenceConfig::new()
.with_min_threshold(0.7)
.with_decay_rate(0.01)
.with_delta_tracking(true);
assert_eq!(config.min_coherence_threshold, 7000);
assert_eq!(config.decay_rate, 100);
assert!(config.track_deltas);
}
#[test]
fn test_coherence_tracker_epoch() {
let mut tracker = CoherenceTracker::default();
assert_eq!(tracker.current_epoch(), 0);
let epoch1 = tracker.advance_epoch();
assert_eq!(epoch1, 1);
let epoch2 = tracker.advance_epoch();
assert_eq!(epoch2, 2);
}
#[test]
fn test_coherence_tracker_average() {
let mut tracker = CoherenceTracker::default();
tracker.on_entry_added(10000); tracker.on_entry_added(8000); tracker.on_entry_added(6000);
assert_eq!(tracker.entry_count(), 3);
assert!((tracker.average_coherence_f32() - 0.8).abs() < 0.01);
}
#[test]
fn test_coherence_tracker_mutation() {
let mut tracker = CoherenceTracker::default();
let initial_meta = tracker.create_initial_meta([0u8; 32]);
tracker.on_entry_added(initial_meta.coherence_score);
assert_eq!(initial_meta.coherence_score, 10000);
assert_eq!(initial_meta.mutation_epoch, 1);
let new_meta = tracker.on_entry_mutated(&initial_meta, 9000, [1u8; 32]);
assert_eq!(new_meta.coherence_score, 9000);
assert_eq!(new_meta.mutation_epoch, 2); assert_eq!(new_meta.proof_attestation_hash, [1u8; 32]);
}
#[test]
fn test_coherence_threshold_violation() {
let tracker = CoherenceTracker::new(CoherenceConfig::new().with_min_threshold(0.5));
assert!(!tracker.would_violate_threshold(6000)); assert!(tracker.would_violate_threshold(4000)); }
#[test]
fn test_coherence_decay() {
let config = CoherenceConfig::new().with_decay_rate(0.1); let mut tracker = CoherenceTracker::new(config);
tracker.on_entry_added(10000);
assert_eq!(tracker.average_coherence, 10000);
tracker.apply_decay(1);
assert_eq!(tracker.average_coherence, 9000);
}
}