use std::{collections::HashSet, hash::Hash};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::traits::HgBasis;
use super::{generic_vec::GeneroVector, EdgeID, EdgeWeight};
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
pub enum EdgeDirection {
Directed,
Oriented,
Symmetric,
Loop,
Undirected,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneroEdge<B: HgBasis> {
pub id: EdgeID,
pub weight: EdgeWeight,
pub in_nodes: B,
pub out_nodes: B,
pub direction: EdgeDirection,
}
impl<B: HgBasis> From<B> for GeneroEdge<B> {
fn from(value: B) -> Self {
Self {
id: Uuid::new_v4(),
weight: 1.,
in_nodes: value,
out_nodes: B::new_empty(),
direction: EdgeDirection::Undirected,
}
}
}
impl<B: HgBasis> From<(B, B)> for GeneroEdge<B> {
fn from(value: (B, B)) -> Self {
Self {
id: Uuid::new_v4(),
weight: 1.,
in_nodes: value.0,
out_nodes: value.1,
direction: EdgeDirection::Directed,
}
}
}
impl<B: HgBasis> GeneroEdge<B> {
pub fn new() -> Self {
GeneroEdge {
id: Uuid::new_v4(),
weight: 1.,
in_nodes: B::new_empty(),
out_nodes: B::new_empty(),
direction: EdgeDirection::Directed,
}
}
pub fn from(in_nodes: B, out_nodes: B, weight: EdgeWeight, edge_type: EdgeDirection) -> Self {
GeneroEdge {
id: Uuid::new_v4(),
weight,
in_nodes,
out_nodes,
direction: edge_type,
}
}
pub fn add_input_nodes(&mut self, node: &B) {
self.in_nodes.add_node(node);
}
pub fn remove_input_node(&mut self, node: &B) {
self.in_nodes.remove_node(node);
}
pub fn add_output_nodes(&mut self, node: &B) {
self.out_nodes.add_node(node);
}
pub fn remove_output_node(&mut self, node: &B) {
self.out_nodes.remove_node(node);
}
pub fn remove_node(&mut self, node: &B) {
self.in_nodes.remove_node(node);
self.out_nodes.remove_node(node);
}
pub fn change_input(&mut self, new_input: B) {
self.in_nodes = new_input;
}
pub fn change_output(&mut self, new_output: B) {
if self.direction != EdgeDirection::Undirected || self.direction != EdgeDirection::Loop {
self.out_nodes = new_output;
}
}
pub fn input_cardinality(&self) -> usize {
self.in_nodes.len()
}
pub fn output_cardinality(&self) -> usize {
if self.direction == EdgeDirection::Undirected || self.direction == EdgeDirection::Loop {
self.in_nodes.len()
} else {
self.out_nodes.len()
}
}
pub fn change_weight(&mut self, new_weight: EdgeWeight) {
self.weight = new_weight
}
pub fn flip_to_and_from(&mut self) {
let tmp = self.in_nodes.clone();
self.in_nodes = self.out_nodes.clone();
self.out_nodes = tmp;
}
pub fn change_direction(&mut self, new_direction: EdgeDirection) {
match new_direction {
EdgeDirection::Directed | EdgeDirection::Oriented | EdgeDirection::Symmetric => {
if self.direction == EdgeDirection::Undirected
|| self.direction == EdgeDirection::Loop
{
self.out_nodes = B::new_empty();
}
self.direction = new_direction;
}
EdgeDirection::Undirected => {
let u = self.in_nodes.union(&self.out_nodes);
self.out_nodes = B::new_empty();
self.in_nodes = u;
self.direction = new_direction;
}
EdgeDirection::Loop => {
self.out_nodes = B::new_empty();
self.direction = new_direction
}
}
}
pub fn clone_input_nodes(&self) -> B {
self.in_nodes.clone()
}
pub fn clone_output_nodes(&self) -> B {
self.out_nodes.clone()
}
pub fn nodes(&self) -> HashSet<B> {
let tot = self.in_nodes.union(&self.out_nodes);
tot.nodes()
}
pub fn can_map_basis(&self, basis: &B) -> bool {
match self.direction {
EdgeDirection::Directed | EdgeDirection::Loop => self.in_nodes == *basis,
EdgeDirection::Oriented | EdgeDirection::Symmetric => {
self.in_nodes == *basis || self.out_nodes == *basis
}
EdgeDirection::Undirected => self.in_nodes.intersection(basis) == *basis,
}
}
pub fn matches_output(&self, basis: &B) -> bool {
match self.direction {
EdgeDirection::Directed | EdgeDirection::Loop => self.out_nodes == *basis,
EdgeDirection::Oriented | EdgeDirection::Symmetric => {
self.in_nodes == *basis || self.out_nodes == *basis
}
EdgeDirection::Undirected => self.in_nodes.intersection(basis) == *basis,
}
}
pub fn is_correctly_mapped(&self, input: &B, output: &B) -> bool {
match self.direction {
EdgeDirection::Directed => self.in_nodes == *input && self.out_nodes == *output,
EdgeDirection::Loop => self.in_nodes == *input && *output == *input,
EdgeDirection::Oriented | EdgeDirection::Symmetric => {
let og_dir = self.in_nodes == *input && self.out_nodes == *output;
let opposite_dir = self.in_nodes == *output && self.out_nodes == *input;
og_dir || opposite_dir
}
EdgeDirection::Undirected => self.in_nodes.complement(input) == *output,
}
}
pub fn matches_undirected(&self, basis: &B) -> bool {
self.direction == EdgeDirection::Undirected && self.in_nodes == *basis
}
pub fn contains(&self, basis: &B) -> bool {
let total = self.in_nodes.union(&self.out_nodes);
total.covers_basis(basis)
}
pub fn map(&self, input: &B) -> Option<(B, EdgeWeight)> {
let v = GeneroVector::from_basis(input.clone(), 1.);
let out = self.map_vector(v);
let mut tups = out.to_tuples();
if tups.len() == 1 {
Some(tups.pop().unwrap())
} else {
None
}
}
pub fn map_to_vector(&self, basis: &B) -> GeneroVector<B> {
if self.can_map_basis(basis) == false {
return GeneroVector::new();
}
let new_vec = GeneroVector::from_basis(basis.clone(), 1.);
self.map_vector(new_vec)
}
pub fn map_vector(&self, mut v: GeneroVector<B>) -> GeneroVector<B> {
match self.direction {
EdgeDirection::Directed => {
let w = v.remove_basis(&self.in_nodes);
if w != 0. {
v.add_basis(self.out_nodes.clone(), w * self.weight);
}
}
EdgeDirection::Oriented => {
let input_basis_w = v.remove_basis(&self.in_nodes);
let output_basis_w = v.remove_basis(&self.out_nodes);
if input_basis_w != 0. {
v.add_basis(self.out_nodes.clone(), input_basis_w * self.weight);
}
if output_basis_w != 0. {
v.add_basis(self.in_nodes.clone(), -1. * output_basis_w * self.weight);
}
}
EdgeDirection::Symmetric => {
let input_basis_w = v.remove_basis(&self.in_nodes);
let output_basis_w = v.remove_basis(&self.out_nodes);
if input_basis_w != 0. {
v.add_basis(self.out_nodes.clone(), input_basis_w * self.weight);
}
if output_basis_w != 0. {
v.add_basis(self.in_nodes.clone(), output_basis_w * self.weight);
}
}
EdgeDirection::Loop => {
if v.basis_to_weight.contains_key(&self.in_nodes) {
let old_w = v
.basis_to_weight
.get_mut(&self.in_nodes)
.expect("just checked");
*old_w = *old_w * self.weight;
}
}
EdgeDirection::Undirected => {
let mut ret = GeneroVector::new();
for (b, w) in v.basis_to_weight.drain() {
if self.in_nodes.covers_basis(&b) {
ret.add_basis(self.in_nodes.complement(&b), w * self.weight);
}
}
v = ret;
}
}
v
}
}
impl<B: HgBasis> Hash for GeneroEdge<B> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
mod test {
use std::collections::HashSet;
use crate::{structs::{GeneroEdge, GeneroVector}, EdgeDirection, SparseBasis};
#[test]
fn test_sparse_map_vec() {
let _nodes: Vec<u16> = vec![11, 23, 492, 493, 203];
let b1 = SparseBasis::from(HashSet::from([11_u16, 23, 492, 493]));
let b2 = SparseBasis::from(HashSet::from([11_u16, 23, 492, 493, 203]));
let b3 = SparseBasis::<u16>::new();
let mut e = GeneroEdge::<SparseBasis<u16>>::new();
e.change_direction(EdgeDirection::Undirected);
e.add_input_nodes(&b2);
println!("e: {:?}", e);
let mut v = GeneroVector::<SparseBasis<u16>>::new();
v.add_basis(b1.clone(), 2.);
v.add_basis(b3.clone(), 3.);
println!("input vector: {:#?}", v);
let out = e.map_vector(v);
println!("output vector: {:#?}", out);
}
fn basic_edge() -> GeneroEdge<SparseBasis<u32>> {
let b = SparseBasis::from_slice(&[1, 2, 3_u32]);
let e = GeneroEdge::from(b, SparseBasis::new(), 1., EdgeDirection::Undirected);
e
}
#[test]
fn test_serialization() {
let e = basic_edge();
let s = serde_json::to_string(&e).expect("could not serialize edge");
dbg!(s);
}
}