/*****************************************************************//**
 * \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 EPSILONBRANCH_H
#define EPSILONBRANCH_H

#include "BranchingRuleAbstract.h"

#include "RestrictedMasterProblemIKEP.h"
#include "PricingProblemIKEP.h"
#include "ColumnGeneration.h"

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

public:
    EpsilonBranch(): BranchingRuleAbstract(){}


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

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

    list<Node<RestrictedMasterProblemIKEP, PricingProblemIKEP,
         ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>> branch(Node<RestrictedMasterProblemIKEP, PricingProblemIKEP,
             ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>& node) const override {
        ::isToBranch = false;
        if (can_branch(node.get_restricted_master_problem()->get_branching_request()) && ::isToBranch) {

            ::isToBranch = false;
            double epsilonValue_lb = ceil(node.get_restricted_master_problem()->get_epsilon_value() - EPSILON);
            double epsilonValue_ub = node.get_best_integer_value();
            
            double diff = epsilonValue_ub - epsilonValue_lb;
            //double epsilonValue = epsilonValue_lb/* + (diff * 0.75)*/;
            //cout << "LB " << epsilonValue_lb << " UB " << epsilonValue_ub << "\t" << epsilonValue <<"\n";


            //double epsilonValue = node.get_restricted_master_problem()->get_epsilon_value()/* + (diff * 0.75)*/;
            //double fractPart = computeFractionalPart(epsilonValue);
            //cout << "frac " << node.get_restricted_master_problem()->get_epsilon_value() << "\t" << epsilonValue <<"\n";
            /*if (node.get_id() == 0) {
                epsilonValue = node.get_restricted_master_problem()->get_epsilon_value();
                diff = computeFractionalPart(epsilonValue);
            }*/

            //Create two nodes when there is a fractional variable
            list<Node<RestrictedMasterProblemIKEP, PricingProblemIKEP,
                ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>> children;
            if (diff > ::EPSILON) {

                for (int i = epsilonValue_lb; i < epsilonValue_ub + 1; i++) {
                    children.push_back(Node<RestrictedMasterProblemIKEP, PricingProblemIKEP,
                        ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>(node));
                    children.back().add_branching_constraint(new Constraint<GRBConstr>(i, "<=", "EPSILONBRANCH"));
                    children.back().add_branching_constraint(new Constraint<GRBConstr>(i, ">=", "EPSILONBRANCH"));
                    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<RestrictedMasterProblemIKEP, PricingProblemIKEP,
                //    ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>(node));
                //children.back().add_branching_constraint(new Constraint<GRBConstr>(floor(epsilonValue + EPSILON), "<=", "EPSILONBRANCH"));
                //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<RestrictedMasterProblemIKEP, PricingProblemIKEP,
                //    ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>(node));
                //children.back().add_branching_constraint(new Constraint<GRBConstr>(ceil(epsilonValue - EPSILON), ">=", "EPSILONBRANCH"));
                //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<RestrictedMasterProblemIKEP, PricingProblemIKEP, ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>::branch(node);
            }
        }
        else
            return BranchingRuleAbstract<RestrictedMasterProblemIKEP, PricingProblemIKEP, ColumnGeneration<RestrictedMasterProblemIKEP, PricingProblemIKEP>, GRBConstr>::branch(node);

    }
};

#endif // !EPSILONBRANCH_H
