use std::collections::HashSet;
use std::fmt::Display;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::{HGraph, HyperGraph};
use crate::EdgeSet;
type EdgeID = u64;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConGraph {
pub(crate) core: HGraph<(), ()>,
}
impl Default for ConGraph {
fn default() -> Self {
Self::new()
}
}
impl ConGraph {
pub fn new() -> ConGraph {
ConGraph {
core: HGraph::new(),
}
}
pub fn add_node(&mut self) -> u32 {
self.core.add_node(())
}
pub fn add_nodes(&mut self, num_nodes: usize) -> Vec<u32> {
(0..num_nodes).map(|_| self.core.add_node(())).collect()
}
pub fn remove_node(&mut self, node: u32) {
self.core.remove_node(node);
}
pub fn remove_nodes(&mut self, nodes: Vec<u32>) {
for node in nodes {
self.core.remove_node(node);
}
}
pub fn nodes(&self) -> Vec<u32> {
self.core.nodes.keys().cloned().collect()
}
pub fn add_edge(&mut self, nodes: impl AsRef<[u32]>) -> EdgeID {
self.core.add_edge(nodes, ())
}
pub fn remove_edge(&mut self, edge_id: EdgeID) {
self.core.remove_edge(edge_id);
}
pub fn find_id<E>(&self, nodes: E) -> Option<EdgeID>
where
E: AsRef<[u32]>,
{
self.core.find_id(nodes)
}
pub fn edges(&self) -> Vec<EdgeID> {
self.core.edges.keys().cloned().collect()
}
pub fn cut(&self, cut_nodes: impl AsRef<[u32]>) -> usize {
let cut_as_edge: EdgeSet<u32> = cut_nodes.into();
let mut counted_edges: HashSet<EdgeID> = HashSet::new();
for node in cut_as_edge.0.iter() {
let out_edges: Vec<EdgeID> = self
.core
.containing_edges_of_nodes([*node])
.into_iter()
.filter(|e_id| counted_edges.contains(e_id) == false)
.collect();
for edge_id in out_edges {
if let Some(e) = self.core.edges.get(&edge_id) {
let intersection = cut_as_edge.intersection(&e.nodes);
if intersection.len() > 0 && intersection.len() < e.nodes.len() {
counted_edges.insert(edge_id);
}
}
}
}
counted_edges.len()
}
pub fn to_disk(&self, path: &Path) {
let s = self.to_string();
let mut file = File::create(path).expect("Cannot create File.");
file.write_all(s.as_bytes()).expect("Cannot write");
}
pub fn from_file(path: &Path) -> Option<Self> {
if path.is_file() == false {
return None;
}
if let Ok(hg_string) = fs::read_to_string(path) {
if let Ok(serde_out) = ConGraph::from_str(&hg_string) {
Some(serde_out)
} else {
None
}
} else {
None
}
}
}
impl Display for ConGraph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.core.nodes.len() == 0 {
println!("Graph is empty. Add nodes for more fun.");
return Ok(());
}
let mut s = String::new();
s.push_str("nodes:\n[");
let x: Vec<String> = self.core.nodes.keys().map(|n| n.to_string()).collect();
for ix in 0..x.len() - 1 {
s.push_str(&x[ix]);
s.push_str(", ");
}
s.push_str(x.last().unwrap());
s.push_str("]\n");
s.push_str("edges:\n");
for e in self.core.edges.values() {
s.push_str(&e.nodes.to_string());
s.push_str("\n");
}
f.write_str(&s)
}
}
impl HyperGraph for ConGraph {
type NodeID = u32;
type EdgeID = u64;
fn query_edge(&self, edge: &Self::EdgeID) -> Option<Vec<Self::NodeID>> {
self.core.query_edge(edge)
}
fn containing_edges_of_nodes(&self, nodes: impl AsRef<[Self::NodeID]>) -> Vec<Self::EdgeID> {
self.core.containing_edges_of_nodes(nodes)
}
fn containing_edges(&self, edge: &Self::EdgeID) -> Vec<Self::EdgeID> {
self.core.containing_edges(edge)
}
fn link(&self, edge: &Self::EdgeID) -> Vec<(Self::EdgeID, Vec<Self::NodeID>)> {
self.core.link(edge)
}
fn link_of_nodes(
&self,
nodes: impl AsRef<[Self::NodeID]>,
) -> Vec<(Self::EdgeID, Vec<Self::NodeID>)> {
self.core.link_of_nodes(nodes)
}
fn maximal_edges(&self, edge_id: &Self::EdgeID) -> Vec<Self::EdgeID> {
self.core.maximal_edges(edge_id)
}
fn maximal_edges_of_nodes(&self, nodes: impl AsRef<[Self::NodeID]>) -> Vec<Self::EdgeID> {
self.core.maximal_edges_of_nodes(nodes)
}
fn edges_of_size(&self, card: usize) -> Vec<Self::EdgeID> {
self.core.edges_of_size(card)
}
fn skeleton(&self, cardinality: usize) -> Vec<Self::EdgeID> {
self.core.skeleton(cardinality)
}
fn boundary_up(&self, edge_id: &Self::EdgeID) -> Vec<Vec<Self::NodeID>> {
self.core.boundary_up(edge_id)
}
fn boundary_down(&self, edge_id: &Self::EdgeID) -> Vec<Vec<Self::NodeID>> {
self.core.boundary_down(edge_id)
}
fn boundary_up_of_nodes(&self, nodes: impl AsRef<[Self::NodeID]>) -> Vec<Vec<Self::NodeID>> {
self.core.boundary_up_of_nodes(nodes)
}
fn boundary_down_of_nodes(&self, nodes: impl AsRef<[Self::NodeID]>) -> Vec<Vec<Self::NodeID>> {
self.core.boundary_down_of_nodes(nodes)
}
}
impl FromStr for ConGraph {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lines: Vec<&str> = s.lines().collect();
let mut nodes_ix = 0;
let mut edges_start_ix = 0;
for ix in 0..lines.len() {
if lines[ix] == "nodes:" {
nodes_ix = ix + 1;
}
if lines[ix] == "edges:" {
edges_start_ix = ix + 1;
}
}
let mut node_string = lines[nodes_ix].to_string();
if node_string.starts_with('[') {
node_string.remove(0);
}
if node_string.ends_with(']') {
node_string.pop();
}
let mut nodes = HashSet::new();
for node in node_string.split(',') {
nodes.insert(node.trim().parse::<u32>().expect("node parse error."));
}
let mut edges = Vec::new();
for edge_ix in edges_start_ix..lines.len() {
let mut edge_string = lines[edge_ix].to_string();
if edge_string.starts_with('{') || edge_string.starts_with('[') {
edge_string.remove(0);
}
if edge_string.ends_with('}') || edge_string.ends_with(']') {
edge_string.pop();
}
let mut node_set = Vec::new();
for node_str in edge_string.split(',') {
println!("node_str: {:}", node_str);
node_set.push(node_str.trim().parse::<u32>().expect("node parse error."));
}
edges.push(node_set);
}
let max_seen_node = nodes.iter().fold(0_u32, |acc, e| acc.max(*e)) + 1;
let mut core = HGraph::<(), (), u32, u64>::new();
let _: Vec<_> = (0..=max_seen_node).map(|_| core.add_node(())).collect();
for ix in 0..=max_seen_node {
if nodes.contains(&ix) == false {
core.remove_node(ix);
}
}
for edge in edges.into_iter() {
core.add_edge(edge, ());
}
Ok(ConGraph { core })
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use crate::{congraph::ConGraph, HyperGraph};
#[test]
fn test_creating_and_deleting_nodes() {
let mut hg = ConGraph::new();
let first_100 = hg.add_nodes(100);
assert_eq!(first_100, (0_u32..100_u32).collect::<Vec<u32>>());
let removed = 99_u32;
hg.remove_node(removed);
let one_hundred = hg.add_nodes(1);
assert_eq!(one_hundred[0], 100_u32);
}
#[test]
fn test_edge_creation_removal() {
let mut hg = ConGraph::new();
let nodes = hg.add_nodes(10);
hg.add_edge(&nodes[0..5]);
let e1 = hg.add_edge(&nodes[0..6]);
hg.remove_edge(e1);
assert!(hg
.find_id(&[nodes[4], nodes[3], nodes[2], nodes[1], nodes[0]])
.is_some());
assert!(hg.find_id(&nodes[0..6]).is_none());
}
#[test]
fn serialization() {
let mut hg = ConGraph::new();
hg.add_nodes(10);
hg.add_edge(&[0, 1]);
hg.add_edge(&[0, 1, 3, 5]);
println!("hg:\n{:}", hg);
let s = hg.to_string();
let hg_parsed = ConGraph::from_str(&s).expect("no parsing?");
println!("hg_parsed:\n{:}", hg_parsed);
dbg!(&hg.core);
let s3 = serde_json::to_string(&hg).expect("could not serialize next_usable_node");
dbg!(s3);
}
#[test]
fn link_and_skeleton() {
let mut hg = ConGraph::new();
let nodes = hg.add_nodes(10);
let mut edges = Vec::new();
for d in 1..=9 {
edges.push(hg.add_edge(&nodes[0..=d]));
}
let mut small_skeleton: Vec<_> = hg.skeleton(4).into_iter().collect();
small_skeleton.sort();
let mut expected_skeleton = vec![edges[0], edges[1], edges[2]];
expected_skeleton.sort();
assert_eq!(small_skeleton, expected_skeleton);
let mut small_link = hg.link_of_nodes(&nodes[0..=6]);
small_link.sort_by(|a, b| a.1.len().partial_cmp(&b.1.len()).unwrap());
dbg!(small_link);
}
#[test]
fn test_skeleton() {
let hg = ConGraph::new();
for size in 1..10 {
println!("{:}-skeleton", size);
println!("{:?}", hg.skeleton(size));
}
}
#[test]
fn test_cut_with_traits() {
let mut hg = ConGraph::new();
let nodes = hg.add_nodes(10);
hg.add_edge(&nodes[..2]);
hg.add_edge(&nodes[..3]);
hg.add_edge(&nodes[..4]);
println!("hg\n{:}", hg);
assert_eq!(hg.cut(&nodes[..2]), 2);
assert_eq!(hg.cut(&nodes[..3]), 1);
assert_eq!(hg.cut(&nodes[..4]), 0);
}
#[test]
fn test_node_as_edge() {
let mut hg = ConGraph::new();
hg.add_nodes(3);
let e0 = hg.add_edge(&[0]);
hg.add_edge(&[0, 1]);
hg.add_edge(&[0, 1, 2]);
let star = hg.containing_edges(&e0);
dbg!(star);
dbg!(&hg);
}
}