/* -*- mode: C -*-  */
/* vim:set ts=4 sw=4 sts=4 et: */
/*
   IGraph R package.
   Copyright (C) 2003-2014  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_layout.h"
#include "igraph_random.h"
#include "igraph_memory.h"
#include "igraph_iterators.h"
#include "igraph_interface.h"
#include "igraph_adjlist.h"
#include "igraph_progress.h"
#include "igraph_interrupt_internal.h"
#include "igraph_paths.h"
#include "igraph_structural.h"
#include "igraph_visitor.h"
#include "igraph_topology.h"
#include "igraph_components.h"
#include "igraph_types_internal.h"
#include "igraph_dqueue.h"
#include "igraph_arpack.h"
#include "igraph_blas.h"
#include "igraph_centrality.h"
#include "igraph_eigen.h"
#include "config.h"
#include <math.h>
#include "igraph_math.h"


/**
 * \section about_layouts
 *
 * <para>Layout generator functions (or at least most of them) try to place the
 * vertices and edges of a graph on a 2D plane or in 3D space in a way
 * which visually pleases the human eye.</para>
 *
 * <para>They take a graph object and a number of parameters as arguments
 * and return an \type igraph_matrix_t, in which each row gives the
 * coordinates of a vertex.</para>
 */

/**
 * \ingroup layout
 * \function igraph_layout_random
 * \brief Places the vertices uniform randomly on a plane.
 *
 * \param graph Pointer to an initialized graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \return Error code. The current implementation always returns with
 * success.
 *
 * Time complexity: O(|V|), the
 * number of vertices.
 */

int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) {

    long int no_of_nodes = igraph_vcount(graph);
    long int i;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));

    RNG_BEGIN();

    for (i = 0; i < no_of_nodes; i++) {
        MATRIX(*res, i, 0) = RNG_UNIF(-1, 1);
        MATRIX(*res, i, 1) = RNG_UNIF(-1, 1);
    }

    RNG_END();

    return 0;
}

/**
 * \function igraph_layout_random_3d
 * \brief Random layout in 3D
 *
 * \param graph The graph to place.
 * \param res Pointer to an initialized matrix object. It will be
 * resized to hold the result.
 * \return Error code. The current implementation always returns with
 * success.
 *
 * Added in version 0.2.</para><para>
 *
 * Time complexity: O(|V|), the number of vertices.
 */

int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) {

    long int no_of_nodes = igraph_vcount(graph);
    long int i;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));

    RNG_BEGIN();

    for (i = 0; i < no_of_nodes; i++) {
        MATRIX(*res, i, 0) = RNG_UNIF(-1, 1);
        MATRIX(*res, i, 1) = RNG_UNIF(-1, 1);
        MATRIX(*res, i, 2) = RNG_UNIF(-1, 1);
    }

    RNG_END();

    return 0;
}

/**
 * \ingroup layout
 * \function igraph_layout_circle
 * \brief Places the vertices uniformly on a circle, in the order of vertex ids.
 *
 * \param graph Pointer to an initialized graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \param order The order of the vertices on the circle. The vertices
 *        not included here, will be placed at (0,0). Supply
 *        \ref igraph_vss_all() here for all vertices, in the order of
 *        their vertex ids.
 * \return Error code.
 *
 * Time complexity: O(|V|), the
 * number of vertices.
 */

int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res,
                         igraph_vs_t order) {

    long int no_of_nodes = igraph_vcount(graph);
    igraph_integer_t vs_size;
    long int i;
    igraph_vit_t vit;

    IGRAPH_CHECK(igraph_vs_size(graph, &order, &vs_size));

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
    igraph_matrix_null(res);

    igraph_vit_create(graph, order, &vit);
    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
        igraph_real_t phi = 2 * M_PI / vs_size * i;
        int idx = IGRAPH_VIT_GET(vit);
        MATRIX(*res, idx, 0) = cos(phi);
        MATRIX(*res, idx, 1) = sin(phi);
    }
    igraph_vit_destroy(&vit);

    return 0;
}

/**
 * \function igraph_layout_star
 * Generate a star-like layout
 *
 * \param graph The input graph.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \param center The id of the vertex to put in the center.
 * \param order A numeric vector giving the order of the vertices
 *      (including the center vertex!). If a null pointer, then the
 *      vertices are placed in increasing vertex id order.
 * \return Error code.
 *
 * Time complexity: O(|V|), linear in the number of vertices.
 *
 * \sa \ref igraph_layout_circle() and other layout generators.
 */

int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res,
                       igraph_integer_t center, const igraph_vector_t *order) {

    long int no_of_nodes = igraph_vcount(graph);
    long int c = center;
    long int i;
    igraph_real_t step;
    igraph_real_t phi;

    if (order && igraph_vector_size(order) != no_of_nodes) {
        IGRAPH_ERROR("Invalid order vector length", IGRAPH_EINVAL);
    }

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));

    if (no_of_nodes == 1) {
        MATRIX(*res, 0, 0) = MATRIX(*res, 0, 1) = 0.0;
    } else {
        for (i = 0, step = 2 * M_PI / (no_of_nodes - 1), phi = 0;
             i < no_of_nodes; i++) {
            long int node = order ? (long int) VECTOR(*order)[i] : i;
            if (node != c) {
                MATRIX(*res, node, 0) = cos(phi);
                MATRIX(*res, node, 1) = sin(phi);
                phi += step;
            } else {
                MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0;
            }
        }
    }

    return 0;
}

/**
 * \function igraph_layout_sphere
 * \brief Places vertices (more or less) uniformly on a sphere.
 *
 * </para><para>
 * The algorithm was described in the following paper:
 * Distributing many points on a sphere by E.B. Saff and
 * A.B.J. Kuijlaars, \emb Mathematical Intelligencer \eme 19.1 (1997)
 * 5--11.
 *
 * \param graph Pointer to an initialized graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \return Error code. The current implementation always returns with
 * success.
 *
 * Added in version 0.2.</para><para>
 *
 * Time complexity: O(|V|), the number of vertices in the graph.
 */

int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) {

    long int no_of_nodes = igraph_vcount(graph);
    long int i;
    igraph_real_t h;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));

    if (no_of_nodes != 0) {
        MATRIX(*res, 0, 0) = M_PI;
        MATRIX(*res, 0, 1) = 0;
    }
    for (i = 1; i < no_of_nodes - 1; i++) {
        h = -1 + 2 * i / (double)(no_of_nodes - 1);
        MATRIX(*res, i, 0) = acos(h);
        MATRIX(*res, i, 1) = fmod((MATRIX(*res, i - 1, 1) +
                                   3.6 / sqrt(no_of_nodes * (1 - h * h))), 2 * M_PI);
        IGRAPH_ALLOW_INTERRUPTION();
    }
    if (no_of_nodes >= 2) {
        MATRIX(*res, no_of_nodes - 1, 0) = 0;
        MATRIX(*res, no_of_nodes - 1, 1) = 0;
    }

    for (i = 0; i < no_of_nodes; i++) {
        igraph_real_t x = cos(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0));
        igraph_real_t y = sin(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0));
        igraph_real_t z = cos(MATRIX(*res, i, 0));
        MATRIX(*res, i, 0) = x;
        MATRIX(*res, i, 1) = y;
        MATRIX(*res, i, 2) = z;
        IGRAPH_ALLOW_INTERRUPTION();
    }

    return 0;
}

/**
 * \ingroup layout
 * \function igraph_layout_grid
 * \brief Places the vertices on a regular grid on the plane.
 *
 * \param graph Pointer to an initialized graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \param width The number of vertices in a single row of the grid.
 *        When zero or negative, the width of the grid will be the
 *        square root of the number of vertices, rounded up if needed.
 * \return Error code. The current implementation always returns with
 *         success.
 *
 * Time complexity: O(|V|), the number of vertices.
 */
int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width) {
    long int i, no_of_nodes = igraph_vcount(graph);
    igraph_real_t x, y;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));

    if (width <= 0) {
        width = (long int) ceil(sqrt(no_of_nodes));
    }

    x = y = 0;
    for (i = 0; i < no_of_nodes; i++) {
        MATRIX(*res, i, 0) = x++;
        MATRIX(*res, i, 1) = y;
        if (x == width) {
            x = 0; y++;
        }
    }

    return 0;
}

/**
 * \ingroup layout
 * \function igraph_layout_grid_3d
 * \brief Places the vertices on a regular grid in the 3D space.
 *
 * \param graph Pointer to an initialized graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized as needed.
 * \param width  The number of vertices in a single row of the grid. When
 *               zero or negative, the width is determined automatically.
 * \param height The number of vertices in a single column of the grid. When
 *               zero or negative, the height is determined automatically.
 *
 * \return Error code. The current implementation always returns with
 *         success.
 *
 * Time complexity: O(|V|), the number of vertices.
 */
int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res,
                          long int width, long int height) {
    long int i, no_of_nodes = igraph_vcount(graph);
    igraph_real_t x, y, z;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));

    if (width <= 0 && height <= 0) {
        width = height = (long int) ceil(pow(no_of_nodes, 1.0 / 3));
    } else if (width <= 0) {
        width = (long int) ceil(sqrt(no_of_nodes / (double)height));
    } else if (height <= 0) {
        height = (long int) ceil(sqrt(no_of_nodes / (double)width));
    }

    x = y = z = 0;
    for (i = 0; i < no_of_nodes; i++) {
        MATRIX(*res, i, 0) = x++;
        MATRIX(*res, i, 1) = y;
        MATRIX(*res, i, 2) = z;
        if (x == width) {
            x = 0; y++;
            if (y == height) {
                y = 0; z++;
            }
        }
    }

    return 0;
}

int igraph_layout_springs(const igraph_t *graph, igraph_matrix_t *res,
                          igraph_real_t mass, igraph_real_t equil, igraph_real_t k,
                          igraph_real_t repeqdis, igraph_real_t kfr, igraph_bool_t repulse) {

    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(res); IGRAPH_UNUSED(mass);
    IGRAPH_UNUSED(equil); IGRAPH_UNUSED(k); IGRAPH_UNUSED(repeqdis);
    IGRAPH_UNUSED(kfr); IGRAPH_UNUSED(repulse);
    IGRAPH_ERROR("Springs layout not implemented", IGRAPH_UNIMPLEMENTED);
    /* TODO */
    return 0;
}

static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) {
    igraph_real_t len = sqrt((*x) * (*x) + (*y) * (*y));
    if (len != 0) {
        *x /= len;
        *y /= len;
    }
}

/**
 * \function igraph_layout_lgl
 * \brief Force based layout algorithm for large graphs.
 *
 * </para><para>
 * This is a layout generator similar to the Large Graph Layout
 * algorithm and program
 * (http://lgl.sourceforge.net/). But unlike LGL, this
 * version uses a Fruchterman-Reingold style simulated annealing
 * algorithm for placing the vertices. The speedup is achieved by
 * placing the vertices on a grid and calculating the repulsion only
 * for vertices which are closer to each other than a limit.
 *
 * \param graph The (initialized) graph object to place.
 * \param res Pointer to an initialized matrix object to hold the
 *   result. It will be resized if needed.
 * \param maxit The maximum number of cooling iterations to perform
 *   for each layout step. A reasonable default is 150.
 * \param maxdelta The maximum length of the move allowed for a vertex
 *   in a single iteration. A reasonable default is the number of
 *   vertices.
 * \param area This parameter gives the area of the square on which
 *   the vertices will be placed. A reasonable default value is the
 *   number of vertices squared.
 * \param coolexp The cooling exponent. A reasonable default value is
 *   1.5.
 * \param repulserad Determines the radius at which vertex-vertex
 *   repulsion cancels out attraction of adjacent vertices. A
 *   reasonable default value is \p area times the number of vertices.
 * \param cellsize The size of the grid cells, one side of the
 *   square. A reasonable default value is the fourth root of
 *   \p area (or the square root of the number of vertices if \p area
 *   is also left at its default value).
 * \param proot The root vertex, this is placed first, its neighbors
 *   in the first iteration, second neighbors in the second, etc. If
 *   negative then a random vertex is chosen.
 * \return Error code.
 *
 * Added in version 0.2.</para><para>
 *
 * Time complexity: ideally O(dia*maxit*(|V|+|E|)), |V| is the number
 * of vertices,
 * dia is the diameter of the graph, worst case complexity is still
 * O(dia*maxit*(|V|^2+|E|)), this is the case when all vertices happen to be
 * in the same grid cell.
 */

int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res,
                      igraph_integer_t maxit, igraph_real_t maxdelta,
                      igraph_real_t area, igraph_real_t coolexp,
                      igraph_real_t repulserad, igraph_real_t cellsize,
                      igraph_integer_t proot) {


    long int no_of_nodes = igraph_vcount(graph);
    long int no_of_edges = igraph_ecount(graph);
    igraph_t mst;
    long int root;
    long int no_of_layers, actlayer = 0;
    igraph_vector_t vids;
    igraph_vector_t layers;
    igraph_vector_t parents;
    igraph_vector_t edges;
    igraph_2dgrid_t grid;
    igraph_vector_t eids;
    igraph_vector_t forcex;
    igraph_vector_t forcey;

    igraph_real_t frk = sqrt(area / no_of_nodes);
    igraph_real_t H_n = 0;

    IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst));
    IGRAPH_FINALLY(igraph_destroy, &mst);

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));

    /* Determine the root vertex, random pick right now */
    if (proot < 0) {
        root = RNG_INTEGER(0, no_of_nodes - 1);
    } else {
        root = proot;
    }

    /* Assign the layers */
    IGRAPH_VECTOR_INIT_FINALLY(&vids, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&layers, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&parents, 0);
    IGRAPH_CHECK(igraph_i_bfs(&mst, (igraph_integer_t) root, IGRAPH_ALL, &vids,
                              &layers, &parents));
    no_of_layers = igraph_vector_size(&layers) - 1;

    /* We don't need the mst any more */
    igraph_destroy(&mst);
    igraph_empty(&mst, 0, IGRAPH_UNDIRECTED); /* to make finalization work */

    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges));
    IGRAPH_VECTOR_INIT_FINALLY(&eids, 0);
    IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes);

    /* Place the vertices randomly */
    IGRAPH_CHECK(igraph_layout_random(graph, res));
    igraph_matrix_scale(res, 1e6);

    /* This is the grid for calculating the vertices near to a given vertex */
    IGRAPH_CHECK(igraph_2dgrid_init(&grid, res,
                                    -sqrt(area / M_PI), sqrt(area / M_PI), cellsize,
                                    -sqrt(area / M_PI), sqrt(area / M_PI), cellsize));
    IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid);

    /* Place the root vertex */
    igraph_2dgrid_add(&grid, root, 0, 0);

    for (actlayer = 1; actlayer < no_of_layers; actlayer++) {
        H_n += 1.0 / actlayer;
    }

    for (actlayer = 1; actlayer < no_of_layers; actlayer++) {

        igraph_real_t c = 1;
        long int i, j;
        igraph_real_t massx, massy;
        igraph_real_t px, py;
        igraph_real_t sx, sy;

        long int it = 0;
        igraph_real_t epsilon = 10e-6;
        igraph_real_t maxchange = epsilon + 1;
        long int pairs;
        igraph_real_t sconst = sqrt(area / M_PI) / H_n;
        igraph_2dgrid_iterator_t vidit;

        /*     printf("Layer %li:\n", actlayer); */

        /*-----------------------------------------*/
        /* Step 1: place the next layer on spheres */
        /*-----------------------------------------*/

        RNG_BEGIN();

        j = (long int) VECTOR(layers)[actlayer];
        for (i = (long int) VECTOR(layers)[actlayer - 1];
             i < VECTOR(layers)[actlayer]; i++) {

            long int vid = (long int) VECTOR(vids)[i];
            long int par = (long int) VECTOR(parents)[vid];
            IGRAPH_ALLOW_INTERRUPTION();
            igraph_2dgrid_getcenter(&grid, &massx, &massy);
            igraph_i_norm2d(&massx, &massy);
            px = MATRIX(*res, vid, 0) - MATRIX(*res, par, 0);
            py = MATRIX(*res, vid, 1) - MATRIX(*res, par, 1);
            igraph_i_norm2d(&px, &py);
            sx = c * (massx + px) + MATRIX(*res, vid, 0);
            sy = c * (massy + py) + MATRIX(*res, vid, 1);

            /* The neighbors of 'vid' */
            while (j < VECTOR(layers)[actlayer + 1] &&
                   VECTOR(parents)[(long int)VECTOR(vids)[j]] == vid) {
                igraph_real_t rx, ry;
                if (actlayer == 1) {
                    igraph_real_t phi = 2 * M_PI / (VECTOR(layers)[2] - 1) * (j - 1);
                    rx = cos(phi);
                    ry = sin(phi);
                } else {
                    rx = RNG_UNIF(-1, 1);
                    ry = RNG_UNIF(-1, 1);
                }
                igraph_i_norm2d(&rx, &ry);
                rx = rx / actlayer * sconst;
                ry = ry / actlayer * sconst;
                igraph_2dgrid_add(&grid, (long int) VECTOR(vids)[j], sx + rx, sy + ry);
                j++;
            }
        }

        RNG_END();

        /*-----------------------------------------*/
        /* Step 2: add the edges of the next layer */
        /*-----------------------------------------*/

        for (j = (long int) VECTOR(layers)[actlayer];
             j < VECTOR(layers)[actlayer + 1]; j++) {
            long int vid = (long int) VECTOR(vids)[j];
            long int k;
            IGRAPH_ALLOW_INTERRUPTION();
            IGRAPH_CHECK(igraph_incident(graph, &eids, (igraph_integer_t) vid,
                                         IGRAPH_ALL));
            for (k = 0; k < igraph_vector_size(&eids); k++) {
                long int eid = (long int) VECTOR(eids)[k];
                igraph_integer_t from, to;
                igraph_edge(graph, (igraph_integer_t) eid, &from, &to);
                if ((from != vid && igraph_2dgrid_in(&grid, from)) ||
                    (to   != vid && igraph_2dgrid_in(&grid, to))) {
                    igraph_vector_push_back(&edges, eid);
                }
            }
        }

        /*-----------------------------------------*/
        /* Step 3: let the springs spring          */
        /*-----------------------------------------*/

        maxchange = epsilon + 1;
        while (it < maxit && maxchange > epsilon) {
            long int jj;
            igraph_real_t t = maxdelta * pow((maxit - it) / (double)maxit, coolexp);
            long int vid, nei;

            IGRAPH_PROGRESS("Large graph layout",
                            100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + ((float)it) / (maxit * (no_of_layers - 1.0))),
                            0);

            /* init */
            igraph_vector_null(&forcex);
            igraph_vector_null(&forcey);
            maxchange = 0;

            /* attractive "forces" along the edges */
            for (jj = 0; jj < igraph_vector_size(&edges); jj++) {
                igraph_integer_t from, to;
                igraph_real_t xd, yd, dist, force;
                IGRAPH_ALLOW_INTERRUPTION();
                igraph_edge(graph, (igraph_integer_t) VECTOR(edges)[jj], &from, &to);
                xd = MATRIX(*res, (long int)from, 0) - MATRIX(*res, (long int)to, 0);
                yd = MATRIX(*res, (long int)from, 1) - MATRIX(*res, (long int)to, 1);
                dist = sqrt(xd * xd + yd * yd);
                if (dist != 0) {
                    xd /= dist;
                    yd /= dist;
                }
                force = dist * dist / frk;
                VECTOR(forcex)[(long int)from] -= xd * force;
                VECTOR(forcex)[(long int)to]   += xd * force;
                VECTOR(forcey)[(long int)from] -= yd * force;
                VECTOR(forcey)[(long int)to]   += yd * force;
            }

            /* repulsive "forces" of the vertices nearby */
            pairs = 0;
            igraph_2dgrid_reset(&grid, &vidit);
            while ( (vid = igraph_2dgrid_next(&grid, &vidit) - 1) != -1) {
                while ( (nei = igraph_2dgrid_next_nei(&grid, &vidit) - 1) != -1) {
                    igraph_real_t xd = MATRIX(*res, (long int)vid, 0) -
                                       MATRIX(*res, (long int)nei, 0);
                    igraph_real_t yd = MATRIX(*res, (long int)vid, 1) -
                                       MATRIX(*res, (long int)nei, 1);
                    igraph_real_t dist = sqrt(xd * xd + yd * yd);
                    igraph_real_t force;
                    if (dist < cellsize) {
                        pairs++;
                        if (dist == 0) {
                            dist = epsilon;
                        };
                        xd /= dist; yd /= dist;
                        force = frk * frk * (1.0 / dist - dist * dist / repulserad);
                        VECTOR(forcex)[(long int)vid] += xd * force;
                        VECTOR(forcex)[(long int)nei] -= xd * force;
                        VECTOR(forcey)[(long int)vid] += yd * force;
                        VECTOR(forcey)[(long int)nei] -= yd * force;
                    }
                }
            }

            /*       printf("verties: %li iterations: %li\n",  */
            /*       (long int) VECTOR(layers)[actlayer+1], pairs); */

            /* apply the changes */
            for (jj = 0; jj < VECTOR(layers)[actlayer + 1]; jj++) {
                long int vvid = (long int) VECTOR(vids)[jj];
                igraph_real_t fx = VECTOR(forcex)[vvid];
                igraph_real_t fy = VECTOR(forcey)[vvid];
                igraph_real_t ded = sqrt(fx * fx + fy * fy);
                if (ded > t) {
                    ded = t / ded;
                    fx *= ded; fy *= ded;
                }
                igraph_2dgrid_move(&grid, vvid, fx, fy);
                if (fx > maxchange) {
                    maxchange = fx;
                }
                if (fy > maxchange) {
                    maxchange = fy;
                }
            }
            it++;
            /*       printf("%li iterations, maxchange: %f\n", it, (double)maxchange); */
        }
    }

    IGRAPH_PROGRESS("Large graph layout", 100.0, 0);
    igraph_destroy(&mst);
    igraph_vector_destroy(&vids);
    igraph_vector_destroy(&layers);
    igraph_vector_destroy(&parents);
    igraph_vector_destroy(&edges);
    igraph_2dgrid_destroy(&grid);
    igraph_vector_destroy(&eids);
    igraph_vector_destroy(&forcex);
    igraph_vector_destroy(&forcey);
    IGRAPH_FINALLY_CLEAN(9);
    return 0;

}

static int igraph_i_layout_reingold_tilford_unreachable(
    const igraph_t *graph,
    igraph_neimode_t mode,
    long int real_root,
    long int no_of_nodes,
    igraph_vector_t *pnewedges) {

    long int no_of_newedges;
    igraph_vector_t visited;
    long int i, j, n;
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
    igraph_adjlist_t allneis;
    igraph_vector_int_t *neis;

    igraph_vector_resize(pnewedges, 0);

    /* traverse from real_root and see what nodes you cannot reach */
    no_of_newedges = 0;
    IGRAPH_VECTOR_INIT_FINALLY(&visited, no_of_nodes);
    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);

    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);

    /* start from real_root and go BFS */
    IGRAPH_CHECK(igraph_dqueue_push(&q, real_root));
    while (!igraph_dqueue_empty(&q)) {
        long int actnode = (long int) igraph_dqueue_pop(&q);
        neis = igraph_adjlist_get(&allneis, actnode);
        n = igraph_vector_int_size(neis);
        VECTOR(visited)[actnode] = 1;
        for (j = 0; j < n; j++) {
            long int neighbor = (long int) VECTOR(*neis)[j];
            if (!(long int)VECTOR(visited)[neighbor]) {
                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
            }
        }
    }

    for (j = 0; j < no_of_nodes; j++) {
        no_of_newedges += 1 - VECTOR(visited)[j];
    }

    /* if any nodes are unreachable, add edges between them and real_root */
    if (no_of_newedges != 0) {

        igraph_vector_resize(pnewedges, no_of_newedges * 2);
        j = 0;
        for (i = 0; i < no_of_nodes; i++) {
            if (!VECTOR(visited)[i]) {
                if (mode != IGRAPH_IN) {
                    VECTOR(*pnewedges)[2 * j] = real_root;
                    VECTOR(*pnewedges)[2 * j + 1] = i;
                } else {
                    VECTOR(*pnewedges)[2 * j] = i;
                    VECTOR(*pnewedges)[2 * j + 1] = real_root;
                }
                j++;
            }
        }
    }

    igraph_dqueue_destroy(&q);
    igraph_adjlist_destroy(&allneis);
    igraph_vector_destroy(&visited);
    IGRAPH_FINALLY_CLEAN(3);

    return IGRAPH_SUCCESS;
}


/* Internal structure for Reingold-Tilford layout */
struct igraph_i_reingold_tilford_vertex {
    long int parent;        /* Parent node index */
    long int level;         /* Level of the node */
    igraph_real_t offset;     /* X offset from parent node */
    long int left_contour;  /* Next left node of the contour
              of the subtree rooted at this node */
    long int right_contour; /* Next right node of the contour
              of the subtree rooted at this node */
    igraph_real_t offset_to_left_contour;  /* X offset when following the left contour */
    igraph_real_t offset_to_right_contour;  /* X offset when following the right contour */
    long int left_extreme;  /* Leftmost node on the deepest layer of the subtree rooted at this node */
    long int right_extreme; /* Rightmost node on the deepest layer of the subtree rooted at this node */
    igraph_real_t offset_to_left_extreme;  /* X offset when jumping to the left extreme node */
    igraph_real_t offset_to_right_extreme;  /* X offset when jumping to the right extreme node */
};

static int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata,
                                                      long int node, long int vcount);
static int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata,
                                                        igraph_matrix_t *res, long int node,
                                                        long int vcount, igraph_real_t xpos);

/* uncomment the next line for debugging the Reingold-Tilford layout */
/* #define LAYOUT_RT_DEBUG 1 */

static int igraph_i_layout_reingold_tilford(const igraph_t *graph,
                                            igraph_matrix_t *res,
                                            igraph_neimode_t mode,
                                            long int root) {
    long int no_of_nodes = igraph_vcount(graph);
    long int i, n, j;
    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
    igraph_adjlist_t allneis;
    igraph_vector_int_t *neis;
    struct igraph_i_reingold_tilford_vertex *vdata;

    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);

    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode));
    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);

    vdata = igraph_Calloc(no_of_nodes, struct igraph_i_reingold_tilford_vertex);
    if (vdata == 0) {
        IGRAPH_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM);
    }
    IGRAPH_FINALLY(igraph_free, vdata);

    for (i = 0; i < no_of_nodes; i++) {
        vdata[i].parent = -1;
        vdata[i].level = -1;
        vdata[i].offset = 0.0;
        vdata[i].left_contour = -1;
        vdata[i].right_contour = -1;
        vdata[i].offset_to_left_contour = 0.0;
        vdata[i].offset_to_right_contour = 0.0;
        vdata[i].left_extreme = i;
        vdata[i].right_extreme = i;
        vdata[i].offset_to_left_extreme = 0.0;
        vdata[i].offset_to_right_extreme = 0.0;
    }
    vdata[root].parent = root;
    vdata[root].level = 0;
    MATRIX(*res, root, 1) = 0;

    /* Step 1: assign Y coordinates based on BFS and setup parents vector */
    IGRAPH_CHECK(igraph_dqueue_push(&q, root));
    IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
    while (!igraph_dqueue_empty(&q)) {
        long int actnode = (long int) igraph_dqueue_pop(&q);
        long int actdist = (long int) igraph_dqueue_pop(&q);
        neis = igraph_adjlist_get(&allneis, actnode);
        n = igraph_vector_int_size(neis);

        for (j = 0; j < n; j++) {
            long int neighbor = (long int) VECTOR(*neis)[j];
            if (vdata[neighbor].parent >= 0) {
                continue;
            }
            MATRIX(*res, neighbor, 1) = actdist + 1;
            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
            IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
            vdata[neighbor].parent = actnode;
            vdata[neighbor].level = actdist + 1;
        }
    }

    /* Step 2: postorder tree traversal, determines the appropriate X
     * offsets for every node */
    igraph_i_layout_reingold_tilford_postorder(vdata, root, no_of_nodes);

    /* Step 3: calculate real coordinates based on X offsets */
    igraph_i_layout_reingold_tilford_calc_coords(vdata, res, root, no_of_nodes, vdata[root].offset);

    igraph_dqueue_destroy(&q);
    igraph_adjlist_destroy(&allneis);
    igraph_free(vdata);
    IGRAPH_FINALLY_CLEAN(3);

    IGRAPH_PROGRESS("Reingold-Tilford tree layout", 100.0, NULL);

#ifdef LAYOUT_RT_DEBUG
    for (i = 0; i < no_of_nodes; i++) {
        printf(
            "%3ld: offset = %.2f, contours = [%ld, %ld], contour offsets = [%.2f, %.2f]\n",
            i, vdata[i].offset,
            vdata[i].left_contour, vdata[i].right_contour,
            vdata[i].offset_to_left_contour, vdata[i].offset_to_right_contour
        );
        if (vdata[i].left_extreme != i || vdata[i].right_extreme != i) {
            printf(
                "     extrema = [%ld, %ld], offsets to extrema = [%.2f, %.2f]\n",
                vdata[i].left_extreme, vdata[i].right_extreme,
                vdata[i].offset_to_left_extreme, vdata[i].offset_to_right_extreme
            );
        }
    }
#endif

    return 0;
}

static int igraph_i_layout_reingold_tilford_calc_coords(
        struct igraph_i_reingold_tilford_vertex *vdata,
        igraph_matrix_t *res, long int node,
        long int vcount, igraph_real_t xpos) {
    long int i;
    MATRIX(*res, node, 0) = xpos;
    for (i = 0; i < vcount; i++) {
        if (i == node) {
            continue;
        }
        if (vdata[i].parent == node) {
            igraph_i_layout_reingold_tilford_calc_coords(vdata, res, i, vcount,
                    xpos + vdata[i].offset);
        }
    }
    return 0;
}

static int igraph_i_layout_reingold_tilford_postorder(
        struct igraph_i_reingold_tilford_vertex *vdata,
        long int node, long int vcount) {
    long int i, j, childcount, leftroot, leftrootidx;
    const igraph_real_t minsep = 1;
    igraph_real_t avg;

#ifdef LAYOUT_RT_DEBUG
    printf("Starting visiting node %ld\n", node);
#endif

    /* Check whether this node is a leaf node */
    childcount = 0;
    for (i = 0; i < vcount; i++) {
        if (i == node) {
            continue;
        }
        if (vdata[i].parent == node) {
            /* Node i is a child, so visit it recursively */
            childcount++;
            igraph_i_layout_reingold_tilford_postorder(vdata, i, vcount);
        }
    }

    if (childcount == 0) {
        return 0;
    }

    /* Here we can assume that all of the subtrees have been placed and their
     * left and right contours are calculated. Let's place them next to each
     * other as close as we can.
     * We will take each subtree in an arbitrary order. The root of the
     * first one will be placed at offset 0, the next ones will be placed
     * as close to each other as possible. leftroot stores the root of the
     * rightmost subtree of the already placed subtrees - its right contour
     * will be checked against the left contour of the next subtree */
    leftroot = leftrootidx = -1;
    avg = 0.0;
#ifdef LAYOUT_RT_DEBUG
    printf("Visited node %ld and arranged its subtrees\n", node);
#endif
    for (i = 0, j = 0; i < vcount; i++) {
        if (i == node) {
            continue;
        }
        if (vdata[i].parent == node) {
            if (leftroot >= 0) {
                /* Now we will follow the right contour of leftroot and the
                 * left contour of the subtree rooted at i */
                long lnode, rnode, auxnode;
                igraph_real_t loffset, roffset, rootsep, newoffset;

#ifdef LAYOUT_RT_DEBUG
                printf("  Placing child %ld on level %ld, to the right of %ld\n", i, vdata[i].level, leftroot);
#endif
                lnode = leftroot; rnode = i;
                rootsep = vdata[leftroot].offset + minsep;
                loffset = vdata[leftroot].offset; roffset = loffset + minsep;

                /* Keep on updating the right contour now that we have attached
                 * a new node to the subtree being built */
                vdata[node].right_contour = i;
                vdata[node].offset_to_right_contour = rootsep;

#ifdef LAYOUT_RT_DEBUG
                printf("    Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n",
                       lnode, rnode, loffset, roffset, rootsep);
#endif
                while ((lnode >= 0) && (rnode >= 0)) {
                    /* Step to the next level on the right contour of the left subtree */
                    if (vdata[lnode].right_contour >= 0) {
                        loffset += vdata[lnode].offset_to_right_contour;
                        lnode = vdata[lnode].right_contour;
                    } else {
                        /* Left subtree ended there. The left and right contour
                         * of the left subtree will continue to the next step
                         * on the right subtree. */
                        if (vdata[rnode].left_contour >= 0) {
                            auxnode = vdata[node].left_extreme;

                            /* this is the "threading" step that the original
                             * paper is talking about */
                            newoffset = (vdata[node].offset_to_right_extreme - vdata[node].offset_to_left_extreme) + minsep + vdata[rnode].offset_to_left_contour;
                            vdata[auxnode].left_contour = vdata[rnode].left_contour;
                            vdata[auxnode].right_contour = vdata[rnode].left_contour;
                            vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset;

                            /* since we attached a larger subtree to the
                             * already placed left subtree, we need to update
                             * the extrema of the subtree rooted at 'node' */
                            vdata[node].left_extreme = vdata[i].left_extreme;
                            vdata[node].right_extreme = vdata[i].right_extreme;
                            vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme + rootsep;
                            vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep;
#ifdef LAYOUT_RT_DEBUG
                            printf("      Left subtree ended earlier, continuing left subtree's left and right contour on right subtree (node %ld gets connected to node %ld)\n", auxnode, vdata[rnode].left_contour);
                            printf("      New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour);
#endif
                        } else {
                            /* Both subtrees are ending at the same time; the
                             * left extreme node of the subtree rooted at
                             * 'node' remains the same but the right extreme
                             * will change */
                            vdata[node].right_extreme = vdata[i].right_extreme;
                            vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep;
                        }
                        lnode = -1;
                    }
                    /* Step to the next level on the left contour of the right subtree */
                    if (vdata[rnode].left_contour >= 0) {
                        roffset += vdata[rnode].offset_to_left_contour;
                        rnode = vdata[rnode].left_contour;
                    } else {
                        /* Right subtree ended here. The right contour of the right
                         * subtree will continue to the next step on the left subtree.
                         * Note that lnode has already been advanced here */
                        if (lnode >= 0) {
                            auxnode = vdata[i].right_extreme;

                            /* this is the "threading" step that the original
                             * paper is talking about */
                            newoffset = loffset - rootsep - vdata[i].offset_to_right_extreme;
                            vdata[auxnode].left_contour = lnode;
                            vdata[auxnode].right_contour = lnode;
                            vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset;

                            /* no need to update the extrema of the subtree
                             * rooted at 'node' because the right subtree was
                             * smaller */
#ifdef LAYOUT_RT_DEBUG
                            printf("      Right subtree ended earlier, continuing right subtree's left and right contour on left subtree (node %ld gets connected to node %ld)\n", auxnode, lnode);
                            printf("      New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour);
#endif
                        }
                        rnode = -1;
                    }
#ifdef LAYOUT_RT_DEBUG
                    printf("    Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", 
                           lnode, rnode, loffset, roffset, rootsep);
#endif

                    /* Push subtrees away if necessary */
                    if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) {
#ifdef LAYOUT_RT_DEBUG
                        printf("    Pushing right subtree away by %lf\n", minsep-roffset+loffset);
#endif
                        rootsep += minsep - roffset + loffset;
                        roffset = loffset + minsep;
                        vdata[node].offset_to_right_contour = rootsep;
                    }
                }

#ifdef LAYOUT_RT_DEBUG
                printf("  Offset of subtree with root node %ld will be %lf\n", i, rootsep);
#endif
                vdata[i].offset = rootsep;
                vdata[node].offset_to_right_contour = rootsep;
                avg = (avg * j) / (j + 1) + rootsep / (j + 1);
                leftrootidx = j;
                leftroot = i;
            } else {
                /* This is the first child of the node being considered so we
                 * can simply place the subtree on our virtual canvas */
#ifdef LAYOUT_RT_DEBUG
                printf("  Placing child %ld on level %ld as first child\n", i, vdata[i].level);
#endif
                leftrootidx = j;
                leftroot = i;
                vdata[node].left_contour = i;
                vdata[node].right_contour = i;
                vdata[node].offset_to_left_contour = 0.0;
                vdata[node].offset_to_right_contour = 0.0;
                vdata[node].left_extreme = vdata[i].left_extreme;
                vdata[node].right_extreme = vdata[i].right_extreme;
                vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme;
                vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme;
                avg = vdata[i].offset;
            }
            j++;
        }
    }
#ifdef LAYOUT_RT_DEBUG
    printf("Shifting node %ld to be centered above children. Shift amount: %lf\n", node, avg);
#endif
    vdata[node].offset_to_left_contour -= avg;
    vdata[node].offset_to_right_contour -= avg;
    vdata[node].offset_to_left_extreme -= avg;
    vdata[node].offset_to_right_extreme -= avg;
    for (i = 0, j = 0; i < vcount; i++) {
        if (i == node) {
            continue;
        }
        if (vdata[i].parent == node) {
            vdata[i].offset -= avg;
        }
    }

    return 0;
}

/**
 * \function igraph_layout_reingold_tilford
 * \brief Reingold-Tilford layout for tree graphs
 *
 * </para><para>
 * Arranges the nodes in a tree where the given node is used as the root.
 * The tree is directed downwards and the parents are centered above its
 * children. For the exact algorithm, see:
 *
 * </para><para>
 * Reingold, E and Tilford, J: Tidier drawing of trees.
 * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981
 *
 * </para><para>
 * If the given graph is not a tree, a breadth-first search is executed
 * first to obtain a possible spanning tree.
 *
 * \param graph The graph object.
 * \param res The result, the coordinates in a matrix. The parameter
 *   should point to an initialized matrix object and will be resized.
 * \param mode Specifies which edges to consider when building the tree.
 *   If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN
 *   then only the incoming edges of a parent are considered. If it is
 *   \c IGRAPH_ALL then all edges are used (this was the behavior in
 *   igraph 0.5 and before). This parameter also influences how the root
 *   vertices are calculated, if they are not given. See the \p roots parameter.
 * \param roots The index of the root vertex or root vertices.
 *   If this is a non-empty vector then the supplied vertex ids are used
 *   as the roots of the trees (or a single tree if the graph is connected).
 *   If it is a null pointer of a pointer to an empty vector, then the root
 *   vertices are automatically calculated based on topological sorting,
 *   performed with the opposite mode than the \p mode argument.
 *   After the vertices have been sorted, one is selected from each component.
 * \param rootlevel This argument can be useful when drawing forests which are
 *   not trees (i.e. they are unconnected and have tree components). It specifies
 *   the level of the root vertices for every tree in the forest. It is only
 *   considered if not a null pointer and the \p roots argument is also given
 *   (and it is not a null pointer of an empty vector).
 * \return Error code.
 *
 * Added in version 0.2.
 *
 * \sa \ref igraph_layout_reingold_tilford_circular().
 *
 * \example examples/simple/igraph_layout_reingold_tilford.c
 */

int igraph_layout_reingold_tilford(const igraph_t *graph,
                                   igraph_matrix_t *res,
                                   igraph_neimode_t mode,
                                   const igraph_vector_t *roots,
                                   const igraph_vector_t *rootlevel) {

    long int no_of_nodes_orig = igraph_vcount(graph);
    long int no_of_nodes = no_of_nodes_orig;
    long int real_root;
    igraph_t extended;
    const igraph_t *pextended = graph;
    igraph_vector_t myroots;
    const igraph_vector_t *proots = roots;
    igraph_neimode_t mode2;
    long int i;
    igraph_vector_t newedges;

    /* TODO: possible speedup could be achieved if we use a table for storing
     * the children of each node in the tree. (Now the implementation uses a
     * single array containing the parent of each node and a node's children
     * are determined by looking for other nodes that have this node as parent)
     */

    /* at various steps it might be necessary to add edges to the graph */
    IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0);

    if (!igraph_is_directed(graph)) {
        mode = IGRAPH_ALL;
    }

    if ( (!roots || igraph_vector_size(roots) == 0) &&
         rootlevel && igraph_vector_size(rootlevel) != 0 ) {
        IGRAPH_WARNING("Reingold-Tilford layout: 'rootlevel' ignored");
    }

    /* ----------------------------------------------------------------------- */
    /* If root vertices are not given, then do a topological sort and take
       the last element from every component for directed graphs and mode == out,
       or the first element from every component for directed graphs and mode ==
       in,or select the vertex with the maximum degree from each component for
       undirected graphs */

    if (!roots || igraph_vector_size(roots) == 0) {

        igraph_vector_t order, membership;
        igraph_integer_t no_comps;
        long int i, noseen = 0;

        IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0);
        IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
        IGRAPH_VECTOR_INIT_FINALLY(&membership, no_of_nodes);

        if (mode != IGRAPH_ALL) {
            /* look for roots by swimming against the stream */
            mode2 = (mode == IGRAPH_IN) ? IGRAPH_OUT : IGRAPH_IN;

            IGRAPH_CHECK(igraph_topological_sorting(graph, &order, mode2));
            IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0,
                                         &no_comps, IGRAPH_WEAK));
        } else {
            IGRAPH_CHECK(igraph_sort_vertex_ids_by_degree(graph, &order,
                         igraph_vss_all(), IGRAPH_ALL, 0, IGRAPH_ASCENDING, 0));
            IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0,
                                         &no_comps, IGRAPH_WEAK));
        }

        IGRAPH_CHECK(igraph_vector_resize(&myroots, no_comps));

        /* go backwards and fill the roots vector with indices [1, no_of_nodes]
           The index 0 is used to signal this root has not been found yet:
           all indices are then decreased by one to [0, no_of_nodes - 1] */
        igraph_vector_null(&myroots);
        proots = &myroots;
        for (i = no_of_nodes - 1; noseen < no_comps && i >= 0; i--) {
            long int v = (long int) VECTOR(order)[i];
            long int mem = (long int) VECTOR(membership)[v];
            if (VECTOR(myroots)[mem] == 0) {
                noseen += 1;
                VECTOR(myroots)[mem] = v + 1;
            }
        }
        for (i = 0; i < no_comps; i++) {
            VECTOR(myroots)[i] -= 1;
        }

        igraph_vector_destroy(&membership);
        igraph_vector_destroy(&order);
        IGRAPH_FINALLY_CLEAN(2);

    } else if (rootlevel && igraph_vector_size(rootlevel) > 0 &&
               igraph_vector_size(roots) > 1) {

        /* ----------------------------------------------------------------------- */
        /* Many roots were given to us, check 'rootlevel' */

        long int plus_levels = 0;
        long int i;

        if (igraph_vector_size(roots) != igraph_vector_size(rootlevel)) {
            IGRAPH_ERROR("Reingold-Tilford: 'roots' and 'rootlevel' lengths differ",
                         IGRAPH_EINVAL);
        }

        /* count the rootlevels that are not zero */
        for (i = 0; i < igraph_vector_size(roots); i++) {
            plus_levels += VECTOR(*rootlevel)[i];
        }

        /* make copy of graph, add vertices/edges */
        if (plus_levels != 0) {
            long int edgeptr = 0;

            pextended = &extended;
            IGRAPH_CHECK(igraph_copy(&extended, graph));
            IGRAPH_FINALLY(igraph_destroy, &extended);
            IGRAPH_CHECK(igraph_add_vertices(&extended,
                                             (igraph_integer_t) plus_levels, 0));

            igraph_vector_resize(&newedges, plus_levels * 2);

            for (i = 0; i < igraph_vector_size(roots); i++) {
                long int rl = (long int) VECTOR(*rootlevel)[i];
                long int rn = (long int) VECTOR(*roots)[i];
                long int j;

                /* zero-level roots don't get anything special */
                if (rl == 0) {
                    continue;
                }

                /* for each nonzero-level root, add vertices
                   and edges at all levels [1, 2, .., rl]
                   piercing through the graph. If mode=="in"
                   they pierce the other way */
                if (mode != IGRAPH_IN) {
                    VECTOR(newedges)[edgeptr++] = no_of_nodes;
                    VECTOR(newedges)[edgeptr++] = rn;
                    for (j = 0; j < rl - 1; j++) {
                        VECTOR(newedges)[edgeptr++] = no_of_nodes + 1;
                        VECTOR(newedges)[edgeptr++] = no_of_nodes;
                        no_of_nodes++;
                    }
                } else {
                    VECTOR(newedges)[edgeptr++] = rn;
                    VECTOR(newedges)[edgeptr++] = no_of_nodes;
                    for (j = 0; j < rl - 1; j++) {
                        VECTOR(newedges)[edgeptr++] = no_of_nodes;
                        VECTOR(newedges)[edgeptr++] = no_of_nodes + 1;
                        no_of_nodes++;
                    }
                }

                /* move on to the next root */
                VECTOR(*roots)[i] = no_of_nodes++;
            }

            /* actually add the edges to the graph */
            IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
        }
    }

    /* We have root vertices now. If one or more nonzero-level roots were
       chosen by the user, we have copied the graph and added a few vertices
       and (directed) edges to connect those floating roots to nonfloating,
       zero-level equivalent roots.

       Below, the function

       igraph_i_layout_reingold_tilford(pextended, res, mode, real_root)

       calculates the actual rt coordinates of the graph. However, for
       simplicity that function requires a connected graph and a single root.
       For directed graphs, it needs not be strongly connected, however all
       nodes must be reachable from the root following the stream (i.e. the
       root must be a "mother vertex").

       So before we call that function we have to make sure the (copied) graph
       satisfies that condition. That requires:
         1. if there is more than one root, defining a single real_root
         2. if a real_root is defined, adding edges to connect all roots to it
         3. ensure real_root is mother of the whole graph. If it is not,
            add shortcut edges from real_root to any disconnected node for now.

      NOTE: 3. could be done better, e.g. by topological sorting of some kind.
      But for now it's ok like this.
    */
    /* if there is only one root, no need for real_root */
    if (igraph_vector_size(proots) == 1) {
        real_root = (long int) VECTOR(*proots)[0];
        if (real_root < 0 || real_root >= no_of_nodes) {
            IGRAPH_ERROR("invalid vertex id", IGRAPH_EINVVID);
        }

        /* else, we need to make real_root */
    } else {
        long int no_of_newedges;

        /* Make copy of the graph unless it exists already */
        if (pextended == graph) {
            pextended = &extended;
            IGRAPH_CHECK(igraph_copy(&extended, graph));
            IGRAPH_FINALLY(igraph_destroy, &extended);
        }

        /* add real_root to the vertices */
        real_root = no_of_nodes;
        IGRAPH_CHECK(igraph_add_vertices(&extended, 1, 0));
        no_of_nodes++;

        /* add edges from the roots to real_root */
        no_of_newedges = igraph_vector_size(proots);
        igraph_vector_resize(&newedges, no_of_newedges * 2);
        for (i = 0; i < no_of_newedges; i++) {
            VECTOR(newedges)[2 * i] = no_of_nodes - 1;
            VECTOR(newedges)[2 * i + 1] = VECTOR(*proots)[i];
        }

        IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
    }

    /* prepare edges to unreachable parts of the graph */
    IGRAPH_CHECK(igraph_i_layout_reingold_tilford_unreachable(pextended, mode, real_root, no_of_nodes, &newedges));

    if (igraph_vector_size(&newedges) != 0) {
        /* Make copy of the graph unless it exists already */
        if (pextended == graph) {
            pextended = &extended;
            IGRAPH_CHECK(igraph_copy(&extended, graph));
            IGRAPH_FINALLY(igraph_destroy, &extended);
        }

        IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
    }
    igraph_vector_destroy(&newedges);
    IGRAPH_FINALLY_CLEAN(1);

    /* ----------------------------------------------------------------------- */
    /* Layout */
    IGRAPH_CHECK(igraph_i_layout_reingold_tilford(pextended, res, mode, real_root));

    /* Remove the new vertices from the layout */
    if (no_of_nodes != no_of_nodes_orig) {
        if (no_of_nodes - 1 == no_of_nodes_orig) {
            IGRAPH_CHECK(igraph_matrix_remove_row(res, no_of_nodes_orig));
        } else {
            igraph_matrix_t tmp;
            long int i;
            IGRAPH_MATRIX_INIT_FINALLY(&tmp, no_of_nodes_orig, 2);
            for (i = 0; i < no_of_nodes_orig; i++) {
                MATRIX(tmp, i, 0) = MATRIX(*res, i, 0);
                MATRIX(tmp, i, 1) = MATRIX(*res, i, 1);
            }
            IGRAPH_CHECK(igraph_matrix_update(res, &tmp));
            igraph_matrix_destroy(&tmp);
            IGRAPH_FINALLY_CLEAN(1);
        }
    }

    if (pextended != graph) {
        igraph_destroy(&extended);
        IGRAPH_FINALLY_CLEAN(1);
    }

    /* Remove the roots vector if it was created by us */
    if (proots != roots) {
        igraph_vector_destroy(&myroots);
        IGRAPH_FINALLY_CLEAN(1);
    }

    return 0;
}

/**
 * \function igraph_layout_reingold_tilford_circular
 * \brief Circular Reingold-Tilford layout for trees
 *
 * </para><para>
 * This layout is almost the same as \ref igraph_layout_reingold_tilford(), but
 * the tree is drawn in a circular way, with the root vertex in the center.
 *
 * \param graph The graph object.
 * \param res The result, the coordinates in a matrix. The parameter
 *   should point to an initialized matrix object and will be resized.
 * \param mode Specifies which edges to consider when building the tree.
 *   If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN
 *   then only the incoming edges of a parent are considered. If it is
 *   \c IGRAPH_ALL then all edges are used (this was the behavior in
 *   igraph 0.5 and before). This parameter also influences how the root
 *   vertices are calculated, if they are not given. See the \p roots parameter.
 * \param roots The index of the root vertex or root vertices.
 *   If this is a non-empty vector then the supplied vertex ids are used
 *   as the roots of the trees (or a single tree if the graph is connected).
 *   If it is a null pointer of a pointer to an empty vector, then the root
 *   vertices are automatically calculated based on topological sorting,
 *   performed with the opposite mode than the \p mode argument.
 *   After the vertices have been sorted, one is selected from each component.
 * \param rootlevel This argument can be useful when drawing forests which are
 *   not trees (i.e. they are unconnected and have tree components). It specifies
 *   the level of the root vertices for every tree in the forest. It is only
 *   considered if not a null pointer and the \p roots argument is also given
 *   (and it is not a null pointer of an empty vector). Note that if you supply
 *   a null pointer here and the graph has multiple components, all of the root
 *   vertices will be mapped to the origin of the coordinate system, which does
 *   not really make sense.
 * \return Error code.
 *
 * \sa \ref igraph_layout_reingold_tilford().
 */

int igraph_layout_reingold_tilford_circular(const igraph_t *graph,
        igraph_matrix_t *res,
        igraph_neimode_t mode,
        const igraph_vector_t *roots,
        const igraph_vector_t *rootlevel) {

    long int no_of_nodes = igraph_vcount(graph);
    long int i;
    igraph_real_t ratio = 2 * M_PI * (no_of_nodes - 1.0) / no_of_nodes;
    igraph_real_t minx, maxx;

    IGRAPH_CHECK(igraph_layout_reingold_tilford(graph, res, mode, roots, rootlevel));

    if (no_of_nodes == 0) {
        return 0;
    }

    minx = maxx = MATRIX(*res, 0, 0);
    for (i = 1; i < no_of_nodes; i++) {
        if (MATRIX(*res, i, 0) > maxx) {
            maxx = MATRIX(*res, i, 0);
        }
        if (MATRIX(*res, i, 0) < minx) {
            minx = MATRIX(*res, i, 0);
        }
    }
    if (maxx > minx) {
        ratio /= (maxx - minx);
    }
    for (i = 0; i < no_of_nodes; i++) {
        igraph_real_t phi = (MATRIX(*res, i, 0) - minx) * ratio;
        igraph_real_t r = MATRIX(*res, i, 1);
        MATRIX(*res, i, 0) = r * cos(phi);
        MATRIX(*res, i, 1) = r * sin(phi);
    }

    return 0;
}

#define COULOMBS_CONSTANT 8987500000.0


static igraph_real_t igraph_i_distance_between(
        const igraph_matrix_t *c,
        long int a, long int b);

static int igraph_i_determine_electric_axal_forces(
        const igraph_matrix_t *pos,
        igraph_real_t *x,
        igraph_real_t *y,
        igraph_real_t directed_force,
        igraph_real_t distance,
        long int other_node,
        long int this_node);

static int igraph_i_apply_electrical_force(
        const igraph_matrix_t *pos,
        igraph_vector_t *pending_forces_x,
        igraph_vector_t *pending_forces_y,
        long int other_node, long int this_node,
        igraph_real_t node_charge,
        igraph_real_t distance);

static int igraph_i_determine_spring_axal_forces(
        const igraph_matrix_t *pos,
        igraph_real_t *x, igraph_real_t *y,
        igraph_real_t directed_force,
        igraph_real_t distance,
        int spring_length,
        long int other_node,
        long int this_node);

static int igraph_i_apply_spring_force(
        const igraph_matrix_t *pos,
        igraph_vector_t *pending_forces_x,
        igraph_vector_t *pending_forces_y,
        long int other_node,
        long int this_node, int spring_length,
        igraph_real_t spring_constant);

static int igraph_i_move_nodes(
        igraph_matrix_t *pos,
        const igraph_vector_t *pending_forces_x,
        const igraph_vector_t *pending_forces_y,
        igraph_real_t node_mass,
        igraph_real_t max_sa_movement);

static igraph_real_t igraph_i_distance_between(
        const igraph_matrix_t *c,
        long int a, long int b) {
    igraph_real_t diffx = MATRIX(*c, a, 0) - MATRIX(*c, b, 0);
    igraph_real_t diffy = MATRIX(*c, a, 1) - MATRIX(*c, b, 1);
    return sqrt( diffx * diffx + diffy * diffy );
}

static int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos,
        igraph_real_t *x,
        igraph_real_t *y,
        igraph_real_t directed_force,
        igraph_real_t distance,
        long int other_node,
        long int this_node) {

    // We know what the directed force is.  We now need to translate it
    // into the appropriate x and y components.
    // First, assume:
    //                 other_node
    //                    /|
    //  directed_force  /  |
    //                /    | y
    //              /______|
    //    this_node     x
    //
    // other_node.x > this_node.x
    // other_node.y > this_node.y
    // the force will be on this_node away from other_node

    // the proportion (distance/y_distance) is equal to the proportion
    // (directed_force/y_force), as the two triangles are similar.
    // therefore, the magnitude of y_force = (directed_force*y_distance)/distance
    // the sign of y_force is negative, away from other_node

    igraph_real_t x_distance, y_distance;
    y_distance = MATRIX(*pos, other_node, 1) - MATRIX(*pos, this_node, 1);
    if (y_distance < 0) {
        y_distance = -y_distance;
    }
    *y = -1 * ((directed_force * y_distance) / distance);

    // the x component works in exactly the same way.
    x_distance = MATRIX(*pos, other_node, 0) - MATRIX(*pos, this_node, 0);
    if (x_distance < 0) {
        x_distance = -x_distance;
    }
    *x = -1 * ((directed_force * x_distance) / distance);

    // Now we need to reverse the polarity of our answers based on the falsness
    // of our assumptions.
    if (MATRIX(*pos, other_node, 0) < MATRIX(*pos, this_node, 0)) {
        *x = *x * -1;
    }
    if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) {
        *y = *y * -1;
    }

    return 0;
}

static int igraph_i_apply_electrical_force(
        const igraph_matrix_t *pos,
        igraph_vector_t *pending_forces_x,
        igraph_vector_t *pending_forces_y,
        long int other_node, long int this_node,
        igraph_real_t node_charge,
        igraph_real_t distance) {

    igraph_real_t directed_force = COULOMBS_CONSTANT *
                                   ((node_charge * node_charge) / (distance * distance));

    igraph_real_t x_force, y_force;
    igraph_i_determine_electric_axal_forces(pos, &x_force, &y_force,
                                            directed_force, distance,
                                            other_node, this_node);

    VECTOR(*pending_forces_x)[this_node] += x_force;
    VECTOR(*pending_forces_y)[this_node] += y_force;
    VECTOR(*pending_forces_x)[other_node] -= x_force;
    VECTOR(*pending_forces_y)[other_node] -= y_force;

    return 0;
}

static int igraph_i_determine_spring_axal_forces(
        const igraph_matrix_t *pos,
        igraph_real_t *x, igraph_real_t *y,
        igraph_real_t directed_force,
        igraph_real_t distance,
        int spring_length,
        long int other_node, long int this_node) {

    // if the spring is just the right size, the forces will be 0, so we can
    // skip the computation.
    //
    // if the spring is too long, our forces will be identical to those computed
    // by determine_electrical_axal_forces() (this_node will be pulled toward
    // other_node).
    //
    // if the spring is too short, our forces will be the opposite of those
    // computed by determine_electrical_axal_forces() (this_node will be pushed
    // away from other_node)
    //
    // finally, since both nodes are movable, only one-half of the total force
    // should be applied to each node, so half the forces for our answer.

    if (distance == spring_length) {
        *x = 0.0;
        *y = 0.0;
    } else {
        igraph_i_determine_electric_axal_forces(pos, x, y, directed_force, distance,
                                                other_node, this_node);
        if (distance < spring_length) {
            *x = -1 * *x;
            *y = -1 * *y;
        }
        *x = 0.5 * *x;
        *y = 0.5 * *y;
    }

    return 0;
}

static int igraph_i_apply_spring_force(
        const igraph_matrix_t *pos,
        igraph_vector_t *pending_forces_x,
        igraph_vector_t *pending_forces_y,
        long int other_node,
        long int this_node, int spring_length,
        igraph_real_t spring_constant) {

    // determined using Hooke's Law:
    //   force = -kx
    // where:
    //   k = spring constant
    //   x = displacement from ideal length in meters

    igraph_real_t distance, displacement, directed_force, x_force, y_force;
    distance = igraph_i_distance_between(pos, other_node, this_node);
    // let's protect ourselves from division by zero by ignoring two nodes that
    // happen to be in the same place.  Since we separate all nodes before we
    // work on any of them, this will only happen in extremely rare circumstances,
    // and when it does, electrical force will probably push one or both of them
    // one way or another anyway.
    if (distance == 0.0) {
        return 0;
    }

    displacement = distance - spring_length;
    if (displacement < 0) {
        displacement = -displacement;
    }
    directed_force = -1 * spring_constant * displacement;
    // remember, this is force directed away from the spring;
    // a negative number is back towards the spring (or, in our case, back towards
    // the other node)

    // get the force that should be applied to >this< node
    igraph_i_determine_spring_axal_forces(pos, &x_force, &y_force,
                                          directed_force, distance, spring_length,
                                          other_node, this_node);

    VECTOR(*pending_forces_x)[this_node] += x_force;
    VECTOR(*pending_forces_y)[this_node] += y_force;
    VECTOR(*pending_forces_x)[other_node] -= x_force;
    VECTOR(*pending_forces_y)[other_node] -= y_force;

    return 0;
}

static int igraph_i_move_nodes(
        igraph_matrix_t *pos,
        const igraph_vector_t *pending_forces_x,
        const igraph_vector_t *pending_forces_y,
        igraph_real_t node_mass,
        igraph_real_t max_sa_movement) {

    // Since each iteration is isolated, time is constant at 1.
    // Therefore:
    //   Force effects acceleration.
    //   acceleration (d(velocity)/time) = velocity
    //   velocity (d(displacement)/time) = displacement
    //   displacement = acceleration

    // determined using Newton's second law:
    //   sum(F) = ma
    // therefore:
    //   acceleration = force / mass
    //   velocity     = force / mass
    //   displacement = force / mass

    long int this_node, no_of_nodes = igraph_vector_size(pending_forces_x);

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

        igraph_real_t x_movement, y_movement;

        x_movement = VECTOR(*pending_forces_x)[this_node] / node_mass;
        if (x_movement > max_sa_movement) {
            x_movement = max_sa_movement;
        } else if (x_movement < -max_sa_movement) {
            x_movement = -max_sa_movement;
        }

        y_movement = VECTOR(*pending_forces_y)[this_node] / node_mass;
        if (y_movement > max_sa_movement) {
            y_movement = max_sa_movement;
        } else if (y_movement < -max_sa_movement) {
            y_movement = -max_sa_movement;
        }

        MATRIX(*pos, this_node, 0) += x_movement;
        MATRIX(*pos, this_node, 1) += y_movement;

    }
    return 0;
}

/**
 * \function igraph_layout_graphopt
 * \brief Optimizes vertex layout via the graphopt algorithm.
 *
 * </para><para>
 * This is a port of the graphopt layout algorithm by Michael Schmuhl.
 * graphopt version 0.4.1 was rewritten in C and the support for
 * layers was removed (might be added later) and a code was a bit
 * reorganized to avoid some unnecessary steps is the node charge (see below)
 * is zero.
 *
 * </para><para>
 * graphopt uses physical analogies for defining attracting and repelling
 * forces among the vertices and then the physical system is simulated
 * until it reaches an equilibrium. (There is no simulated annealing or
 * anything like that, so a stable fixed point is not guaranteed.)
 *
 * </para><para>
 * See also http://www.schmuhl.org/graphopt/ for the original graphopt.
 * \param graph The input graph.
 * \param res Pointer to an initialized matrix, the result will be stored here
 *    and its initial contents is used the starting point of the simulation
 *    if the \p use_seed argument is true. Note that in this case the
 *    matrix should have the proper size, otherwise a warning is issued and
 *    the supplied values are ignored. If no starting positions are given
 *    (or they are invalid) then a random staring position is used.
 *    The matrix will be resized if needed.
 * \param niter Integer constant, the number of iterations to perform.
 *    Should be a couple of hundred in general. If you have a large graph
 *    then you might want to only do a few iterations and then check the
 *    result. If it is not good enough you can feed it in again in
 *    the \p res argument. The original graphopt default if 500.
 * \param node_charge The charge of the vertices, used to calculate electric
 *    repulsion. The original graphopt default is 0.001.
 * \param node_mass The mass of the vertices, used for the spring forces.
 *    The original graphopt defaults to 30.
 * \param spring_length The length of the springs, an integer number.
 *    The original graphopt defaults to zero.
 * \param spring_constant The spring constant, the original graphopt defaults
 *    to one.
 * \param max_sa_movement Real constant, it gives the maximum amount of movement
 *    allowed in a single step along a single axis. The original graphopt
 *    default is 5.
 * \param use_seed Logical scalar, whether to use the positions in \p res as
 *    a starting configuration. See also \p res above.
 * \return Error code.
 *
 * Time complexity: O(n (|V|^2+|E|) ), n is the number of iterations,
 * |V| is the number of vertices, |E| the number
 * of edges. If \p node_charge is zero then it is only O(n|E|).
 */

int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res,
                           igraph_integer_t niter,
                           igraph_real_t node_charge, igraph_real_t node_mass,
                           igraph_real_t spring_length,
                           igraph_real_t spring_constant,
                           igraph_real_t max_sa_movement,
                           igraph_bool_t use_seed) {

    long int no_of_nodes = igraph_vcount(graph);
    long int no_of_edges = igraph_ecount(graph);
    int my_spring_length = (int) spring_length;
    igraph_vector_t pending_forces_x, pending_forces_y;
    /* Set a flag to calculate (or not) the electrical forces that the nodes */
    /* apply on each other based on if both node types' charges are zero. */
    igraph_bool_t apply_electric_charges = (node_charge != 0);

    long int this_node, other_node, edge;
    igraph_real_t distance;
    long int i;

    IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes);

    if (use_seed) {
        if (igraph_matrix_nrow(res) != no_of_nodes ||
            igraph_matrix_ncol(res) != 2) {
            IGRAPH_WARNING("Invalid size for initial matrix, starting from random layout");
            IGRAPH_CHECK(igraph_layout_random(graph, res));
        }
    } else {
        IGRAPH_CHECK(igraph_layout_random(graph, res));
    }

    IGRAPH_PROGRESS("Graphopt layout", 0, NULL);
    for (i = niter; i > 0; i--) {
        /* Report progress in approx. every 100th step */
        if (i % 10 == 0) {
            IGRAPH_PROGRESS("Graphopt layout", 100.0 - 100.0 * i / niter, NULL);
        }

        /* Clear pending forces on all nodes */
        igraph_vector_null(&pending_forces_x);
        igraph_vector_null(&pending_forces_y);

        // Apply electrical force applied by all other nodes
        if (apply_electric_charges) {
            // Iterate through all nodes
            for (this_node = 0; this_node < no_of_nodes; this_node++) {
                IGRAPH_ALLOW_INTERRUPTION();
                for (other_node = this_node + 1;
                     other_node < no_of_nodes;
                     other_node++) {
                    distance = igraph_i_distance_between(res, this_node, other_node);
                    // let's protect ourselves from division by zero by ignoring
                    // two nodes that happen to be in the same place.  Since we
                    // separate all nodes before we work on any of them, this
                    // will only happen in extremely rare circumstances, and when
                    // it does, springs will probably pull them apart anyway.
                    // also, if we are more than 50 away, the electric force
                    // will be negligible.
                    // ***** may not always be desirable ****
                    if ((distance != 0.0) && (distance < 500.0)) {
                        //    if (distance != 0.0) {
                        // Apply electrical force from node(counter2) on
                        // node(counter)
                        igraph_i_apply_electrical_force(res, &pending_forces_x,
                                                        &pending_forces_y,
                                                        other_node, this_node,
                                                        node_charge,
                                                        distance);
                    }
                }
            }
        }

        // Apply force from springs
        for (edge = 0; edge < no_of_edges; edge++) {
            long int tthis_node = IGRAPH_FROM(graph, edge);
            long int oother_node = IGRAPH_TO(graph, edge);
            // Apply spring force on both nodes
            igraph_i_apply_spring_force(res, &pending_forces_x, &pending_forces_y,
                                        oother_node, tthis_node, my_spring_length,
                                        spring_constant);
        }

        // Effect the movement of the nodes based on all pending forces
        igraph_i_move_nodes(res, &pending_forces_x, &pending_forces_y, node_mass,
                            max_sa_movement);
    }
    IGRAPH_PROGRESS("Graphopt layout", 100, NULL);

    igraph_vector_destroy(&pending_forces_y);
    igraph_vector_destroy(&pending_forces_x);
    IGRAPH_FINALLY_CLEAN(2);

    return 0;
}

/* not 'static', used in tests */
int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid,
                              long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r,
                              igraph_real_t cx, igraph_real_t cy, igraph_real_t startr,
                              igraph_real_t killr);

/* TODO: not 'static' because used in tests */
int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x,
                              igraph_real_t *y, igraph_real_t *r);
int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x,
                              igraph_real_t *y, igraph_real_t *z,
                              igraph_real_t *r);

/**
 * \function igraph_layout_merge_dla
 * \brief Merge multiple layouts by using a DLA algorithm
 *
 * </para><para>
 * First each layout is covered by a circle. Then the layout of the
 * largest graph is placed at the origin. Then the other layouts are
 * placed by the DLA algorithm, larger ones first and smaller ones
 * last.
 * \param thegraphs Pointer vector containing the graph object of
 *        which the layouts will be merged.
 * \param coords Pointer vector containing matrix objects with the 2d
 *        layouts of the graphs in \p thegraphs.
 * \param res Pointer to an initialized matrix object, the result will
 *        be stored here. It will be resized if needed.
 * \return Error code.
 *
 * Added in version 0.2. This function is experimental.
 *
 * </para><para>
 * Time complexity: TODO.
 */

int igraph_layout_merge_dla(igraph_vector_ptr_t *thegraphs,
                            igraph_vector_ptr_t *coords,
                            igraph_matrix_t *res) {
    long int graphs = igraph_vector_ptr_size(coords);
    igraph_vector_t sizes;
    igraph_vector_t x, y, r;
    igraph_vector_t nx, ny, nr;
    long int allnodes = 0;
    long int i, j;
    long int actg;
    igraph_i_layout_mergegrid_t grid;
    long int jpos = 0;
    igraph_real_t minx, maxx, miny, maxy;
    igraph_real_t area = 0;
    igraph_real_t maxr = 0;
    long int respos;

    /* Graphs are currently not used, only the coordinates */
    IGRAPH_UNUSED(thegraphs);

    IGRAPH_VECTOR_INIT_FINALLY(&sizes, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&x, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&y, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&r, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&nx, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&ny, graphs);
    IGRAPH_VECTOR_INIT_FINALLY(&nr, graphs);

    RNG_BEGIN();

    for (i = 0; i < igraph_vector_ptr_size(coords); i++) {
        igraph_matrix_t *mat = VECTOR(*coords)[i];
        long int size = igraph_matrix_nrow(mat);

        if (igraph_matrix_ncol(mat) != 2) {
            IGRAPH_ERROR("igraph_layout_merge_dla works for 2D layouts only",
                         IGRAPH_EINVAL);
        }

        IGRAPH_ALLOW_INTERRUPTION();
        allnodes += size;
        VECTOR(sizes)[i] = size;
        VECTOR(r)[i] = pow(size, .75);
        area += VECTOR(r)[i] * VECTOR(r)[i];
        if (VECTOR(r)[i] > maxr) {
            maxr = VECTOR(r)[i];
        }

        igraph_i_layout_sphere_2d(mat,
                                  igraph_vector_e_ptr(&nx, i),
                                  igraph_vector_e_ptr(&ny, i),
                                  igraph_vector_e_ptr(&nr, i));

    }
    igraph_vector_order2(&sizes); /* largest first */

    /* 0. create grid */
    minx = miny = -sqrt(5 * area);
    maxx = maxy = sqrt(5 * area);
    igraph_i_layout_mergegrid_init(&grid, minx, maxx, 200,
                                   miny, maxy, 200);
    IGRAPH_FINALLY(igraph_i_layout_mergegrid_destroy, &grid);

    /*   fprintf(stderr, "Ok, starting DLA\n"); */

    /* 1. place the largest  */
    actg = (long int) VECTOR(sizes)[jpos++];
    igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[actg], actg);

    IGRAPH_PROGRESS("Merging layouts via DLA", 0.0, NULL);
    while (jpos < graphs) {
        IGRAPH_ALLOW_INTERRUPTION();
        /*     fprintf(stderr, "comp: %li", jpos); */
        IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / graphs, NULL);

        actg = (long int) VECTOR(sizes)[jpos++];
        /* 2. random walk, TODO: tune parameters */
        igraph_i_layout_merge_dla(&grid, actg,
                                  igraph_vector_e_ptr(&x, actg),
                                  igraph_vector_e_ptr(&y, actg),
                                  VECTOR(r)[actg], 0, 0,
                                  maxx, maxx + 5);

        /* 3. place sphere */
        igraph_i_layout_merge_place_sphere(&grid, VECTOR(x)[actg], VECTOR(y)[actg],
                                           VECTOR(r)[actg], actg);
    }
    IGRAPH_PROGRESS("Merging layouts via DLA", 100.0, NULL);

    /* Create the result */
    IGRAPH_CHECK(igraph_matrix_resize(res, allnodes, 2));
    respos = 0;
    for (i = 0; i < graphs; i++) {
        long int size = igraph_matrix_nrow(VECTOR(*coords)[i]);
        igraph_real_t xx = VECTOR(x)[i];
        igraph_real_t yy = VECTOR(y)[i];
        igraph_real_t rr = VECTOR(r)[i] / VECTOR(nr)[i];
        igraph_matrix_t *mat = VECTOR(*coords)[i];
        IGRAPH_ALLOW_INTERRUPTION();
        if (VECTOR(nr)[i] == 0) {
            rr = 1;
        }
        for (j = 0; j < size; j++) {
            MATRIX(*res, respos, 0) = rr * (MATRIX(*mat, j, 0) - VECTOR(nx)[i]);
            MATRIX(*res, respos, 1) = rr * (MATRIX(*mat, j, 1) - VECTOR(ny)[i]);
            MATRIX(*res, respos, 0) += xx;
            MATRIX(*res, respos, 1) += yy;
            ++respos;
        }
    }

    RNG_END();

    igraph_i_layout_mergegrid_destroy(&grid);
    igraph_vector_destroy(&sizes);
    igraph_vector_destroy(&x);
    igraph_vector_destroy(&y);
    igraph_vector_destroy(&r);
    igraph_vector_destroy(&nx);
    igraph_vector_destroy(&ny);
    igraph_vector_destroy(&nr);
    IGRAPH_FINALLY_CLEAN(8);
    return 0;
}

int igraph_i_layout_sphere_2d(igraph_matrix_t *coords,
                              igraph_real_t *x, igraph_real_t *y,
                              igraph_real_t *r) {
    long int nodes = igraph_matrix_nrow(coords);
    long int i;
    igraph_real_t xmin, xmax, ymin, ymax;

    xmin = xmax = MATRIX(*coords, 0, 0);
    ymin = ymax = MATRIX(*coords, 0, 1);
    for (i = 1; i < nodes; i++) {

        if (MATRIX(*coords, i, 0) < xmin) {
            xmin = MATRIX(*coords, i, 0);
        } else if (MATRIX(*coords, i, 0) > xmax) {
            xmax = MATRIX(*coords, i, 0);
        }

        if (MATRIX(*coords, i, 1) < ymin) {
            ymin = MATRIX(*coords, i, 1);
        } else if (MATRIX(*coords, i, 1) > ymax) {
            ymax = MATRIX(*coords, i, 1);
        }

    }

    *x = (xmin + xmax) / 2;
    *y = (ymin + ymax) / 2;
    *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) ) / 2;

    return 0;
}

int igraph_i_layout_sphere_3d(igraph_matrix_t *coords,
                              igraph_real_t *x, igraph_real_t *y,
                              igraph_real_t *z, igraph_real_t *r) {
    long int nodes = igraph_matrix_nrow(coords);
    long int i;
    igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax;

    xmin = xmax = MATRIX(*coords, 0, 0);
    ymin = ymax = MATRIX(*coords, 0, 1);
    zmin = zmax = MATRIX(*coords, 0, 2);
    for (i = 1; i < nodes; i++) {

        if (MATRIX(*coords, i, 0) < xmin) {
            xmin = MATRIX(*coords, i, 0);
        } else if (MATRIX(*coords, i, 0) > xmax) {
            xmax = MATRIX(*coords, i, 0);
        }

        if (MATRIX(*coords, i, 1) < ymin) {
            ymin = MATRIX(*coords, i, 1);
        } else if (MATRIX(*coords, i, 1) > ymax) {
            ymax = MATRIX(*coords, i, 1);
        }

        if (MATRIX(*coords, i, 2) < zmin) {
            zmin = MATRIX(*coords, i, 2);
        } else if (MATRIX(*coords, i, 2) > zmax) {
            zmax = MATRIX(*coords, i, 2);
        }

    }

    *x = (xmin + xmax) / 2;
    *y = (ymin + ymax) / 2;
    *z = (zmin + zmax) / 2;
    *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) +
               (zmax - zmin) * (zmax - zmin) ) / 2;

    return 0;
}

#define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2)))

int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid,
                              long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r,
                              igraph_real_t cx, igraph_real_t cy, igraph_real_t startr,
                              igraph_real_t killr) {
    long int sp = -1;
    igraph_real_t angle, len;
    long int steps = 0;

    /* The graph is not used, only its coordinates */
    IGRAPH_UNUSED(actg);

    while (sp < 0) {
        /* start particle */
        do {
            steps++;
            angle = RNG_UNIF(0, 2 * M_PI);
            len = RNG_UNIF(.5 * startr, startr);
            *x = cx + len * cos(angle);
            *y = cy + len * sin(angle);
            sp = igraph_i_layout_mergegrid_get_sphere(grid, *x, *y, r);
        } while (sp >= 0);

        while (sp < 0 && DIST(*x, *y) < killr) {
            igraph_real_t nx, ny;
            steps++;
            angle = RNG_UNIF(0, 2 * M_PI);
            len = RNG_UNIF(0, startr / 100);
            nx = *x + len * cos(angle);
            ny = *y + len * sin(angle);
            sp = igraph_i_layout_mergegrid_get_sphere(grid, nx, ny, r);
            if (sp < 0) {
                *x = nx; *y = ny;
            }
        }
    }

    /*   fprintf(stderr, "%li ", steps); */
    return 0;
}

static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from,
                                    int n, void *extra);

static int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res,
                                      igraph_matrix_t *dist, long int dim);

static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from,
                                    int n, void *extra) {
    igraph_matrix_t* matrix = (igraph_matrix_t*)extra;
    IGRAPH_UNUSED(n);
    igraph_blas_dgemv_array(0, 1, matrix, from, 0, to);
    return 0;
}

/* MDS layout for a connected graph, with no error checking on the
 * input parameters. The distance matrix will be modified in-place. */
int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res,
                               igraph_matrix_t *dist, long int dim) {

    long int no_of_nodes = igraph_vcount(graph);
    long int nev = dim;
    igraph_matrix_t vectors;
    igraph_vector_t values, row_means;
    igraph_real_t grand_mean;
    long int i, j, k;
    igraph_eigen_which_t which;

    /* Handle the trivial cases */
    if (no_of_nodes == 1) {
        IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim));
        igraph_matrix_fill(res, 0);
        return IGRAPH_SUCCESS;
    }
    if (no_of_nodes == 2) {
        IGRAPH_CHECK(igraph_matrix_resize(res, 2, dim));
        igraph_matrix_fill(res, 0);
        for (j = 0; j < dim; j++) {
            MATRIX(*res, 1, j) = 1;
        }
        return IGRAPH_SUCCESS;
    }

    /* Initialize some stuff */
    IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
    IGRAPH_CHECK(igraph_matrix_init(&vectors, no_of_nodes, dim));
    IGRAPH_FINALLY(igraph_matrix_destroy, &vectors);

    /* Take the square of the distance matrix */
    for (i = 0; i < no_of_nodes; i++) {
        for (j = 0; j < no_of_nodes; j++) {
            MATRIX(*dist, i, j) *= MATRIX(*dist, i, j);
        }
    }

    /* Double centering of the distance matrix */
    IGRAPH_VECTOR_INIT_FINALLY(&row_means, no_of_nodes);
    igraph_vector_fill(&values, 1.0 / no_of_nodes);
    igraph_blas_dgemv(0, 1, dist, &values, 0, &row_means);
    grand_mean = igraph_vector_sum(&row_means) / no_of_nodes;
    igraph_matrix_add_constant(dist, grand_mean);
    for (i = 0; i < no_of_nodes; i++) {
        for (j = 0; j < no_of_nodes; j++) {
            MATRIX(*dist, i, j) -= VECTOR(row_means)[i] + VECTOR(row_means)[j];
            MATRIX(*dist, i, j) *= -0.5;
        }
    }
    igraph_vector_destroy(&row_means);
    IGRAPH_FINALLY_CLEAN(1);

    /* Calculate the top `dim` eigenvectors. */
    which.pos = IGRAPH_EIGEN_LA;
    which.howmany = (int) nev;
    IGRAPH_CHECK(igraph_eigen_matrix_symmetric(/*A=*/ 0, /*sA=*/ 0,
                 /*fun=*/ igraph_i_layout_mds_step,
                 /*n=*/ (int) no_of_nodes, /*extra=*/ dist,
                 /*algorithm=*/ IGRAPH_EIGEN_LAPACK,
                 &which, /*options=*/ 0, /*storage=*/ 0,
                 &values, &vectors));

    /* Calculate and normalize the final coordinates */
    for (j = 0; j < nev; j++) {
        VECTOR(values)[j] = sqrt(fabs(VECTOR(values)[j]));
    }
    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, dim));
    for (i = 0; i < no_of_nodes; i++) {
        for (j = 0, k = nev - 1; j < nev; j++, k--) {
            MATRIX(*res, i, k) = VECTOR(values)[j] * MATRIX(vectors, i, j);
        }
    }

    igraph_matrix_destroy(&vectors);
    igraph_vector_destroy(&values);
    IGRAPH_FINALLY_CLEAN(2);

    return IGRAPH_SUCCESS;
}

/**
 * \function igraph_layout_mds
 * \brief Place the vertices on a plane using multidimensional scaling.
 *
 * </para><para>
 * This layout requires a distance matrix, where the intersection of
 * row i and column j specifies the desired distance between vertex i
 * and vertex j. The algorithm will try to place the vertices in a
 * space having a given number of dimensions in a way that approximates
 * the distance relations prescribed in the distance matrix. igraph
 * uses the classical multidimensional scaling by Torgerson; for more
 * details, see Cox &amp; Cox: Multidimensional Scaling (1994), Chapman
 * and Hall, London.
 *
 * </para><para>
 * If the input graph is disconnected, igraph will decompose it
 * first into its subgraphs, lay out the subgraphs one by one
 * using the appropriate submatrices of the distance matrix, and
 * then merge the layouts using \ref igraph_layout_merge_dla.
 * Since \ref igraph_layout_merge_dla works for 2D layouts only,
 * you cannot run the MDS layout on disconnected graphs for
 * more than two dimensions.
 *
 * </para><para>
 * Warning: if the graph is symmetric to the exchange of two vertices
 * (as is the case with leaves of a tree connecting to the same parent),
 * classical multidimensional scaling may assign the same coordinates to
 * these vertices.
 *
 * \param graph A graph object.
 * \param res Pointer to an initialized matrix object. This will
 *        contain the result and will be resized if needed.
 * \param dist The distance matrix. It must be symmetric and this
 *        function does not check whether the matrix is indeed
 *        symmetric. Results are unspecified if you pass a non-symmetric
 *        matrix here. You can set this parameter to null; in this
 *        case, the shortest path lengths between vertices will be
 *        used as distances.
 * \param dim The number of dimensions in the embedding space. For
 *        2D layouts, supply 2 here.
 * \param options This argument is currently ignored, it was used for
 *        ARPACK, but LAPACK is used now for calculating the eigenvectors.
 * \return Error code.
 *
 * Added in version 0.6.
 *
 * </para><para>
 * Time complexity: usually around O(|V|^2 dim).
 */

int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res,
                      const igraph_matrix_t *dist, long int dim,
                      igraph_arpack_options_t *options) {
    long int i, no_of_nodes = igraph_vcount(graph);
    igraph_matrix_t m;
    igraph_bool_t conn;

    RNG_BEGIN();

    /* Check the distance matrix */
    if (dist && (igraph_matrix_nrow(dist) != no_of_nodes ||
                 igraph_matrix_ncol(dist) != no_of_nodes)) {
        IGRAPH_ERROR("invalid distance matrix size", IGRAPH_EINVAL);
    }

    /* Check the number of dimensions */
    if (dim <= 1) {
        IGRAPH_ERROR("dim must be positive", IGRAPH_EINVAL);
    }
    if (dim > no_of_nodes) {
        IGRAPH_ERROR("dim must be less than the number of nodes", IGRAPH_EINVAL);
    }

    /* Copy or obtain the distance matrix */
    if (dist == 0) {
        IGRAPH_CHECK(igraph_matrix_init(&m, no_of_nodes, no_of_nodes));
        IGRAPH_FINALLY(igraph_matrix_destroy, &m);
        IGRAPH_CHECK(igraph_shortest_paths(graph, &m,
                                           igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL));
    } else {
        IGRAPH_CHECK(igraph_matrix_copy(&m, dist));
        IGRAPH_FINALLY(igraph_matrix_destroy, &m);
        /* Make sure that the diagonal contains zeroes only */
        for (i = 0; i < no_of_nodes; i++) {
            MATRIX(m, i, i) = 0.0;
        }
    }

    /* Check whether the graph is connected */
    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
    if (conn) {
        /* Yes, it is, just do the MDS */
        IGRAPH_CHECK(igraph_i_layout_mds_single(graph, res, &m, dim));
    } else {
        /* The graph is not connected, lay out the components one by one */
        igraph_vector_ptr_t layouts;
        igraph_vector_t comp, vertex_order;
        igraph_t subgraph;
        igraph_matrix_t *layout;
        igraph_matrix_t dist_submatrix;
        igraph_bool_t *seen_vertices;
        long int j, n, processed_vertex_count = 0;

        IGRAPH_VECTOR_INIT_FINALLY(&comp, 0);
        IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes);

        IGRAPH_CHECK(igraph_vector_ptr_init(&layouts, 0));
        IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layouts);
        igraph_vector_ptr_set_item_destructor(&layouts, (igraph_finally_func_t*)igraph_matrix_destroy);

        IGRAPH_CHECK(igraph_matrix_init(&dist_submatrix, 0, 0));
        IGRAPH_FINALLY(igraph_matrix_destroy, &dist_submatrix);

        seen_vertices = igraph_Calloc(no_of_nodes, igraph_bool_t);
        if (seen_vertices == 0) {
            IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM);
        }
        IGRAPH_FINALLY(igraph_free, seen_vertices);

        for (i = 0; i < no_of_nodes; i++) {
            if (seen_vertices[i]) {
                continue;
            }

            /* This is a vertex whose component we did not lay out so far */
            IGRAPH_CHECK(igraph_subcomponent(graph, &comp, i, IGRAPH_ALL));
            /* Take the subgraph */
            IGRAPH_CHECK(igraph_induced_subgraph(graph, &subgraph, igraph_vss_vector(&comp),
                                                 IGRAPH_SUBGRAPH_AUTO));
            IGRAPH_FINALLY(igraph_destroy, &subgraph);
            /* Calculate the submatrix of the distances */
            IGRAPH_CHECK(igraph_matrix_select_rows_cols(&m, &dist_submatrix,
                         &comp, &comp));
            /* Allocate a new matrix for storing the layout */
            layout = igraph_Calloc(1, igraph_matrix_t);
            if (layout == 0) {
                IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM);
            }
            IGRAPH_FINALLY(igraph_free, layout);
            IGRAPH_CHECK(igraph_matrix_init(layout, 0, 0));
            IGRAPH_FINALLY(igraph_matrix_destroy, layout);
            /* Lay out the subgraph */
            IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim));
            /* Store the layout */
            IGRAPH_CHECK(igraph_vector_ptr_push_back(&layouts, layout));
            IGRAPH_FINALLY_CLEAN(2);  /* ownership of layout taken by layouts */
            /* Free the newly created subgraph */
            igraph_destroy(&subgraph);
            IGRAPH_FINALLY_CLEAN(1);
            /* Mark all the vertices in the component as visited */
            n = igraph_vector_size(&comp);
            for (j = 0; j < n; j++) {
                seen_vertices[(long int)VECTOR(comp)[j]] = 1;
                VECTOR(vertex_order)[(long int)VECTOR(comp)[j]] = processed_vertex_count++;
            }
        }
        /* Merge the layouts - reusing dist_submatrix here */
        IGRAPH_CHECK(igraph_layout_merge_dla(0, &layouts, &dist_submatrix));
        /* Reordering the rows of res to match the original graph */
        IGRAPH_CHECK(igraph_matrix_select_rows(&dist_submatrix, res, &vertex_order));

        igraph_free(seen_vertices);
        igraph_matrix_destroy(&dist_submatrix);
        igraph_vector_ptr_destroy_all(&layouts);
        igraph_vector_destroy(&vertex_order);
        igraph_vector_destroy(&comp);
        IGRAPH_FINALLY_CLEAN(5);
    }

    RNG_END();

    igraph_matrix_destroy(&m);
    IGRAPH_FINALLY_CLEAN(1);

    return IGRAPH_SUCCESS;
}

/**
 * \function igraph_layout_bipartite
 * Simple layout for bipartite graphs
 *
 * The layout is created by first placing the vertices in two rows,
 * according to their types. Then the positions within the rows are
 * optimized to minimize edge crossings, by calling \ref
 * igraph_layout_sugiyama().
 *
 * \param graph The input graph.
 * \param types A boolean vector containing ones and zeros, the vertex
 *     types. Its length must match the number of vertices in the graph.
 * \param res Pointer to an initialized matrix, the result, the x and
 *     y coordinates are stored here.
 * \param hgap The preferred minimum horizontal gap between vertices
 *     in the same layer (i.e. vertices of the same type).
 * \param vgap  The distance between layers.
 * \param maxiter Maximum number of iterations in the crossing
 *     minimization stage. 100 is a reasonable default; if you feel
 *     that you have too many edge crossings, increase this.
 * \return Error code.
 *
 * \sa \ref igraph_layout_sugiyama().
 */

int igraph_layout_bipartite(const igraph_t *graph,
                            const igraph_vector_bool_t *types,
                            igraph_matrix_t *res, igraph_real_t hgap,
                            igraph_real_t vgap, long int maxiter) {

    long int i, no_of_nodes = igraph_vcount(graph);
    igraph_vector_t layers;

    if (igraph_vector_bool_size(types) != no_of_nodes) {
        IGRAPH_ERROR("Invalid vertex type vector size", IGRAPH_EINVAL);
    }

    IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes);
    for (i = 0; i < no_of_nodes; i++) {
        VECTOR(layers)[i] = 1 - VECTOR(*types)[i];
    }

    IGRAPH_CHECK(igraph_layout_sugiyama(graph, res, /*extd_graph=*/ 0,
                                        /*extd_to_orig_eids=*/ 0, &layers, hgap,
                                        vgap, maxiter, /*weights=*/ 0));

    igraph_vector_destroy(&layers);
    IGRAPH_FINALLY_CLEAN(1);

    return 0;
}
