All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.math.graph.UndirectedGraphImpl Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.math.graph;

import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.powsybl.commons.PowsyblException;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.linked.TIntLinkedList;
import gnu.trove.set.hash.TIntHashSet;

import java.io.PrintStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 *
 * @author Geoffroy Jamgotchian {@literal }
 */
public class UndirectedGraphImpl implements UndirectedGraph {

    private static final int VERTICES_CAPACITY = 10;

    private static final int EDGES_CAPACITY = 15;

    private static final int NEIGHBORS_CAPACITY = 2;

    private static final class Vertex {

        private E object;

        private Vertex() {
        }

        public E getObject() {
            return object;
        }

        public void setObject(E object) {
            this.object = object;
        }

    }

    private static final class Edge {

        private final int v1;

        private final int v2;

        private E object;

        private Edge(int v1, int v2, E object) {
            this.v1 = v1;
            this.v2 = v2;
            this.object = object;
        }

        public int getV1() {
            return v1;
        }

        public int getV2() {
            return v2;
        }

        public E getObject() {
            return object;
        }

        public void setObject(E object) {
            this.object = object;
        }

        @Override
        public String toString() {
            return object.toString();
        }

    }

    /* vertices */
    private final List> vertices = new ArrayList<>(VERTICES_CAPACITY);

    /* edges */
    private final List> edges = new ArrayList<>(EDGES_CAPACITY);

    /* cached adjacency list */
    private TIntArrayList[] adjacencyListCache;

    private final Lock adjacencyListCacheLock = new ReentrantLock();

    private final TIntHashSet availableVertices = new TIntHashSet();

    private final TIntLinkedList removedEdges = new TIntLinkedList();

    private final List> listeners = new CopyOnWriteArrayList<>();

    private final int vertexLimit;

    public UndirectedGraphImpl(int vertexLimit) {
        if (vertexLimit < 1) {
            throw new PowsyblException("Vertex limit should be positive");
        }
        this.vertexLimit = vertexLimit;
    }

    private void checkVertex(int v) {
        if (v < 0 || v >= vertices.size() || vertices.get(v) == null) {
            throw new PowsyblException("Vertex " + v + " not found");
        }
    }

    private void checkEdge(int e) {
        if (e < 0 || e >= edges.size() || edges.get(e) == null) {
            throw new PowsyblException("Edge " + e + " not found");
        }
    }

    public int addVertex() {
        return addVertex(true);
    }

    @Override
    public int addVertex(boolean notify) {
        int v;
        if (availableVertices.isEmpty()) {
            v = vertices.size();
            vertices.add(new Vertex<>());
        } else {
            v = availableVertices.iterator().next();
            availableVertices.remove(v);
            vertices.set(v, new Vertex<>());
        }
        invalidateAdjacencyList();
        if (notify) {
            notifyVertexAdded(v);
        }
        return v;
    }

    @Override
    public void addVertexIfNotPresent(int v) {
        addVertexIfNotPresent(v, true);
    }

    @Override
    public void addVertexIfNotPresent(int v, boolean notify) {
        if (v < 0) {
            throw new PowsyblException("Invalid vertex " + v);
        }
        if (v >= this.vertexLimit) {
            throw new PowsyblException("Vertex index too high: " + v + ". Limit is " + this.vertexLimit);
        }
        if (v < vertices.size()) {
            if (availableVertices.contains(v)) {
                vertices.set(v, new Vertex<>());
                availableVertices.remove(v);
                invalidateAdjacencyList();
                if (notify) {
                    notifyVertexAdded(v);
                }
            }
        } else {
            for (int i = vertices.size(); i < v; i++) {
                vertices.add(null);
                availableVertices.add(i);
            }
            vertices.add(new Vertex<>());
            invalidateAdjacencyList();
            if (notify) {
                notifyVertexAdded(v);
            }
        }
    }

    @Override
    public boolean vertexExists(int v) {
        if (v < 0) {
            throw new PowsyblException("Invalid vertex " + v);
        }
        return v < vertices.size() && vertices.get(v) != null;
    }

    private V removeVertexInternal(int v, boolean notify) {
        V obj = vertices.get(v).getObject();
        if (v == vertices.size() - 1) {
            vertices.remove(v);
            cleanVertices(v - 1);
        } else {
            vertices.set(v, null);
            availableVertices.add(v);
        }
        if (notify) {
            notifyVertexRemoved(v, obj);
        }
        return obj;
    }

    @Override
    public V removeVertex(int v) {
        return removeVertex(v, true);
    }

    @Override
    public V removeVertex(int v, boolean notify) {
        checkVertex(v);
        for (Edge e : edges) {
            if (e != null && (e.getV1() == v || e.getV2() == v)) {
                throw new PowsyblException("An edge is connected to vertex " + v);
            }
        }
        V obj = removeVertexInternal(v, notify);
        invalidateAdjacencyList();
        return obj;
    }

    private void cleanVertices(int v) {
        for (int i = v; i >= 0; i--) {
            if (!availableVertices.contains(i)) {
                return;
            }
            availableVertices.remove(i);
            vertices.remove(i);
        }
    }

    @Override
    public int getVertexCount() {
        return vertices.size() - availableVertices.size();
    }

    @Override
    public void removeAllVertices() {
        removeAllVertices(true);
    }

    @Override
    public void removeAllVertices(boolean notify) {
        if (!edges.isEmpty()) {
            throw new PowsyblException("Cannot remove all vertices because there is still some edges in the graph");
        }
        vertices.clear();
        availableVertices.clear();
        invalidateAdjacencyList();
        if (notify) {
            notifyAllVerticesRemoved();
        }
    }

    @Override
    public int addEdge(int v1, int v2, E obj) {
        return addEdge(v1, v2, obj, true);
    }

    @Override
    public int addEdge(int v1, int v2, E obj, boolean notify) {
        checkVertex(v1);
        checkVertex(v2);
        int e;
        Edge edge = new Edge<>(v1, v2, obj);
        if (removedEdges.isEmpty()) {
            e = edges.size();
            edges.add(edge);
        } else {
            e = removedEdges.removeAt(0);
            edges.set(e, edge);
        }
        invalidateAdjacencyList();
        if (notify) {
            notifyEdgeAdded(e, obj);
        }
        return e;
    }

    private E removeEdgeInternal(int e, boolean notify) {
        E obj = edges.get(e).getObject();
        if (notify) {
            notifyEdgeBeforeRemoval(e, obj);
        }
        if (e == edges.size() - 1) {
            edges.remove(e);
        } else {
            edges.set(e, null);
            removedEdges.add(e);
        }
        if (notify) {
            notifyEdgeRemoved(e, obj);
        }
        return obj;
    }

    @Override
    public E removeEdge(int e) {
        return removeEdge(e, true);
    }

    @Override
    public E removeEdge(int e, boolean notify) {
        checkEdge(e);
        E obj = removeEdgeInternal(e, notify);
        invalidateAdjacencyList();
        return obj;
    }

    @Override
    public void removeAllEdges() {
        removeAllEdges(true);
    }

    @Override
    public void removeAllEdges(boolean notify) {
        Collection allEdges = edges.stream().filter(Objects::nonNull).map(Edge::getObject).collect(Collectors.toList());
        if (notify) {
            notifyAllEdgesBeforeRemoval(allEdges);
        }
        edges.clear();
        removedEdges.clear();
        invalidateAdjacencyList();
        if (notify) {
            notifyAllEdgesRemoved(allEdges);
        }
    }

    @Override
    public int getEdgeCount() {
        return edges.size() - removedEdges.size();
    }

    @Override
    public int[] getVertices() {
        TIntArrayList t = new TIntArrayList(vertices.size());
        for (int i = 0; i < vertices.size(); i++) {
            if (vertices.get(i) != null) {
                t.add(i);
            }
        }
        return t.toArray();
    }

    @Override
    public int[] getEdges() {
        TIntArrayList t = new TIntArrayList(getEdgeCount());
        for (int e = 0; e < edges.size(); e++) {
            if (edges.get(e) != null) {
                t.add(e);
            }
        }
        return t.toArray();
    }

    @Override
    public int getVertexCapacity() {
        return vertices.size();
    }

    @Override
    public Iterable getVerticesObj() {
        return FluentIterable.from(vertices)
                .filter(Predicates.notNull())
                .transform(Vertex::getObject);
    }

    @Override
    public Stream getVertexObjectStream() {
        return vertices.stream().filter(Objects::nonNull).map(Vertex::getObject);
    }

    @Override
    public V getVertexObject(int v) {
        checkVertex(v);
        return vertices.get(v).getObject();
    }

    @Override
    public void setVertexObject(int v, V obj) {
        setVertexObject(v, obj, true);
    }

    @Override
    public void setVertexObject(int v, V obj, boolean notify) {
        checkVertex(v);
        vertices.get(v).setObject(obj);
        if (notify) {
            notifyVertexObjectSet(v, obj);
        }
    }

    @Override
    public int getEdgeVertex1(int e) {
        checkEdge(e);
        return edges.get(e).getV1();
    }

    @Override
    public List getEdgeObjectsConnectedToVertex(int v) {
        return getEdgeObjectConnectedToVertexStream(v).collect(Collectors.toList());
    }

    @Override
    public Stream getEdgeObjectConnectedToVertexStream(int v) {
        return getEdgeConnectedToVertexStream(v).mapToObj(this::getEdgeObject);
    }

    @Override
    public List getEdgesConnectedToVertex(int v) {
        return getEdgeConnectedToVertexStream(v).boxed().collect(Collectors.toList());
    }

    @Override
    public IntStream getEdgeConnectedToVertexStream(int v) {
        checkVertex(v);
        TIntArrayList[] adjacencyList = getAdjacencyList();
        TIntArrayList adjacentEdges = adjacencyList[v];
        return IntStream.range(0, adjacentEdges.size()).map(adjacentEdges::getQuick);
    }

    @Override
    public int getEdgeVertex2(int e) {
        checkEdge(e);
        return edges.get(e).getV2();
    }

    @Override
    public Iterable getEdgesObject() {
        return FluentIterable.from(edges)
                .filter(Predicates.notNull())
                .transform(Edge::getObject);
    }

    @Override
    public Stream getEdgeObjectStream() {
        return edges.stream().filter(Objects::nonNull).map(Edge::getObject);
    }

    @Override
    public E getEdgeObject(int e) {
        checkEdge(e);
        return edges.get(e).getObject();
    }

    @Override
    public List getEdgeObjects(int v1, int v2) {
        checkVertex(v1);
        checkVertex(v2);
        List edgeObjects = new ArrayList<>(1);
        TIntArrayList[] adjacencyList = getAdjacencyList();
        TIntArrayList adjacentEdges = adjacencyList[v1];
        for (int i = 0; i < adjacentEdges.size(); i++) {
            int e = adjacentEdges.getQuick(i);
            Edge edge = edges.get(e);
            if (edge.getV1() == v1 && edge.getV2() == v2
                    || edge.getV1() == v2 && edge.getV2() == v1) {
                edgeObjects.add(edge.getObject());
            }
        }
        return edgeObjects;
    }

    /**
     * Create the adjacency list of this graph.
     * @return the adjacency list as a {@link TIntArrayList} of {@link TIntArrayList}.
     */
    private TIntArrayList[] getAdjacencyList() {
        adjacencyListCacheLock.lock();
        try {
            if (adjacencyListCache == null) {
                adjacencyListCache = new TIntArrayList[vertices.size()];
                for (int v = 0; v < vertices.size(); v++) {
                    Vertex vertex = vertices.get(v);
                    if (vertex != null) {
                        adjacencyListCache[v] = new TIntArrayList(NEIGHBORS_CAPACITY);
                    }
                }
                for (int e = 0; e < edges.size(); e++) {
                    Edge edge = edges.get(e);
                    if (edge != null) {
                        int v1 = edge.getV1();
                        int v2 = edge.getV2();
                        adjacencyListCache[v1].add(e);
                        adjacencyListCache[v2].add(e);
                    }
                }
            }
            return adjacencyListCache;
        } finally {
            adjacencyListCacheLock.unlock();
        }
    }

    /**
     * Invalidate the adjacency list.
     */
    private void invalidateAdjacencyList() {
        adjacencyListCache = null;
    }

    private void traverseVertex(int vToTraverse, boolean[] vEncountered, Deque edgesToTraverse,
                                TIntArrayList[] adjacencyList, TraversalType traversalType) {
        if (vEncountered[vToTraverse]) {
            return;
        }
        vEncountered[vToTraverse] = true;
        TIntArrayList adjacentEdges = adjacencyList[vToTraverse];
        for (int i = 0; i < adjacentEdges.size(); i++) {
            // For depth-first traversal, we're going to poll the last element added in the deque. Hence, edges have to
            // be added in reverse order, otherwise the depth-first traversal will be "on the right side" instead of
            // "on the left side" of the tree. That is, it would always go deeper taking with last neighbour of visited vertex.
            int iEdge = switch (traversalType) {
                case DEPTH_FIRST -> adjacentEdges.size() - i - 1;
                case BREADTH_FIRST -> i;
            };

            int adjacentEdgeIndex = adjacentEdges.getQuick(iEdge);
            boolean flippedEdge = edges.get(adjacentEdgeIndex).v1 != vToTraverse;
            edgesToTraverse.add(new DirectedEdge(adjacentEdgeIndex, flippedEdge));
        }
    }

    /**
     * Record to store which edge has to be traversed and in which direction
     * @param index index of the edge within the edges list
     * @param flippedDirection if true, edge.getNode2() has already been visited, otherwise it's edge.getNode1()
     */
    private record DirectedEdge(int index, boolean flippedDirection) {
    }

    @Override
    public boolean traverse(int v, TraversalType traversalType, Traverser traverser, boolean[] encounteredVertices) {
        checkVertex(v);
        Objects.requireNonNull(traverser);
        Objects.requireNonNull(encounteredVertices);

        if (encounteredVertices.length < vertices.size()) {
            throw new PowsyblException("Encountered array is too small");
        }

        boolean[] encounteredEdges = new boolean[edges.size()];
        TIntArrayList[] adjacencyList = getAdjacencyList();
        boolean keepGoing = true;

        Deque edgesToTraverse = new ArrayDeque<>();
        traverseVertex(v, encounteredVertices, edgesToTraverse, adjacencyList, traversalType);
        while (!edgesToTraverse.isEmpty() && keepGoing) {
            DirectedEdge directedEdge = switch (traversalType) {
                case DEPTH_FIRST -> edgesToTraverse.pollLast();
                case BREADTH_FIRST -> edgesToTraverse.pollFirst();
            };

            if (!encounteredEdges[directedEdge.index]) {
                encounteredEdges[directedEdge.index] = true;

                Edge edge = edges.get(directedEdge.index);
                int vOrigin = directedEdge.flippedDirection ? edge.getV2() : edge.getV1();
                int vDest = directedEdge.flippedDirection ? edge.getV1() : edge.getV2();

                TraverseResult traverserResult = traverser.traverse(vOrigin, directedEdge.index, vDest);
                switch (traverserResult) {
                    case CONTINUE -> traverseVertex(vDest, encounteredVertices, edgesToTraverse, adjacencyList, traversalType);
                    case TERMINATE_TRAVERSER -> keepGoing = false; // the whole traversing needs to stop
                    case TERMINATE_PATH -> {
                        // Path ends on edge e before reaching vDest, continuing with next edge in the deque
                    }
                }
            }
        }
        return keepGoing;
    }

    @Override
    public boolean traverse(int v, TraversalType traversalType, Traverser traverser) {
        boolean[] encountered = new boolean[vertices.size()];
        Arrays.fill(encountered, false);
        return traverse(v, traversalType, traverser, encountered);
    }

    @Override
    public boolean traverse(int[] startingVertices, TraversalType traversalType, Traverser traverser) {
        boolean[] encountered = new boolean[vertices.size()];
        Arrays.fill(encountered, false);
        for (int startingVertex : startingVertices) {
            if (!encountered[startingVertex] && !traverse(startingVertex, traversalType, traverser, encountered)) {
                return false;
            }
        }
        return true;
    }

    /**
     * {@inheritDoc}
     * 

* This method allocates a {@link List} of {@link TIntArrayList} to store the paths, a {@link BitSet} to store the encountered vertices * and calls {@link #findAllPaths(int, Predicate, Predicate, TIntArrayList, BitSet, List)}. *

* In the output, the paths are sorted by size considering the number of switches in each path. */ @Override public List findAllPaths(int from, Predicate pathComplete, Predicate pathCancelled) { return findAllPaths(from, pathComplete, pathCancelled, Comparator.comparing(TIntArrayList::size)); } /** * {@inheritDoc} *

* This method allocates a {@link List} of {@link TIntArrayList} to store the paths, a {@link BitSet} to store the encountered vertices * and calls {@link #findAllPaths(int, Predicate, Predicate, TIntArrayList, BitSet, List)}. * In the output, the paths are sorted by using the given comparator. *

*/ public List findAllPaths(int from, Predicate pathComplete, Predicate pathCancelled, Comparator comparator) { Objects.requireNonNull(pathComplete); List paths = new ArrayList<>(); BitSet encountered = new BitSet(vertices.size()); TIntArrayList path = new TIntArrayList(1); findAllPaths(from, pathComplete, pathCancelled, path, encountered, paths); // sort paths by size according to the given comparator paths.sort(comparator); return paths; } /** * This method is called by {@link #findAllPaths(int, Predicate, Predicate, TIntArrayList, BitSet, List)} each time a vertex is traversed. * The path is added to the paths list if it's complete, otherwise this method calls the {@link #findAllPaths(int, Predicate, Predicate, TIntArrayList, BitSet, List)} * to continue the recursion. * * @param e the index of the current edge. * @param v1or2 the index of the current vertex. * @param pathComplete a function that returns true when the target vertex is found. * @param pathCancelled pathCanceled a function that returns true when the edge must not be traversed. * @param path a list that contains the traversed edges. * @param encountered a BitSet that contains the traversed vertex. * @param paths a list that contains the complete paths. * @return true if the path is complete, false otherwise. */ private boolean findAllPaths(int e, int v1or2, Predicate pathComplete, Predicate pathCancelled, TIntArrayList path, BitSet encountered, List paths) { if (encountered.get(v1or2)) { return false; } Vertex obj1or2 = vertices.get(v1or2); path.add(e); if (Boolean.TRUE.equals(pathComplete.test(obj1or2.getObject()))) { paths.add(path); return true; } else { findAllPaths(v1or2, pathComplete, pathCancelled, path, encountered, paths); return false; } } /** * This method is called by {@link #findAllPaths(int, Predicate, Predicate)} or {@link #findAllPaths(int, int, Predicate, Predicate, TIntArrayList, BitSet, List)}. * For each adjacent edges for which the pathCanceled returns {@literal false}, traverse the other vertex calling {@link #findAllPaths(int, int, Predicate, Predicate, TIntArrayList, BitSet, List)}. * * @param v the current vertex * @param pathComplete a function that returns true when the target vertex is found. * @param pathCancelled a function that returns true when the edge must not be traversed. * @param path a list that contains the traversed edges. * @param encountered a BitSet that contains the traversed vertex. * @param paths a list that contains the complete paths. */ private void findAllPaths(int v, Predicate pathComplete, Predicate pathCancelled, TIntArrayList path, BitSet encountered, List paths) { checkVertex(v); encountered.set(v, true); TIntArrayList[] adjacencyList = getAdjacencyList(); TIntArrayList adjacentEdges = adjacencyList[v]; for (int i = 0; i < adjacentEdges.size(); i++) { int e = adjacentEdges.getQuick(i); Edge edge = edges.get(e); if (pathCancelled != null && pathCancelled.test(edge.getObject())) { continue; } int v1 = edge.getV1(); int v2 = edge.getV2(); TIntArrayList path2; BitSet encountered2; if (i < adjacentEdges.size() - 1) { path2 = new TIntArrayList(path); encountered2 = new BitSet(vertices.size()); encountered2.or(encountered); } else { path2 = path; encountered2 = encountered; } if (v == v2) { findAllPaths(e, v1, pathComplete, pathCancelled, path2, encountered2, paths); } else if (v == v1) { findAllPaths(e, v2, pathComplete, pathCancelled, path2, encountered2, paths); } else { throw new IllegalStateException(); } } } @Override public void addListener(UndirectedGraphListener l) { listeners.add(l); } @Override public void removeListener(UndirectedGraphListener l) { listeners.remove(l); } private void notifyVertexAdded(int v) { for (UndirectedGraphListener l : listeners) { l.vertexAdded(v); } } private void notifyVertexObjectSet(int v, V obj) { for (UndirectedGraphListener l : listeners) { l.vertexObjectSet(v, obj); } } private void notifyVertexRemoved(int v, V obj) { for (UndirectedGraphListener l : listeners) { l.vertexRemoved(v, obj); } } private void notifyAllVerticesRemoved() { for (UndirectedGraphListener l : listeners) { l.allVerticesRemoved(); } } private void notifyEdgeAdded(int e, E obj) { for (UndirectedGraphListener l : listeners) { l.edgeAdded(e, obj); } } private void notifyEdgeRemoved(int e, E obj) { for (UndirectedGraphListener l : listeners) { l.edgeRemoved(e, obj); } } private void notifyEdgeBeforeRemoval(int e, E obj) { for (UndirectedGraphListener l : listeners) { l.edgeBeforeRemoval(e, obj); } } private void notifyAllEdgesBeforeRemoval(Collection obj) { for (UndirectedGraphListener l : listeners) { l.allEdgesBeforeRemoval(obj); } } private void notifyAllEdgesRemoved(Collection obj) { for (UndirectedGraphListener l : listeners) { l.allEdgesRemoved(obj); } } @Override public void print(PrintStream out, Function vertexToString, Function edgeToString) { out.append("Vertices:").append(System.lineSeparator()); for (int v = 0; v < vertices.size(); v++) { Vertex vertex = vertices.get(v); if (vertex != null) { String str = vertexToString == null ? Objects.toString(vertex.getObject()) : vertexToString.apply(vertex.getObject()); out.append(Integer.toString(v)).append(": ") .append(str) .append(System.lineSeparator()); } } out.append("Edges:").append(System.lineSeparator()); for (int e = 0; e < edges.size(); e++) { Edge edge = edges.get(e); if (edge != null) { String str = edgeToString == null ? Objects.toString(edge.getObject()) : edgeToString.apply(edge.getObject()); out.append(Integer.toString(e)).append(": ") .append(Integer.toString(edge.getV1())).append("<->") .append(Integer.toString(edge.getV2())).append(" ") .append(str).append(System.lineSeparator()); } } } private void removeIsolatedVertices(int v, TIntArrayList[] adjacencyList, boolean notify) { Vertex vertex = vertices.get(v); if (vertex != null && vertex.getObject() == null) { TIntArrayList adjacentEdges = adjacencyList[v]; if (adjacentEdges.isEmpty()) { removeVertexInternal(v, notify); adjacencyList[v] = null; if (!adjacentEdges.isEmpty()) { int e = adjacentEdges.getQuick(0); removeDanglingEdgeAndPropagate(e, v, adjacencyList, notify); } } } } private void removeDanglingEdgeAndPropagate(int edgeToRemove, int vFrom, TIntArrayList[] adjacencyList, boolean notify) { Edge edge = edges.get(edgeToRemove); int v1 = edge.getV1(); int v2 = edge.getV2(); int vTo = v1 == vFrom ? v2 : v1; // updating adjacency list of vFrom & vTo is not done here, as: // - vFrom adjacency list has been set to null when vertex vFrom has been removed // - vTo adjacency list is updated hereafter removeEdgeInternal(edgeToRemove, notify); Vertex vertex = vertices.get(vTo); TIntArrayList adjacentEdges = adjacencyList[vTo]; if (vertex == null || vertex.getObject() != null || adjacentEdges.size() > 2) { // propagation stops: update adjacency list of vertex adjacentEdges.remove(edgeToRemove); return; } // propagate: we know that one of the neighbours (vFrom) of this vertex has been removed, hence: // - if only one adjacent edge, this is a newly isolated vertex // - if only two adjacent edges, this is a newly dangling vertex removeVertexInternal(vTo, notify); adjacencyList[vTo] = null; // find the other edge to remove if dangling vertex if (adjacentEdges.size() == 2) { int otherEdgeToRemove = adjacentEdges.getQuick(0) == edgeToRemove ? adjacentEdges.getQuick(1) : adjacentEdges.getQuick(0); removeDanglingEdgeAndPropagate(otherEdgeToRemove, vTo, adjacencyList, notify); } } @Override public void removeIsolatedVertices() { removeIsolatedVertices(true); } @Override public void removeIsolatedVertices(boolean notify) { TIntArrayList[] adjacencyList = getAdjacencyList(); for (int v = 0; v < vertices.size(); v++) { removeIsolatedVertices(v, adjacencyList, notify); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy