org.graphper.def.UndirectedEdgeGraph 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.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
/**
* Edge undirected graph based on {@link AdjEdgeGraph}.
*
* 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 UndirectedEdgeGraph}, 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 UndirectedEdgeGraph> extends AdjEdgeGraph
implements Graph.EdgeGraph {
private static final long serialVersionUID = -3215868703245301095L;
/**
* Construct graph with default capacity.
*/
public UndirectedEdgeGraph() {
super();
}
/**
* Construct a graph with a specified capacity.
*
* @param capacity specified capacity
* @throws IllegalArgumentException Capacity is less than or equal to 0
*/
public UndirectedEdgeGraph(int capacity) {
super(capacity);
}
/**
* Initialize with edge array.
*
* @param edges edge array
* @throws IllegalArgumentException vertex array is empty
*/
public UndirectedEdgeGraph(E[] edges) {
super(edges);
}
/**
* Adds an undirected edge to the graph.
*
* @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);
V v, w;
EdgeBag bagV = (EdgeBag) adjacent(v = e.either());
if (bagV == EdgeBag.EMPTY) {
bagV = addBag(v);
}
EdgeBag bagW = (EdgeBag) adjacent(w = e.other(v));
if (bagW == EdgeBag.EMPTY) {
bagW = addBag(w);
}
bagV.add(e);
bagW.add(e);
// The added edge is a self-loop
if (bagV == bagW) {
bagV.loopNum++;
}
edgeNum++;
}
/**
* Removes a directed edge to the graph. If the graph changes due to removing this edge, return
* true.
*
* @param edge edge to be removed to this graph
* @return true if this graph changed as a result of the call
*/
@Override
public boolean removeEdge(E edge) {
if (vertexNum == 0 || edgeNum == 0 || edge == null) {
return false;
}
EdgeBag bagV, bagW;
if ((bagV = (EdgeBag) adjacent(edge.either())) == EdgeBag.EMPTY
|| (bagW = (EdgeBag) adjacent(edge.other(edge.either()))) == EdgeBag.EMPTY) {
return false;
}
if (!bagV.remove(edge) || !bagW.remove(edge)) {
return false;
}
// The removed edges are self-loops
if (bagV == bagW) {
bagV.loopNum--;
}
edgeNum--;
return true;
}
/**
* Remove a vertex from the graph. If the graph changes due to removing this vertex, return true.
*
* @param vertex vertex to be removed from this graph, if present
* @return true if this contains the specified vertex
*/
@Override
public boolean remove(Object vertex) {
int index = 0;
EdgeBag bag = null, tBag;
if (vertex instanceof VertexIndex) {
Integer i = ((VertexIndex) vertex).getGraphIndex()
.get(checkAndReturnGraphRef());
if (i != null
&& i >= 0
&& i < vertexNum
&& Objects.equals(bags[i].vertex, vertex)
) {
bag = bags[index = i];
}
} else {
for (; index < vertexNum; index++) {
if (Objects.equals((bag = bags[index]).vertex, vertex)) {
break;
}
}
if (index == vertexNum) {
return false;
}
}
if (bag == null) {
return false;
}
// The number of edges of the deleted vertex
int bagEdges = bag.degree - bag.loopNum;
// Delete records of adjacent vertices and edges
for (E e : bag) {
tBag = (EdgeBag) adjacent(e.other(bag.vertex));
if (bag == tBag) {
continue;
}
if (tBag != EdgeBag.EMPTY) {
tBag.remove(e);
}
}
if (index != vertexNum) {
System.arraycopy(bags, index + 1, bags, index, vertexNum - index - 1);
}
int nv = --vertexNum;
if (bag.vertex instanceof VertexIndex) {
for (int i = index; i < nv; i++) {
((VertexIndex) bags[i].vertex)
.getGraphIndex()
.computeIfPresent(checkAndReturnGraphRef(), (k, v) -> v - 1);// Update index
}
}
bags[nv] = null;
edgeNum -= bagEdges;
modCount++;
bag.bModCount++;
return true;
}
/**
* Returns a copy of the {@code UndirectedEdgeGraph}.
*
* @return a copy of current graph
*/
@Override
public UndirectedEdgeGraph copy() {
UndirectedEdgeGraph graph = new UndirectedEdgeGraph<>(this.bags.length);
graph.bags = bagRepl();
graph.vertexNum = vertexNum;
graph.edgeNum = edgeNum;
if (vertexNum > 0
&& graph.bags[0].vertex instanceof VertexIndex) {
VertexIndex.GraphRef gf = graph.checkAndReturnGraphRef();
for (int i = 0; i < graph.vertexNum; i++) {
VertexIndex v = ((VertexIndex) graph.bags[i].vertex);
v.getGraphIndex().put(gf, v.index(checkAndReturnGraphRef()));
}
}
return graph;
}
@Override
public Iterator iterator() {
return new UndirectedEdgeIterator();
}
@Override
public boolean equals(Object o) {
return super.equals(o) && this.getClass() == o.getClass();
}
@Override
public int hashCode() {
return super.hashCode() + this.getClass().hashCode();
}
private class UndirectedEdgeIterator extends AdjIterator {
@Override
public void remove() {
checkIsConcurrentModify();
if (index == 0) {
throw new IllegalStateException();
}
EdgeBag bag = bags[index - 1], tBag;
if (bag == null) {
throw new NoSuchElementException();
}
// The number of edges of the deleted vertex
int bagEdges = bag.degree - bag.loopNum;
// Delete records of adjacent vertices and edges
for (E e : bag) {
tBag = (EdgeBag) adjacent(e.other(bag.vertex));
// Self-loop does not need to be processed
if (tBag == bag) {
continue;
}
if (tBag != EdgeBag.EMPTY) {
tBag.remove(e);
}
}
if (index != vertexNum) {
System.arraycopy(bags, index, bags, index - 1, vertexNum - index);
}
int nv = --vertexNum;
--index;
if (bag.vertex instanceof VertexIndex) {
for (int i = index; i < nv; i++) {
((VertexIndex) bags[i].vertex)
.getGraphIndex()
.computeIfPresent(checkAndReturnGraphRef(), (k, v) -> v - 1);// Update index
}
}
bags[nv] = null;
exceptModCount++;
modCount++;
edgeNum -= bagEdges;
}
}
}