package spimedb.graph;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jcog.list.ArrayUnenforcedSet;
import org.eclipse.collections.api.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
//import org.jgrapht.DirectedGraph;
//import org.jgrapht.Graph;
//import org.jgrapht.util.ArrayUnenforcedSet;
/**
* High-performance customizable JGraphT DirectedGraph impl
*/
public class MapGraph<V, E> implements Serializable {
@JsonIgnore public final Map<V, VertexContainer<V,E>> vertices;
@JsonIgnore private transient final Supplier<Set> edgeSetBuilder;
/**
* Construct a new graph. The graph can either be directed or undirected,
* depending on the specified edge factory.
*
* @throws NullPointerException if the specified edge factory is <code>
* null</code>.
*/
public MapGraph(Map<V, VertexContainer<V, E>> vertexMap, Supplier<Set> edgeSetBuilder) {
vertices = vertexMap;
this.edgeSetBuilder = edgeSetBuilder;
}
@Override
public String toString() {
return vertices.toString();
}
/**
* adds vertices if they dont already exist
*
* @see Graph#addEdge(Object, Object, Object)
*/
public boolean addEdge(@NotNull V sourceVertex, @NotNull V targetVertex, @NotNull E e) {
if (sourceVertex.equals(targetVertex)) {
throw new IllegalArgumentException("loops not allowed");
}
VertexContainer<V, E> src = vertex(sourceVertex, true);
return addEdge(src, sourceVertex, targetVertex, e);
}
public boolean addEdge(VertexContainer<V, E> src, @NotNull V sourceVertex, @NotNull V targetVertex, @NotNull E e) {
if (src.addOutgoingEdge(e, targetVertex)) {
if (!vertex(targetVertex, true).addIncomingEdge(sourceVertex, e)) {
throw new RuntimeException("incidence fault");
}
return true;
}
return false; //already added
}
public boolean removeEdge(VertexContainer<V, E> srcC, @NotNull V src, @NotNull V tgt, @NotNull E e) {
if (srcC.removeOutgoingEdge(e, tgt)) {
if (!vertex(tgt, false).removeIncomingEdge(src, e)) {
throw new RuntimeException("incidence fault");
}
return true;
}
return false; //already added
}
public boolean removeEdge(V src, V tgt, E e) {
VertexContainer<V, E> s = vertex(src, false);
if (s!=null) {
VertexContainer<V, E> t = vertex(tgt, false);
if (t!=null && s.removeOutgoingEdge(e, tgt)) {
if (!t.removeIncomingEdge(src, e))
throw new RuntimeException("incidence fault");
return true;
}
}
return false;
}
/**
* @see Graph#removeVertex(Object)
*/
public boolean removeVertex(V v) {
VertexContainer<V,E> container = vertices.remove(v);
if (container!=null) {
container.incoming.forEach(e -> {
vertex(e.getOne(), false).removeOutgoingEdge(e.getTwo(), v);
});
container.outgoing.forEach(e -> {
vertex(e.getTwo(), false).removeIncomingEdge(v, e.getOne());
});
return true;
}
return false;
}
public Set<V> vertexSet() {
return vertices.keySet();
}
public boolean containsVertex(V v) {
return vertices.containsKey(v);
}
/** if vertex exists, still return true */
public VertexContainer<V, E> addVertex(V id) {
// add with a lazy edge container entry
//return vertices.putIfAbsent(v, null) == null;
return vertex(id, true);
}
protected Set<V> getVertexSet() {
return vertices.keySet();
}
/**
* @see Graph#getAllEdges(Object, Object)
*/
public Set<E> getAllEdges(V sourceVertex, V targetVertex) {
Set<E> edges = null;
VertexContainer<V,E> S = vertex(sourceVertex, false);
if (S!=null && containsVertex(targetVertex)) {
edges = new ArrayUnenforcedSet<>();
for (Pair<E,V> e : S.outgoing) {
if (e.getTwo().equals(targetVertex)) {
edges.add(e.getOne());
}
}
}
return edges;
}
/**
* @see DirectedGraph#inDegreeOf(Object)
*/
public int inDegreeOf(V vertex) {
return incomingEdgesOf(vertex).size();
}
/**
* @see DirectedGraph#incomingEdgesOf(Object)
*/
public Set<Pair<V,E>> incomingEdgesOf(V vertex) {
VertexContainer<V, E> vv = vertex(vertex, false);
return vv!=null ? vv.incoming : Collections.emptySet();
}
/**
* @see DirectedGraph#outDegreeOf(Object)
*/
public int outDegreeOf(V vertex) {
return outgoingEdgesOf(vertex).size();
}
/**
* @see DirectedGraph#outgoingEdgesOf(Object)
*/
public Set<Pair<E,V>> outgoingEdgesOf(V vertex) {
VertexContainer<V, E> vv = vertex(vertex, false);
return vv!=null ? vv.outgoing : Collections.emptySet();
}
/**
* A lazy build of edge container for specified vertex.
*
* @param vertex a vertex in this graph.
* @return EdgeContainer
*/
public VertexContainer<V,E> vertex(V vertex, boolean createIfMissing) {
if (createIfMissing) {
return vertices.computeIfAbsent(vertex, v ->
new VertexContainer<>(edgeSetBuilder.get(), edgeSetBuilder.get()));
} else {
return vertices.get(vertex);
}
}
public boolean containsEdge(V s, V t, E e) {
VertexContainer<V, E> vs = vertex(s, false);
return vs != null && vs.containsOutgoingEdge(e, t);
}
public boolean isLeaf(V id) {
return inDegreeOf(id)==0;
}
}