/*****************************************************************//**
 * \file   ArcBranch.h
 * \brief  Branches the node into two children depending on the most fractional variable in the solution.
 *
 * \author Matteo Petris
 * \date   February 2023
 *********************************************************************/
#ifndef ARCBRANCH_H
#define ARCBRANCH_H

#include "BranchingRuleAbstract.h"

#include "RestrictedMasterProblem.h"
#include "PricingProblem.h"
#include "ColumnGeneration.h"
#include "ArcBranchConstraint.h"

 /**
  * \class ArcBranch.
  * \brief  Branches the node into two children depending on the most fractional variable in the solution.
  */
class ArcBranch :
    public BranchingRuleAbstract<RestrictedMasterProblem, PricingProblem, ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr> {

public:
    ArcBranch(): BranchingRuleAbstract(){}

    /**
    * \brief Destructor.
    *
    */
    virtual ~ArcBranch() {}

    /**
   * \brief Evaluates if the handler (the algorithm) can handle (solve) the cutting procedure based on the code of request.
   *
   * \param request : string
   * \return True if the concrete algorithm can find the cuts, false otherwise.
   */
    bool can_branch(const string& request) const override {
        return request == "ARCBRANCH" || request == "WHATEVER";
    }

    pair<int, int> detect_arc_to_branch(const size_t& vertexNumber, const vector<vector<double>>& arcsValues) const {
        double distaceFromMiddle = ::OMEGA;
        pair<int, int> argmin_most_fractional_sum = make_pair(-1, -1);
        for (int i = 0; i < vertexNumber; i++) {
            for (int j = 0; j < vertexNumber; j++) {
                if (i != j && arcsValues.at(i).at(j) > EPSILON) {
                    double fractionalPart = computeFractionalPart(arcsValues.at(i).at(j));
                    if (fractionalPart > EPSILON && ::strictly_less(abs(fractionalPart - 0.5), distaceFromMiddle)) {
                        distaceFromMiddle = abs(fractionalPart - 0.5);
                        argmin_most_fractional_sum = make_pair(i, j);
                        if (distaceFromMiddle < ::EPSILON) {
                            return argmin_most_fractional_sum;
                        }
                    }
                }
            }
        }
        return argmin_most_fractional_sum;
    }

    list<Node<RestrictedMasterProblem, PricingProblem,
         ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>> branch(Node<RestrictedMasterProblem, PricingProblem,
             ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>& node) const override {

        if (can_branch(node.get_restricted_master_problem()->get_branching_request())) {
            //Searching for the arc (i,j) with the most fractional \sum_{k in columns} a^k_ij lambda_k

            //node.get_restricted_master_problem()->write_solution_value();

            //pair<int, int> argmin_most_fractional_sum;
            size_t vertexNumber = node.get_restricted_master_problem()->get_instance().getVertices().size();
            //double distaceFromMiddle = ::OMEGA;
            //double sum;
            vector<vector<double>> arcsValues(vertexNumber, vector<double>(vertexNumber, 0.0));
            int size = node.get_restricted_master_problem()->get_columns().size();

            for (int k = 0; k < size; k++) {
                double val = node.get_solution().at(k);
                if (val > ::EPSILON) {
                    const Exchange* e = &node.get_restricted_master_problem()->get_columns().at(k);
                    for (int i = 0; i < e->getSequence().size() - 1; i++) {
                        arcsValues.at(e->getSequence().at(i)).at(e->getSequence().at(i + 1)) += val;
                    }
                }
            }

            pair<int, int> argmin_most_fractional_sum = detect_arc_to_branch(vertexNumber, arcsValues);

            //for (int i = 0; i < node.get_restricted_master_problem()->get_instance().getVertices().size(); i++) {

            //    const Vertex* v = &node.get_restricted_master_problem()->get_instance().getVertices().at(i);
            //    for (unordered_map<int, int>::const_iterator it = v->getOutGoingArcs().begin(); it != v->getOutGoingArcs().end(); it++) {

            //        
            //        /*if (v->getId() == 0 && it->first == 10) {
            //            cout << "ARC\t" << v->getId() << "\t" << it->first << "\t" << it->second << "\n";
            //        }*/

            //        sum = 0.0;
            //        for (int k = 0; k < node.get_restricted_master_problem()->get_columns().size(); k++) {
            //            /*if (v->getId() == 0 && it->first == 10) {
            //                cout << node.get_restricted_master_problem()->get_columns().at(k).toString() << endl;
            //                cout << "ISZE " << node.get_solution().size() << " K " << k << endl;
            //            }*/
            //            if (node.get_solution().at(k) > ::EPSILON) {
            //                int traverses = node.get_restricted_master_problem()->get_columns().at(k).getTraverses(v->getId(), it->first);
            //                sum += traverses * node.get_solution().at(k);
            //            }
            //            
            //        }
            //        double fractionalPart = computeFractionalPart(sum);
            //        /*if (sum > ::EPSILON) {
            //            cout << v->getId() << "\t" << it->first << "\t" << sum << "\t" << fractionalPart << endl;
            //        }*/
            //        if (fractionalPart > EPSILON && ::strictly_less(abs(fractionalPart - 0.5), distaceFromMiddle)) {
            //            distaceFromMiddle = abs(fractionalPart - 0.5);
            //            argmin_most_fractional_sum = make_pair(v->getId(), it->first);
            //        }

            //    }

            //}

           /* cout << "CHOSEN: " << argmin_most_fractional_sum.first << "\t" << argmin_most_fractional_sum.second 
                << "\tVAL: "<<arcsValues.at(argmin_most_fractional_sum.first).at(argmin_most_fractional_sum.second) << "\n";*/

            //Create two nodes when there is a fractional variable
            list<Node<RestrictedMasterProblem, PricingProblem,
                ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>> children;
            if (argmin_most_fractional_sum.first > -1) {



                double val = arcsValues.at(argmin_most_fractional_sum.first).at(argmin_most_fractional_sum.second);
                double fractionalPart = computeFractionalPart(val);

                if (/*::strictly_less(fractionalPart, 0.5)*/true) {
                    children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                        ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                    children.back().add_branching_constraint(new ArcBranchConstraint<GRBConstr>(0, "<=", argmin_most_fractional_sum, "ARCBRANCH"));
                    global_id++;
                    children.back().set_id(global_id);
                    children.back().set_parent_id(node.get_id());
                    children.back().set_parent_bound(node.get_dual_bound()); //CHANGE
                    children.back().set_depth(node.get_depth() + 1);
                    children.back().set_nb_columns_added(0);
                    children.back().set_nb_cuts_added(0);
                    children.back().set_column_generation_time_spent(0);
                    children.back().set_cut_generation_time_spent(0);

                    children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                        ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                    children.back().add_branching_constraint(new ArcBranchConstraint<GRBConstr>(1, ">=", argmin_most_fractional_sum, "ARCBRANCH"));
                    global_id++;
                    children.back().set_id(global_id);
                    children.back().set_parent_id(node.get_id());
                    children.back().set_parent_bound(node.get_dual_bound()); //CHANGE
                    children.back().set_depth(node.get_depth() + 1);
                    children.back().set_nb_columns_added(0);
                    children.back().set_nb_cuts_added(0);
                    children.back().set_column_generation_time_spent(0);
                    children.back().set_cut_generation_time_spent(0);
                }
                else {
                    children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                        ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                    children.back().add_branching_constraint(new ArcBranchConstraint<GRBConstr>(1, ">=", argmin_most_fractional_sum, "ARCBRANCH"));
                    global_id++;
                    children.back().set_id(global_id);
                    children.back().set_parent_id(node.get_id());
                    children.back().set_parent_bound(node.get_dual_bound()); //CHANGE
                    children.back().set_depth(node.get_depth() + 1);
                    children.back().set_nb_columns_added(0);
                    children.back().set_nb_cuts_added(0);
                    children.back().set_column_generation_time_spent(0);
                    children.back().set_cut_generation_time_spent(0);

                    children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                        ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                    children.back().add_branching_constraint(new ArcBranchConstraint<GRBConstr>(0, "<=", argmin_most_fractional_sum, "ARCBRANCH"));
                    global_id++;
                    children.back().set_id(global_id);
                    children.back().set_parent_id(node.get_id());
                    children.back().set_parent_bound(node.get_dual_bound()); //CHANGE
                    children.back().set_depth(node.get_depth() + 1);
                    children.back().set_nb_columns_added(0);
                    children.back().set_nb_cuts_added(0);
                    children.back().set_column_generation_time_spent(0);
                    children.back().set_cut_generation_time_spent(0);
                }



                
                return children;
            }
            else {
                return BranchingRuleAbstract<RestrictedMasterProblem, PricingProblem, ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>::branch(node);
            }
        }
        else
            return BranchingRuleAbstract<RestrictedMasterProblem, PricingProblem, ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>::branch(node);

    }
};

#endif // !ARCBRANCH_H
