use triblespace::prelude::*;
pub mod social {
use triblespace::prelude::*;
attributes! {
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" as follows: inlineencodings::GenId;
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" as likes: inlineencodings::GenId;
}
}
#[test]
fn simple_path() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
kb += entity! { &a @ social::follows: &b };
let a_val = a.to_inline();
let b_val = b.to_inline();
let results: Vec<_> =
find!((s: Inline<_>, e: Inline<_>), path!(kb.clone(), s social::follows e)).collect();
assert!(results.contains(&(a_val, b_val)));
}
#[test]
fn alternation() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &a @ social::likes: &c };
let a_val = a.to_inline();
let b_val = b.to_inline();
let c_val = c.to_inline();
let results: Vec<_> =
find!((s: Inline<_>, e: Inline<_>), path!(kb.clone(), s (social::follows | social::likes) e))
.collect();
assert!(results.contains(&(a_val, b_val)));
assert!(results.contains(&(a_val, c_val)));
}
#[test]
fn repetition() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &b @ social::follows: &c };
let start_val = a.to_inline();
let end_val = c.to_inline();
let results: Vec<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(start_val), e.is(end_val), path!(kb.clone(), s social::follows+ e)))
.collect();
assert!(results.contains(&(start_val, end_val)));
}
#[test]
fn inverse_prefix_single() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
kb += entity! { &a @ social::follows: &b };
let a_val = a.to_inline();
let b_val = b.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(b_val), path!(kb.clone(), s ^social::follows e)))
.map(|(_, e)| e)
.collect();
assert!(results.contains(&a_val),
"^follows from B should reach A (who follows B)");
assert_eq!(results.len(), 1, "exactly one source: {:?}", results);
}
#[test]
fn inverse_of_group() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &a @ social::likes: &c };
let a_val = a.to_inline();
let b_val = b.to_inline();
let c_val = c.to_inline();
let from_b: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(b_val), path!(kb.clone(), s ^(social::follows | social::likes) e)))
.map(|(_, e)| e)
.collect();
assert!(from_b.contains(&a_val),
"^(follows|likes) from B reaches A");
let from_c: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(c_val), path!(kb.clone(), s ^(social::follows | social::likes) e)))
.map(|(_, e)| e)
.collect();
assert!(from_c.contains(&a_val),
"^(follows|likes) from C reaches A");
}
#[test]
fn inverse_with_postfix_modifier() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &c @ social::follows: &a };
let a_val = a.to_inline();
let b_val = b.to_inline();
let c_val = c.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(b_val), path!(kb.clone(), s ^social::follows+ e)))
.map(|(_, e)| e)
.collect();
assert!(results.contains(&a_val), "^follows+ from B reaches A");
assert!(results.contains(&c_val), "^follows+ from B reaches C (via A)");
assert_eq!(results.len(), 2, "exactly two ancestors: {:?}", results);
}
#[test]
fn not_attr_single_attribute() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &a @ social::likes: &c };
let a_val = a.to_inline();
let b_val = b.to_inline();
let c_val = c.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(a_val), path!(kb.clone(), s !social::follows e)))
.map(|(_, e)| e)
.collect();
assert!(!results.contains(&b_val),
"!follows excludes the follows destination");
assert!(results.contains(&c_val),
"!follows includes the likes destination");
assert_eq!(results.len(), 1, "exactly one destination: {:?}", results);
}
#[test]
fn end_bound_propose_start_via_inverse_bfs() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
let c = fucid();
let d = fucid();
kb += entity! { &a @ social::follows: &b };
kb += entity! { &b @ social::follows: &c };
kb += entity! { &c @ social::follows: &d };
let a_val = a.to_inline();
let b_val = b.to_inline();
let c_val = c.to_inline();
let d_val = d.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(e.is(d_val), path!(kb.clone(), s social::follows+ e)))
.map(|(s, _)| s)
.collect();
assert!(results.contains(&a_val), "A should reach D (3 hops)");
assert!(results.contains(&b_val), "B should reach D (2 hops)");
assert!(results.contains(&c_val), "C should reach D (1 hop)");
assert!(!results.contains(&d_val), "Plus excludes the start when no cycle");
assert_eq!(results.len(), 3, "exactly 3 upstream starts: {:?}", results);
}
#[test]
fn end_bound_propose_start_reflexive() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
kb += entity! { &a @ social::follows: &b };
let a_val = a.to_inline();
let b_val = b.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(e.is(b_val), path!(kb.clone(), s social::follows* e)))
.map(|(s, _)| s)
.collect();
assert!(results.contains(&a_val), "A reaches B via 1 hop");
assert!(results.contains(&b_val), "B reaches itself via 0 hops (Star reflexive)");
assert_eq!(results.len(), 2, "exactly 2 starts: {:?}", results);
}
#[test]
fn optional_question_mark() {
let mut kb = TribleSet::new();
let a = fucid();
let b = fucid();
kb += entity! { &a @ social::follows: &b };
let a_val = a.to_inline();
let b_val = b.to_inline();
let results: std::collections::HashSet<_> = find!((s: Inline<_>, e: Inline<_>),
and!(s.is(a_val), path!(kb.clone(), s social::follows? e)))
.map(|(_, e)| e)
.collect();
assert!(results.contains(&a_val), "0-hop reflexive should include start");
assert!(results.contains(&b_val), "1-hop should include neighbor");
assert_eq!(results.len(), 2, "exactly 2 destinations: {:?}", results);
}