package edu.claflin.finder.algo.clustering.struct.infomap_struct;

import java.util.ArrayList;
import java.lang.Math;
import java.util.List;

import edu.claflin.finder.logic.Graph;
import edu.claflin.finder.logic.Node;

//Helper class for the map equation. Contains all the methods for the math.
public class MapEquation {
    /*
     * Method that takes the sum of the exit probabilities for each module.
     * @param q an arraylist containing the exit probabilities of each module.
     * @param m the number of modules.
     */
    public double sigmaNotationQi(ArrayList<Double> q, int m) {
        double sum = 0;
        for(int i = 0; i < m; i++) {
            sum += q.get(i);
        }
        return sum;
    }

    /*
     * Method that takes the sum of the exit probabilities for each module times the log of those same
     * probabilities.
     * @param q an arraylist containing the exit probabilities of each module.
     * @param m the number of modules.
     */
    public double sigmaNotationQiLogQi(ArrayList<Double> q, int m) {
        double sum = 0;
        for(int i = 0; i < m; i++){
            sum += q.get(i) * Math.log(q.get(i));
        }
        return sum;
    }
    /*
     * Method that takes the sum of the probabilities of a random walker to visit a node in a module mutliplied
     * by the log of those same probabilities.
     * @param p an arraylist containing the probabilities of a random walker visiting nodes in a module.
     * @param n the node count.
     */
    public double sigmaNotationPaLogPa(ArrayList<Double> p, int n) {
        double sum = 0;
        for(int a = 0; a < n; a++) {
            sum += p.get(a) * Math.log(p.get(a));
        }
        return sum;
    }
    /*
     * Method that takes the sum of the probabilities of a random walker to visit a node in a module.
     * @param p  an arraylist containing the probabilities of a random walker visiting nodes in a module.
     * @param n the node count.
     */
    public double sigmaNotationPa(ArrayList<Double> p, int n) {
        double sum = 0;
        for(int a = 0; a < n; a++) {
            sum += p.get(a);
        }
        return sum;
    }

    public double longSigmaNotation(ArrayList<Double> q, int m, List<List<Node>> communities, ArrayList<ArrayList<Double>> pProb) {
        double sum = 0;
        double sumOfPa = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < communities.get(i).size(); j++){
                sumOfPa += pProb.get(i).get(j);
            }
            sum += (q.get(i) + sumOfPa) * Math.log(q.get(i) + sumOfPa);
            sumOfPa = 0;
        }
        return sum;
    }

    /*
    Below is the map equation helper assuming you are using an undirected graph
    Given that most/all the test graphs are undirected, the functions will be more
    useful more often than the helper functions above depicting the general map equation

    USEFUL SYMBOL NAMES FOR REFERENCE:
    y: refers to the half circle looking symbol in the map equation
    a: refers to the alpha symbol. When using the α symbol, typically it refers to a given node in a graph/module
    wiy: refers to the relative weight of links exiting module i (find a way to get all edges exiting a module, and add up all their weights)
    wi: refers to the summation of all wa in a given module (uses a for each loop by going through all nodes in module i)
    wy: refers to the summation of all wiy for i = 0 to i < m
    wa: refers to the relative weight of the edges connected to a node / 2 * all edge weights in a graph
    */

    public double SigmaWyLogSigmaWy(int m, ArrayList<Double> wy){
        double sum = 0;
        for (int i = 0; i < m; i++){
            sum += wy.get(i); //wy.get(i) is getting the summation of all edge weights exiting module i
        }
        sum = sum * Math.log(sum);
        return sum;
    }

    public double SigmaNotationWiyLogWiy(int m, ArrayList<Double> wy){
        double sum = 0;
        for(int i = 0; i < m; i++){
            sum += wy.get(i) * Math.log(wy.get(i));
        }
        return sum;
    }

    public double SigmaWaLogWa(int n, ArrayList<Double> w){
        double sum = 0;
        for (int a = 0; a < n; a++){
            sum += w.get(a) * Math.log(w.get(a));
        }
        return sum;
    }

    public double LongSigmaWiyAndWi(int m, ArrayList<Double> wy, List<List<Node>> communities, ArrayList<ArrayList<Double>> wi){
        double sum = 0;
        double sumOfWa = 0;
        for (int i = 0; i < m; i++){
            for (int j = 0; j < communities.size(); j++){
                sumOfWa += wi.get(i).get(j);
            }
            sum += (wy.get(i) + sumOfWa) * (Math.log(wy.get(i) + sumOfWa));
        }
        return sum;
    }

}
