/*
 * TriangleEdgeScore.cpp
 *
 *  Created on: 29.08.2014
 *      Author: Michael Hamann, Gerd Lindner
 */

#include <networkit/edgescores/TriangleEdgeScore.hpp>
#include <networkit/auxiliary/Log.hpp>
#include <networkit/auxiliary/Timer.hpp>
#include <omp.h>

namespace NetworKit {

TriangleEdgeScore::TriangleEdgeScore(const Graph& G) : EdgeScore<count>(G) {
}

void TriangleEdgeScore::run() {
    if (!G.hasEdgeIds()) {
        throw std::runtime_error("edges have not been indexed - call indexEdges first");
    }

    // direct edge from high to low-degree nodes
    auto isOutEdge = [&](node u, node v) {
        return G.degree(u) > G.degree(v) || (G.degree(u) == G.degree(v) && u < v);
    };

    Aux::Timer filterEdgesTimer;
    filterEdgesTimer.start();
    // Store in-edges explicitly. Idea: all nodes have (relatively) low in-degree
    std::vector<index> inBegin(G.upperNodeIdBound() + 1);
    std::vector<node> inEdges(G.numberOfEdges());

    {
        index pos = 0;
        for (index u = 0; u < G.upperNodeIdBound(); ++u) {
            inBegin[u] = pos;
            if (G.hasNode(u)) {
                G.forEdgesOf(u, [&](node, node v, edgeid) {
                    if (isOutEdge(v, u)) {
                        inEdges[pos++] = v;
                    }
                });
            }
        }
        inBegin[G.upperNodeIdBound()] = pos;
    }

    filterEdgesTimer.stop();
    INFO("Needed ", filterEdgesTimer.elapsedMilliseconds(), "ms for filtering edges");

    //Edge attribute: triangle count
    std::vector<count> triangleCount(G.upperEdgeIdBound(), 0);
    // Store triangle counts of edges incident to the current node indexed by the adjacent node
    // none indicates that the edge to that node does not exist
    std::vector<std::vector<count> > incidentTriangleCount(omp_get_max_threads(), std::vector<count>(G.upperNodeIdBound(), none));

    Aux::Timer triangleTimer;
    triangleTimer.start();

    G.balancedParallelForNodes([&](node u) {
        auto tid = omp_get_thread_num();

        // mark nodes as neighbors
        G.forEdgesOf(u, [&](node, node v) {
            incidentTriangleCount[tid][v] = 0;
        });

        // Find all triangles of the form u-v-w-u where (v, w) is an in-edge.
        // Note that we find each triangle u is part of once.
        G.forEdgesOf(u, [&](node, node v) {
            // for all in-edges (v, w).
            for (index i = inBegin[v]; i < inBegin[v + 1]; ++i) {
                auto w = inEdges[i];

                if (incidentTriangleCount[tid][w] != none) {
                    // we have found a triangle u-v-w-u

                    // Record triangle count only for edges outgoing edges from u that should have consecutive edge ids
                    // This should be the same condition as in Graph::useEdgeInIteration and in Graph::indexEdges
                    // The count on (v, w) is updated when v or w is the central node

                    // Possibly record triangle for (u, v)
                    if (u >= v) {
                        ++incidentTriangleCount[tid][v];
                    }

                    // Possibly record triangle for (u, w)
                    if (u >= w) {
                        ++incidentTriangleCount[tid][w];
                    }
                }
            }
        });

        // Write local triangle counts into global array, unset local counters
        G.forEdgesOf(u, [&](node, node v, edgeid eid) {
            if (incidentTriangleCount[tid][v] > 0) {
                triangleCount[eid] += incidentTriangleCount[tid][v];
            }
            incidentTriangleCount[tid][v] = none;
        });
    });

    triangleTimer.stop();
    INFO("Needed ", triangleTimer.elapsedMilliseconds(), "ms for counting triangles");

    scoreData = std::move(triangleCount);
    hasRun = true;
}

count TriangleEdgeScore::score(node, node) {
    throw std::runtime_error("Not implemented: Use scores() instead.");
}

count TriangleEdgeScore::score(edgeid) {
    throw std::runtime_error("Not implemented: Use scores() instead.");
}

} /* namespace NetworKit */
