ConcreteDGraph.java

package org.thegalactic.dgraph;

/*
 * ConcreteDGraph.java
 *
 * Copyright: 2010-2015 Karell Bertet, France
 * Copyright: 2015-2016 The Galactic Organization, France
 *
 * License: http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html CeCILL-B license
 *
 * This file is part of java-lattices.
 * You can redistribute it and/or modify it under the terms of the CeCILL-B license.
 */
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * This class gives a standard representation for a directed graph by sets of
 * successors and predecessors.
 *
 * A directed graph is composed of
 *
 * - a treeset of node, defined by class {@link Node}; - a treemap of successors
 * that associates to each node a treeset of successors, defined by class
 * {@link Edge}; - a treemap of predecessors that associates to each node a
 * treeset of predecessors, defined by class {@link Edge}.
 *
 * This class provides methods implementing classical operation on a directed
 * graph:
 *
 * - sinks - wells - subgraph - reflexive closure and reduction - transitive
 * closure - strongly connected components - ...
 *
 * This class also provides a static method randomly generating a directed
 * graph.
 *
 * ![ConcreteDGraph](ConcreteDGraph.png)
 *
 * @param <N> Node content type
 * @param <E> Edge content type
 *
 * @uml DGraph.png
 * !include resources/org/thegalactic/dgraph/DGraph.iuml
 * !include resources/org/thegalactic/dgraph/Edge.iuml
 * !include resources/org/thegalactic/dgraph/Node.iuml
 *
 * hide members
 * show DGraph members
 * class DGraph #LightCyan
 * title DGraph UML graph
 */
public class ConcreteDGraph<N, E> extends AbstractDGraph<N, E> implements Cloneable {

    /*
     * ------------- FIELDS ------------------
     */
    /**
     * A set of elements.
     */
    private TreeSet<Node<N>> nodes;

    /**
     * A map to associate a set of successors to each node.
     *
     * @todo use a TreeSet for edges with a specific comparator for predecessors
     */
    private TreeMap<Node<N>, TreeSet<Edge<N, E>>> successors;

    /**
     * A map to associate a set of predecessors to each node.
     */
    private TreeMap<Node<N>, TreeSet<Edge<N, E>>> predecessors;


    /*
     * ------------- CONSTRUCTORS ------------------
     */
    /**
     * Constructs a new directed graph with an empty set of node.
     */
    public ConcreteDGraph() {
        super();
        this.nodes = new TreeSet<Node<N>>();
        this.successors = new TreeMap<Node<N>, TreeSet<Edge<N, E>>>();
        this.predecessors = new TreeMap<Node<N>, TreeSet<Edge<N, E>>>();
    }

    /**
     * Constructs a new directed graph source the specified set of nodes.
     *
     * Successors and predecessors of each nodes are initialised by an empty
     * set.
     *
     * @param set the set of nodes
     */
    public ConcreteDGraph(final SortedSet<Node<N>> set) {
        this();
        this.nodes.addAll(set);
        for (final Node<N> node : this.nodes) {
            this.successors.put(node, new TreeSet<Edge<N, E>>());
            this.predecessors.put(node, new TreeSet<Edge<N, E>>());
        }
    }

    /**
     * Constructs this component as a copy of the specified directed graph.
     *
     * @param graph the directed graph to be copied
     */
    public ConcreteDGraph(final DGraph<N, E> graph) {
        this(graph.getNodes());
        for (final Edge<N, E> edge : graph.getEdges()) {
            this.addEdge(edge);
        }
    }

    /*
     * --------------- ACCESSOR AND OVERLAPPING METHODS ------------
     */
    /**
     * Returns a clone of this component composed of a clone of each node and
     * each edge.
     *
     * @return the clone of this
     *
     * @throws java.lang.CloneNotSupportedException
     */
    @Override
    public ConcreteDGraph<N, E> clone() throws CloneNotSupportedException {
        final ConcreteDGraph<N, E> graph = (ConcreteDGraph<N, E>) super.clone();
        graph.nodes = (TreeSet) this.nodes.clone();
        graph.successors = new TreeMap<Node<N>, TreeSet<Edge<N, E>>>();
        graph.predecessors = new TreeMap<Node<N>, TreeSet<Edge<N, E>>>();
        for (final Map.Entry<Node<N>, TreeSet<Edge<N, E>>> entry : this.successors.entrySet()) {
            graph.successors.put(entry.getKey(), (TreeSet<Edge<N, E>>) entry.getValue().clone());
        }
        for (final Map.Entry<Node<N>, TreeSet<Edge<N, E>>> entry : this.predecessors.entrySet()) {
            graph.predecessors.put(entry.getKey(), (TreeSet<Edge<N, E>>) entry.getValue().clone());
        }
        return graph;
    }

    /**
     * Returns the set of nodes of this component.
     *
     * @return the set of nodes
     */
    public final SortedSet<Node<N>> getNodes() {
        return Collections.unmodifiableSortedSet(this.nodes);
    }

    /**
     * Returns the set of edges of this component.
     *
     * @return the set of edges
     */
    public final SortedSet<Edge<N, E>> getEdges() {
        return new Edges(this);
    }

    /**
     * Returns the set of edges successors of the specified node.
     *
     * @param node the node to search for
     *
     * @return the set of edges
     */
    public final SortedSet<Edge<N, E>> getSuccessorEdges(final Node<N> node) {
        return Collections.unmodifiableSortedSet(this.successors.get(node));
    }

    /**
     * Returns the set of edges predecessors of the specified node.
     *
     * @param node the node to search for
     *
     * @return the set of edges
     */
    public final SortedSet<Edge<N, E>> getPredecessorEdges(final Node<N> node) {
        return Collections.unmodifiableSortedSet(this.predecessors.get(node));
    }

    /**
     * Returns the set of nodes successors of the specified node.
     *
     * @param node the node to search for
     *
     * @return the set of nodes
     *
     * @todo use iterator pattern (some changes in ArrowRelation.java and
     * Lattice.java)
     */
    public final SortedSet<Node<N>> getSuccessorNodes(final Node<N> node) {
        final SortedSet<Node<N>> successorsFromNode = new TreeSet<Node<N>>();
        for (final Edge<N, E> edge : this.successors.get(node)) {
            successorsFromNode.add(edge.getTarget());
        }
        return Collections.unmodifiableSortedSet(successorsFromNode);
    }

    /**
     * Returns the set of nodes predecessors of the specified node.
     *
     * @param node the node to search for
     *
     * @return the set of nodes
     *
     * @todo use iterator pattern (some changes in ArrowRelation.java and
     * Lattice.java)
     */
    public final SortedSet<Node<N>> getPredecessorNodes(final Node<N> node) {
        final SortedSet<Node<N>> predecessorsFromNode = new TreeSet<Node<N>>();
        for (final Edge<N, E> edge : this.predecessors.get(node)) {
            predecessorsFromNode.add(edge.getSource());
        }
        return Collections.unmodifiableSortedSet(predecessorsFromNode);
    }

    /**
     * Returns, if it exists, the edge between node source and node to.
     *
     * @param source The origin node
     * @param target The destination node
     *
     * @return the found edge or null
     *
     * @todo see getNode
     */
    public final Edge<N, E> getEdge(final Node<N> source, final Node<N> target) {
        Edge<N, E> edge = null;
        if (source != null && target != null) {
            try {
                final Edge<N, E> search = new Edge<N, E>(source, target);
                edge = this.successors.get(source).subSet(search, true, search, true).first();
            } catch (NoSuchElementException ex) {
            }
        }
        return edge;
    }

    /**
     * Returns the node that is equal to the specified one.
     *
     * @param search The node to search for
     *
     * @return the found node or null
     *
     * @todo maybe use try { return this.nodes.subSet(search, true, search,
     * true).first(); } catch (NoSuchElementException e) { return null; }
     *
     */
    public final Node<N> getNode(final Node<N> search) {
        for (final Node<N> node : this.nodes) {
            if (node.equals(search)) {
                return node;
            }
        }
        return null;
    }

    /**
     * Returns the node whose content is equal to the specified one.
     *
     * @param content The content to search for
     *
     * @return the found node or null
     *
     * @todo this method is not efficient. Do we remove it or add an index on
     * DGraph using content field? Verify where it is called for migrating it if
     * necessary.
     */
    public final Node<N> getNodeByContent(final Object content) {
        for (final Node<N> node : this.nodes) {
            if (node.getContent().equals(content)) {
                return node;
            }
        }
        return null;
    }

    /**
     * Returns the node whose ident is equal to the specified one.
     *
     * @param identifier node identifier
     *
     * @return the found node or null
     */
    public final Node<N> getNodeByIdentifier(int identifier) {
        for (final Node<N> node : this.nodes) {
            if (node.getIdentifier() == identifier) {
                return node;
            }
        }
        return null;
    }

    /**
     * Returns the number of nodes of this component.
     *
     * @return the number of nodes
     */
    public final int sizeNodes() {
        return this.nodes.size();
    }

    /**
     * Returns the number of edges of this component.
     *
     * @return the number of edges
     */
    public final int sizeEdges() {
        int size = 0;
        for (final Node<N> node : this.nodes) {
            size += this.successors.get(node).size();
        }
        return size;
    }


    /*
     * --------------- NODES AND EDGES MODIFICATION METHODS ------------
     */
    /**
     * Checks if the specified node belong to this component.
     *
     * @param node the node to insert
     *
     * @return true if the node has been successfully inserted
     */
    public final boolean containsNode(final Node<N> node) {
        return this.nodes.contains(node);
    }

    /**
     * Adds the specified node to the set of node of this component.
     *
     * @param node the node to insert
     *
     * @return true if the node has been successfully inserted
     */
    public final boolean addNode(final Node<N> node) {
        if (!this.containsNode(node)) {
            this.nodes.add(node);
            this.successors.put(node, new TreeSet<Edge<N, E>>());
            this.predecessors.put(node, new TreeSet<Edge<N, E>>());
            return true;
        }
        return false;
    }

    /**
     * Removes the specified node from this component.
     *
     * @param node the node to remove
     *
     * @return true if the node was successfully removed
     */
    public final boolean removeNode(final Node<N> node) {
        if (this.containsNode(node)) {
            // Remove the edges (node,target) with key node in successors, and key target in predecessors
            for (final Edge<N, E> successor : this.successors.get(node)) {
                if (successor.getTarget().compareTo(node) != 0) {
                    for (final Edge<N, E> predecessor : this.predecessors.get(successor.getTarget())) {
                        if (predecessor.getSource().compareTo(node) == 0) {
                            this.predecessors.get(successor.getTarget()).remove(predecessor);
                        }
                    }
                }
                this.successors.remove(node);
            }
            // Remove the edges (source,node) with key node in predecessors, and key source in successors
            for (final Edge<N, E> predecessor : this.predecessors.get(node)) {
                if (predecessor.getSource().compareTo(node) != 0) {
                    for (final Edge<N, E> successor : this.successors.get(predecessor.getSource())) {
                        if (successor.getTarget().compareTo(node) == 0) {
                            this.successors.get(predecessor.getSource()).remove(successor);
                        }
                    }
                }
                this.predecessors.remove(node);
            }
            // Remove node
            this.nodes.remove(node);
            return true;
        }
        return false;
    }

    /**
     * Removes the specified set of nodes from this component.
     *
     * @param nodes set of nodes
     *
     * @return true if all nodes were removed
     */
    public final boolean removeNodes(final Set<Node<N>> nodes) {
        boolean all = true;
        for (final Node<N> node : nodes) {
            if (!this.removeNode(node)) {
                all = false;
            }
        }
        return all;
    }

    /**
     * Checks if there exists an edge between the two specified nodes.
     *
     * @param source the node origine of the edge
     * @param target the node destination of the edge
     *
     * @return true if the edge is contained by this component
     */
    public final boolean containsEdge(final Node<N> source, final Node<N> target) {
        return this.containsNode(source)
                && this.containsNode(target)
                && this.getSuccessorNodes(source).contains(target)
                && this.getPredecessorNodes(target).contains(source);
    }

    /**
     * Checks if the specified edge belong to this component.
     *
     * @param edge the edge to be checked
     *
     * @return true if the edge is contained by this component
     */
    public final boolean containsEdge(final Edge<N, E> edge) {
        return this.containsEdge(edge.getSource(), edge.getTarget());
    }

    /**
     * Adds an edge between the specified nodes to this component: `to` is added
     * as a successor of `source`.
     *
     * If the case where specified nodes don't belongs to the node set, then the
     * edge will not be added.
     *
     * @param source  the node origin of the edge
     * @param target  the node destination of the edge
     * @param content the edge content
     *
     * @return true if the edge was successfully added
     */
    public final boolean addEdge(final Node<N> source, final Node<N> target, final Object content) {
        if (this.containsNode(source) && this.containsNode(target)) {
            final Edge<N, E> edge = new Edge(source, target, content);
            this.successors.get(source).add(edge);
            this.predecessors.get(target).add(edge);
            return true;
        }
        return false;
    }

    /**
     * Adds an edge between the specified nodes to this component: `to` is added
     * as a successor of `source`.
     *
     * If the case where specified nodes don't belongs to the node set, then the
     * edge will not be added.
     *
     * @param source the node origin of the edge
     * @param target the node destination of the edge
     *
     * @return true if the edge was successfully added
     */
    public final boolean addEdge(final Node<N> source, final Node<N> target) {
        return this.addEdge(source, target, null);
    }

    /**
     * Adds the specified edge to this component in the successors of
     * edge.getSource() and in the predecessors of edge.getTarget().
     *
     * If the case where nodes to and source of this edges don't belongs to the
     * node set, then the edge will not be added.
     *
     * @param edge the edge to be added
     *
     * @return true if the edge was added
     */
    public final boolean addEdge(final Edge<N, E> edge) {
        if (this.containsNode(edge.getSource()) && this.containsNode(edge.getTarget())) {
            this.successors.get(edge.getSource()).add(edge);
            this.predecessors.get(edge.getTarget()).add(edge);
            return true;
        }
        return false;
    }

    /**
     * Removes source this component the edge between the specified node.
     *
     * `to` is removed source the successors of `source` and `to` is removed
     * source
     * the predecessors of `source`.
     *
     * @param source the node origine of the edge
     * @param target the node destination of the edge
     *
     * @return true if the edge was removed
     */
    public final boolean removeEdge(final Node<N> source, final Node<N> target) {
        if (this.containsEdge(source, target)) {
            final Edge<N, E> edge = new Edge(source, target);
            this.successors.get(source).remove(edge);
            this.predecessors.get(target).remove(edge);
            return true;
        }
        return false;
    }

    /**
     * Removes source this component the specified edge source the successors of
     * edge.getSource() and source the predecessors of edge.getTarget().
     *
     * @param edge the edge to be removed.
     *
     * @return true if the edge was removed
     */
    public final boolean removeEdge(final Edge<N, E> edge) {
        if (this.containsEdge(edge)) {
            this.successors.get(edge.getSource()).remove(edge);
            this.predecessors.get(edge.getTarget()).remove(edge);
            return true;
        }
        return false;
    }

    /*
     * --------------- ACYCLIC CHECKING METHODS ------------
     */
    /**
     * Returns the subgraph of this component induced by the specified set of
     * nodes.
     *
     * The subgraph only contains nodes of the specified set that also are in
     * this component.
     *
     * @param nodes The set of nodes
     *
     * @return The subgraph
     *
     * @todo implement a SubGraph class?
     */
    public ConcreteDGraph<N, E> getSubgraphByNodes(final Set<Node<N>> nodes) {
        ConcreteDGraph<N, E> graph = new ConcreteDGraph<N, E>();
        // addition of nodes in the subgraph
        for (Node<N> node : nodes) {
            if (this.containsNode(node)) {
                graph.addNode(node);
            }
        }
        // addition of edges in the subgraph
        for (Edge<N, E> edge : this.getEdges()) {
            if (graph.containsNode(edge.getTarget())) {
                graph.addEdge(edge);
            }
        }
        return graph;
    }

    /**
     * Returns the subgraph of this component induced by the specified set of
     * edges.
     *
     * The subgraph contains all nodes of this components, and only edges of the
     * specified set that also are in this component.
     *
     * @param edges The set of edges
     *
     * @return The subgraph
     *
     * @todo implement a SubGraph class?
     */
    public ConcreteDGraph<N, E> getSubgraphByEdges(final Set<Edge<N, E>> edges) {
        ConcreteDGraph<N, E> graph = new ConcreteDGraph<N, E>();
        // addition of all nodes in the subgraph
        for (Node<N> node : this.nodes) {
            graph.addNode(node);
        }
        // addition of specified edges in the subgraph
        for (Edge<N, E> edge : edges) {
            if (this.containsEdge(edge)) {
                graph.addEdge(edge);
            }
        }
        return graph;
    }

    /**
     * Replaces this component by its complementary graph.
     *
     * There is an edge between to nodes in the complementary graph when there
     * is no edges between the nodes in this component.
     */
    public void complementary() {
        for (Node<N> source : this.nodes) {
            TreeSet<Node<N>> newSuccessors = new TreeSet<Node<N>>(this.nodes);
            newSuccessors.removeAll(this.getSuccessorNodes(source));
            TreeSet<Node<N>> oldSuccessors = new TreeSet<Node<N>>(this.getSuccessorNodes(source));
            for (Node<N> target : oldSuccessors) {
                this.removeEdge(source, target);
            }
            for (Node<N> target : newSuccessors) {
                this.addEdge(source, target);
            }
        }
    }

    /*
     * --------------- GRAPH TREATMENT METHODS ------------
     */
    /**
     * Computes the reflexive reduction of this component.
     *
     * @return the number of removed edges
     */
    public final int reflexiveReduction() {
        int size = 0;
        for (final Node<N> node : this.nodes) {
            if (this.containsEdge(node, node)) {
                size++;
                this.removeEdge(node, node);
            }
        }
        return size;
    }

    /**
     * Computes the reflexive closure of this component.
     *
     * @return the number of added edges
     */
    public final int reflexiveClosure() {
        int size = 0;
        for (final Node<N> node : this.nodes) {
            if (!this.containsEdge(node, node)) {
                size++;
                this.addEdge(node, node);
            }
        }
        return size;
    }

    /**
     * Computes the transitive closure of this component.
     *
     * This treatment is performed in $O(nm+m_c)$, where $n$ corresponds to the
     * number of nodes, $m$ to the number of edges, and $m_c$ to the number of edges
     * in the closure. This treatment improves the Roy-Warshall algorithm that
     * directly implements the definition in $O(log(m) n^3)$.
     *
     * This treatment is overridden in class {@link DAGraph} with a more
     * efficient algorithm dedicated to directed acyclic graph.
     *
     * @return the number of added edges
     */
    public int transitiveClosure() {
        int size = 0;
        // mark each node to false
        final TreeMap<Node<N>, Boolean> mark = new TreeMap<Node<N>, Boolean>();
        for (final Node<N> node : this.nodes) {
            mark.put(node, Boolean.FALSE);
        }
        // treatment of nodes
        final List<Node<N>> list = new ArrayList<Node<N>>();
        for (final Node<N> source : this.nodes) {
            list.clear();
            list.add(source);
            while (!list.isEmpty()) {
                // Remove the first node
                final Node<N> source2 = list.remove(0);
                for (final Node<N> target : this.getSuccessorNodes(source2)) {
                    // treatment of target when not marked
                    if (!mark.get(target)) {
                        mark.put(target, Boolean.TRUE);
                        this.addEdge(source, target);
                        list.add(target);
                        size++;
                    }
                }
            }
            for (final Node<N> target : this.getSuccessorNodes(source)) {
                mark.put(target, Boolean.FALSE);
            }
        }
        return size;
    }

    /**
     * Transposes this component by replacing for each node its successor set by
     * its predecessor set, and its predecessor set by its successor set.
     */
    public final void transpose() {
        ConcreteDGraph<N, E> tmp = new ConcreteDGraph<N, E>(this);
        for (Edge<N, E> edge : tmp.getEdges()) {
            this.removeEdge(edge);
            this.addEdge(new Edge(edge.getTarget(), edge.getSource(), edge.getContent()));
        }
    }

    /**
     * Returns the directed acyclic graph where each node corresponds to a
     * strongly connected component (SCC) of this component stored in a TreeSet
     * of nodes.
     *
     * When two nodes in two different SCC are in relation, the same is for the
     * SCC they belongs to.
     *
     * @return The directed acyclic graph
     */
    public DAGraph<SortedSet<Node<N>>, Object> getStronglyConnectedComponent() {
        DAGraph<SortedSet<Node<N>>, Object> cc = new DAGraph<SortedSet<Node<N>>, Object>();
        // first depth first search
        ConcreteDGraph<N, E> tmp = new ConcreteDGraph<N, E>(this);
        tmp.transitiveClosure();
        ArrayList<Node<N>> last = tmp.depthFirstSearch()[1];
        // transposition of the graph
        ConcreteDGraph<N, E> transposedGraph = new ConcreteDGraph<N, E>(this);
        transposedGraph.transpose();
        // sort nodes according to the reverse last sort
        ArrayList<Node<N>> sort = new ArrayList<Node<N>>();
        Object[] array = last.toArray();
        for (int i = array.length - 1; i >= 0; i--) {
            sort.add((Node<N>) array[i]);
        }
        // second depth first search according to sort
        TreeSet<Node<N>> visited = new TreeSet<Node<N>>();
        for (Node<N> node : sort) {
            if (!visited.contains(node)) {
                last = transposedGraph.depthFirstSearch(node, visited, sort)[1];
                visited.addAll(last);
                TreeSet<Node<N>> sCC = new TreeSet<Node<N>>(last);
                // a strongly connected component is generated
                cc.addNode(new Node<SortedSet<Node<N>>>(sCC));    // addition of
            }
        }
        // edges between strongly connected component
        for (Node<SortedSet<Node<N>>> ccSource : cc.getNodes()) {
            for (Node<SortedSet<Node<N>>> ccTarget : cc.getNodes()) {
                for (Node<N> source : ccSource.getContent()) {
                    for (Node<N> target : ccTarget.getContent()) {
                        if (tmp.getSuccessorNodes(source).contains(target)) {
                            cc.addEdge(ccSource, ccTarget);
                        }
                    }
                }
            }
        }
        cc.reflexiveReduction();
        return cc;
    }

    /**
     * Set the set of nodes of this component.
     *
     * @param nodes The nodes
     *
     * @return this for chaining
     */
    protected ConcreteDGraph<N, E> setNodes(final TreeSet<Node<N>> nodes) {
        this.nodes = nodes;
        return this;
    }

    /**
     * Returns the successors of this component.
     *
     * @return the map
     */
    protected TreeMap<Node<N>, TreeSet<Edge<N, E>>> getSuccessors() {
        return this.successors;
    }

    /**
     * Set the successors of this component.
     *
     * @param successors The successors
     *
     * @return this for chaining
     */
    protected ConcreteDGraph<N, E> setSuccessors(final TreeMap<Node<N>, TreeSet<Edge<N, E>>> successors) {
        this.successors = successors;
        return this;
    }

    /**
     * Returns the predecessors of this component.
     *
     * @return the map
     */
    protected TreeMap<Node<N>, TreeSet<Edge<N, E>>> getPredecessors() {
        return this.predecessors;
    }

    /**
     * Set the predecessors of this component.
     *
     * @param predecessors The predecessors
     *
     * @return this for chaining
     */
    protected ConcreteDGraph<N, E> setPredecessors(final TreeMap<Node<N>, TreeSet<Edge<N, E>>> predecessors) {
        this.predecessors = predecessors;
        return this;
    }

    /**
     * Returns a two cells array containing the first visited sort on the nodes,
     * and the last visited sort on the nodes, by a depth first traverse issued
     * source the specified node.
     *
     * @param source  The source node
     * @param visited The visited nodes
     * @param sort    The sort parameter
     *
     * @return The array
     *
     * @todo Do we change that to iterators? Change to private and add a method
     * that return an iterator
     */
    private ArrayList<Node<N>>[] depthFirstSearch(Node<N> source, TreeSet<Node<N>> visited, ArrayList<Node<N>> sort) {
        ArrayList<Node<N>> first = new ArrayList<Node<N>>();
        ArrayList<Node<N>> last = new ArrayList<Node<N>>();
        first.add(source);
        visited.add(source);
        ArrayList<Node<N>>[] arrayVisited = new ArrayList[2];
        if (sort != null) {
            // search according to a sort
            for (Node<N> node : sort) {
                if (this.getSuccessorNodes(source).contains(node) && !visited.contains(node)) {
                    arrayVisited = this.depthFirstSearch(node, visited, sort);
                    visited.addAll(arrayVisited[0]);
                    first.addAll(arrayVisited[0]);
                    last.addAll(arrayVisited[1]);
                }
            }
        } else {
            // classical search
            for (Node<N> node : this.getSuccessorNodes(source)) {
                if (!visited.contains(node)) {
                    arrayVisited = this.depthFirstSearch(node, visited, sort);
                    visited.addAll(arrayVisited[0]);
                    first.addAll(arrayVisited[0]);
                    last.addAll(arrayVisited[1]);
                }
            }
        }
        last.add(source);
        arrayVisited[0] = first;
        arrayVisited[1] = last;
        return arrayVisited;
    }

    /**
     * Returns a two cells array containing the first visited sort on the nodes,
     * and the last visited sort on the nodes, by a depth first search.
     *
     * @return The array
     */
    private ArrayList<Node<N>>[] depthFirstSearch() {
        TreeSet<Node<N>> visited = new TreeSet<Node<N>>();
        ArrayList<Node<N>>[] arrayVisited = new ArrayList[2];
        ArrayList<Node<N>> first = new ArrayList<Node<N>>();
        ArrayList<Node<N>> last = new ArrayList<Node<N>>();
        for (Node<N> node : this.nodes) {
            if (!visited.contains(node)) {
                arrayVisited = this.depthFirstSearch(node, visited, null);
                visited.addAll(arrayVisited[0]);
                first.addAll(arrayVisited[0]);
                last.addAll(arrayVisited[1]);
            }
        }
        arrayVisited[0] = first;
        arrayVisited[1] = last;
        return arrayVisited;
    }

    /**
     * This class implements a sorted set of the edges.
     *
     * @param <N> Node content type
     * @param <E> Edge content type
     */
    private class Edges<N, E> extends AbstractSet<Edge<N, E>> implements SortedSet<Edge<N, E>> {

        /**
         * The underlying graph.
         */
        private final ConcreteDGraph<N, E> graph;

        /**
         * Constructs a sorted set of the edges source a graph.
         *
         * @param graph A ConcreteDGraph
         */
        Edges(final ConcreteDGraph<N, E> graph) {
            this.graph = graph;
        }

        /**
         * Get the underlying graph.
         *
         * @return the graph
         */
        protected final ConcreteDGraph<N, E> getGraph() {
            return this.graph;
        }

        /**
         * Implements the SortedSet interface.
         *
         * @return the first edge
         */
        public final Edge<N, E> first() {
            throw new UnsupportedOperationException();
        }

        /**
         * Implements the SortedSet interface.
         *
         * @return the last edge
         */
        public final Edge<N, E> last() {
            throw new UnsupportedOperationException();
        }

        /**
         * Implements the SortedSet interface.
         *
         * @param edge the to edge
         *
         * @return The head set
         *
         * @throws UnsupportedOperationException
         */
        public final SortedSet<Edge<N, E>> headSet(final Edge<N, E> edge) {
            throw new UnsupportedOperationException();
        }

        /**
         * Implements the SortedSet interface.
         *
         * @param edge the source edge
         *
         * @return The tail set
         *
         * @throws UnsupportedOperationException
         */
        public final SortedSet<Edge<N, E>> tailSet(final Edge<N, E> edge) {
            throw new UnsupportedOperationException();
        }

        /**
         * Implements the SortedSet interface.
         *
         * @param fromEdge the source edge
         * @param toEdge   the to edge
         *
         * @return The sub set
         *
         * @throws UnsupportedOperationException
         */
        public final SortedSet<Edge<N, E>> subSet(final Edge<N, E> fromEdge, final Edge<N, E> toEdge) {
            throw new UnsupportedOperationException();
        }

        /**
         * Implements the SortedSet interface.
         *
         * @return null
         */
        public final Comparator<? super Edge<N, E>> comparator() {
            return null;
        }

        /**
         * Implements the AbstractCollection class.
         *
         * @return the size of the collection
         */
        public final int size() {
            return this.graph.sizeEdges();
        }

        /**
         * Implements the AbstractCollection class.
         *
         * @return a new edges iterator
         */
        public final EdgesIterator iterator() {
            return new EdgesIterator(this);
        }

        /**
         * This class implements an iterator over the edges of a graph.
         */
        private class EdgesIterator implements Iterator<Edge<N, E>> {

            /**
             * The nodes iterator.
             */
            private final Iterator<Node<N>> nodesIterator;

            /**
             * The edges iterator for the current node.
             */
            private Iterator<Edge<N, E>> edgesIterator;

            /**
             * The edges object.
             */
            private final Edges<N, E> edges;

            /**
             * The hasNext flag.
             */
            private boolean hasNext;

            /**
             * Constructs the iterator source a set of edges.
             *
             * @param edges The edges.
             */
            EdgesIterator(final Edges<N, E> edges) {
                this.edges = edges;
                this.nodesIterator = edges.graph.nodes.iterator();
                this.prepareNext();
            }

            /**
             * Prepare the next edge and the hasNext flag.
             */
            private void prepareNext() {
                this.hasNext = false;
                while (!this.hasNext && this.nodesIterator.hasNext()) {
                    this.edgesIterator = this.edges.getGraph().successors.get(this.nodesIterator.next()).iterator();
                    this.hasNext = this.edgesIterator.hasNext();
                }
            }

            /**
             * The remove operation is not supported.
             *
             * @throws UnsupportedOperationException
             */
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

            /**
             * The next method returns the next edge.
             *
             * @return The next edge
             */
            public Edge<N, E> next() {
                Edge<N, E> edge = this.edgesIterator.next();
                if (!this.edgesIterator.hasNext()) {
                    this.prepareNext();
                }
                return edge;
            }

            /**
             * The hasNext method return true if the iterator has a next edge.
             *
             * @return true if the iterator has a next edge
             */
            public boolean hasNext() {
                return this.hasNext;
            }
        }
    }
}