use std::ops::Index;
#[derive(Debug, Clone)]
pub struct Ring<T> {
nodes: Vec<T>,
}
impl<T> Ring<T> {
pub fn new(nodes: Vec<T>) -> Self {
assert!(!nodes.is_empty(), "Ring cannot be empty");
Self { nodes }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
nodes: Vec::with_capacity(capacity),
}
}
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
#[inline]
pub fn get(&self, idx: usize) -> &T {
&self.nodes[idx % self.len()]
}
#[inline]
pub fn get_mut(&mut self, idx: usize) -> &mut T {
let len = self.len();
&mut self.nodes[idx % len]
}
#[inline]
pub fn next(&self, idx: usize) -> usize {
(idx + 1) % self.len()
}
#[inline]
pub fn prev(&self, idx: usize) -> usize {
if idx == 0 {
self.len() - 1
} else {
idx - 1
}
}
#[inline]
pub fn ring_distance(&self, a: usize, b: usize) -> usize {
let n = self.len();
let a = a % n;
let b = b % n;
let forward = (b + n - a) % n;
let backward = (a + n - b) % n;
forward.min(backward)
}
pub fn neighbors(&self, idx: usize, radius: usize) -> impl Iterator<Item = &T> {
let n = self.len();
let idx = idx % n;
(1..=radius).flat_map(move |d| {
let prev_idx = (idx + n - d) % n;
let next_idx = (idx + d) % n;
if prev_idx == next_idx {
vec![&self.nodes[prev_idx]].into_iter()
} else {
vec![&self.nodes[prev_idx], &self.nodes[next_idx]].into_iter()
}
})
}
pub fn neighbor_indices(&self, idx: usize, radius: usize) -> Vec<usize> {
let n = self.len();
let idx = idx % n;
let mut indices = Vec::with_capacity(radius * 2);
for d in 1..=radius {
let prev_idx = (idx + n - d) % n;
let next_idx = (idx + d) % n;
if prev_idx == next_idx {
indices.push(prev_idx);
} else {
indices.push(prev_idx);
indices.push(next_idx);
}
}
indices
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.nodes.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.nodes.iter_mut()
}
pub fn iter_from(&self, start: usize) -> impl Iterator<Item = &T> {
let n = self.len();
let start = start % n;
(0..n).map(move |i| &self.nodes[(start + i) % n])
}
pub fn push(&mut self, node: T) {
self.nodes.push(node);
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.nodes
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.nodes
}
pub fn into_inner(self) -> Vec<T> {
self.nodes
}
}
impl<T> Index<usize> for Ring<T> {
type Output = T;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
self.get(idx)
}
}
impl<T> FromIterator<T> for Ring<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::new(iter.into_iter().collect())
}
}
#[derive(Debug, Clone)]
pub struct RingNode<T> {
pub value: T,
pub weight: f32,
pub forward_weight: f32,
pub backward_weight: f32,
}
impl<T> RingNode<T> {
pub fn new(value: T) -> Self {
Self {
value,
weight: 1.0,
forward_weight: 1.0,
backward_weight: 1.0,
}
}
pub fn with_weight(value: T, weight: f32) -> Self {
Self {
value,
weight,
forward_weight: 1.0,
backward_weight: 1.0,
}
}
}
pub type EpisodeRing<E> = Ring<RingNode<E>>;
pub fn build_weighted_ring<T>(episodes: Vec<T>, weights: &[f32]) -> Ring<RingNode<T>> {
assert_eq!(episodes.len(), weights.len(), "Episode and weight counts must match");
let nodes: Vec<RingNode<T>> = episodes
.into_iter()
.zip(weights.iter())
.map(|(e, &w)| RingNode::with_weight(e, w))
.collect();
Ring::new(nodes)
}
#[derive(Debug, Clone)]
pub struct DualRing<T> {
nodes: Vec<DualRingNode<T>>,
influence_order: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct DualRingNode<T> {
pub value: T,
pub temporal_idx: usize,
pub influence: f32,
pub attention_received: f32,
pub attention_given: f32,
}
impl<T> DualRingNode<T> {
pub fn new(temporal_idx: usize, value: T, influence: f32) -> Self {
Self {
value,
temporal_idx,
influence,
attention_received: 0.0,
attention_given: 0.0,
}
}
pub fn with_attention(
temporal_idx: usize,
value: T,
influence: f32,
attention_received: f32,
attention_given: f32,
) -> Self {
Self {
value,
temporal_idx,
influence,
attention_received,
attention_given,
}
}
}
impl<T> DualRing<T> {
pub fn new(nodes: Vec<DualRingNode<T>>) -> Self {
assert!(!nodes.is_empty(), "DualRing cannot be empty");
let mut influence_order: Vec<usize> = (0..nodes.len()).collect();
influence_order.sort_by(|&a, &b| {
nodes[b].influence.partial_cmp(&nodes[a].influence)
.unwrap_or(std::cmp::Ordering::Equal)
});
Self { nodes, influence_order }
}
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
#[inline]
pub fn get_temporal(&self, idx: usize) -> &DualRingNode<T> {
&self.nodes[idx % self.len()]
}
#[inline]
pub fn temporal_next(&self, idx: usize) -> usize {
(idx + 1) % self.len()
}
#[inline]
pub fn temporal_prev(&self, idx: usize) -> usize {
if idx == 0 { self.len() - 1 } else { idx - 1 }
}
#[inline]
pub fn temporal_distance(&self, a: usize, b: usize) -> usize {
let n = self.len();
let a = a % n;
let b = b % n;
let forward = (b + n - a) % n;
let backward = (a + n - b) % n;
forward.min(backward)
}
pub fn iter_temporal(&self) -> impl Iterator<Item = &DualRingNode<T>> {
self.nodes.iter()
}
pub fn iter_temporal_from(&self, start: usize) -> impl Iterator<Item = &DualRingNode<T>> {
let n = self.len();
let start = start % n;
(0..n).map(move |i| &self.nodes[(start + i) % n])
}
pub fn temporal_neighbors(&self, idx: usize, radius: usize) -> impl Iterator<Item = &DualRingNode<T>> {
let n = self.len();
let idx = idx % n;
(1..=radius).flat_map(move |d| {
let prev_idx = (idx + n - d) % n;
let next_idx = (idx + d) % n;
if prev_idx == next_idx {
vec![&self.nodes[prev_idx]].into_iter()
} else {
vec![&self.nodes[prev_idx], &self.nodes[next_idx]].into_iter()
}
})
}
#[inline]
pub fn get_by_influence_rank(&self, rank: usize) -> &DualRingNode<T> {
let idx = self.influence_order[rank % self.len()];
&self.nodes[idx]
}
pub fn influence_rank_of(&self, temporal_idx: usize) -> usize {
self.influence_order.iter()
.position(|&idx| idx == temporal_idx)
.unwrap_or(self.len())
}
pub fn iter_by_influence(&self) -> impl Iterator<Item = &DualRingNode<T>> {
self.influence_order.iter().map(move |&idx| &self.nodes[idx])
}
pub fn top_influential(&self, k: usize) -> impl Iterator<Item = &DualRingNode<T>> {
self.influence_order.iter()
.take(k)
.map(move |&idx| &self.nodes[idx])
}
pub fn influence_distance(&self, a_temporal: usize, b_temporal: usize) -> usize {
let a_rank = self.influence_rank_of(a_temporal);
let b_rank = self.influence_rank_of(b_temporal);
let n = self.len();
let forward = (b_rank + n - a_rank) % n;
let backward = (a_rank + n - b_rank) % n;
forward.min(backward)
}
pub fn influence_neighbors(&self, temporal_idx: usize, radius: usize) -> impl Iterator<Item = &DualRingNode<T>> {
let rank = self.influence_rank_of(temporal_idx);
let n = self.len();
(1..=radius).flat_map(move |d| {
let prev_rank = if rank >= d { rank - d } else { n - (d - rank) };
let next_rank = (rank + d) % n;
let prev_idx = self.influence_order[prev_rank];
let next_idx = self.influence_order[next_rank];
if prev_idx == next_idx {
vec![&self.nodes[prev_idx]].into_iter()
} else {
vec![&self.nodes[prev_idx], &self.nodes[next_idx]].into_iter()
}
})
}
pub fn influence_temporal_spread(&self) -> f32 {
if self.len() < 2 {
return 0.0;
}
let mut total_distance = 0.0;
let count = self.len().min(5);
for i in 0..count {
for j in (i + 1)..count {
let idx_i = self.influence_order[i];
let idx_j = self.influence_order[j];
total_distance += self.temporal_distance(idx_i, idx_j) as f32;
}
}
let pairs = (count * (count - 1)) / 2;
if pairs > 0 {
total_distance / pairs as f32
} else {
0.0
}
}
pub fn forward_attention_flow(&self) -> f32 {
self.nodes.iter()
.map(|n| n.attention_given)
.sum()
}
pub fn inverse_attention_flow(&self) -> f32 {
self.nodes.iter()
.map(|n| n.attention_received)
.sum()
}
pub fn update_influence(&mut self, temporal_idx: usize, new_influence: f32) {
let idx = temporal_idx % self.len();
self.nodes[idx].influence = new_influence;
self.influence_order.sort_by(|&a, &b| {
self.nodes[b].influence.partial_cmp(&self.nodes[a].influence)
.unwrap_or(std::cmp::Ordering::Equal)
});
}
pub fn as_slice(&self) -> &[DualRingNode<T>] {
&self.nodes
}
}
pub fn build_dual_ring<T>(
episodes: Vec<T>,
influences: &[f32],
) -> DualRing<T> {
assert_eq!(episodes.len(), influences.len(), "Episode and influence counts must match");
let nodes: Vec<DualRingNode<T>> = episodes
.into_iter()
.enumerate()
.zip(influences.iter())
.map(|((idx, value), &influence)| DualRingNode::new(idx, value, influence))
.collect();
DualRing::new(nodes)
}
pub fn build_dual_ring_with_attention<T>(
episodes: Vec<T>,
influences: &[f32],
attention_received: &[f32],
attention_given: &[f32],
) -> DualRing<T> {
assert_eq!(episodes.len(), influences.len());
assert_eq!(episodes.len(), attention_received.len());
assert_eq!(episodes.len(), attention_given.len());
let nodes: Vec<DualRingNode<T>> = episodes
.into_iter()
.enumerate()
.zip(influences.iter())
.zip(attention_received.iter())
.zip(attention_given.iter())
.map(|((((idx, value), &inf), &recv), &given)| {
DualRingNode::with_attention(idx, value, inf, recv, given)
})
.collect();
DualRing::new(nodes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_ring() {
let ring = Ring::new(vec![1, 2, 3, 4, 5]);
assert_eq!(ring.len(), 5);
assert!(!ring.is_empty());
}
#[test]
#[should_panic(expected = "Ring cannot be empty")]
fn test_empty_ring_panics() {
let _ring: Ring<i32> = Ring::new(vec![]);
}
#[test]
fn test_get_wrapping() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
assert_eq!(*ring.get(0), 0);
assert_eq!(*ring.get(4), 4);
assert_eq!(*ring.get(5), 0); assert_eq!(*ring.get(7), 2); }
#[test]
fn test_next_prev() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
assert_eq!(ring.next(0), 1);
assert_eq!(ring.next(4), 0); assert_eq!(ring.prev(0), 4); assert_eq!(ring.prev(3), 2);
}
#[test]
fn test_ring_distance() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
assert_eq!(ring.ring_distance(0, 0), 0);
assert_eq!(ring.ring_distance(0, 1), 1);
assert_eq!(ring.ring_distance(0, 4), 1);
assert_eq!(ring.ring_distance(0, 2), 2);
assert_eq!(ring.ring_distance(0, 3), 2);
assert_eq!(ring.ring_distance(1, 4), 2);
}
#[test]
fn test_neighbors() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
let neighbors: Vec<_> = ring.neighbors(0, 1).copied().collect();
assert_eq!(neighbors.len(), 2);
assert!(neighbors.contains(&4));
assert!(neighbors.contains(&1));
let neighbors: Vec<_> = ring.neighbors(0, 2).copied().collect();
assert_eq!(neighbors.len(), 4);
assert!(neighbors.contains(&3));
assert!(neighbors.contains(&4));
assert!(neighbors.contains(&1));
assert!(neighbors.contains(&2));
}
#[test]
fn test_neighbor_indices() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
let indices = ring.neighbor_indices(0, 1);
assert_eq!(indices.len(), 2);
assert!(indices.contains(&4));
assert!(indices.contains(&1));
}
#[test]
fn test_iter_from() {
let ring = Ring::new(vec![0, 1, 2, 3, 4]);
let from_2: Vec<_> = ring.iter_from(2).copied().collect();
assert_eq!(from_2, vec![2, 3, 4, 0, 1]);
let from_0: Vec<_> = ring.iter_from(0).copied().collect();
assert_eq!(from_0, vec![0, 1, 2, 3, 4]);
}
#[test]
fn test_index_operator() {
let ring = Ring::new(vec![10, 20, 30]);
assert_eq!(ring[0], 10);
assert_eq!(ring[2], 30);
assert_eq!(ring[3], 10); }
#[test]
fn test_from_iterator() {
let ring: Ring<i32> = (0..5).collect();
assert_eq!(ring.len(), 5);
assert_eq!(*ring.get(0), 0);
assert_eq!(*ring.get(4), 4);
}
#[test]
fn test_ring_node() {
let node = RingNode::new(42);
assert_eq!(node.value, 42);
assert!((node.weight - 1.0).abs() < 1e-6);
let weighted = RingNode::with_weight("hello", 0.5);
assert_eq!(weighted.value, "hello");
assert!((weighted.weight - 0.5).abs() < 1e-6);
}
#[test]
fn test_build_weighted_ring() {
let episodes = vec!["a", "b", "c"];
let weights = vec![0.1, 0.5, 0.9];
let ring = build_weighted_ring(episodes, &weights);
assert_eq!(ring.len(), 3);
assert_eq!(ring.get(0).value, "a");
assert!((ring.get(0).weight - 0.1).abs() < 1e-6);
assert!((ring.get(2).weight - 0.9).abs() < 1e-6);
}
#[test]
fn test_dual_ring_creation() {
let nodes = vec![
DualRingNode::new(0, "a", 0.3),
DualRingNode::new(1, "b", 0.9),
DualRingNode::new(2, "c", 0.5),
DualRingNode::new(3, "d", 0.1),
];
let dual = DualRing::new(nodes);
assert_eq!(dual.len(), 4);
}
#[test]
fn test_dual_ring_temporal_order() {
let nodes = vec![
DualRingNode::new(0, "first", 0.3),
DualRingNode::new(1, "second", 0.9),
DualRingNode::new(2, "third", 0.5),
];
let dual = DualRing::new(nodes);
let temporal: Vec<_> = dual.iter_temporal().map(|n| n.value).collect();
assert_eq!(temporal, vec!["first", "second", "third"]);
}
#[test]
fn test_dual_ring_influence_order() {
let nodes = vec![
DualRingNode::new(0, "low", 0.1),
DualRingNode::new(1, "high", 0.9),
DualRingNode::new(2, "medium", 0.5),
];
let dual = DualRing::new(nodes);
let influence: Vec<_> = dual.iter_by_influence().map(|n| n.value).collect();
assert_eq!(influence, vec!["high", "medium", "low"]);
}
#[test]
fn test_dual_ring_top_influential() {
let nodes = vec![
DualRingNode::new(0, "a", 0.2),
DualRingNode::new(1, "b", 0.8),
DualRingNode::new(2, "c", 0.5),
DualRingNode::new(3, "d", 0.9),
DualRingNode::new(4, "e", 0.1),
];
let dual = DualRing::new(nodes);
let top2: Vec<_> = dual.top_influential(2).map(|n| n.value).collect();
assert_eq!(top2, vec!["d", "b"]); }
#[test]
fn test_dual_ring_influence_rank() {
let nodes = vec![
DualRingNode::new(0, "a", 0.1),
DualRingNode::new(1, "b", 0.9),
DualRingNode::new(2, "c", 0.5),
];
let dual = DualRing::new(nodes);
assert_eq!(dual.influence_rank_of(1), 0);
assert_eq!(dual.influence_rank_of(2), 1);
assert_eq!(dual.influence_rank_of(0), 2);
}
#[test]
fn test_dual_ring_temporal_distance() {
let nodes: Vec<DualRingNode<i32>> = (0..5)
.map(|i| DualRingNode::new(i, i as i32, 0.5))
.collect();
let dual = DualRing::new(nodes);
assert_eq!(dual.temporal_distance(0, 1), 1);
assert_eq!(dual.temporal_distance(0, 4), 1); assert_eq!(dual.temporal_distance(0, 2), 2);
}
#[test]
fn test_dual_ring_influence_distance() {
let nodes = vec![
DualRingNode::new(0, "a", 0.1), DualRingNode::new(1, "b", 0.9), DualRingNode::new(2, "c", 0.5), ];
let dual = DualRing::new(nodes);
assert_eq!(dual.influence_distance(1, 2), 1);
assert_eq!(dual.influence_distance(1, 0), 1); }
#[test]
fn test_dual_ring_temporal_neighbors() {
let nodes: Vec<DualRingNode<i32>> = (0..5)
.map(|i| DualRingNode::new(i, i as i32, 0.5))
.collect();
let dual = DualRing::new(nodes);
let neighbors: Vec<_> = dual.temporal_neighbors(0, 1).map(|n| n.value).collect();
assert_eq!(neighbors.len(), 2);
assert!(neighbors.contains(&4));
assert!(neighbors.contains(&1));
}
#[test]
fn test_dual_ring_update_influence() {
let nodes = vec![
DualRingNode::new(0, "a", 0.1),
DualRingNode::new(1, "b", 0.9),
DualRingNode::new(2, "c", 0.5),
];
let mut dual = DualRing::new(nodes);
assert_eq!(dual.get_by_influence_rank(0).value, "b");
dual.update_influence(0, 0.99);
assert_eq!(dual.get_by_influence_rank(0).value, "a");
}
#[test]
fn test_dual_ring_influence_temporal_spread() {
let clustered = vec![
DualRingNode::new(0, "a", 0.95), DualRingNode::new(1, "b", 0.90), DualRingNode::new(2, "c", 0.85), DualRingNode::new(3, "d", 0.80), DualRingNode::new(4, "e", 0.75), DualRingNode::new(5, "f", 0.10), DualRingNode::new(6, "g", 0.10), ];
let dual_clustered = DualRing::new(clustered);
let spread = vec![
DualRingNode::new(0, "a", 0.95), DualRingNode::new(1, "b", 0.10), DualRingNode::new(2, "c", 0.90), DualRingNode::new(3, "d", 0.10), DualRingNode::new(4, "e", 0.85), DualRingNode::new(5, "f", 0.10), DualRingNode::new(6, "g", 0.80), ];
let dual_spread = DualRing::new(spread);
assert!(
dual_spread.influence_temporal_spread() >= dual_clustered.influence_temporal_spread(),
"spread={}, clustered={}",
dual_spread.influence_temporal_spread(),
dual_clustered.influence_temporal_spread()
);
}
#[test]
fn test_build_dual_ring() {
let episodes = vec!["a", "b", "c"];
let influences = vec![0.3, 0.9, 0.5];
let dual = build_dual_ring(episodes, &influences);
assert_eq!(dual.len(), 3);
assert_eq!(dual.get_temporal(0).value, "a");
assert!((dual.get_temporal(1).influence - 0.9).abs() < 1e-6);
}
#[test]
fn test_build_dual_ring_with_attention() {
let episodes = vec!["a", "b"];
let influences = vec![0.5, 0.8];
let received = vec![0.3, 0.7];
let given = vec![0.4, 0.6];
let dual = build_dual_ring_with_attention(episodes, &influences, &received, &given);
assert_eq!(dual.len(), 2);
assert!((dual.get_temporal(0).attention_received - 0.3).abs() < 1e-6);
assert!((dual.get_temporal(1).attention_given - 0.6).abs() < 1e-6);
}
}