wolf-graph 0.1.0

Data structures and algorithms for working with graphs with reference or value semantics.
Documentation
#![cfg(all(test, feature = "serde", feature = "serde_json"))]

use bytes::Bytes;
use anyhow::Result;

use wolf_graph::prelude::*;

mod common;
use common::*;

#[test]
fn creation_1() -> Result<()> {
    let mut g1 = BlankGraph::new();
    g1.add_node(nid!("A"))?;
    g1.add_node(nid!("B"))?;
    g1.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g1, g1);
    recode(&g1, r#"[["A","B"],[["AB","A","B"]]]"#)?;

    let mut g2: Graph<String, (), ()> = Graph::new_with_data("Hello".to_string());
    g2.add_node(nid!("A"))?;
    g2.add_node(nid!("B"))?;
    g2.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g2, g2);
    recode(&g2, r#"[["A","B"],[["AB","A","B"]],"Hello"]"#)?;

    let mut g3: Graph<i32, (), ()> = Graph::new_with_data(1);
    g3.add_node(nid!("A"))?;
    g3.add_node(nid!("B"))?;
    g3.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g3, g3);
    recode(&g3, r#"[["A","B"],[["AB","A","B"]],1]"#)?;

    let mut g4: Graph<String, (), i32> = Graph::new_with_data("Hello".to_string());
    g4.add_node(nid!("A"))?;
    g4.add_node(nid!("B"))?;
    g4.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), 1)?;
    assert_eq!(g4, g4);
    recode(&g4, r#"[["A","B"],[["AB","A","B",1]],"Hello"]"#)?;

    let mut g5: Graph<i32, (), String> = Graph::new_with_data(1);
    g5.add_node(nid!("A"))?;
    g5.add_node(nid!("B"))?;
    g5.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), "AB".to_string())?;
    assert_eq!(g5, g5);
    recode(&g5, r#"[["A","B"],[["AB","A","B","AB"]],1]"#)?;

    Ok(())
}

#[test]
fn creation_2() -> Result<()> {
    let mut g1: Graph<(), String, ()> = Graph::new();
    g1.add_node_with_data(nid!("A"), "AData".to_string())?;
    g1.add_node(nid!("B"))?;
    g1.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g1, g1);
    recode(&g1, r#"[[["A","AData"],["B",""]],[["AB","A","B"]]]"#)?;

    let mut g2: Graph<String, i32, ()> = Graph::new_with_data("Hello".to_string());
    g2.add_node_with_data(nid!("A"), 1)?;
    g2.add_node(nid!("B"))?;
    g2.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g2, g2);
    recode(&g2, r#"[[["A",1],["B",0]],[["AB","A","B"]],"Hello"]"#)?;

    let mut g3: Graph<i32, (), ()> = Graph::new_with_data(1);
    g3.add_node(nid!("A"))?;
    g3.add_node(nid!("B"))?;
    g3.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g3, g3);
    recode(&g3, r#"[["A","B"],[["AB","A","B"]],1]"#)?;

    let mut g4: Graph<String, i32, i32> = Graph::new_with_data("Hello".to_string());
    g4.add_node_with_data(nid!("A"), 1)?;
    g4.add_node(nid!("B"))?;
    g4.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), 1)?;
    assert_eq!(g4, g4);
    recode(&g4, r#"[[["A",1],["B",0]],[["AB","A","B",1]],"Hello"]"#)?;

    let mut g5: Graph<i32, (), String> = Graph::new_with_data(1);
    g5.add_node(nid!("A"))?;
    g5.add_node(nid!("B"))?;
    g5.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), "AB".to_string())?;
    assert_eq!(g5, g5);
    recode(&g5, r#"[["A","B"],[["AB","A","B","AB"]],1]"#)?;

    Ok(())
}

#[test]
fn creation_3() -> Result<()> {
    let mut g1: Graph<Data, Data, Data> = Graph::new();
    g1.add_node(nid!("A"))?;
    g1.add_node(nid!("B"))?;
    g1.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    assert_eq!(g1, g1);
    recode(&g1, r#"[[["A",""],["B",""]],[["AB","A","B",""]],""]"#)?;

    let mut g2: Graph<Data, Data, Data> = Graph::new_with_data(Data::from(vec![1, 2, 3, 4]));
    g2.add_node_with_data(nid!("A"), Data::from(&[5, 6, 7, 8]))?;
    g2.add_node_with_data(nid!("B"), Data::from(&[9, 10, 11, 12]))?;
    g2.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), Bytes::from_static(&[13, 14, 15, 16]).into())?;
    assert_eq!(g2, g2);
    recode(&g2, r#"[[["A","BQYHCA=="],["B","CQoLDA=="]],[["AB","A","B","DQ4PEA=="]],"AQIDBA=="]"#)?;

    Ok(())
}

#[test]
fn graph_with_data() -> Result<()> {
    let g: Graph<String, String, String> = Graph::new_with_data("Graph".to_string())
        .adding_node_with_data(nid!("101"), "A".to_string())?
        .adding_node_with_data(nid!("102"), "B".to_string())?
        .adding_node_with_data(nid!("103"), "C".to_string())?
        .adding_node_with_data(nid!("104"), "D".to_string())?
        .adding_edge_with_data(eid!("1"), nid!("101"), nid!("102"), "AB".to_string())?
        .adding_edge_with_data(eid!("2"), nid!("101"), nid!("103"), "AC".to_string())?;
    assert_eq!(g, g);

    recode(&g, r#"[[["101","A"],["102","B"],["103","C"],["104","D"]],[["1","101","102","AB"],["2","101","103","AC"]],"Graph"]"#)
}

#[test]
fn graph_with_no_data() -> Result<()> {
    let g = BlankGraph::new()
        .adding_node(nid!("101"))?
        .adding_node(nid!("102"))?
        .adding_node(nid!("103"))?
        .adding_node(nid!("104"))?
        .adding_edge(eid!("1"), nid!("101"), nid!("102"))?
        .adding_edge(eid!("2"), nid!("101"), nid!("103"))?;
    assert_eq!(g, g);

    recode(&g, r#"[["101","102","103","104"],[["1","101","102"],["2","101","103"]]]"#)
}

#[test]
fn test_graph() -> Result<()> {
    assert_eq!(format!("{}", common::make_graph()), r#"[["A","B","C","D","E","F","G","H","I","J","K"],[["AC","A","C"],["AD","A","D"],["AE","A","E"],["BA","B","A"],["BC","B","C"],["BG","B","G"],["CD","C","D"],["ED","E","D"],["FD","F","D"],["FE","F","E"],["GI","G","I"],["HJ","H","J"],["IB","I","B"],["IC","I","C"],["IK","I","K"],["JA","J","A"],["JE","J","E"],["JF","J","F"]]]"#);
    assert_eq!(format!("{}", common::make_dag()), r#"[["A","B","C","D","E","F","G","H","I","J","K"],[["AC","A","C"],["AD","A","D"],["AE","A","E"],["BA","B","A"],["BC","B","C"],["BG","B","G"],["BI","B","I"],["CD","C","D"],["ED","E","D"],["FD","F","D"],["FE","F","E"],["HJ","H","J"],["IC","I","C"],["IG","I","G"],["IK","I","K"],["JA","J","A"],["JE","J","E"],["JF","J","F"]]]"#);
    assert_eq!(format!("{}", common::make_tree()), r#"[["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O"],[["AB","A","B"],["AC","A","C"],["AD","A","D"],["BI","B","I"],["CH","C","H"],["DE","D","E"],["DF","D","F"],["DG","D","G"],["EM","E","M"],["EN","E","N"],["EO","E","O"],["FL","F","L"],["HJ","H","J"],["HK","H","K"]]]"#);
    Ok(())
}

#[test]
fn test_graph_queries() -> Result<()> {
    let empty_graph = BlankGraph::new();
    assert!(empty_graph.is_empty());

    let g = common::make_graph();
    assert!(!g.is_empty());
    assert_eq!(g.node_count(), 11);
    assert_eq!(g.edge_count(), 18);
    assert_eq!(format_ids(g.all_nodes()), "[A, B, C, D, E, F, G, H, I, J, K]");
    assert_eq!(format_ids(g.all_edges()), "[AC, AD, AE, BA, BC, BG, CD, ED, FD, FE, GI, HJ, IB, IC, IK, JA, JE, JF]");
    assert!(g.has_node(nid!("F")));
    assert!(!g.has_node(nid!("Z")));
    assert!(g.has_edge(eid!("CD")));
    assert!(!g.has_edge(eid!("ZZ")));
    assert!(g.has_edge_from_to(nid!("A"), nid!("E")));
    assert!(!g.has_edge_from_to(nid!("E"), nid!("A")));
    assert!(g.has_edge_between(nid!("A"), nid!("E")));
    assert!(g.has_edge_between(nid!("E"), nid!("A")));
    assert_eq!(g.source(eid!("BA"))?, nid!("B"));
    assert_eq!(g.target(eid!("BA"))?, nid!("A"));
    assert_eq!(g.endpoints(eid!("BA"))?, (nid!("B"), nid!("A")));
    assert_eq!(format_ids(g.out_edges(nid!("A"))?), "[AC, AD, AE]");
    assert_eq!(format_ids(g.in_edges(nid!("A"))?), "[BA, JA]");
    assert_eq!(format_ids(g.incident_edges(nid!("A"))?), "[AC, AD, AE, BA, JA]");
    assert_eq!(g.out_degree(nid!("A"))?, 3);
    assert_eq!(g.in_degree(nid!("A"))?, 2);
    assert_eq!(g.degree(nid!("A"))?, 5);
    assert_eq!(format_ids(g.successors(nid!("A"))?), "[C, D, E]");
    assert_eq!(format_ids(g.predecessors(nid!("A"))?), "[B, J]");
    assert_eq!(format_ids(g.neighbors(nid!("A"))?), "[B, C, D, E, J]");
    // assert_eq!(g.node_data(nid!("A"))?, None);
    // assert_eq!(g.edge_data(eid!("BA"))?, None);
    assert_eq!(format_ids(g.all_roots()), "[H]");
    assert_eq!(format_ids(g.all_leaves()), "[D, K]");
    assert_eq!(format_ids(g.all_internals()), "[A, B, C, E, F, G, I, J]");
    assert!(g.is_leaf(nid!("K"))?);
    assert!(!g.is_leaf(nid!("H"))?);
    assert!(g.is_root(nid!("H"))?);
    assert!(!g.is_root(nid!("D"))?);
    assert!(g.is_internal(nid!("B"))?);
    assert!(!g.is_internal(nid!("H"))?);
    assert!(!g.is_internal(nid!("K"))?);
    // assert_eq!(g.data, None);

    Ok(())
}

#[test]
fn test_graph_mutation() -> Result<()> {
    let mut g1 = BlankGraph::new();
    g1.add_node(nid!("A"))?;
    g1.add_node(nid!("B"))?;
    g1.add_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    let g1s = r#"[["A","B"],[["AB","A","B"]]]"#;
    recode(&g1, g1s)?;
    assert!(g1.add_node(nid!("A")).is_err());
    assert!(g1.add_edge(eid!("AB"), nid!("A"), nid!("B")).is_err());

    let mut g2 = g1.clone();
    assert_eq!(g1, g2);
    g2.add_node(nid!("C"))?;
    recode(&g2, r#"[["A","B","C"],[["AB","A","B"]]]"#)?;
    g2.add_edge(eid!("AC"), nid!("A"), nid!("C"))?;
    let g2s = r#"[["A","B","C"],[["AB","A","B"],["AC","A","C"]]]"#;
    recode(&g2, g2s)?;
    assert_ne!(g1, g2);

    recode(&g1, g1s)?;

    let mut g3 = g2.clone();
    assert_eq!(g2, g3);
    g3.remove_edge(eid!("AB"))?;
    recode(&g3, r#"[["A","B","C"],[["AC","A","C"]]]"#)?;
    g3.remove_node(nid!("C"))?;
    recode(&g3, r#"[["A","B"],[]]"#)?;

    Ok(())
}

#[test]
fn test_graph_builders() -> Result<()> {
    let g1 = BlankGraph::new()
        .adding_node(nid!("A"))?
        .adding_node(nid!("B"))?
        .adding_edge(eid!("AB"), nid!("A"), nid!("B"))?;
    let g1s = r#"[["A","B"],[["AB","A","B"]]]"#;
    recode(&g1, g1s)?;

    let g2 = g1
        .adding_node(nid!("C"))?
        .adding_edge(eid!("AC"), nid!("A"), nid!("C"))?;
    let g2s = r#"[["A","B","C"],[["AB","A","B"],["AC","A","C"]]]"#;
    recode(&g2, g2s)?;

    let g3 = g2
        .removing_edge(eid!("AB"))?
        .removing_node(nid!("C"))?;
    recode(&g3, r#"[["A","B"],[]]"#)?;

    recode(&g1, g1s)?;

    Ok(())
}

#[test]
fn test_move_edge() -> Result<()> {
    let g = BlankGraph::new()
        .adding_node(nid!("A"))?
        .adding_node(nid!("B"))?
        .adding_node(nid!("C"))?
        .adding_node(nid!("D"))?
        .adding_edge(eid!("1"), nid!("A"), nid!("B"))?;
    recode(&g, r#"[["A","B","C","D"],[["1","A","B"]]]"#)?;

    let g2 = g.moving_edge(eid!("1"), nid!("A"), nid!("C"))?;
    recode(&g2, r#"[["A","B","C","D"],[["1","A","C"]]]"#)?;

    let g3 = g.moving_edge(eid!("1"), nid!("B"), nid!("A"))?;
    recode(&g3, r#"[["A","B","C","D"],[["1","B","A"]]]"#)?;

    let g4 = g.moving_edge(eid!("1"), nid!("D"), nid!("D"))?;
    recode(&g4, r#"[["A","B","C","D"],[["1","D","D"]]]"#)?;

    Ok(())
}

#[test]
fn test_with_data() -> Result<()> {
    let mut g: Graph<TestElemData, TestElemData, TestElemData> = Graph::new();
    recode(&g, r#"[[],[],[0,""]]"#)?;
    g.set_data(TestElemData(10, "dog".to_string()));
    recode(&g, r#"[[],[],[10,"dog"]]"#)?;
    assert_eq!(g.data(), &TestElemData(10, "dog".to_string()));
    g.with_data(&|data| {
        data.0 = 20;
        data.1 = "cat".to_string();
    });
    recode(&g, r#"[[],[],[20,"cat"]]"#)?;

    g.add_node_with_data(nid!("A"), TestElemData(1, "a".to_string()))?;
    g.add_node_with_data(nid!("B"), TestElemData(2, "b".to_string()))?;
    g.add_node(nid!("C"))?;
    g.add_edge_with_data(eid!("AB"), nid!("A"), nid!("B"), TestElemData(3, "c".to_string()))?;
    g.add_edge_with_data(eid!("AC"), nid!("A"), nid!("C"), TestElemData::default())?;
    recode(&g, r#"[[["A",[1,"a"]],["B",[2,"b"]],["C",[0,""]]],[["AB","A","B",[3,"c"]],["AC","A","C",[0,""]]],[20,"cat"]]"#)?;
    g.with_node_data(nid!("A"), &|data| {
        data.0 = 30;
        data.1 = "ape".to_string();
    })?;
    assert_eq!(g.node_data(nid!("A"))?.into_owned(), TestElemData(30, "ape".to_string()));
    g.with_edge_data(eid!("AB"), &|data| {
        data.0 = 40;
        data.1 = "bat".to_string();
    })?;
    assert_eq!(g.edge_data(eid!("AB"))?.into_owned(), TestElemData(40, "bat".to_string()));
    recode(&g, r#"[[["A",[30,"ape"]],["B",[2,"b"]],["C",[0,""]]],[["AB","A","B",[40,"bat"]],["AC","A","C",[0,""]]],[20,"cat"]]"#)?;

    Ok(())
}

#[test]
fn test_transitive() -> Result<()> {
    let g = common::make_graph();
    assert_eq!(format_ids(g.transitive_successors(&[nid!("F"), nid!("I")].into())?), "[A, B, C, D, E, G, I, K]");
    assert_eq!(format_ids(g.transitive_predecessors(&[nid!("F"), nid!("I")].into())?), "[B, G, H, I, J]");

    let r = ReversedGraph::new(g);
    assert_eq!(format_ids(r.transitive_successors(&[nid!("F"), nid!("I")].into())?), "[B, G, H, I, J]");
    assert_eq!(format_ids(r.transitive_predecessors(&[nid!("F"), nid!("I")].into())?), "[A, B, C, D, E, G, I, K]");

    Ok(())
}

#[test]
fn test_edges() -> Result<()> {
    let g = common::make_graph()
        .adding_edge(eid!("JA2"), nid!("J"), nid!("A"))? // codirected
        .adding_edge(eid!("EJ"), nid!("E"), nid!("J"))?; // parallel (reversed)
    assert_eq!(format_ids(g.edges_from_to(nid!("J"), nid!("E"))?), "[JE]");
    assert_eq!(format_ids(g.edges_from_to(nid!("J"), nid!("A"))?), "[JA, JA2]");
    assert_eq!(format_ids(g.edges_between(nid!("J"), nid!("E"))?), "[EJ, JE]");
    assert_eq!(format_ids(g.edges_from_to_nodes(&[nid!("J"), nid!("B")].into(), &[nid!("A"), nid!("C"), nid!("E"), nid!("I")].into())?), "[BA, BC, JA, JA2, JE]");
    assert_eq!(format_ids(g.edges_between_nodes(&[nid!("J"), nid!("B")].into(), &[nid!("A"), nid!("C"), nid!("E"), nid!("I")].into())?), "[BA, BC, EJ, IB, JA, JA2, JE]");

    Ok(())
}