com.hazelcast.org.apache.calcite.util.graph.DefaultDirectedGraph Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 com.hazelcast.org.apache.calcite.util.graph;
import com.hazelcast.com.google.common.collect.Ordering;
import org.apiguardian.api.API;
import com.hazelcast.org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
import com.hazelcast.org.checkerframework.checker.initialization.qual.UnknownInitialization;
import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.hazelcast.org.apache.calcite.linq4j.Nullness.castNonNull;
/**
* Default implementation of {@link DirectedGraph}.
*
* @param Vertex type
* @param Edge type
*/
public class DefaultDirectedGraph
implements DirectedGraph {
final Set edges = new LinkedHashSet<>();
final Map> vertexMap = new LinkedHashMap<>();
final @NotOnlyInitialized EdgeFactory edgeFactory;
/** Creates a graph. */
public DefaultDirectedGraph(@UnknownInitialization EdgeFactory edgeFactory) {
this.edgeFactory = edgeFactory;
}
public static DefaultDirectedGraph create() {
return create(DefaultEdge.factory());
}
public static DefaultDirectedGraph create(
EdgeFactory edgeFactory) {
return new DefaultDirectedGraph<>(edgeFactory);
}
public String toStringUnordered() {
return "graph("
+ "vertices: " + vertexMap.keySet()
+ ", edges: " + edges + ")";
}
@Override public String toString() {
@SuppressWarnings("unchecked")
final Ordering vertexOrdering = (Ordering) Ordering.usingToString();
@SuppressWarnings("unchecked")
final Ordering edgeOrdering = (Ordering) Ordering.usingToString();
return toString(vertexOrdering, edgeOrdering);
}
/** Returns the string representation of this graph, using the given
* orderings to ensure that the output order of vertices and edges is
* deterministic. */
private String toString(Ordering vertexOrdering,
Ordering edgeOrdering) {
return "graph("
+ "vertices: " + vertexOrdering.sortedCopy((Set) vertexMap.keySet())
+ ", edges: " + edgeOrdering.sortedCopy(edges) + ")";
}
@Override public boolean addVertex(V vertex) {
if (vertexMap.containsKey(vertex)) {
return false;
} else {
vertexMap.put(vertex, new VertexInfo<>());
return true;
}
}
@API(since = "1.26", status = API.Status.EXPERIMENTAL)
protected final VertexInfo getVertex(V vertex) {
@SuppressWarnings("argument.type.incompatible")
final VertexInfo info = vertexMap.get(vertex);
if (info == null) {
throw new IllegalArgumentException("no vertex " + vertex);
}
return info;
}
@Override public Set edgeSet() {
return Collections.unmodifiableSet(edges);
}
@Override public @Nullable E addEdge(V vertex, V targetVertex) {
final VertexInfo info = getVertex(vertex);
final VertexInfo targetInfo = getVertex(targetVertex);
final E edge = edgeFactory.createEdge(vertex, targetVertex);
if (edges.add(edge)) {
info.outEdges.add(edge);
targetInfo.inEdges.add(edge);
return edge;
} else {
return null;
}
}
@Override public @Nullable E getEdge(V source, V target) {
// REVIEW: could instead use edges.get(new DefaultEdge(source, target))
final VertexInfo info = getVertex(source);
for (E outEdge : info.outEdges) {
if (outEdge.target.equals(target)) {
return outEdge;
}
}
return null;
}
@Override public boolean removeEdge(V source, V target) {
// remove out edges
final List outEdges = getVertex(source).outEdges;
boolean outRemoved = false;
for (int i = 0, size = outEdges.size(); i < size; i++) {
E edge = outEdges.get(i);
if (edge.target.equals(target)) {
outEdges.remove(i);
edges.remove(edge);
outRemoved = true;
break;
}
}
// remove in edges
final List inEdges = getVertex(target).inEdges;
boolean inRemoved = false;
for (int i = 0, size = inEdges.size(); i < size; i++) {
E edge = inEdges.get(i);
if (edge.source.equals(source)) {
inEdges.remove(i);
inRemoved = true;
break;
}
}
assert outRemoved == inRemoved;
return outRemoved;
}
@SuppressWarnings("return.type.incompatible")
@Override public Set vertexSet() {
// Set -> Set
return vertexMap.keySet();
}
@Override public void removeAllVertices(Collection collection) {
// The point at which collection is large enough to make the 'majority'
// algorithm more efficient.
final float threshold = 0.35f;
final int thresholdSize = (int) (vertexMap.size() * threshold);
if (collection.size() > thresholdSize && !(collection instanceof Set)) {
// Convert collection to a set, so that collection.contains() is
// faster. If there are duplicates, collection.size() will get smaller.
collection = new HashSet<>(collection);
}
if (collection.size() > thresholdSize) {
removeMajorityVertices((Set) collection);
} else {
removeMinorityVertices(collection);
}
// remove all edges ref from this.edges
for (V v: collection) {
edges.removeIf(e -> e.source.equals(v) || e.target.equals(v));
}
}
/** Implementation of {@link #removeAllVertices(Collection)} that is efficient
* if {@code collection} is a small fraction of the set of vertices. */
private void removeMinorityVertices(Collection collection) {
for (V v : collection) {
@SuppressWarnings("argument.type.incompatible") // nullable keys are supported by .get
final VertexInfo info = vertexMap.get(v);
if (info == null) {
continue;
}
// remove all edges pointing to v
for (E edge : info.inEdges) {
@SuppressWarnings("unchecked")
final V source = (V) edge.source;
final VertexInfo sourceInfo = getVertex(source);
sourceInfo.outEdges.removeIf(e -> e.target.equals(v));
}
// remove all edges starting from v
for (E edge : info.outEdges) {
@SuppressWarnings("unchecked")
final V target = (V) edge.target;
final VertexInfo targetInfo = getVertex(target);
targetInfo.inEdges.removeIf(e -> e.source.equals(v));
}
}
vertexMap.keySet().removeAll(collection);
}
/** Implementation of {@link #removeAllVertices(Collection)} that is efficient
* if {@code vertexSet} is a large fraction of the set of vertices in the
* graph. */
private void removeMajorityVertices(Set vertexSet) {
vertexMap.keySet().removeAll(vertexSet);
for (VertexInfo info : vertexMap.values()) {
info.outEdges.removeIf(e -> vertexSet.contains(castNonNull((V) e.target)));
info.inEdges.removeIf(e -> vertexSet.contains(castNonNull((V) e.source)));
}
}
@Override public List getOutwardEdges(V source) {
return getVertex(source).outEdges;
}
@Override public List getInwardEdges(V target) {
return getVertex(target).inEdges;
}
final V source(E edge) {
//noinspection unchecked
return (V) edge.source;
}
final V target(E edge) {
//noinspection unchecked
return (V) edge.target;
}
/**
* Information about a vertex.
*
* @param Vertex type
* @param Edge type
*/
static class VertexInfo {
final List outEdges = new ArrayList<>();
final List inEdges = new ArrayList<>();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy