module BranchAndBound_Options

  use Constants

  implicit none

  ! ************************************************************************
  ! ************************************************************************

  type bb_options

     private

     integer :: branchingVariableStrategy
     integer :: nodeSelectionStrategy
     integer :: multiStartStrategy
     integer :: numRootResolutions,numNodeResolutions
     integer :: numIterationsForBestBoundRule

     ! 
     integer :: numNodeResolutionsIncrease

     integer :: updatesBeforeTrust

     integer :: boxConstrainedSolver

     real(kind=8) :: cutoff,boundTolerance

     logical :: solveRoundedSolution

  end type bb_options

  ! ************************************************************************
  ! ************************************************************************

  ! BRANCHING VARIABLE SELECTION
  ! ----------------------------

  ! Strategies for selecting the branching variables.

  ! Select the variable with the lowest index.
  !integer, parameter :: LOWEST_INDEX_FIRST = 0

  ! Select the most fractional value, that is, the variable whose
  ! value is farthest from an integer value.
  !integer, parameter :: MOST_FRACTIONAL    = 1

  ! Select randomly a fractional variable to branch on.
  !integer, parameter :: RANDOM_VARIABLE    = 2

  ! ************************************************************************
  ! ************************************************************************

  ! NODE SELECTION
  ! --------------

  ! Strategies for the selection of the nodes.

  ! Select the node with best bound, that is, in a minimization
  ! problem, select the node with the lower lower bound.
  integer, parameter :: BEST_BOUND_RULE = 0

  ! Perform a breadth-first search in the tree.
  integer, parameter :: BREADTH_FIRST   = 1

  ! Perform a depth-first search in the tree.
  integer, parameter :: DEPTH_FIRST     = 2

  ! Initially perform a depth-first search and, after finding a
  ! certain number of feasible solutions, switch the strategy to the
  ! best bound rule.
  integer, parameter :: SWITCH_DF_BBR   = 3

  ! ************************************************************************
  ! ************************************************************************

  ! MULTI-START STRATEGIES
  ! ----------------------

  ! Strategies for the resolution of the subproblems. When applying
  ! the multi-start strategy, it can be stopped by either accepting
  ! the first feasible solution or the best solution found.
  !integer, parameter :: BEST_FEASIBLE  = 0
  !integer, parameter :: FIRST_FEASIBLE = 1

contains

  ! ************************************************************************
  ! ************************************************************************

  integer function getBranchingVariableStrategy(options)

    implicit none

    type(bb_options), intent(in) :: options

    getBranchingVariableStrategy = options%branchingVariableStrategy

  end function getBranchingVariableStrategy

  ! ************************************************************************
  ! ************************************************************************

  subroutine setBranchingVariableStrategy(options,strategy)

    implicit none

    integer, intent(in) :: strategy

    type(bb_options), intent(in out) :: options

    options%branchingVariableStrategy = strategy

  end subroutine  setBranchingVariableStrategy

  ! ************************************************************************
  ! ************************************************************************

  integer function getNodeSelectionStrategy(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNodeSelectionStrategy = options%nodeSelectionStrategy

  end function getNodeSelectionStrategy

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNodeSelectionStrategy(options,strategy)

    implicit none

    integer, intent(in) :: strategy

    type(bb_options), intent(in out) :: options

    options%nodeSelectionStrategy = strategy

  end subroutine setNodeSelectionStrategy

  ! ************************************************************************
  ! ************************************************************************

  integer function getMultiStartStrategy(options)

    implicit none

    type(bb_options), intent(in) :: options

    getMultiStartStrategy = options%multiStartStrategy

  end function getMultiStartStrategy

  ! ************************************************************************
  ! ************************************************************************

  subroutine setMultiStartStrategy(options,strategy)

    implicit none

    integer, intent(in) :: strategy

    type(bb_options), intent(in out) :: options

    options%multiStartStrategy = strategy

  end subroutine setMultiStartStrategy

  ! ************************************************************************
  ! ************************************************************************

  integer function getNumRootResolutions(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNumRootResolutions = options%numRootResolutions

  end function getNumRootResolutions

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNumRootResolutions(options,number)

    implicit none

    integer, intent(in) :: number

    type(bb_options), intent(in out) :: options

    options%NumRootResolutions = number

  end subroutine setNumRootResolutions

  ! ************************************************************************
  ! ************************************************************************

  integer function getNumNodeResolutions(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNumNodeResolutions = options%numNodeResolutions

  end function getNumNodeResolutions

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNumNodeResolutions(options,number)

    implicit none

    integer, intent(in) :: number

    type(bb_options), intent(in out) :: options

    options%NumNodeResolutions = number

  end subroutine setNumNodeResolutions

  ! ************************************************************************
  ! ************************************************************************

  integer function getNumNodeResolutionsIncrease(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNumNodeResolutionsIncrease = options%numNodeResolutionsIncrease

  end function getNumNodeResolutionsIncrease

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNumNodeResolutionsIncrease(options,number)

    implicit none

    integer, intent(in) :: number

    type(bb_options), intent(in out) :: options

    options%numNodeResolutionsIncrease = number

  end subroutine setNumNodeResolutionsIncrease

  ! ************************************************************************
  ! ************************************************************************

  integer function getNumIterationsForBestBoundRule(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNumIterationsForBestBoundRule = options%numIterationsForBestBoundRule

  end function getNumIterationsForBestBoundRule

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNumIterationsForBestBoundRule(options,number)

    implicit none

    integer, intent(in) :: number

    type(bb_options), intent(in out) :: options

    options%numIterationsForBestBoundRule = number

  end subroutine setNumIterationsForBestBoundRule

  ! ************************************************************************
  ! ************************************************************************

  real(kind=8) function getCutOff(options)

    implicit none

    type(bb_options), intent(in) :: options

    getCutOff = options%cutoff

  end function getCutOff

  ! ************************************************************************
  ! ************************************************************************

  subroutine setCutOff(options,value)

    implicit none

    type(bb_options), intent(in out) :: options
    real(kind=8),     intent(in)     :: value

    options%cutoff = value

  end subroutine setCutOff

  ! ************************************************************************
  ! ************************************************************************

  real(kind=8) function getBoundTolerance(options)

    implicit none

    type(bb_options), intent(in) :: options

    getBoundTolerance = options%boundTolerance

  end function getBoundTolerance

  ! ************************************************************************
  ! ************************************************************************

  subroutine setBoundTolerance(options,value)

    implicit none

    type(bb_options), intent(in out) :: options
    real(kind=8),     intent(in)     :: value

    options%boundTolerance = value

  end subroutine setBoundTolerance

  ! ************************************************************************
  ! ************************************************************************

  integer function getNumberOfUpdatesBeforeTrust(options)

    implicit none

    type(bb_options), intent(in) :: options

    getNumberOfUpdatesBeforeTrust = options%updatesBeforeTrust

  end function getNumberOfUpdatesBeforeTrust

  ! ************************************************************************
  ! ************************************************************************

  subroutine setNumberOfUpdatesBeforeTrust(options,number)

    implicit none

    type(bb_options), intent(in out) :: options
    integer,          intent(in)     :: number

    options%updatesBeforeTrust = number

  end subroutine setNumberOfUpdatesBeforeTrust

  ! ************************************************************************
  ! ************************************************************************

  integer function getBoxConstrainedSolver(options)

    implicit none

    type(bb_options), intent(in) :: options

    getBoxConstrainedSolver = options%boxConstrainedSolver

  end function getBoxConstrainedSolver

  ! ************************************************************************
  ! ************************************************************************

  subroutine setSolveRoundedSolution(options,value)

    implicit none

    type(bb_options), intent(in out) :: options
    logical,          intent(in)     :: value

    options%solveRoundedSolution = value

  end subroutine setSolveRoundedSolution

  ! ************************************************************************
  ! ************************************************************************

  logical function getSolveRoundedSolution(options)

    implicit none

    type(bb_options), intent(in) :: options

    getSolveRoundedSolution = options%solveRoundedSolution

  end function getSolveRoundedSolution

  ! ************************************************************************
  ! ************************************************************************

  subroutine setBoxConstrainedSolver(options,solver)

    implicit none

    type(bb_options), intent(in out) :: options
    integer,          intent(in)     :: solver

    options%boxConstrainedSolver = solver

  end subroutine setBoxConstrainedSolver

  ! ************************************************************************
  ! ************************************************************************

  subroutine initializeOptions(options)

    implicit none

    type(bb_options), intent(out) :: options

    call defaultOptions(options)

  end subroutine initializeOptions

  ! ************************************************************************
  ! ************************************************************************

  subroutine defaultOptions(options)

    use Constants

    implicit none

    type(bb_options), intent(in out) :: options

    call setBranchingVariableStrategy(options,PSEUDO_COSTS)
    call setNodeSelectionStrategy(options,DEPTH_FIRST)
    call setMultiStartStrategy(options,BEST_FEASIBLE)
    call setNumRootResolutions(options,1)
    call setNumNodeResolutions(options,1)
    call setNumIterationsForBestBoundRule(options,10)
    call setCutOff(options,-huge(1.0d0))
    call setBoundTolerance(options,1.0d0)
    call setNumNodeResolutionsIncrease(options,0)
    call setNumberOfUpdatesBeforeTrust(options,-1)
    call setBoxConstrainedSolver(options,GENCAN)
    call setSolveRoundedSolution(options,.false.)

  end subroutine defaultOptions

  ! ************************************************************************
  ! ************************************************************************

end module BranchAndBound_Options
