org.graphper.def.DedirectedEdgeGraph Maven / Gradle / Ivy
Show all versions of graph-support Show documentation
/*
* Copyright 2022 The graph-support project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.graphper.def;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import org.graphper.def.DedirectedEdgeGraph.ReverseEdge;
import org.graphper.util.CollectionUtils;
/**
* A bidirectional directed graph of edge operations.
*
* The type of vertex is recommended to use the subclass of {@link VertexIndex}. When the
* subclass of {@link VertexIndex} is stored as a vertex in {@code DirectedEdgeGraph}, the vertex is
* searched with a complexity of O(1), otherwise it is O(N).
*
* @param the type of vertex
* @param the type of directed edge
* @author Jamison Jiang
*/
public class DedirectedEdgeGraph>
extends ProxyDedigraph, DirectedEdgeGraph>>
implements EdgeDedigraph {
private static final long serialVersionUID = -5712574722294920575L;
/*
* Record the reverse edges
*/
private final HashMap>> reverseEdgeMap;
public DedirectedEdgeGraph() {
this(new DirectedEdgeGraph<>(), new DirectedEdgeGraph<>());
}
/**
* Construct a graph with a specified capacity.
*
* @param capacity specified capacity
* @throws IllegalArgumentException Capacity is less than or equal to 0
*/
public DedirectedEdgeGraph(int capacity) {
this(new DirectedEdgeGraph<>(capacity), new DirectedEdgeGraph<>(capacity));
}
/**
* Initialize with edge array.
*
* @param edges edge array
* @throws IllegalArgumentException vertex array is empty
*/
public DedirectedEdgeGraph(E[] edges) {
this(new DirectedEdgeGraph<>(), new DirectedEdgeGraph<>());
if (edges == null || edges.length == 0) {
throw new IllegalArgumentException("edges can not be empty");
}
for (E edge : edges) {
addEdge(edge);
}
}
/**
* Initialize with edge collections.
*
* @param edges edge collection
* @throws IllegalArgumentException vertex array is empty
*/
public DedirectedEdgeGraph(Collection edges) {
this(new DirectedEdgeGraph<>(), new DirectedEdgeGraph<>());
if (CollectionUtils.isEmpty(edges)) {
throw new IllegalArgumentException("edges can not be empty");
}
for (E edge : edges) {
addEdge(edge);
}
}
private DedirectedEdgeGraph(DirectedEdgeGraph digraph,
DirectedEdgeGraph> reDigraph) {
super(digraph, reDigraph);
this.reverseEdgeMap = new HashMap<>(digraph.edgeNum());
reDigraph.forEachEdges(edge -> putEdgeMap(edge.edge, edge));
}
@Override
public void clear() {
super.clear();
if (reverseEdgeMap != null) {
reverseEdgeMap.clear();
}
}
@Override
@SuppressWarnings("unchecked")
protected Iterable> inIte(Object v) {
return reDigraph.adjacent(v);
}
@Override
@SuppressWarnings("unchecked")
protected Iterable outIte(Object v) {
return digraph.adjacent(v);
}
/**
* Adds an edge to the graph, which may or may not be directed.
*
* @param e edge to be added to this graph
* @throws NullPointerException if the edge is null
*/
@Override
public void addEdge(E e) {
Objects.requireNonNull(e);
digraph.addEdge(e);
ReverseEdge re = new ReverseEdge<>(e.to(), e.from(), e.weight(), e);
putEdgeMap(e, re);
reDigraph.addEdge(re);
}
/**
* Remove a vertex from the graph. If the graph changes due to removing this vertex, return true.
*
* @param v vertex to be removed from this graph, if present
* @return true if this contains the specified vertex
*/
@Override
public boolean remove(Object v) {
// Remove the reverse edges record
for (E e : adjacent(v)) {
List> reverseEdges = reverseEdgeMap.get(e);
if (CollectionUtils.isEmpty(reverseEdges)) {
continue;
}
reverseEdges.remove(reverseEdges.size() - 1);
if (CollectionUtils.isEmpty(reverseEdges)) {
reverseEdgeMap.remove(e);
}
}
return super.remove(v);
}
/**
* Removes an edge to the graph. If the graph changes due to removing this edge, return true.
*
* @param e edge to be removed to this graph
* @return true if this graph changed as a result of the call
*/
@Override
public boolean removeEdge(E e) {
if (vertexNum() == 0 || edgeNum() == 0 || e == null) {
return false;
}
boolean result = digraph.removeEdge(e);
if (!result) {
return false;
}
List> reverseEdges = reverseEdgeMap.get(e);
ReverseEdge reverseEdge = reverseEdges.remove(reverseEdges.size() - 1);
reDigraph.removeEdge(reverseEdge);
if (CollectionUtils.isEmpty(reverseEdges)) {
reverseEdgeMap.remove(e);
}
return true;
}
/**
* Returns all edges in the graph.
*
* @return all edges in the graph
*/
@Override
public Iterable edges() {
return digraph.edges();
}
@Override
public void forEachEdges(Consumer consumer) {
digraph.forEachEdges(consumer);
}
/**
* If the incoming directed edge exists in the graph, the original directed edge will be deleted
* in the graph, and a reversed direction edge will be created and inserted into the current
* graph, and finally the new reversed directed edge will be returned. Returns null if
* the edge does not exist. This action is equivalent to the following sequence of actions:
* {@code
* EdgeDigraph digraph = ...;
* E edge = ...;
*
* // Remove edge from graph
* if (digraph.remove(edge)) {
* // Manually flip edges
* edge = edge.reverse();
* // Insert the reversed edge
* digraph.addEdge(edge);
* }
* }
*
* @param e edge that needs to be reversed
* @return reversed edge
* @throws NullPointerException if the specified edge is null
*/
@Override
public E reverseEdge(E e) {
Objects.requireNonNull(e);
if (!removeEdge(e)) {
return null;
}
E reverse = e.reverse();
addEdge(reverse);
return reverse;
}
/**
* Returns a copy of the {@code DedirectedEdgeGraph}.
*
* @return a copy of current graph
*/
@Override
public DedirectedEdgeGraph copy() {
return new DedirectedEdgeGraph<>(digraph.copy(), reDigraph.copy());
}
/**
* Returns a directed graph reversed from the current directed graph.
*
* @return directed graph reversed from the current directed graph
*/
@Override
public DedirectedEdgeGraph reverse() {
if (edgeNum() == 0) {
return new DedirectedEdgeGraph<>();
}
List res = new ArrayList<>(edgeNum());
digraph.forEachEdges(edge -> res.add(edge.reverse()));
DedirectedEdgeGraph g = new DedirectedEdgeGraph<>(res);
for (V v : digraph) {
g.add(v);
}
return g;
}
/**
* Returns all edges adjacent to the specified vertex.
*
* For a de-directed graph, "adjacent" contains edges in both directions. The function seems
* to fall back to the state of {@link org.graphper.def.Graph.EdgeGraph#adjacent(Object)} from
* {@link org.graphper.def.Digraph.EdgeDigraph#adjacent(Object)}.
*
* @param v vertex to be queried
* @return all adjacent edges
*/
@Override
@SuppressWarnings("unchecked")
public Iterable adjacent(Object v) {
return new ComConcatItr<>(outAdjacent(v), inAdjacent(v));
}
/**
* Returns all incoming edges representing the vertex.
*
* @param v vertex to be queried
* @return all incoming adjacent edges
*/
@Override
public Iterable inAdjacent(Object v) {
return () -> new ReverseIterator<>(reDigraph.adjacent(v).iterator());
}
/**
* Returns all outgoing edges representing the vertex.
*
* @param v vertex to be queried
* @return all outgoing adjacent edges
*/
@Override
public Iterable outAdjacent(Object v) {
return new BiConcatIterable<>(digraph.adjacent(v), Collections.emptyList());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
DedirectedEdgeGraph, ?> that = (DedirectedEdgeGraph, ?>) o;
return Objects.equals(reverseEdgeMap, that.reverseEdgeMap);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), reverseEdgeMap);
}
private void putEdgeMap(E edge, ReverseEdge reverseEdge) {
reverseEdgeMap.compute(edge, (k, v) -> {
if (v == null) {
v = new ArrayList<>(1);
}
v.add(reverseEdge);
return v;
});
}
// ------------------------------------------- Subclass -------------------------------------------
static class ComConcatItr> extends BiConcatIterable {
public ComConcatItr(Iterable extends E>... iterables) {
super(iterables);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ComConcatItr)) {
return false;
}
Iterator i1 = iterator();
Iterator> i2 = ((ComConcatItr, ?>) o).iterator();
while (i1.hasNext() && i2.hasNext()) {
if (!Objects.equals(i1.next(), i2.next())) {
return false;
}
}
return i1.hasNext() == i2.hasNext();
}
@Override
public int hashCode() {
int hashCode = 1;
Iterator iterator = iterator();
while (iterator.hasNext()) {
E e = iterator.next();
hashCode += e.hashCode();
}
return hashCode;
}
}
static class ReverseIterator> implements Iterator {
private final Iterator> reverseEdgeIterator;
private ReverseIterator(Iterator> reverseEdgeIterator) {
Objects.requireNonNull(reverseEdgeIterator);
this.reverseEdgeIterator = reverseEdgeIterator;
}
@Override
public boolean hasNext() {
return reverseEdgeIterator.hasNext();
}
@Override
public E next() {
return reverseEdgeIterator.next().edge;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Adjacent Iterator not support delete!");
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ReverseIterator, ?> that = (ReverseIterator, ?>) o;
return Objects.equals(reverseEdgeIterator, that.reverseEdgeIterator);
}
@Override
public int hashCode() {
return Objects.hash(reverseEdgeIterator);
}
}
static class ReverseEdge>
extends AbstractEdge>
implements DirectedEdge>, Serializable {
private static final long serialVersionUID = 4362288930468885917L;
private final B edge;
protected ReverseEdge(V from, V to, double weight, B edge) {
super(from, to, weight);
Objects.requireNonNull(edge);
this.edge = edge;
}
@Override
public V from() {
return left;
}
@Override
public V to() {
return right;
}
@Override
public ReverseEdge reverse() {
return new ReverseEdge<>(to(), from(), weight, edge.reverse());
}
@Override
public ReverseEdge copy() {
return new ReverseEdge<>(from(), to(), weight, edge);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
ReverseEdge, ?> that = (ReverseEdge, ?>) o;
return Objects.equals(edge, that.edge);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), edge);
}
}
}