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

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

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

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

*/

#include "igraph_structural.h"

#include "igraph_interface.h"

/**
 * \section about_structural
 *
 * <para>These functions usually calculate some structural property
 * of a graph, like its diameter, the degree of the nodes, etc.</para>
 */

/**
 * \function igraph_density
 * \brief Calculate the density of a graph.
 *
 * The density of a graph is simply the ratio of the actual number of its
 * edges and the largest possible number of edges it could have.
 * The maximum number of edges depends on interpretation: are vertices
 * allowed to have a connection to themselves? This is controlled by the
 * \p loops parameter.
 *
 * </para><para>
 * The classic definition of the density is formulated for unweighted
 * graphs without multi-edges. This function allows multigraphs and
 * weighted graphs as well. In this case, it computes the ratio of the
 * total edge weight to the largest possible number of adjacent vertex
 * pairs the graph could have. This value may be larger than 1.
 *
 * </para><para>
 * If you need the density concept for simple graphs, make sure to
 * eliminate any multi-edges appropriately. This can be done using
 * \ref igraph_simplify().
 *
 * \param graph The input graph object. It must not have parallel edges.
 * \param res Pointer to a real number, the result will be stored here.
 * \param weights Vector of edge weights. Pass \c NULL to to perform
 *   an unweighted density calculation.
 * \param loops Boolean constant, whether to include self-loops in the
 *   calculation. If this constant is \c true then
 *   loop edges are thought to be possible in the graph (this does not
 *   necessarily mean that the graph really contains any loops). If
 *   this is \c false then the result is only correct if the graph does not
 *   contain loops. This function does not check if loops are actually
 *   present.
 * \return Error code.
 *
 * Time complexity: O(1).
 */
igraph_error_t igraph_density(
        const igraph_t *graph,
        const igraph_vector_t *weights,
        igraph_real_t *res,
        igraph_bool_t loops) {

    const igraph_bool_t directed = igraph_is_directed(graph);
    const igraph_int_t ecount = igraph_ecount(graph);
    const igraph_real_t vcount = (igraph_real_t) igraph_vcount(graph); /* note real type */
    igraph_real_t total_weight;

    if (vcount == 0) {
        *res = IGRAPH_NAN;
        return IGRAPH_SUCCESS;
    }

    if (weights) {
        if (igraph_vector_size(weights) != ecount) {
            IGRAPH_ERROR("Weight vector length does not match edge count.", IGRAPH_EINVAL);
        }
        total_weight = igraph_vector_sum(weights);
    } else {
        total_weight = ecount;
    }

    if (!loops) {
        if (vcount == 1) {
            *res = IGRAPH_NAN;
        } else if (directed) {
            *res = total_weight / vcount / (vcount - 1);
        } else {
            *res = total_weight / vcount * 2.0 / (vcount - 1);
        }
    } else {
        if (directed) {
            *res = total_weight / vcount / vcount;
        } else {
            *res = total_weight / vcount * 2.0 / (vcount + 1);
        }
    }

    return IGRAPH_SUCCESS;
}

/**
 * \function igraph_mean_degree
 * \brief The mean degree of a graph.
 *
 * This is a convenience function that computes the average of all vertex
 * degrees. In directed graphs, the average of out-degrees and in-degrees is
 * the same; this is the number that is returned. For the null graph, which
 * has no vertices, NaN is returned.
 *
 * \param graph The input graph object.
 * \param res Pointer to a real number, the result will be stored here.
 * \param loops Whether to consider self-loops during the calculation.
 * \return Error code.
 *
 * Time complexity: O(1) if self-loops are considered,
 * O(|E|) where |E| is the number of edges if self-loops are ignored.
 */
igraph_error_t igraph_mean_degree(const igraph_t *graph, igraph_real_t *res,
                                  igraph_bool_t loops) {

    igraph_int_t no_of_nodes = igraph_vcount(graph);
    igraph_int_t no_of_edges = igraph_ecount(graph);
    igraph_bool_t directed = igraph_is_directed(graph);

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

    if (! loops) {
        igraph_int_t loop_count;
        IGRAPH_CHECK(igraph_count_loops(graph, &loop_count));
        no_of_edges -= loop_count;
    }

    *res = (directed ? 1.0 : 2.0) * (igraph_real_t) no_of_edges / (igraph_real_t) no_of_nodes;

    return IGRAPH_SUCCESS;
}

/**
 * \function igraph_diversity
 * \brief Structural diversity index of the vertices.
 *
 * This measure was defined in Nathan Eagle, Michael Macy and Rob
 * Claxton: Network Diversity and Economic Development, Science 328,
 * 1029--1031, 2010.
 *
 * </para><para>
 * It is simply the (normalized) Shannon entropy of the
 * incident edges' weights.
 * <code>D(i) = H(i) / log(k[i])</code>,
 * and
 * <code>H(i) = -sum(p[i,j] log(p[i,j]), j=1..k[i])</code>,
 * where <code>p[i,j] = w[i,j] / sum(w[i,l], l=1..k[i])</code>,
 * <code>k[i]</code> is the (total) degree of vertex \c i,
 * and <code>w[i,j]</code> is the weight of the edge(s) between
 * vertex \c i and \c j. The diversity of isolated vertices will be NaN
 * (not-a-number), while that of vertices with a single connection
 * will be zero.
 *
 * </para><para>
 * The measure works only if the graph is undirected and has no multiple edges.
 * If the graph has multiple edges, simplify it first using \ref
 * igraph_simplify(). If the graph is directed, convert it into an undirected
 * graph with \ref igraph_to_undirected() .
 *
 * \param graph The undirected input graph.
 * \param weights The edge weights, in the order of the edge IDs, must
 *    have appropriate length. Weights must be non-negative.
 * \param res An initialized vector, the results are stored here.
 * \param vids Vertex selector that specifies the vertices which to calculate
 *    the measure.
 * \return Error code.
 *
 * Time complexity: O(|V|+|E|), linear.
 *
 */
igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights,
                     igraph_vector_t *res, const igraph_vs_t vids) {

    igraph_int_t no_of_edges = igraph_ecount(graph);
    igraph_int_t k, i;
    igraph_vector_int_t incident;
    igraph_bool_t has_multiple;
    igraph_vit_t vit;

    if (igraph_is_directed(graph)) {
        IGRAPH_ERROR("Diversity measure works with undirected graphs only.", IGRAPH_EINVAL);
    }

    if (!weights) {
        IGRAPH_ERROR("Edge weights must be given.", IGRAPH_EINVAL);
    }

    if (igraph_vector_size(weights) != no_of_edges) {
        IGRAPH_ERROR("Invalid edge weight vector length.", IGRAPH_EINVAL);
    }

    IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple));
    if (has_multiple) {
        IGRAPH_ERROR("Diversity measure works only if the graph has no multiple edges.", IGRAPH_EINVAL);
    }

    if (no_of_edges > 0) {
        igraph_real_t minweight = igraph_vector_min(weights);
        if (minweight < 0) {
            IGRAPH_ERROR("Weight vector must be non-negative.", IGRAPH_EINVAL);
        } else if (isnan(minweight)) {
            IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL);
        }
    }

    IGRAPH_VECTOR_INT_INIT_FINALLY(&incident, 10);

    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);

    igraph_vector_clear(res);
    IGRAPH_CHECK(igraph_vector_reserve(res, IGRAPH_VIT_SIZE(vit)));

    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
        igraph_real_t d;
        igraph_int_t v = IGRAPH_VIT_GET(vit);

        IGRAPH_CHECK(igraph_incident(graph, &incident, v, IGRAPH_ALL, IGRAPH_LOOPS));
        k = igraph_vector_int_size(&incident); /* degree */

        /*
         * Non-normalized diversity is defined as
         * d = -sum_i w_i/s log (w_i/s)
         * where s = sum_i w_i. In order to avoid two passes through the w vector,
         * we use the equivalent formulation of
         * d = log s - (sum_i w_i log w_i) / s
         * However, this formulation may not give an exact 0.0 for some w when k=1,
         * due to roundoff errors (examples: w=3 or w=7). For this reason, we
         * special-case the computation for k=1 even for the unnormalized diversity
         * insted of just setting the normalization factor to 1 for this case.
         */
        if (k == 0) {
            d = IGRAPH_NAN;
        } else if (k == 1) {
            if (VECTOR(*weights)[0] > 0) d = 0.0; /* s > 0 */
            else d = IGRAPH_NAN; /* s == 0 */
        } else {
            igraph_real_t s = 0.0, ent = 0.0;
            for (i = 0; i < k; i++) {
                igraph_real_t w = VECTOR(*weights)[VECTOR(incident)[i]];
                if (w == 0) continue;
                s += w;
                ent += (w * log(w));
            }
            d = (log(s) - ent / s) / log(k);
        }

        igraph_vector_push_back(res, d); /* reserved */
    }

    igraph_vit_destroy(&vit);
    igraph_vector_int_destroy(&incident);
    IGRAPH_FINALLY_CLEAN(2);

    return IGRAPH_SUCCESS;
}

/**
 * \ingroup structural
 * \function igraph_reciprocity
 * \brief Calculates the reciprocity of a directed graph.
 *
 * In a directed graph, the measure of reciprocity defines the proportion of
 * mutual connections. It is most commonly defined as the probability that the
 * opposite counterpart of a randomly chosen directed edge is also included in
 * the graph. In adjacency matrix notation:
 * <code>1 - (sum_ij |A_ij - A_ji|) / (2 sum_ij A_ij)</code>.
 * In multigraphs, each parallel edge between two vertices must have its own
 * separate reciprocal edge, in accordance with the above formula. This measure
 * is calculated if the \p mode argument is \c IGRAPH_RECIPROCITY_DEFAULT.
 *
 * </para><para>
 * For directed graphs with no edges, NaN is returned.
 * For undirected graphs, 1 is returned unconditionally.
 *
 * </para><para>
 * Prior to igraph version 0.6, another measure was implemented, defined as the
 * probability of having mutual connections between a vertex pair if we know
 * that there is a (possibly non-mutual) connection between them. In other
 * words, (unordered) vertex pairs are classified into three groups:
 * (1) disconnected, (2) non-reciprocally connected, (3) reciprocally connected.
 * The result is the size of group (3), divided by the sum of group
 * sizes (2)+(3). This measure is calculated if \p mode is
 * \c IGRAPH_RECIPROCITY_RATIO.
 *
 * \param graph The graph object.
 * \param res Pointer to an \c igraph_real_t which will contain the result.
 * \param ignore_loops Whether to ignore self-loops when counting edges.
 *    Self-loops are considered as a mutual connection.
 * \param mode Type of reciprocity to calculate, possible values are
 *    \c IGRAPH_RECIPROCITY_DEFAULT and \c IGRAPH_RECIPROCITY_RATIO,
 *    please see their description above.
 * \return Error code:
 *         \c IGRAPH_EINVAL: graph has no edges
 *         \c IGRAPH_ENOMEM: not enough memory for
 *         temporary data.
 *
 * Time complexity: O(|V|+|E|), |V| is the number of vertices,
 * |E| is the number of edges.
 *
 * \example examples/simple/igraph_reciprocity.c
 */
igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res,
                       igraph_bool_t ignore_loops,
                       igraph_reciprocity_t mode) {

    igraph_int_t nonrec = 0, rec = 0, loops = 0;
    igraph_vector_int_t inneis, outneis;
    igraph_int_t no_of_nodes = igraph_vcount(graph);

    if (mode != IGRAPH_RECIPROCITY_DEFAULT &&
        mode != IGRAPH_RECIPROCITY_RATIO) {
        IGRAPH_ERROR("Invalid reciprocity type.", IGRAPH_EINVAL);
    }

    /* Undirected graphs have reciprocity 1.0 by definition. */
    if (!igraph_is_directed(graph)) {
        *res = 1.0;
        return IGRAPH_SUCCESS;
    }

    IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0);
    IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0);

    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
        igraph_int_t ip, op, indeg, outdeg;
        IGRAPH_CHECK(igraph_neighbors(
            graph, &inneis, i, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE
        ));
        IGRAPH_CHECK(igraph_neighbors(
            graph, &outneis, i, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE
        ));

        indeg = igraph_vector_int_size(&inneis);
        outdeg = igraph_vector_int_size(&outneis);

        ip = op = 0;
        while (ip < indeg && op < outdeg) {
            if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) {
                nonrec += 1;
                ip++;
            } else if (VECTOR(inneis)[ip] > VECTOR(outneis)[op]) {
                nonrec += 1;
                op++;
            } else {

                /* loop edge? */
                if (VECTOR(inneis)[ip] == i) {
                    loops += 1;
                    if (!ignore_loops) {
                        rec += 1;
                    }
                } else {
                    rec += 1;
                }

                ip++;
                op++;
            }
        }
        nonrec += (indeg - ip) + (outdeg - op);
    }

    /* If we found non-loop mutual connections, we can set the cache. */
    if (rec - (ignore_loops ? 0 : loops) > 0) {
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MUTUAL, true);
    }

    if (mode == IGRAPH_RECIPROCITY_DEFAULT) {
        if (ignore_loops) {
            *res = (igraph_real_t) rec / (igraph_ecount(graph) - loops);
        } else {
            *res = (igraph_real_t) rec / (igraph_ecount(graph));
        }
    } else if (mode == IGRAPH_RECIPROCITY_RATIO) {
        *res = (igraph_real_t) rec / (rec + nonrec);
    }

    igraph_vector_int_destroy(&inneis);
    igraph_vector_int_destroy(&outneis);
    IGRAPH_FINALLY_CLEAN(2);

    return IGRAPH_SUCCESS;
}
