use super::triple_ring::TripleRing;
use crate::graph::rdf::{Term, Triple, TriplePattern};
#[derive(Debug)]
pub struct RingIterator<'a> {
ring: &'a TripleRing,
pos: usize,
end: usize,
component: u8,
bound_id: Option<u32>,
rank: usize,
count: usize,
iterate_all: bool,
}
impl<'a> RingIterator<'a> {
pub fn all(ring: &'a TripleRing) -> Self {
Self {
ring,
pos: 0,
end: ring.len(),
component: 0,
bound_id: None,
rank: 0,
count: ring.len(),
iterate_all: true,
}
}
pub fn with_subject(ring: &'a TripleRing, subject: &Term) -> Self {
let (bound_id, count) = if let Some(id) = ring.dictionary().get_id(subject) {
let count = ring.count(&TriplePattern::with_subject(subject.clone()));
(Some(id), count)
} else {
(None, 0)
};
Self {
ring,
pos: 0,
end: ring.len(),
component: 0,
bound_id,
rank: 0,
count,
iterate_all: false,
}
}
pub fn with_predicate(ring: &'a TripleRing, predicate: &Term) -> Self {
let (bound_id, count) = if let Some(id) = ring.dictionary().get_id(predicate) {
let count = ring.count(&TriplePattern::with_predicate(predicate.clone()));
(Some(id), count)
} else {
(None, 0)
};
Self {
ring,
pos: 0,
end: ring.len(),
component: 1,
bound_id,
rank: 0,
count,
iterate_all: false,
}
}
pub fn with_object(ring: &'a TripleRing, object: &Term) -> Self {
let (bound_id, count) = if let Some(id) = ring.dictionary().get_id(object) {
let count = ring.count(&TriplePattern::with_object(object.clone()));
(Some(id), count)
} else {
(None, 0)
};
Self {
ring,
pos: 0,
end: ring.len(),
component: 2,
bound_id,
rank: 0,
count,
iterate_all: false,
}
}
#[must_use]
pub fn position(&self) -> usize {
self.pos
}
#[must_use]
pub fn has_next(&self) -> bool {
if self.iterate_all {
self.pos < self.end
} else if self.bound_id.is_some() {
self.rank < self.count
} else {
false
}
}
#[must_use]
pub fn current_term_id(&self, component: u8) -> Option<u32> {
if self.pos >= self.end {
return None;
}
let wt = match component {
0 => self.ring.subjects_wt(),
1 => self.ring.predicates_wt(),
_ => self.ring.objects_wt(),
};
Some(wt.access(self.pos) as u32)
}
pub fn seek_term(&mut self, component: u8, target_id: u32) -> bool {
while self.pos < self.end {
if let Some(current_id) = self.current_term_id(component)
&& current_id >= target_id
{
return true;
}
if self.iterate_all {
self.pos += 1;
} else if self.bound_id.is_some() {
self.rank += 1;
if !self.has_next() {
return false;
}
let wt = match self.component {
0 => self.ring.subjects_wt(),
1 => self.ring.predicates_wt(),
_ => self.ring.objects_wt(),
};
if let Some(next_pos) = wt.select(
self.bound_id
.expect("bound_id confirmed Some by outer check")
as u64,
self.rank,
) {
self.pos = next_pos;
} else {
return false;
}
} else {
return false;
}
}
false
}
pub fn seek(&mut self, target: usize) {
if self.iterate_all {
self.pos = target.min(self.end);
} else if self.bound_id.is_some() {
while self.has_next() {
let wt = match self.component {
0 => self.ring.subjects_wt(),
1 => self.ring.predicates_wt(),
_ => self.ring.objects_wt(),
};
if let Some(next_pos) = wt.select(
self.bound_id
.expect("bound_id confirmed Some by outer check")
as u64,
self.rank,
) {
if next_pos >= target {
self.pos = next_pos;
return;
}
self.rank += 1;
} else {
break;
}
}
self.pos = self.end;
}
}
}
impl Iterator for RingIterator<'_> {
type Item = Triple;
fn next(&mut self) -> Option<Self::Item> {
if !self.has_next() {
return None;
}
let pos = if self.iterate_all {
let p = self.pos;
self.pos += 1;
p
} else if let Some(id) = self.bound_id {
let wt = match self.component {
0 => self.ring.subjects_wt(),
1 => self.ring.predicates_wt(),
_ => self.ring.objects_wt(),
};
let next_pos = wt.select(id as u64, self.rank)?;
self.rank += 1;
self.pos = next_pos + 1;
next_pos
} else {
return None;
};
self.ring.get_spo(pos)
}
}
#[derive(Debug, Clone)]
pub struct AnnotatedPattern {
pub pattern: TriplePattern,
pub subject_var: Option<String>,
pub predicate_var: Option<String>,
pub object_var: Option<String>,
}
impl AnnotatedPattern {
fn var_at(&self, component: u8) -> Option<&str> {
match component {
0 => self.subject_var.as_deref(),
1 => self.predicate_var.as_deref(),
2 => self.object_var.as_deref(),
_ => None,
}
}
}
pub struct LeapfrogRing<'a> {
ring: &'a TripleRing,
patterns: Vec<TriplePattern>,
annotated: Option<Vec<AnnotatedPattern>>,
results: Vec<Vec<Triple>>,
result_idx: usize,
exhausted: bool,
}
impl<'a> LeapfrogRing<'a> {
pub fn new(ring: &'a TripleRing, patterns: Vec<TriplePattern>) -> Self {
let exhausted = patterns.is_empty() || ring.is_empty();
Self {
ring,
patterns,
annotated: None,
results: Vec::new(),
result_idx: 0,
exhausted,
}
}
pub fn with_variables(ring: &'a TripleRing, annotated: Vec<AnnotatedPattern>) -> Self {
let exhausted = annotated.is_empty() || ring.is_empty();
let patterns = annotated.iter().map(|a| a.pattern.clone()).collect();
Self {
ring,
patterns,
annotated: Some(annotated),
results: Vec::new(),
result_idx: 0,
exhausted,
}
}
#[must_use]
pub fn patterns(&self) -> &[TriplePattern] {
&self.patterns
}
#[must_use]
pub fn is_exhausted(&self) -> bool {
self.exhausted
}
fn materialize_results(&mut self) {
use std::collections::HashMap;
if self.patterns.is_empty() {
return;
}
let annotated = match &self.annotated {
Some(a) => a.clone(),
None => {
self.materialize_simple();
return;
}
};
let first_triples: Vec<Triple> = self.ring.find(&annotated[0].pattern).collect();
for first_triple in &first_triples {
let mut bindings: HashMap<String, Term> = HashMap::new();
Self::bind_triple(&annotated[0], first_triple, &mut bindings);
let mut matched = vec![first_triple.clone()];
let mut all_match = true;
for ann_pattern in &annotated[1..] {
let refined = Self::refine_pattern(ann_pattern, &bindings);
let mut found_match = false;
for triple in self.ring.find(&refined) {
if Self::is_consistent(ann_pattern, &triple, &bindings) {
Self::bind_triple(ann_pattern, &triple, &mut bindings);
matched.push(triple);
found_match = true;
break;
}
}
if !found_match {
all_match = false;
break;
}
}
if all_match {
self.results.push(matched);
}
}
}
fn materialize_simple(&mut self) {
for triple in self.ring.find(&self.patterns[0]) {
let mut matched = vec![triple.clone()];
let mut all_match = true;
for pattern in &self.patterns[1..] {
if let Some(t) = self.ring.find(pattern).next() {
matched.push(t);
} else {
all_match = false;
break;
}
}
if all_match {
self.results.push(matched);
}
}
}
fn bind_triple(
ann: &AnnotatedPattern,
triple: &Triple,
bindings: &mut std::collections::HashMap<String, Term>,
) {
if let Some(ref var) = ann.subject_var {
bindings
.entry(var.clone())
.or_insert_with(|| triple.subject().clone());
}
if let Some(ref var) = ann.predicate_var {
bindings
.entry(var.clone())
.or_insert_with(|| triple.predicate().clone());
}
if let Some(ref var) = ann.object_var {
bindings
.entry(var.clone())
.or_insert_with(|| triple.object().clone());
}
}
fn refine_pattern(
ann: &AnnotatedPattern,
bindings: &std::collections::HashMap<String, Term>,
) -> TriplePattern {
let mut refined = ann.pattern.clone();
if let Some(ref var) = ann.subject_var
&& let Some(term) = bindings.get(var)
{
refined.subject = Some(term.clone());
}
if let Some(ref var) = ann.predicate_var
&& let Some(term) = bindings.get(var)
{
refined.predicate = Some(term.clone());
}
if let Some(ref var) = ann.object_var
&& let Some(term) = bindings.get(var)
{
refined.object = Some(term.clone());
}
refined
}
fn is_consistent(
ann: &AnnotatedPattern,
triple: &Triple,
bindings: &std::collections::HashMap<String, Term>,
) -> bool {
for (component, term) in [
(0u8, triple.subject()),
(1, triple.predicate()),
(2, triple.object()),
] {
if let Some(var_name) = ann.var_at(component)
&& let Some(bound_term) = bindings.get(var_name)
&& bound_term != term
{
return false;
}
}
true
}
}
impl Iterator for LeapfrogRing<'_> {
type Item = Vec<Triple>;
fn next(&mut self) -> Option<Self::Item> {
if self.exhausted {
return None;
}
if self.result_idx == 0 && self.results.is_empty() {
self.materialize_results();
}
if self.result_idx < self.results.len() {
let result = self.results[self.result_idx].clone();
self.result_idx += 1;
Some(result)
} else {
self.exhausted = true;
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_triple(s: &str, p: &str, o: &str) -> Triple {
Triple::new(Term::iri(s), Term::iri(p), Term::iri(o))
}
#[test]
fn test_ring_iterator_all() {
let triples = vec![make_triple("s1", "p1", "o1"), make_triple("s2", "p2", "o2")];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::all(&ring);
let results: Vec<Triple> = iter.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn test_ring_iterator_with_subject() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("alix", "knows", "harm"),
make_triple("gus", "knows", "harm"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::with_subject(&ring, &Term::iri("alix"));
let results: Vec<Triple> = iter.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn test_ring_iterator_with_predicate() {
let triples = vec![
make_triple("s1", "type", "Person"),
make_triple("s2", "type", "Place"),
make_triple("s1", "name", "Alix"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::with_predicate(&ring, &Term::iri("type"));
let results: Vec<Triple> = iter.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn test_ring_iterator_not_found() {
let triples = vec![make_triple("s1", "p1", "o1")];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::with_subject(&ring, &Term::iri("nonexistent"));
let results: Vec<Triple> = iter.collect();
assert!(results.is_empty());
}
#[test]
fn test_leapfrog_empty() {
let ring = TripleRing::from_triples(std::iter::empty());
let lf = LeapfrogRing::new(&ring, vec![]);
assert!(lf.is_exhausted());
}
#[test]
fn test_leapfrog_single_pattern() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("gus", "knows", "harm"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let pattern = TriplePattern::with_subject(Term::iri("alix"));
let mut lf = LeapfrogRing::new(&ring, vec![pattern]);
let result = lf.next();
assert!(result.is_some());
let triples = result.unwrap();
assert_eq!(triples.len(), 1);
assert_eq!(triples[0].subject(), &Term::iri("alix"));
}
#[test]
fn test_ring_iterator_with_object() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("harm", "knows", "gus"),
make_triple("dave", "likes", "eve"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::with_object(&ring, &Term::iri("gus"));
let results: Vec<Triple> = iter.collect();
assert_eq!(results.len(), 2);
for triple in &results {
assert_eq!(triple.object(), &Term::iri("gus"));
}
}
#[test]
fn test_ring_iterator_with_object_not_found() {
let triples = vec![make_triple("s1", "p1", "o1")];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::with_object(&ring, &Term::iri("nonexistent"));
let results: Vec<Triple> = iter.collect();
assert!(results.is_empty());
}
#[test]
fn test_ring_iterator_position() {
let triples = vec![
make_triple("s1", "p1", "o1"),
make_triple("s2", "p2", "o2"),
make_triple("s3", "p3", "o3"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
assert_eq!(iter.position(), 0);
iter.next();
assert_eq!(iter.position(), 1);
iter.next();
assert_eq!(iter.position(), 2);
}
#[test]
fn test_ring_iterator_seek_iterate_all() {
let triples = vec![
make_triple("s1", "p1", "o1"),
make_triple("s2", "p2", "o2"),
make_triple("s3", "p3", "o3"),
make_triple("s4", "p4", "o4"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
assert_eq!(iter.position(), 0);
iter.seek(2);
assert_eq!(iter.position(), 2);
assert!(iter.has_next());
let remaining: Vec<Triple> = iter.collect();
assert_eq!(remaining.len(), 2);
}
#[test]
fn test_ring_iterator_seek_past_end() {
let triples = vec![make_triple("s1", "p1", "o1")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
iter.seek(100);
assert!(!iter.has_next());
assert!(iter.next().is_none());
}
#[test]
fn test_ring_iterator_seek_bound() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("harm", "knows", "dave"),
make_triple("alix", "likes", "eve"),
make_triple("frank", "knows", "alix"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::with_subject(&ring, &Term::iri("alix"));
assert!(iter.has_next());
iter.seek(1);
let results: Vec<Triple> = iter.collect();
for triple in &results {
assert_eq!(triple.subject(), &Term::iri("alix"));
}
}
#[test]
fn test_ring_iterator_seek_not_found_term() {
let triples = vec![make_triple("s1", "p1", "o1")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::with_subject(&ring, &Term::iri("nonexistent"));
iter.seek(0);
assert!(!iter.has_next());
}
#[test]
fn test_ring_iterator_has_next_empty() {
let ring = TripleRing::from_triples(std::iter::empty());
let iter = RingIterator::all(&ring);
assert!(!iter.has_next());
}
#[test]
fn test_leapfrog_patterns_accessor() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("gus", "knows", "harm"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let pattern1 = TriplePattern::with_subject(Term::iri("alix"));
let pattern2 = TriplePattern::with_predicate(Term::iri("knows"));
let lf = LeapfrogRing::new(&ring, vec![pattern1.clone(), pattern2.clone()]);
let patterns = lf.patterns();
assert_eq!(patterns.len(), 2);
}
#[test]
fn test_leapfrog_multi_pattern() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("gus", "knows", "harm"),
make_triple("harm", "likes", "alix"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let pattern1 = TriplePattern::with_subject(Term::iri("alix"));
let pattern2 = TriplePattern::with_predicate(Term::iri("knows"));
let mut lf = LeapfrogRing::new(&ring, vec![pattern1, pattern2]);
let result = lf.next();
assert!(result.is_some());
let matched = result.unwrap();
assert_eq!(matched.len(), 2);
}
#[test]
fn test_leapfrog_no_match() {
let triples = vec![
make_triple("alix", "knows", "gus"),
make_triple("gus", "knows", "harm"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let pattern = TriplePattern::with_subject(Term::iri("nonexistent"));
let mut lf = LeapfrogRing::new(&ring, vec![pattern]);
let result = lf.next();
assert!(result.is_none());
assert!(lf.is_exhausted());
}
#[test]
fn test_leapfrog_exhausted_after_iteration() {
let triples = vec![make_triple("alix", "knows", "gus")];
let ring = TripleRing::from_triples(triples.into_iter());
let pattern = TriplePattern::with_subject(Term::iri("alix"));
let mut lf = LeapfrogRing::new(&ring, vec![pattern]);
assert!(!lf.is_exhausted());
let result = lf.next();
assert!(result.is_some());
let second_result = lf.next();
assert!(second_result.is_none());
assert!(lf.is_exhausted());
}
#[test]
fn test_leapfrog_empty_ring_with_patterns() {
let ring = TripleRing::from_triples(std::iter::empty());
let pattern = TriplePattern::with_subject(Term::iri("alix"));
let lf = LeapfrogRing::new(&ring, vec![pattern]);
assert!(lf.is_exhausted());
}
#[test]
fn test_ring_iterator_predicate_not_found() {
let triples = vec![make_triple("s1", "p1", "o1")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::with_predicate(&ring, &Term::iri("nonexistent"));
assert!(!iter.has_next());
assert!(iter.next().is_none());
}
#[test]
fn test_ring_iterator_all_single_triple() {
let triples = vec![make_triple("s", "p", "o")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
assert!(iter.has_next());
let triple = iter.next().unwrap();
assert_eq!(triple.subject(), &Term::iri("s"));
assert_eq!(triple.predicate(), &Term::iri("p"));
assert_eq!(triple.object(), &Term::iri("o"));
assert!(!iter.has_next());
assert!(iter.next().is_none());
}
#[test]
fn test_ring_iterator_seek_to_zero() {
let triples = vec![make_triple("s1", "p1", "o1"), make_triple("s2", "p2", "o2")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
iter.seek(0);
assert_eq!(iter.position(), 0);
let results: Vec<Triple> = iter.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn test_leapfrog_shared_subject() {
let triples = vec![
make_triple("alix", "knows", "bob"),
make_triple("alix", "knows", "harm"),
make_triple("dave", "knows", "bob"),
make_triple("eve", "knows", "harm"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let annotated = vec![
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: Some(Term::iri("bob")),
},
subject_var: Some("x".to_string()),
predicate_var: None,
object_var: None,
},
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: Some(Term::iri("harm")),
},
subject_var: Some("x".to_string()),
predicate_var: None,
object_var: None,
},
];
let lf = LeapfrogRing::with_variables(&ring, annotated);
let results: Vec<Vec<Triple>> = lf.collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0][0].subject(), &Term::iri("alix"));
assert_eq!(results[0][1].subject(), &Term::iri("alix"));
}
#[test]
fn test_leapfrog_triangle() {
let triples = vec![
make_triple("alix", "knows", "bob"),
make_triple("bob", "knows", "harm"),
make_triple("harm", "knows", "alix"),
make_triple("dave", "knows", "eve"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let annotated = vec![
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: None,
},
subject_var: Some("x".to_string()),
predicate_var: None,
object_var: Some("y".to_string()),
},
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: None,
},
subject_var: Some("y".to_string()),
predicate_var: None,
object_var: Some("z".to_string()),
},
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: None,
},
subject_var: Some("z".to_string()),
predicate_var: None,
object_var: Some("x".to_string()),
},
];
let lf = LeapfrogRing::with_variables(&ring, annotated);
let results: Vec<Vec<Triple>> = lf.collect();
assert_eq!(results.len(), 3, "Expected three rotations of the triangle");
assert_eq!(results[0].len(), 3);
}
#[test]
fn test_leapfrog_empty_intersection() {
let triples = vec![
make_triple("alix", "knows", "bob"),
make_triple("harm", "knows", "dave"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let annotated = vec![
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: Some(Term::iri("bob")),
},
subject_var: Some("x".to_string()),
predicate_var: None,
object_var: None,
},
AnnotatedPattern {
pattern: TriplePattern {
subject: None,
predicate: Some(Term::iri("knows")),
object: Some(Term::iri("dave")),
},
subject_var: Some("x".to_string()),
predicate_var: None,
object_var: None,
},
];
let lf = LeapfrogRing::with_variables(&ring, annotated);
let results: Vec<Vec<Triple>> = lf.collect();
assert!(results.is_empty(), "Expected no matches");
}
#[test]
fn test_ring_iterator_current_term_id() {
let triples = vec![
make_triple("alix", "knows", "bob"),
make_triple("harm", "likes", "dave"),
];
let ring = TripleRing::from_triples(triples.into_iter());
let iter = RingIterator::all(&ring);
let id = iter.current_term_id(0);
assert!(id.is_some());
}
#[test]
fn test_ring_iterator_current_term_id_past_end() {
let triples = vec![make_triple("s", "p", "o")];
let ring = TripleRing::from_triples(triples.into_iter());
let mut iter = RingIterator::all(&ring);
iter.next(); let id = iter.current_term_id(0);
assert!(id.is_none());
}
}