/* -*- mode: C -*-  */
/*
   IGraph library.
   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
   334 Harvard street, Cambridge, MA 02139 USA

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301 USA

*/

#include "igraph_components.h"

#include "igraph_adjlist.h"
#include "igraph_dqueue.h"
#include "igraph_interface.h"
#include "igraph_memory.h"
#include "igraph_operators.h"
#include "igraph_progress.h"
#include "igraph_stack.h"
#include "igraph_structural.h"
#include "igraph_vector.h"

#include "core/interruption.h"
#include "operators/subgraph.h"

#include <limits.h>

static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership,
                                  igraph_vector_t *csize, igraph_integer_t *no);

static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership,
                                    igraph_vector_t *csize, igraph_integer_t *no);

/**
 * \ingroup structural
 * \function igraph_clusters
 * \brief Calculates the (weakly or strongly) connected components in a graph.
 *
 * \param graph The graph object to analyze.
 * \param membership First half of the result will be stored here. For
 *        every vertex the id of its component is given. The vector
 *        has to be preinitialized and will be resized. Alternatively
 *        this argument can be \c NULL, in which case it is ignored.
 * \param csize The second half of the result. For every component it
 *        gives its size, the order is defined by the component ids.
 *        The vector has to be preinitialized and will be resized.
 *        Alternatively this argument can be \c NULL, in which
 *        case it is ignored.
 * \param no Pointer to an integer, if not \c NULL then the number of
 *        clusters will be stored here.
 * \param mode For directed graph this specifies whether to calculate
 *        weakly or strongly connected components. Possible values:
 *        \c IGRAPH_WEAK,
 *        \c IGRAPH_STRONG. This argument is
 *        ignored for undirected graphs.
 * \return Error code:
 *         \c IGRAPH_EINVAL: invalid mode argument.
 *
 * Time complexity: O(|V|+|E|),
 * |V| and
 * |E| are the number of vertices and
 * edges in the graph.
 */

int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership,
                    igraph_vector_t *csize, igraph_integer_t *no,
                    igraph_connectedness_t mode) {
    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
        return igraph_i_clusters_weak(graph, membership, csize, no);
    } else if (mode == IGRAPH_STRONG) {
        return igraph_i_clusters_strong(graph, membership, csize, no);
    }

    IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL);
}

static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership,
                                  igraph_vector_t *csize, igraph_integer_t *no) {

    long int no_of_nodes = igraph_vcount(graph);
    char *already_added;
    long int first_node, act_cluster_size = 0, no_of_clusters = 1;

    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;

    long int i;
    igraph_vector_t neis = IGRAPH_VECTOR_NULL;

    already_added = IGRAPH_CALLOC(no_of_nodes, char);
    if (already_added == 0) {
        IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_ENOMEM);
    }
    IGRAPH_FINALLY(igraph_free, already_added);

    IGRAPH_DQUEUE_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10);
    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);

    /* Memory for result, csize is dynamically allocated */
    if (membership) {
        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
    }
    if (csize) {
        igraph_vector_clear(csize);
    }

    /* The algorithm */

    for (first_node = 0; first_node < no_of_nodes; ++first_node) {
        if (already_added[first_node] == 1) {
            continue;
        }
        IGRAPH_ALLOW_INTERRUPTION();

        already_added[first_node] = 1;
        act_cluster_size = 1;
        if (membership) {
            VECTOR(*membership)[first_node] = no_of_clusters - 1;
        }
        IGRAPH_CHECK(igraph_dqueue_push(&q, first_node));

        while ( !igraph_dqueue_empty(&q) ) {
            long int act_node = (long int) igraph_dqueue_pop(&q);
            IGRAPH_CHECK(igraph_neighbors(graph, &neis,
                                          (igraph_integer_t) act_node, IGRAPH_ALL));
            for (i = 0; i < igraph_vector_size(&neis); i++) {
                long int neighbor = (long int) VECTOR(neis)[i];
                if (already_added[neighbor] == 1) {
                    continue;
                }
                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                already_added[neighbor] = 1;
                act_cluster_size++;
                if (membership) {
                    VECTOR(*membership)[neighbor] = no_of_clusters - 1;
                }
            }
        }
        no_of_clusters++;
        if (csize) {
            IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size));
        }
    }

    /* Cleaning up */

    if (no) {
        *no = (igraph_integer_t) no_of_clusters - 1;
    }

    IGRAPH_FREE(already_added);
    igraph_dqueue_destroy(&q);
    igraph_vector_destroy(&neis);
    IGRAPH_FINALLY_CLEAN(3);

    return 0;
}

static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership,
                                    igraph_vector_t *csize, igraph_integer_t *no) {

    long int no_of_nodes = igraph_vcount(graph);
    igraph_vector_t next_nei = IGRAPH_VECTOR_NULL;

    long int i, n, num_seen;
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;

    long int no_of_clusters = 1;
    long int act_cluster_size;

    igraph_vector_t out = IGRAPH_VECTOR_NULL;
    const igraph_vector_int_t* tmp;

    igraph_adjlist_t adjlist;

    /* The result */

    IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&out, 0);
    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);

    if (membership) {
        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
    }
    IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes));

    igraph_vector_null(&out);
    if (csize) {
        igraph_vector_clear(csize);
    }

    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);

    num_seen = 0;
    for (i = 0; i < no_of_nodes; i++) {
        IGRAPH_ALLOW_INTERRUPTION();

        tmp = igraph_adjlist_get(&adjlist, i);
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
            continue;
        }

        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
        while (!igraph_dqueue_empty(&q)) {
            long int act_node = (long int) igraph_dqueue_back(&q);
            tmp = igraph_adjlist_get(&adjlist, act_node);
            if (VECTOR(next_nei)[act_node] == 0) {
                /* this is the first time we've met this vertex */
                VECTOR(next_nei)[act_node]++;
            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
                /* we've already met this vertex but it has more children */
                long int neighbor = (long int) VECTOR(*tmp)[(long int)
                                    VECTOR(next_nei)[act_node] - 1];
                if (VECTOR(next_nei)[neighbor] == 0) {
                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                }
                VECTOR(next_nei)[act_node]++;
            } else {
                /* we've met this vertex and it has no more children */
                IGRAPH_CHECK(igraph_vector_push_back(&out, act_node));
                igraph_dqueue_pop_back(&q);
                num_seen++;

                if (num_seen % 10000 == 0) {
                    /* time to report progress and allow the user to interrupt */
                    IGRAPH_PROGRESS("Strongly connected components: ",
                                    num_seen * 50.0 / no_of_nodes, NULL);
                    IGRAPH_ALLOW_INTERRUPTION();
                }
            }
        } /* while q */
    }  /* for */

    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);

    igraph_adjlist_destroy(&adjlist);
    IGRAPH_FINALLY_CLEAN(1);

    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);

    /* OK, we've the 'out' values for the nodes, let's use them in
       decreasing order with the help of a heap */

    igraph_vector_null(&next_nei);             /* mark already added vertices */
    num_seen = 0;

    while (!igraph_vector_empty(&out)) {
        long int grandfather = (long int) igraph_vector_pop_back(&out);

        if (VECTOR(next_nei)[grandfather] != 0) {
            continue;
        }
        VECTOR(next_nei)[grandfather] = 1;
        act_cluster_size = 1;
        if (membership) {
            VECTOR(*membership)[grandfather] = no_of_clusters - 1;
        }
        IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather));

        num_seen++;
        if (num_seen % 10000 == 0) {
            /* time to report progress and allow the user to interrupt */
            IGRAPH_PROGRESS("Strongly connected components: ",
                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
            IGRAPH_ALLOW_INTERRUPTION();
        }

        while (!igraph_dqueue_empty(&q)) {
            long int act_node = (long int) igraph_dqueue_pop_back(&q);
            tmp = igraph_adjlist_get(&adjlist, act_node);
            n = igraph_vector_int_size(tmp);
            for (i = 0; i < n; i++) {
                long int neighbor = (long int) VECTOR(*tmp)[i];
                if (VECTOR(next_nei)[neighbor] != 0) {
                    continue;
                }
                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                VECTOR(next_nei)[neighbor] = 1;
                act_cluster_size++;
                if (membership) {
                    VECTOR(*membership)[neighbor] = no_of_clusters - 1;
                }

                num_seen++;
                if (num_seen % 10000 == 0) {
                    /* time to report progress and allow the user to interrupt */
                    IGRAPH_PROGRESS("Strongly connected components: ",
                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
                    IGRAPH_ALLOW_INTERRUPTION();
                }
            }
        }

        no_of_clusters++;
        if (csize) {
            IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size));
        }
    }

    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);

    if (no) {
        *no = (igraph_integer_t) no_of_clusters - 1;
    }

    /* Clean up, return */

    igraph_adjlist_destroy(&adjlist);
    igraph_vector_destroy(&out);
    igraph_dqueue_destroy(&q);
    igraph_vector_destroy(&next_nei);
    IGRAPH_FINALLY_CLEAN(4);

    return 0;
}

static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res);

/**
 * \ingroup structural
 * \function igraph_is_connected
 * \brief Decides whether the graph is (weakly or strongly) connected.
 *
 * A graph is considered connected when any of its vertices is reachable
 * from any other. A directed graph with this property is called
 * \em strongly connected. A directed graph that would be connected when
 * ignoring the directions of its edges is called \em weakly connected.
 *
 * </para><para>
 * A graph with zero vertices (i.e. the null graph) is \em not connected by
 * definition. This behaviour changed in igraph 0.9; earlier versions assumed
 * that the null graph is connected. See the following issue on Github for the
 * argument that led us to change the definition:
 * https://github.com/igraph/igraph/issues/1539
 *
 * \param graph The graph object to analyze.
 * \param res Pointer to a logical variable, the result will be stored
 *        here.
 * \param mode For a directed graph this specifies whether to calculate
 *        weak or strong connectedness. Possible values:
 *        \c IGRAPH_WEAK,
 *        \c IGRAPH_STRONG. This argument is
 *        ignored for undirected graphs.
 * \return Error code:
 *        \c IGRAPH_EINVAL: invalid mode argument.
 *
 * Time complexity: O(|V|+|E|), the
 * number of vertices
 * plus the number of edges in the graph.
 */

int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res,
                        igraph_connectedness_t mode) {

    long int no_of_nodes = igraph_vcount(graph);

    if (no_of_nodes == 0) {
        /* Changed in igraph 0.9; see https://github.com/igraph/igraph/issues/1539
         * for the reasoning behind the change */
        *res = 0;
        return IGRAPH_SUCCESS;
    }

    if (no_of_nodes == 1) {
        *res = 1;
        return IGRAPH_SUCCESS;
    }

    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
        return igraph_is_connected_weak(graph, res);
    } else if (mode == IGRAPH_STRONG) {
        int retval;
        igraph_integer_t no;

        /* A strongly connected graph has at least as many edges as vertices,
         * except for the singleton graph, which is handled above. */
        if (igraph_ecount(graph) < no_of_nodes) {
            *res = 0;
            return IGRAPH_SUCCESS;
        }

        retval = igraph_i_clusters_strong(graph, NULL, NULL, &no);
        *res = (no == 1);
        return retval;
    }

    IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
}

static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) {

    long int no_of_nodes = igraph_vcount(graph), no_of_edges = igraph_ecount(graph);
    long int added_count;
    char *already_added;
    igraph_vector_t neis = IGRAPH_VECTOR_NULL;
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;

    /* By convention, the null graph is not considered connected.
     * See https://github.com/igraph/igraph/issues/1538 */
    if (no_of_nodes == 0) {
        *res = 0;
        return IGRAPH_SUCCESS;
    }

    /* A connected graph has at least |V| - 1 edges. */
    if (no_of_edges < no_of_nodes - 1) {
        *res = 0;
        return IGRAPH_SUCCESS;
    }

    already_added = IGRAPH_CALLOC(no_of_nodes, char);
    if (already_added == 0) {
        IGRAPH_ERROR("Weak connectedness check failed.", IGRAPH_ENOMEM);
    }
    IGRAPH_FINALLY(igraph_free, already_added);

    IGRAPH_DQUEUE_INIT_FINALLY(&q, 10);
    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);

    /* Try to find at least two clusters */
    already_added[0] = 1;
    IGRAPH_CHECK(igraph_dqueue_push(&q, 0));

    added_count = 1;
    while ( !igraph_dqueue_empty(&q)) {
        IGRAPH_ALLOW_INTERRUPTION();

        long int actnode = (long int) igraph_dqueue_pop(&q);

        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, IGRAPH_ALL));
        long int nei_count = igraph_vector_size(&neis);

        for (long int i = 0; i < nei_count; i++) {
            long int neighbor = (long int) VECTOR(neis)[i];
            if (already_added[neighbor]) {
                continue;
            }

            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
            added_count++;
            already_added[neighbor] = 1;

            if (added_count == no_of_nodes) {
                /* We have already reached all nodes: the graph is connected.
                 * We can stop the traversal now. */
                igraph_dqueue_clear(&q);
                break;
            }
        }
    }

    /* Connected? */
    *res = (added_count == no_of_nodes);

    IGRAPH_FREE(already_added);
    igraph_dqueue_destroy(&q);
    igraph_vector_destroy(&neis);
    IGRAPH_FINALLY_CLEAN(3);

    return IGRAPH_SUCCESS;
}

/**
 * \function igraph_decompose_destroy
 * \brief Free the memory allocated by \ref igraph_decompose().
 *
 * This function destroys and frees all <type>igraph_t</type>
 * objects held in \p complist. However, it does not destroy
 * \p complist itself, as it was not allocated by \ref igraph_decompose().
 * Use \ref igraph_vector_ptr_destroy() to destroy \p complist.
 *
 * \param complist The list of graph components, as returned by
 *        \ref igraph_decompose().
 *
 * Time complexity: O(c), c is the number of components.
 */

void igraph_decompose_destroy(igraph_vector_ptr_t *complist) {
    long int i;
    for (i = 0; i < igraph_vector_ptr_size(complist); i++) {
        if (VECTOR(*complist)[i] != 0) {
            igraph_destroy(VECTOR(*complist)[i]);
            igraph_free(VECTOR(*complist)[i]);
        }
    }
}

static int igraph_i_decompose_weak(const igraph_t *graph,
                                   igraph_vector_ptr_t *components,
                                   long int maxcompno, long int minelements);

static int igraph_i_decompose_strong(const igraph_t *graph,
                                     igraph_vector_ptr_t *components,
                                     long int maxcompno, long int minelements);

/**
 * \function igraph_decompose
 * \brief Decompose a graph into connected components.
 *
 * Create separate graph for each component of a graph. Note that the
 * vertex ids in the new graphs will be different than in the original
 * graph. (Except if there is only one component in the original graph.)
 *
 * \param graph The original graph.
 * \param components This pointer vector will contain pointers to the
 *   subcomponent graphs. It should be initialized before calling this
 *   function and will be resized to hold the graphs. Don't forget to
 *   call \ref igraph_destroy() and \ref igraph_free() on the elements of
 *   this pointer vector to free unneeded memory. Alternatively, you can
 *   simply call \ref igraph_decompose_destroy() that does this for you.
 * \param mode Either \c IGRAPH_WEAK or \c IGRAPH_STRONG for weakly
 *    and strongly connected components respectively.
 * \param maxcompno The maximum number of components to return. The
 *    first \p maxcompno components will be returned (which hold at
 *    least \p minelements vertices, see the next parameter), the
 *    others will be ignored. Supply -1 here if you don't want to limit
 *    the number of components.
 * \param minelements The minimum number of vertices a component
 *    should contain in order to place it in the \p components
 *    vector. Eg. supply 2 here to ignore isolated vertices.
 * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory
 *   to perform the operation.
 *
 * Added in version 0.2.</para><para>
 *
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
 * of edges.
 *
 * \example examples/simple/igraph_decompose.c
 */

int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components,
                     igraph_connectedness_t mode,
                     long int maxcompno, long int minelements) {
    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
        return igraph_i_decompose_weak(graph, components, maxcompno, minelements);
    } else if (mode == IGRAPH_STRONG) {
        return igraph_i_decompose_strong(graph, components, maxcompno, minelements);
    }

    IGRAPH_ERROR("Cannot decompose graph", IGRAPH_EINVAL);
}

static int igraph_i_decompose_weak(const igraph_t *graph,
                                   igraph_vector_ptr_t *components,
                                   long int maxcompno, long int minelements) {

    long int actstart;
    long int no_of_nodes = igraph_vcount(graph);
    long int resco = 0;   /* number of graphs created so far */
    char *already_added;
    igraph_dqueue_t q;
    igraph_vector_t verts;
    igraph_vector_t neis;
    igraph_vector_t vids_old2new;
    long int i;
    igraph_t *newg;


    if (maxcompno < 0) {
        maxcompno = LONG_MAX;
    }

    igraph_vector_ptr_clear(components);
    IGRAPH_FINALLY(igraph_decompose_destroy, components);

    /* already_added keeps track of what nodes made it into a graph already */
    already_added = IGRAPH_CALLOC(no_of_nodes, char);
    if (already_added == 0) {
        IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
    }
    IGRAPH_FINALLY(igraph_free, already_added);

    IGRAPH_CHECK(igraph_dqueue_init(&q, 100));
    IGRAPH_FINALLY(igraph_dqueue_destroy, &q);
    IGRAPH_VECTOR_INIT_FINALLY(&verts, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes);

    /* vids_old2new would have been created internally in igraph_induced_subgraph(),
       but it is slow if the graph is large and consists of many small components,
       so we create it once here and then re-use it */

    /* add a node and its neighbors at once, recursively
       then switch to next node that has not been added already */
    for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) {

        if (already_added[actstart]) {
            continue;
        }
        IGRAPH_ALLOW_INTERRUPTION();

        igraph_vector_clear(&verts);

        /* add the node itself */
        already_added[actstart] = 1;
        IGRAPH_CHECK(igraph_vector_push_back(&verts, actstart));
        IGRAPH_CHECK(igraph_dqueue_push(&q, actstart));

        /* add the neighbors, recursively */
        while (!igraph_dqueue_empty(&q) ) {
            /* pop from the queue of this component */
            long int actvert = (long int) igraph_dqueue_pop(&q);
            IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actvert,
                                          IGRAPH_ALL));
            /* iterate over the neighbors */
            for (i = 0; i < igraph_vector_size(&neis); i++) {
                long int neighbor = (long int) VECTOR(neis)[i];
                if (already_added[neighbor] == 1) {
                    continue;
                }
                /* add neighbor */
                already_added[neighbor] = 1;

                /* recursion: append neighbor to the queues */
                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor));
            }
        }

        /* ok, we have a component */
        if (igraph_vector_size(&verts) < minelements) {
            continue;
        }

        newg = IGRAPH_CALLOC(1, igraph_t);
        if (newg == 0) {
            IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
        }
        IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg));
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
            graph, newg, igraph_vss_vector(&verts),
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
            /* invmap = */ 0, /* map_is_prepared = */ 1
        ));
        resco++;

        /* vids_old2new does not have to be cleaned up here; since we are doing
         * weak decomposition, each vertex will appear in only one of the
         * connected components so we won't ever touch an item in vids_old2new
         * if it was already set to a non-zero value in a previous component */

    } /* for actstart++ */

    igraph_vector_destroy(&vids_old2new);
    igraph_vector_destroy(&neis);
    igraph_vector_destroy(&verts);
    igraph_dqueue_destroy(&q);
    IGRAPH_FREE(already_added);
    IGRAPH_FINALLY_CLEAN(6);  /* + components */

    return 0;
}

static int igraph_i_decompose_strong(const igraph_t *graph,
                                     igraph_vector_ptr_t *components,
                                     long int maxcompno, long int minelements) {


    long int no_of_nodes = igraph_vcount(graph);

    /* this is a heap used twice for checking what nodes have
     * been counted already */
    igraph_vector_t next_nei = IGRAPH_VECTOR_NULL;

    long int i, n, num_seen;
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;

    long int no_of_clusters = 0;
    long int act_cluster_size;

    igraph_vector_t out = IGRAPH_VECTOR_NULL;
    const igraph_vector_int_t* tmp;

    igraph_adjlist_t adjlist;
    igraph_vector_t verts;
    igraph_vector_t vids_old2new;
    igraph_t *newg;

    if (maxcompno < 0) {
        maxcompno = LONG_MAX;
    }

    igraph_vector_ptr_clear(components);
    IGRAPH_FINALLY(igraph_decompose_destroy, components);

    /* The result */

    IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&verts, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&out, 0);
    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);

    IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes));

    igraph_vector_null(&out);

    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);

    /* vids_old2new would have been created internally in igraph_induced_subgraph(),
       but it is slow if the graph is large and consists of many small components,
       so we create it once here and then re-use it */

    /* number of components seen */
    num_seen = 0;
    /* populate the 'out' vector by browsing a node and following up
       all its neighbors recursively, then switching to the next
       unassigned node */
    for (i = 0; i < no_of_nodes; i++) {
        IGRAPH_ALLOW_INTERRUPTION();

        /* get all the 'out' neighbors of this node
         * NOTE: next_nei is initialized [0, 0, ...] */
        tmp = igraph_adjlist_get(&adjlist, i);
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
            continue;
        }

        /* add this node to the queue for this component */
        IGRAPH_CHECK(igraph_dqueue_push(&q, i));

        /* consume the tree from this node ("root") recursively
         * until there is no more */
        while (!igraph_dqueue_empty(&q)) {
            /* this looks up but does NOT consume the queue */
            long int act_node = (long int) igraph_dqueue_back(&q);

            /* get all neighbors of this node */
            tmp = igraph_adjlist_get(&adjlist, act_node);
            if (VECTOR(next_nei)[act_node] == 0) {
                /* this is the first time we've met this vertex,
                     * because next_nei is initialized [0, 0, ...] */
                VECTOR(next_nei)[act_node]++;
                /* back to the queue, same vertex is up again */

            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
                /* we've already met this vertex but it has more children */
                long int neighbor = (long int) VECTOR(*tmp)[(long int)
                                    VECTOR(next_nei)[act_node] - 1];
                if (VECTOR(next_nei)[neighbor] == 0) {
                    /* add the root of the other children to the queue */
                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                }
                VECTOR(next_nei)[act_node]++;
            } else {
                /* we've met this vertex and it has no more children */
                IGRAPH_CHECK(igraph_vector_push_back(&out, act_node));
                /* this consumes the queue, since there's nowhere to go */
                igraph_dqueue_pop_back(&q);
                num_seen++;

                if (num_seen % 10000 == 0) {
                    /* time to report progress and allow the user to interrupt */
                    IGRAPH_PROGRESS("Strongly connected components: ",
                                    num_seen * 50.0 / no_of_nodes, NULL);
                    IGRAPH_ALLOW_INTERRUPTION();
                }
            }
        } /* while q */
    }  /* for */

    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);

    igraph_adjlist_destroy(&adjlist);
    IGRAPH_FINALLY_CLEAN(1);

    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);

    /* OK, we've the 'out' values for the nodes, let's use them in
     * decreasing order with the help of the next_nei heap */

    igraph_vector_null(&next_nei);             /* mark already added vertices */

    /* number of components built */
    num_seen = 0;
    while (!igraph_vector_empty(&out) && no_of_clusters < maxcompno) {
        /* consume the vector from the last element */
        long int grandfather = (long int) igraph_vector_pop_back(&out);

        /* been here, done that
         * NOTE: next_nei is initialized as [0, 0, ...] */
        if (VECTOR(next_nei)[grandfather] != 0) {
            continue;
        }

        /* collect all the members of this component */
        igraph_vector_clear(&verts);

        /* this node is gone for any future components */
        VECTOR(next_nei)[grandfather] = 1;
        act_cluster_size = 1;

        /* add to component */
        IGRAPH_CHECK(igraph_vector_push_back(&verts, grandfather));
        IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather));

        num_seen++;
        if (num_seen % 10000 == 0) {
            /* time to report progress and allow the user to interrupt */
            IGRAPH_PROGRESS("Strongly connected components: ",
                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
            IGRAPH_ALLOW_INTERRUPTION();
        }

        while (!igraph_dqueue_empty(&q)) {
            /* consume the queue from this node */
            long int act_node = (long int) igraph_dqueue_pop_back(&q);
            tmp = igraph_adjlist_get(&adjlist, act_node);
            n = igraph_vector_int_size(tmp);
            for (i = 0; i < n; i++) {
                long int neighbor = (long int) VECTOR(*tmp)[i];
                if (VECTOR(next_nei)[neighbor] != 0) {
                    continue;
                }
                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
                VECTOR(next_nei)[neighbor] = 1;
                act_cluster_size++;

                /* add to component */
                IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor));

                num_seen++;
                if (num_seen % 10000 == 0) {
                    /* time to report progress and allow the user to interrupt */
                    IGRAPH_PROGRESS("Strongly connected components: ",
                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
                    IGRAPH_ALLOW_INTERRUPTION();
                }
            }
        }

        /* ok, we have a component */
        if (igraph_vector_size(&verts) < minelements) {
            continue;
        }

        newg = IGRAPH_CALLOC(1, igraph_t);
        if (newg == 0) {
            IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
        }
        IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg));
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
            graph, newg, igraph_vss_vector(&verts),
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
            /* invmap = */ 0, /* map_is_prepared = */ 1
        ));

        /* vids_old2new has to be cleaned up here because a vertex may appear
         * in multiple strongly connected components. Simply calling
         * igraph_vector_fill() would be an O(n) operation where n is the number
         * of vertices in the large graph so we cannot do that; we have to
         * iterate over 'verts' instead */
        n = igraph_vector_size(&verts);
        for (i = 0; i < n; i++) {
            VECTOR(vids_old2new)[(igraph_integer_t) VECTOR(verts)[i]] = 0;
        }

        no_of_clusters++;
    }

    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);

    /* Clean up, return */

    igraph_vector_destroy(&vids_old2new);
    igraph_vector_destroy(&verts);
    igraph_adjlist_destroy(&adjlist);
    igraph_vector_destroy(&out);
    igraph_dqueue_destroy(&q);
    igraph_vector_destroy(&next_nei);
    IGRAPH_FINALLY_CLEAN(7);  /* + components */

    return 0;

}

/**
 * \function igraph_articulation_points
 * Find the articulation points in a graph.
 *
 * A vertex is an articulation point if its removal increases
 * the number of connected components in the graph.
 * \param graph The input graph.
 * \param res Pointer to an initialized vector, the
 *    articulation points will be stored here.
 * \return Error code.
 *
 * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
 *
 * \sa \ref igraph_biconnected_components(), \ref igraph_clusters(), \ref igraph_bridges()
 */

int igraph_articulation_points(const igraph_t *graph,
                               igraph_vector_t *res) {

    igraph_integer_t no;
    return igraph_biconnected_components(graph, &no, 0, 0, 0, res);
}

void igraph_i_free_vectorlist(igraph_vector_ptr_t *list);

void igraph_i_free_vectorlist(igraph_vector_ptr_t *list) {
    long int i, n = igraph_vector_ptr_size(list);
    for (i = 0; i < n; i++) {
        igraph_vector_t *v = VECTOR(*list)[i];
        if (v) {
            igraph_vector_destroy(v);
            IGRAPH_FREE(v);
        }
    }
    igraph_vector_ptr_destroy(list);
}

/**
 * \function igraph_biconnected_components
 * \brief Calculate biconnected components.
 *
 * A graph is biconnected if the removal of any single vertex (and
 * its incident edges) does not disconnect it.
 *
 * </para><para>
 * A biconnected component of a graph is a maximal biconnected
 * subgraph of it. The biconnected components of a graph can be given
 * by the partition of its edges: every edge is a member of exactly
 * one biconnected component. Note that this is not true for
 * vertices: the same vertex can be part of many biconnected
 * components.
 *
 * </para><para>
 * Note that some authors do not consider the graph consisting of
 * two connected vertices as biconnected, however, igraph does.
 *
 * </para><para>
 * Somewhat arbitrarily, igraph does not consider components containing
 * a single vertex only as being biconnected. Isolated vertices will
 * not be part of any of the biconnected components.
 *
 * \param graph The input graph.
 * \param no The number of biconnected components will be stored here.
 * \param tree_edges If not a NULL pointer, then the found components
 *     are stored here, in a list of vectors. Every vector in the list
 *     is a biconnected component, represented by its edges. More precisely,
 *     a spanning tree of the biconnected component is returned.
 *     Note you'll have to
 *     destroy each vector first by calling \ref igraph_vector_destroy()
 *     and then \ref igraph_free() on it, plus you need to call
 *     \ref igraph_vector_ptr_destroy() on the list to regain all
 *     allocated memory.
 * \param component_edges If not a NULL pointer, then the edges of the
 *     biconnected components are stored here, in the same form as for
 *     \c tree_edges.
 * \param components If not a NULL pointer, then the vertices of the
 *     biconnected components are stored here, in the same format as
 *     for the previous two arguments.
 * \param articulation_points If not a NULL pointer, then the
 *     articulation points of the graph are stored in this vector.
 *     A vertex is an articulation point if its removal increases the
 *     number of (weakly) connected components in the graph.
 * \return Error code.
 *
 * Time complexity: O(|V|+|E|), linear in the number of vertices and
 * edges, but only if you do not calculate \c components and
 * \c component_edges. If you calculate \c components, then it is
 * quadratic in the number of vertices. If you calculate \c
 * component_edges as well, then it is cubic in the number of
 * vertices.
 *
 * \sa \ref igraph_articulation_points(), \ref igraph_clusters().
 *
 * \example examples/simple/igraph_biconnected_components.c
 */

int igraph_biconnected_components(const igraph_t *graph,
                                  igraph_integer_t *no,
                                  igraph_vector_ptr_t *tree_edges,
                                  igraph_vector_ptr_t *component_edges,
                                  igraph_vector_ptr_t *components,
                                  igraph_vector_t *articulation_points) {

    long int no_of_nodes = igraph_vcount(graph);
    igraph_vector_long_t nextptr;
    igraph_vector_long_t num, low;
    igraph_vector_bool_t found;
    igraph_vector_int_t *adjedges;
    igraph_stack_t path;
    igraph_vector_t edgestack;
    igraph_inclist_t inclist;
    long int i, counter, rootdfs = 0;
    igraph_vector_long_t vertex_added;
    long int comps = 0;
    igraph_vector_ptr_t *mycomponents = components, vcomponents;

    IGRAPH_CHECK(igraph_vector_long_init(&nextptr, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_long_destroy, &nextptr);
    IGRAPH_CHECK(igraph_vector_long_init(&num, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_long_destroy, &num);
    IGRAPH_CHECK(igraph_vector_long_init(&low, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_long_destroy, &low);
    IGRAPH_CHECK(igraph_vector_bool_init(&found, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_bool_destroy, &found);

    IGRAPH_CHECK(igraph_stack_init(&path, 100));
    IGRAPH_FINALLY(igraph_stack_destroy, &path);
    IGRAPH_VECTOR_INIT_FINALLY(&edgestack, 0);
    IGRAPH_CHECK(igraph_vector_reserve(&edgestack, 100));

    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);

    IGRAPH_CHECK(igraph_vector_long_init(&vertex_added, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_long_destroy, &vertex_added);

    if (no) {
        *no = 0;
    }
    if (tree_edges) {
        igraph_vector_ptr_clear(tree_edges);
    }
    if (components) {
        igraph_vector_ptr_clear(components);
    }
    if (component_edges) {
        igraph_vector_ptr_clear(component_edges);
    }
    if (articulation_points) {
        igraph_vector_clear(articulation_points);
    }
    if (component_edges && !components) {
        mycomponents = &vcomponents;
        IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0));
        IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents);
    }

    for (i = 0; i < no_of_nodes; i++) {

        if (VECTOR(low)[i] != 0) {
            continue;    /* already visited */
        }

        IGRAPH_ALLOW_INTERRUPTION();

        IGRAPH_CHECK(igraph_stack_push(&path, i));
        counter = 1;
        rootdfs = 0;
        VECTOR(low)[i] = VECTOR(num)[i] = counter++;
        while (!igraph_stack_empty(&path)) {
            long int n;
            long int act = (long int) igraph_stack_top(&path);
            long int actnext = VECTOR(nextptr)[act];

            adjedges = igraph_inclist_get(&inclist, act);
            n = igraph_vector_int_size(adjedges);
            if (actnext < n) {
                /* Step down (maybe) */
                long int edge = (long int) VECTOR(*adjedges)[actnext];
                long int nei = IGRAPH_OTHER(graph, edge, act);
                if (VECTOR(low)[nei] == 0) {
                    if (act == i) {
                        rootdfs++;
                    }
                    IGRAPH_CHECK(igraph_vector_push_back(&edgestack, edge));
                    IGRAPH_CHECK(igraph_stack_push(&path, nei));
                    VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
                } else {
                    /* Update low value if needed */
                    if (VECTOR(num)[nei] < VECTOR(low)[act]) {
                        VECTOR(low)[act] = VECTOR(num)[nei];
                    }
                }
                VECTOR(nextptr)[act] += 1;
            } else {
                /* Step up */
                igraph_stack_pop(&path);
                if (!igraph_stack_empty(&path)) {
                    long int prev = (long int) igraph_stack_top(&path);
                    /* Update LOW value if needed */
                    if (VECTOR(low)[act] < VECTOR(low)[prev]) {
                        VECTOR(low)[prev] = VECTOR(low)[act];
                    }
                    /* Check for articulation point */
                    if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
                        if (articulation_points && !VECTOR(found)[prev]
                            && prev != i /* the root */) {
                            IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev));
                            VECTOR(found)[prev] = 1;
                        }
                        if (no) {
                            *no += 1;
                        }

                        /*------------------------------------*/
                        /* Record the biconnected component just found */
                        if (tree_edges || mycomponents) {
                            igraph_vector_t *v = 0, *v2 = 0;
                            comps++;
                            if (tree_edges) {
                                v = IGRAPH_CALLOC(1, igraph_vector_t);
                                if (!v) {
                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
                                }
                                IGRAPH_CHECK(igraph_vector_init(v, 0));
                                IGRAPH_FINALLY(igraph_vector_destroy, v);
                            }
                            if (mycomponents) {
                                v2 = IGRAPH_CALLOC(1, igraph_vector_t);
                                if (!v2) {
                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
                                }
                                IGRAPH_CHECK(igraph_vector_init(v2, 0));
                                IGRAPH_FINALLY(igraph_vector_destroy, v2);
                            }

                            while (!igraph_vector_empty(&edgestack)) {
                                long int e = (long int) igraph_vector_pop_back(&edgestack);
                                long int from = IGRAPH_FROM(graph, e);
                                long int to = IGRAPH_TO(graph, e);
                                if (tree_edges) {
                                    IGRAPH_CHECK(igraph_vector_push_back(v, e));
                                }
                                if (mycomponents) {
                                    if (VECTOR(vertex_added)[from] != comps) {
                                        VECTOR(vertex_added)[from] = comps;
                                        IGRAPH_CHECK(igraph_vector_push_back(v2, from));
                                    }
                                    if (VECTOR(vertex_added)[to] != comps) {
                                        VECTOR(vertex_added)[to] = comps;
                                        IGRAPH_CHECK(igraph_vector_push_back(v2, to));
                                    }
                                }
                                if (from == prev || to == prev) {
                                    break;
                                }
                            }

                            if (mycomponents) {
                                IGRAPH_CHECK(igraph_vector_ptr_push_back(mycomponents, v2));
                                IGRAPH_FINALLY_CLEAN(1);
                            }
                            if (tree_edges) {
                                IGRAPH_CHECK(igraph_vector_ptr_push_back(tree_edges, v));
                                IGRAPH_FINALLY_CLEAN(1);
                            }
                            if (component_edges) {
                                igraph_vector_t *nodes = VECTOR(*mycomponents)[comps - 1];
                                igraph_vector_t *vv = IGRAPH_CALLOC(1, igraph_vector_t);
                                long int ii, no_vert = igraph_vector_size(nodes);
                                if (!vv) {
                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
                                }
                                IGRAPH_CHECK(igraph_vector_init(vv, 0));
                                IGRAPH_FINALLY(igraph_vector_destroy, vv);
                                for (ii = 0; ii < no_vert; ii++) {
                                    long int vert = (long int) VECTOR(*nodes)[ii];
                                    igraph_vector_int_t *edges = igraph_inclist_get(&inclist,
                                                                 vert);
                                    long int j, nn = igraph_vector_int_size(edges);
                                    for (j = 0; j < nn; j++) {
                                        long int e = (long int) VECTOR(*edges)[j];
                                        long int nei = IGRAPH_OTHER(graph, e, vert);
                                        if (VECTOR(vertex_added)[nei] == comps && nei < vert) {
                                            IGRAPH_CHECK(igraph_vector_push_back(vv, e));
                                        }
                                    }
                                }
                                IGRAPH_CHECK(igraph_vector_ptr_push_back(component_edges, vv));
                                IGRAPH_FINALLY_CLEAN(1);
                            }
                        } /* record component if requested */
                        /*------------------------------------*/

                    }
                } /* !igraph_stack_empty(&path) */
            }

        } /* !igraph_stack_empty(&path) */

        if (articulation_points && rootdfs >= 2) {
            IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i));
        }

    } /* i < no_of_nodes */

    if (mycomponents != components) {
        igraph_i_free_vectorlist(mycomponents);
        IGRAPH_FINALLY_CLEAN(1);
    }

    igraph_vector_long_destroy(&vertex_added);
    igraph_inclist_destroy(&inclist);
    igraph_vector_destroy(&edgestack);
    igraph_stack_destroy(&path);
    igraph_vector_bool_destroy(&found);
    igraph_vector_long_destroy(&low);
    igraph_vector_long_destroy(&num);
    igraph_vector_long_destroy(&nextptr);
    IGRAPH_FINALLY_CLEAN(8);

    return 0;
}


/**
 * \function igraph_bridges
 * \brief Find all bridges in a graph.
 *
 * An edge is a bridge if its removal increases the number of (weakly)
 * connected components in the graph.
 *
 * \param graph The input graph. It will be treated as undirected.
 * \param res Pointer to an initialized vector, the
 *    bridges will be stored here as edge indices.
 * \return Error code.
 *
 * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
 *
 * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), \ref igraph_clusters()
 */

int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) {
    /* The algorithm is based on https://www.geeksforgeeks.org/bridge-in-a-graph/
       but instead of keeping track of the parent of each vertex in the DFS tree
       we keep track of its incoming edge. This is necessary to support multigraphs.
       Additionally, we use explicit stacks instead of recursion to avoid
       stack overflow. */

    long no_of_nodes = igraph_vcount(graph);
    igraph_inclist_t il;
    igraph_vector_bool_t visited;
    igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */
    igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */
    igraph_vector_int_t incoming_edge;
    igraph_stack_int_t su, si;
    igraph_integer_t time;

    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
    IGRAPH_FINALLY(igraph_inclist_destroy, &il);

    IGRAPH_CHECK(igraph_vector_bool_init(&visited, no_of_nodes));
    IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);

    IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes);

    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);

    IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes);
    igraph_vector_int_fill(&incoming_edge, -1);

    IGRAPH_CHECK(igraph_stack_int_init(&su, 0));
    IGRAPH_FINALLY(igraph_stack_int_destroy, &su);

    IGRAPH_CHECK(igraph_stack_int_init(&si, 0));
    IGRAPH_FINALLY(igraph_stack_int_destroy, &si);

    igraph_vector_clear(bridges);

    time = 0;
    for (long start = 0; start < no_of_nodes; ++start) {
        if (! VECTOR(visited)[start]) {
            /* Perform a DFS from 'start'.
             * The top of the su stack is u, the vertex currently being visited.
             * The top of the si stack is i, the index of u's neighbour that will
             * be processed next. */

            IGRAPH_CHECK(igraph_stack_int_push(&su, start));
            IGRAPH_CHECK(igraph_stack_int_push(&si, 0));

            while (! igraph_stack_int_empty(&su)) {
                long u = igraph_stack_int_pop(&su);
                long i = igraph_stack_int_pop(&si);

                if (i == 0) {
                    /* We are at the first step of visiting vertex u. */

                    VECTOR(visited)[u] = 1;

                    time += 1;

                    VECTOR(vis)[u] = time;
                    VECTOR(low)[u] = time;
                }

                igraph_vector_int_t *incedges = igraph_inclist_get(&il, u);

                if (i < igraph_vector_int_size(incedges)) {
                    IGRAPH_CHECK(igraph_stack_int_push(&su, u));
                    IGRAPH_CHECK(igraph_stack_int_push(&si, i+1));

                    long edge = (long) VECTOR(*incedges)[i];
                    igraph_integer_t v = IGRAPH_OTHER(graph, edge, u);

                    if (! VECTOR(visited)[v]) {
                        VECTOR(incoming_edge)[v] = edge;

                        IGRAPH_CHECK(igraph_stack_int_push(&su, v));
                        IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
                    } else if (edge != VECTOR(incoming_edge)[u]) {
                        VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v];
                    }
                } else {
                    /* We are done visiting vertex u, so it won't be put back on the stack.
                     * We are ready to update the 'low' value of its parent w, and decide
                     * whether its incoming edge is a bridge. */

                    long edge = VECTOR(incoming_edge)[u];
                    if (edge >= 0) {
                        long w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */
                        VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u];
                        if (VECTOR(low)[u] > VECTOR(vis)[w]) {
                            IGRAPH_CHECK(igraph_vector_push_back(bridges, edge));
                        }
                    }
                }
            }
        }
    }

    igraph_stack_int_destroy(&si);
    igraph_stack_int_destroy(&su);
    igraph_vector_int_destroy(&incoming_edge);
    igraph_vector_int_destroy(&low);
    igraph_vector_int_destroy(&vis);
    igraph_vector_bool_destroy(&visited);
    igraph_inclist_destroy(&il);
    IGRAPH_FINALLY_CLEAN(7);

    return IGRAPH_SUCCESS;
}

/**
 * \ingroup structural
 * \function igraph_subcomponent
 * \brief The vertices in the same component as a given vertex.
 *
 * \param graph The graph object.
 * \param res The result, vector with the ids of the vertices in the
 *        same component.
 * \param vertex The id of the vertex of which the component is
 *        searched.
 * \param mode Type of the component for directed graphs, possible
 *        values:
 *        \clist
 *        \cli IGRAPH_OUT
 *          the set of vertices reachable \em from the
 *          \p vertex,
 *        \cli IGRAPH_IN
 *          the set of vertices from which the
 *          \p vertex is reachable.
 *        \cli IGRAPH_ALL
 *          the graph is considered as an
 *          undirected graph. Note that this is \em not the same
 *          as the union of the previous two.
 *        \endclist
 * \return Error code:
 *        \clist
 *        \cli IGRAPH_ENOMEM
 *          not enough memory for temporary data.
 *        \cli IGRAPH_EINVVID
 *           \p vertex is an invalid vertex id
 *        \cli IGRAPH_EINVMODE
 *           invalid mode argument passed.
 *        \endclist
 *
 * Time complexity: O(|V|+|E|),
 * |V| and
 * |E| are the number of vertices and
 * edges in the graph.
 *
 * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only
 * a given set of vertices and the edges between them.
 */
int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vertex,
                        igraph_neimode_t mode) {

    long int no_of_nodes = igraph_vcount(graph);
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
    char *already_added;
    long int i, vsize;
    igraph_vector_t tmp = IGRAPH_VECTOR_NULL;

    if (!IGRAPH_FINITE(vertex) || vertex < 0 || vertex >= no_of_nodes) {
        IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID);
    }
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
        mode != IGRAPH_ALL) {
        IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
    }

    already_added = IGRAPH_CALLOC(no_of_nodes, char);
    if (already_added == 0) {
        IGRAPH_ERROR("Subcomponent failed.", IGRAPH_ENOMEM);
    }
    IGRAPH_FINALLY(igraph_free, already_added);

    igraph_vector_clear(res);

    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);

    IGRAPH_CHECK(igraph_dqueue_push(&q, vertex));
    IGRAPH_CHECK(igraph_vector_push_back(res, vertex));
    already_added[(long int)vertex] = 1;

    while (!igraph_dqueue_empty(&q)) {
        long int actnode = (long int) igraph_dqueue_pop(&q);

        IGRAPH_ALLOW_INTERRUPTION();

        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode,
                                      mode));
        vsize = igraph_vector_size(&tmp);
        for (i = 0; i < vsize; i++) {
            long int neighbor = (long int) VECTOR(tmp)[i];

            if (already_added[neighbor]) {
                continue;
            }
            already_added[neighbor] = 1;
            IGRAPH_CHECK(igraph_vector_push_back(res, neighbor));
            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
        }
    }

    igraph_dqueue_destroy(&q);
    igraph_vector_destroy(&tmp);
    IGRAPH_FREE(already_added);
    IGRAPH_FINALLY_CLEAN(3);

    return IGRAPH_SUCCESS;
}
