use std::collections::HashSet;
use std::sync::Arc;
use crate::types::VertexId;
use crate::vertex_edge_path::Step;
use crate::{Edge, Path, Vertex};
#[derive(Debug, Clone)]
pub struct NPath {
parent: Option<Arc<NPath>>,
vertex: Arc<Vertex>,
edge: Option<Arc<Edge>>,
length: usize,
hash: u64,
}
impl NPath {
pub fn new(vertex: Arc<Vertex>) -> Self {
let hash = Self::compute_hash(&vertex, None, None);
Self {
parent: None,
vertex,
edge: None,
length: 0,
hash,
}
}
pub fn extend(parent: Arc<NPath>, edge: Arc<Edge>, vertex: Arc<Vertex>) -> Self {
let length = parent.length + 1;
let hash = Self::compute_hash(&vertex, Some(&edge), Some(parent.hash));
Self {
parent: Some(parent),
vertex,
edge: Some(edge),
length,
hash,
}
}
pub fn extend_with_set(
parent: Arc<NPath>,
edge: Arc<Edge>,
vertex: Arc<Vertex>,
seen_vertices: &mut HashSet<VertexId>,
) -> Option<Self> {
if seen_vertices.contains(&vertex.vid) {
return None;
}
let new_path = Self::extend(parent, edge, vertex);
seen_vertices.insert(new_path.vertex.vid);
Some(new_path)
}
pub fn from_path(path: &Path) -> Arc<Self> {
let start_vertex = Arc::new((*path.src).clone());
let mut current = Arc::new(Self::new(start_vertex));
for step in &path.steps {
let edge = Arc::new((*step.edge).clone());
let vertex = Arc::new((*step.dst).clone());
current = Arc::new(Self::extend(current, edge, vertex));
}
current
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn vertex(&self) -> &Arc<Vertex> {
&self.vertex
}
pub fn edge(&self) -> Option<&Arc<Edge>> {
self.edge.as_ref()
}
pub fn parent(&self) -> Option<&Arc<NPath>> {
self.parent.as_ref()
}
pub fn start_vertex(&self) -> &Arc<Vertex> {
let mut current = self;
while let Some(ref parent) = current.parent {
current = parent;
}
¤t.vertex
}
pub fn end_vertex(&self) -> &Arc<Vertex> {
&self.vertex
}
pub fn to_path(&self) -> Path {
let mut steps = Vec::with_capacity(self.length);
let mut current = self;
while let Some(ref parent) = current.parent {
if let Some(ref edge) = current.edge {
steps.push(Step {
dst: Box::new((*current.vertex).clone()),
edge: Box::new((**edge).clone()),
});
}
current = parent;
}
steps.reverse();
Path {
src: Box::new((*current.vertex).clone()),
steps,
}
}
pub fn contains_vertex(&self, vid: &VertexId) -> bool {
if &self.vertex.vid == vid {
return true;
}
if let Some(ref parent) = self.parent {
return parent.contains_vertex(vid);
}
false
}
pub fn contains_edge(&self, edge_key: &(VertexId, VertexId, String)) -> bool {
if let Some(ref edge) = self.edge {
let key = (edge.src, edge.dst, edge.edge_type.clone());
if &key == edge_key {
return true;
}
}
if let Some(ref parent) = self.parent {
return parent.contains_edge(edge_key);
}
false
}
pub fn has_common_vertices(&self, other: &NPath) -> bool {
let self_vertices: HashSet<_> = self.iter_vertices().map(|v| &v.vid).collect();
other
.iter_vertices()
.any(|v| self_vertices.contains(&v.vid))
}
pub fn collect_vertex_ids(&self) -> Vec<VertexId> {
self.iter_vertices().map(|v| v.vid).collect()
}
pub fn collect_edges(&self) -> Vec<Arc<Edge>> {
self.iter_edges().cloned().collect()
}
pub fn iter_vertices(&self) -> NPathVertexIter<'_> {
NPathVertexIter::new(self)
}
pub fn iter_edges(&self) -> NPathEdgeIter<'_> {
NPathEdgeIter::new(self)
}
pub fn iter(&self) -> NPathIter<'_> {
NPathIter::new(self)
}
fn compute_hash(
vertex: &Arc<Vertex>,
edge: Option<&Arc<Edge>>,
parent_hash: Option<u64>,
) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
if let Some(ph) = parent_hash {
ph.hash(&mut hasher);
}
vertex.vid.hash(&mut hasher);
if let Some(e) = edge {
e.edge_type.hash(&mut hasher);
e.src.hash(&mut hasher);
e.dst.hash(&mut hasher);
}
hasher.finish()
}
pub fn hash(&self) -> u64 {
self.hash
}
}
impl PartialEq for NPath {
fn eq(&self, other: &Self) -> bool {
if self.hash != other.hash || self.length != other.length {
return false;
}
self.vertex.vid == other.vertex.vid
&& self.edge == other.edge
&& self.parent == other.parent
}
}
impl Eq for NPath {}
pub struct NPathIter<'a> {
current: Option<&'a NPath>,
}
impl<'a> NPathIter<'a> {
fn new(path: &'a NPath) -> Self {
Self {
current: Some(path),
}
}
}
impl<'a> Iterator for NPathIter<'a> {
type Item = &'a NPath;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.current?;
self.current = curr.parent.as_deref();
Some(curr)
}
}
pub struct NPathVertexIter<'a> {
current: Option<&'a NPath>,
}
impl<'a> NPathVertexIter<'a> {
fn new(path: &'a NPath) -> Self {
Self {
current: Some(path),
}
}
}
impl<'a> Iterator for NPathVertexIter<'a> {
type Item = &'a Arc<Vertex>;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.current?;
let vertex = &curr.vertex;
self.current = curr.parent.as_deref();
Some(vertex)
}
}
pub struct NPathEdgeIter<'a> {
current: Option<&'a NPath>,
}
impl<'a> NPathEdgeIter<'a> {
fn new(path: &'a NPath) -> Self {
Self {
current: Some(path),
}
}
}
impl<'a> Iterator for NPathEdgeIter<'a> {
type Item = &'a Arc<Edge>;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.current?;
let edge = curr.edge.as_ref()?;
self.current = curr.parent.as_deref();
Some(edge)
}
}
pub mod utils {
use super::*;
pub fn combine_paths(left: &Arc<NPath>, right: &Arc<NPath>) -> Option<Path> {
if left.vertex.vid != right.vertex.vid {
return None;
}
let left_path = left.to_path();
let mut right_path = right.to_path();
right_path.reverse();
let mut combined = left_path;
combined.steps.extend(right_path.steps);
Some(combined)
}
pub fn batch_to_paths(npaths: &[Arc<NPath>]) -> Vec<Path> {
npaths.iter().map(|np| np.to_path()).collect()
}
pub fn has_duplicates(npaths: &[Arc<NPath>]) -> bool {
let mut seen = HashSet::new();
for np in npaths {
if !seen.insert(np.hash()) {
return true;
}
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::VertexId;
use crate::vertex_edge_path::Tag;
use std::collections::HashMap;
fn create_test_vertex(id: i64) -> Arc<Vertex> {
Arc::new(Vertex::new(
VertexId::try_from_int64(id).expect("test vertex id"),
Tag::new(String::new(), HashMap::new()),
))
}
fn create_test_edge(src_id: i64, dst_id: i64, edge_type: &str) -> Arc<Edge> {
use std::collections::HashMap;
Arc::new(Edge::new(
VertexId::try_from_int64(src_id).expect("test vertex id"),
VertexId::try_from_int64(dst_id).expect("test vertex id"),
edge_type.to_string(),
0,
HashMap::new(),
))
}
#[test]
fn test_npath_new() {
let v = create_test_vertex(1);
let path = NPath::new(v.clone());
assert_eq!(path.len(), 0);
assert!(path.is_empty());
assert_eq!(
path.vertex().vid,
VertexId::try_from_int64(1).expect("test vertex id")
);
assert!(path.parent().is_none());
assert!(path.edge().is_none());
}
#[test]
fn test_npath_extend() {
let v1 = create_test_vertex(1);
let v2 = create_test_vertex(2);
let e = create_test_edge(1, 2, "friend");
let start = Arc::new(NPath::new(v1));
let extended = NPath::extend(start, e, v2);
assert_eq!(extended.len(), 1);
assert!(!extended.is_empty());
assert_eq!(
extended.vertex().vid,
VertexId::try_from_int64(2).expect("test vertex id")
);
assert!(extended.parent().is_some());
assert!(extended.edge().is_some());
}
#[test]
fn test_npath_to_path() {
let v1 = create_test_vertex(1);
let v2 = create_test_vertex(2);
let v3 = create_test_vertex(3);
let e1 = create_test_edge(1, 2, "friend");
let e2 = create_test_edge(2, 3, "friend");
let start = Arc::new(NPath::new(v1));
let p2 = Arc::new(NPath::extend(start, e1, v2));
let p3 = Arc::new(NPath::extend(p2, e2, v3));
let path = p3.to_path();
assert_eq!(path.len(), 2);
assert_eq!(
path.src.vid,
VertexId::try_from_int64(1).expect("test vertex id")
);
}
#[test]
fn test_npath_contains_vertex() {
let v1 = create_test_vertex(1);
let v2 = create_test_vertex(2);
let v3 = create_test_vertex(3);
let e1 = create_test_edge(1, 2, "friend");
let e2 = create_test_edge(2, 3, "friend");
let start = Arc::new(NPath::new(v1));
let p2 = Arc::new(NPath::extend(start, e1, v2));
let p3 = Arc::new(NPath::extend(p2, e2, v3));
assert!(p3.contains_vertex(&VertexId::try_from_int64(1).expect("test vertex id")));
assert!(p3.contains_vertex(&VertexId::try_from_int64(2).expect("test vertex id")));
assert!(p3.contains_vertex(&VertexId::try_from_int64(3).expect("test vertex id")));
assert!(!p3.contains_vertex(&VertexId::try_from_int64(4).expect("test vertex id")));
}
#[test]
fn test_npath_iter_vertices() {
let v1 = create_test_vertex(1);
let v2 = create_test_vertex(2);
let v3 = create_test_vertex(3);
let e1 = create_test_edge(1, 2, "friend");
let e2 = create_test_edge(2, 3, "friend");
let start = Arc::new(NPath::new(v1));
let p2 = Arc::new(NPath::extend(start, e1, v2));
let p3 = Arc::new(NPath::extend(p2, e2, v3));
let vertices: Vec<_> = p3.iter_vertices().collect();
assert_eq!(vertices.len(), 3);
}
#[test]
fn test_npath_equality() {
let v1 = create_test_vertex(1);
let v2 = create_test_vertex(2);
let e = create_test_edge(1, 2, "friend");
let start1 = Arc::new(NPath::new(v1.clone()));
let path1 = Arc::new(NPath::extend(start1, e.clone(), v2.clone()));
let start2 = Arc::new(NPath::new(v1));
let path2 = Arc::new(NPath::extend(start2, e, v2));
assert_eq!(path1.hash(), path2.hash());
}
}