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

#include "BranchingRuleAbstract.h"

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

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

public:
    ExchangeNumberBranch(): BranchingRuleAbstract(){}

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

    /**
   * \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 == "EXCHANGENUMBERBRANCH" || 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

            double sum = 0.0;
            for (int k = 0; k < node.get_restricted_master_problem()->get_columns().size(); k++) {
                sum += node.get_restricted_master_problem()->get_solution().at(k);
            }

            double fractionalPart = ::computeFractionalPart(sum);

            /*if (::isToPrint) {
                cout << "NUMBER OF PATHS " << sum << "\t" << "\t" << fractionalPart << endl;
            }*/

            //Create two nodes when there is a fractional variable
            list<Node<RestrictedMasterProblem, PricingProblem,
                ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>> children;
            if (fractionalPart > ::EPSILON) {
                sum += ::EPSILON;
                double integerPart = floor(sum);
                //cout << "INT PART " << integerPart << endl;
                children.push_back(Node<RestrictedMasterProblem, PricingProblem,
                    ColumnGeneration<RestrictedMasterProblem, PricingProblem>, GRBConstr>(node));
                children.back().add_branching_constraint(new ExchangeNumberConstraint<GRBConstr>(integerPart, "<=", "EXCHANGENUMBERBRANCH"));
                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 ExchangeNumberConstraint<GRBConstr>(integerPart + 1, ">=", "EXCHANGENUMBERBRANCH"));
                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 // !EXCHANGENUMBERBRANCH_H
