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

#include "BranchingRuleAbstract.h"

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

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

public:
    VisitBranch(): BranchingRuleAbstract(){}

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

    /**
   * \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 == "VISITBRANCH" || request == "WHATEVER";
    }

    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

            int argmin_most_fractional_sum;
            size_t vertexNumber = node.get_restricted_master_problem()->get_instance().getAltruisticDonorsNumber();
            unordered_map<int, double> fractional_sum_i;
            double distaceFromMiddle = ::OMEGA;
            double sum;

            for (int i = 1; i < node.get_restricted_master_problem()->get_instance().getAltruisticDonors().size(); i++) {

                int id = node.get_restricted_master_problem()->get_instance().getAltruisticDonors().at(i);
                const Vertex* v = &node.get_restricted_master_problem()->get_instance().getVertices().at(id);

                sum = 0.0;
                for (int k = 0; k < node.get_restricted_master_problem()->get_columns().size(); k++) {
                    if (node.get_solution().at(k) > ::EPSILON) {
                        int visits = node.get_restricted_master_problem()->get_columns().at(k).getVisit(v->getId());
                        sum += visits * node.get_solution().at(k);
                    }
                }

                fractional_sum_i[v->getId()] = sum;
                double fractionalPart = computeFractionalPart(sum);
                if (fractionalPart > EPSILON && ::strictly_less(abs(fractionalPart - 0.5), distaceFromMiddle)) {
                    distaceFromMiddle = abs(fractionalPart - 0.5);
                    argmin_most_fractional_sum = v->getId();
                }

            }

            /*if (::isToPrint) {
                cout << "CHOSEN: " << argmin_most_fractional_sum << "\t" << fractional_sum_i[argmin_most_fractional_sum] << endl;
            }*/

            //Create two nodes when there is a fractional variable
            list<Node<RestrictedMasterProblem, PricingProblem,
                ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>> children;
            if (distaceFromMiddle < ::OMEGA) {
                children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                    ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                children.back().add_branching_constraint(new VisitBranchConstraint<GRBConstr>(0, "<=", argmin_most_fractional_sum, "VISITBRANCH"));
                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 VisitBranchConstraint<GRBConstr>(1, ">=", argmin_most_fractional_sum, "VISITBRANCH"));
                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 // !VISITBRANCH_H
