/*===========================================================================*/
/*                                                                           */
/* This file is part of the SYMPHONY MILP Solver Framework.                  */
/*                                                                           */
/* SYMPHONY was jointly developed by Ted Ralphs (ted@lehigh.edu) and         */
/* Laci Ladanyi (ladanyi@us.ibm.com).                                        */
/*                                                                           */
/* (c) Copyright 2000-2023 Ted Ralphs. All Rights Reserved.                  */
/*                                                                           */
/* This software is licensed under the Eclipse Public License. Please see    */
/* accompanying file for terms.                                              */
/*                                                                           */
/*===========================================================================*/

#define COMPILING_FOR_TM

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#if !defined(_MSC_VER) && !defined(__MNO_CYGWIN) && defined(SIGHANDLER)
#include <signal.h>
#if !defined(HAS_SRANDOM)
extern int srandom PROTO((unsigned seed));
#endif
#if !defined(HAS_RANDOM)
extern long random PROTO((void));
#endif
#endif
#ifdef _OPENMP
#include "omp.h"
#endif
#if !defined (_MSC_VER)
#include <unistd.h>            /* this defines sleep() */
#endif

#include "sym_tm.h"
#include "sym_constants.h"
#include "sym_types.h"
#include "sym_macros.h"
#include "sym_messages.h"
#include "sym_proccomm.h"
#include "sym_timemeas.h"
#include "sym_pack_cut.h"
#include "sym_pack_array.h"
#include "sym_qsort.h"
#ifdef COMPILE_IN_LP
#include "sym_lp.h"
#endif
#ifdef COMPILE_IN_TM
#include "sym_master.h"
#else
#include "sym_cp.h"
#endif

int c_count = 0;

/*===========================================================================*/

/*===========================================================================*\
 * This file contains basic functions associated with the treemanager process
\*===========================================================================*/

/*===========================================================================*/

/*===========================================================================*\
 * This function receives the intitial parameters and data and sets up
 * the tree manager data structures, etc.
\*===========================================================================*/

int tm_initialize(tm_prob *tm, base_desc *base, node_desc *rootdesc)
{
#ifndef COMPILE_IN_TM
   int r_bufid, bytes, msgtag, i;
#endif
   FILE *f = NULL;
   tm_params *par;
   bc_node *root = (bc_node *) calloc(1, sizeof(bc_node));
#ifdef COMPILE_IN_LP
   int i;
#else
#ifdef COMPILE_IN_TM
   int i;
#endif
   int s_bufid;
#endif
#if !defined(_MSC_VER) && !defined(__MNO_CYGWIN) && defined(SIGHANDLER)
   signal(SIGINT, sym_catch_c);    
#endif   
   par = &tm->par;
   
#ifdef _OPENMP
   tm->rpath =
      (bc_node ***) calloc(par->max_active_nodes, sizeof(bc_node **));
   tm->rpath_size = (int *) calloc(par->max_active_nodes, sizeof(int));
   tm->bpath =
      (branch_desc **) calloc(par->max_active_nodes, sizeof(branch_desc *));
   tm->bpath_size = (int *) calloc(par->max_active_nodes, sizeof(int));
   tm->termcodes = (int *) calloc(par->max_active_nodes, sizeof(int));
#else
   tm->rpath = (bc_node ***) calloc(1, sizeof(bc_node **));
   tm->rpath_size = (int *) calloc(1, sizeof(int));
   tm->bpath = (branch_desc **) calloc(1, sizeof(branch_desc *));
   tm->bpath_size = (int *) calloc(1, sizeof(int));
   tm->termcodes = (int *) calloc(1, sizeof(int));
#endif
   
   /*------------------------------------------------------------------------*\
    * Receives data from the master
   \*------------------------------------------------------------------------*/

#ifdef COMPILE_IN_TM
   tm->bvarnum = base->varnum;
   tm->bcutnum = base->cutnum;
#else
   r_bufid = receive_msg(ANYONE, TM_DATA);
   bufinfo(r_bufid, &bytes, &msgtag, &tm->master);
   receive_char_array((char *)par, sizeof(tm_params));
   receive_char_array(&tm->has_ub, 1);
   if (tm->has_ub)
      receive_dbl_array(&tm->ub, 1);
   receive_char_array(&tm->has_ub_estimate, 1);
   if (tm->has_ub_estimate)
      receive_dbl_array(&tm->ub_estimate, 1);
   READ_STR_LIST(par->lp_mach_num, MACH_NAME_LENGTH,
		     par->lp_machs[0], par->lp_machs);
   READ_STR_LIST(par->cg_mach_num, MACH_NAME_LENGTH,
		     par->cg_machs[0], par->cg_machs);
   READ_STR_LIST(par->cp_mach_num, MACH_NAME_LENGTH,
		     par->cp_machs[0], par->cp_machs);
   receive_int_array(&tm->bvarnum, 1);
   receive_int_array(&tm->bcutnum, 1);
#ifdef TRACE_PATH
   receive_int_array(&tm->feas_sol_size, 1);
   if (tm->feas_sol_size){
      tm->feas_sol = (int *) calloc (tm->feas_sol_size, sizeof(int));
      receive_int_array(tm->feas_sol, tm->feas_sol_size);
   }
#endif   
   freebuf(r_bufid);
#endif   
   
   SRANDOM(par->random_seed);

#ifdef COMPILE_IN_LP
   tm->active_nodes = (bc_node **) calloc(par->max_active_nodes, sizeof(bc_node *));
#ifndef COMPILE_IN_TM
   tm->lpp = (lp_prob **)
      malloc(par->max_active_nodes * sizeof(lp_prob *));
   for (i = 0; i < par->max_active_nodes; i++){
      tm->lpp[i] = (lp_prob *) calloc(1, sizeof(lp_prob));
      tm->lpp[i]->proc_index = i;
   }
#ifdef COMPILE_IN_CG
   tm->cgp = (cg_prob **) malloc(par->max_active_nodes * sizeof(cg_prob *));
   for (i = 0; i < par->max_active_nodes; i++)
      tm->lpp[i]->cgp = tm->cgp[i] = (cg_prob *) calloc(1, sizeof(cg_prob));
   par->use_cg = FALSE;
#endif
#endif
#pragma omp parallel for shared(tm)
   for (i = 0; i < par->max_active_nodes; i++){
      if ((tm->termcodes[i] = lp_initialize(tm->lpp[i], 0)) < 0){
	 printf("LP initialization failed with error code %i in thread %i\n\n",
		tm->termcodes[i], i); 
      }
      tm->lpp[i]->tm = tm;
#ifdef _OPENMP
      tm->lpp[i]->proc_index = omp_get_thread_num();
#else
      tm->lpp[i]->proc_index = 0;
#endif
   }
   //The master thread isn't used unless there is only one thread
   tm->lp.free_num = MAX(par->max_active_nodes-1, 1);
   for (i = 0; i < par->max_active_nodes; i++){
      if (tm->termcodes[i] < 0){
	 return(tm->termcodes[i]);
      }
   }
#else
   tm->active_nodes =
      (bc_node **) malloc(par->max_active_nodes * sizeof(bc_node *));

   /*------------------------------------------------------------------------*\
    * Start the lp, cg processes and send cg tid's to the lp's.
    * Also, start the cp, sp processes.
   \*------------------------------------------------------------------------*/

   tm->lp = start_processes(tm, par->max_active_nodes, par->lp_exe,
			    par->lp_debug, par->lp_mach_num, par->lp_machs);
#endif

#pragma omp critical (cut_pool)
   if (!tm->cuts){
      tm->cuts = (cut_data **) malloc(BB_BUNCH * sizeof(cut_data *));
   }

   if (par->use_cg){
#ifndef COMPILE_IN_CG
      tm->cg = start_processes(tm, par->max_active_nodes, par->cg_exe,
			       par->cg_debug, par->cg_mach_num, par->cg_machs);
      
#ifdef COMPILE_IN_LP
      for (i = 0; i < par->max_active_nodes; i++)
	 tm->lpp[i]->cut_gen = tm->cg.procs[i];
#else
      for (i = 0; i < tm->lp.procnum; i++){
	 s_bufid = init_send(DataInPlace);
	 send_int_array(tm->cg.procs + i, 1);
	 send_msg(tm->lp.procs[i], LP__CG_TID_INFO);
      }
#endif
#endif
   }
   
   if (par->max_cp_num){
#ifdef COMPILE_IN_CP
#ifndef COMPILE_IN_TM
      tm->cpp = (cut_pool **) malloc(par->max_cp_num * sizeof(cut_pool *));
#endif
      for (i = 0; i < par->max_cp_num; i++){
#ifndef COMPILE_IN_TM
	 tm->cpp[i] = (cut_pool *) calloc(1, sizeof(cut_pool));
#endif
	 cp_initialize(tm->cpp[i], tm->master);
      }
      tm->cp.free_num = par->max_cp_num;
      tm->cp.procnum = par->max_cp_num;
      tm->cp.free_ind = (int *) malloc(par->max_cp_num * ISIZE);
      for (i = par->max_cp_num - 1; i >= 0; i--)
	 tm->cp.free_ind[i] = i;
#else
      tm->cp = start_processes(tm, par->max_cp_num, par->cp_exe,
			      par->cp_debug, par->cp_mach_num, par->cp_machs);
#endif
      tm->nodes_per_cp = (int *) calloc(tm->par.max_cp_num, ISIZE);
      tm->active_nodes_per_cp = (int *) calloc(tm->par.max_cp_num, ISIZE);
   }else{
#ifdef COMPILE_IN_CP
      tm->cpp = (cut_pool **) calloc(1, sizeof(cut_pool *));
#endif
   }
   
   /*------------------------------------------------------------------------*\
    * Receive the root node and send out initial data to the LP processes
   \*------------------------------------------------------------------------*/
   
   if (tm->par.warm_start){
      if (!tm->rootnode){
	 if (!(f = fopen(tm->par.warm_start_tree_file_name, "r"))){
	    printf("Error reading warm start file %s\n\n",
		   tm->par.warm_start_tree_file_name);
	    return(ERROR__READING_WARM_START_FILE);
	 }
	 read_tm_info(tm, f);
      }else{
	 free(root);
	 root = tm->rootnode;
      }
      read_subtree(tm, root, f);
      if (f)
	 fclose(f);
      if (!tm->rootnode){
	 if (!read_tm_cut_list(tm, tm->par.warm_start_cut_file_name)){
	    printf("Error reading warm start file %s\n\n",
		   tm->par.warm_start_cut_file_name);
	    return(ERROR__READING_WARM_START_FILE);
	 }
      }
      tm->rootnode = root;
      if(root->node_status != NODE_STATUS__WARM_STARTED)
	root->node_status = NODE_STATUS__ROOT;
   }else{
#ifdef COMPILE_IN_TM
      (tm->rootnode = root)->desc = *rootdesc;
      /* Copy the root description in case it is still needed */
      root->desc.uind.list = (int *) malloc(rootdesc->uind.size*ISIZE);
      memcpy((char *)root->desc.uind.list, (char *)rootdesc->uind.list,
	     rootdesc->uind.size*ISIZE);
      root->bc_index = tm->stat.created++;
      root->lower_bound = -MAXDOUBLE;
      tm->stat.tree_size++;
      insert_new_node(tm, root);
      tm->phase = 0;
      tm->lb = -SYM_INFINITY;
      tm->printed_lb = -SYM_INFINITY;
#else
      r_bufid = receive_msg(tm->master, TM_ROOT_DESCRIPTION);
      receive_node_desc(tm, root);
      if (root->desc.cutind.size > 0){ /* Hey we got cuts, too! Unpack them. */
	 unpack_cut_set(tm, 0, 0, NULL);
      }
      freebuf(r_bufid);
#endif
#ifdef TRACE_PATH
      root->optimal_path = TRUE;
#endif
   }

   return(FUNCTION_TERMINATED_NORMALLY);
}
   
/*===========================================================================*/

/*===========================================================================*\
 * This is the main loop that solves the problem
\*===========================================================================*/

int solve(tm_prob *tm)
{
#ifndef COMPILE_IN_LP
   int r_bufid;
#endif
   double start_time = tm->start_time;
   double no_work_start, ramp_up_tm = 0, ramp_down_time = 0;
   char ramp_down = FALSE, ramp_up = TRUE;
   double then, then2, then3, now;
   double timeout2 = tm->par.status_interval, timeout3 = tm->par.logging_interval, timeout4 = 10;

   /*------------------------------------------------------------------------*\
    * The Main Loop
   \*------------------------------------------------------------------------*/

   no_work_start = wall_clock(NULL);

   tm->termcode = TM_UNFINISHED;
   for (; tm->phase <= 1; tm->phase++){
      if (tm->phase == 1 && !tm->par.warm_start){
	 if ((tm->termcode = tasks_before_phase_two(tm)) ==
	     FUNCTION_TERMINATED_NORMALLY){
	    tm->termcode = TM_FINISHED; /* Continue normally */
	 }
      }
#pragma omp parallel default(shared) private(now, then2, then3)
{
#ifdef _OPENMP
      int i, ret, thread_num = omp_get_thread_num(), scand_num;
#else
      int i, ret, thread_num = 0, scand_num;
#endif
      tm->termcodes[thread_num] = TM_UNFINISHED;
      then  = wall_clock(NULL);
      then2 = wall_clock(NULL);
      then3 = wall_clock(NULL);
      while (tm->termcode == TM_UNFINISHED){
	 /*------------------------------------------------------------------*\
	  * while there are nodes being processed or while there are nodes
	  * waiting to be processed, continue to execute this loop
	 \*------------------------------------------------------------------*/
	 i = NEW_NODE__STARTED;
	 while (tm->lp.free_num > 0
		&& (tm->par.time_limit >= 0.0 ?
		(wall_clock(NULL) - start_time < tm->par.time_limit) : TRUE) &&
		(tm->par.node_limit >= 0 ?
		tm->stat.analyzed < tm->par.node_limit : TRUE)
		&& ((tm->has_ub && (tm->stat.analyzed > 0 && tm->par.gap_limit >= 0.0)) ?
		 d_gap(tm->ub, tm->lb, tm->obj_offset, tm->obj_sense) > tm->par.gap_limit : TRUE)
		&& !(tm->par.find_first_feasible && tm->has_ub && 
		     tm->lp_stat.ip_sols > 0) &&
		!(tm->par.rs_mode_enabled && tm->lp_stat.lp_iter_num > tm->par.rs_lp_iter_limit) &&
		c_count <= 0){
#pragma omp critical (tree_update)
            {
            scand_num = tm->samephase_candnum;
            }
	    if (scand_num > 0
#ifndef __PVM__
		&& (thread_num != 0 || tm->par.max_active_nodes == 1)
#endif
		){
	       i = start_node(tm, thread_num);
	    }else{
	       i = NEW_NODE__NONE;
	    }
#pragma omp master
{
   //printf("Master entering omp master thread %d\n", thread_num); 
            now = wall_clock(NULL);
	    if (tm->stat.analyzed>tm->active_node_num &&
		tm->has_ub && tm->par.tighten_root_bounds){
	       tighten_root_bounds(tm);
	    }
	    //printf("%f %f %f\n", now, then2, timeout2); 
	    if (now - then2 > timeout2){
	       if(tm->par.verbosity >= -1 ){
		  print_tree_status(tm);
	       }
	       then2 = now;
	    }
	    if (now - then3 > timeout3){
	       write_log_files(tm);
	       then3 = now;
	    }
 
	    if (ramp_up){
	       ramp_up_tm = wall_clock(NULL) - start_time;
	    }
	    if (ramp_down){
	       ramp_down_time += wall_clock(NULL) - no_work_start;
	    }
	       
#ifdef COMPILE_IN_LP
	    if (tm->active_node_num >= tm->par.max_active_nodes-1){
#else
            if (!tm->lp.free_num){
#endif
	       ramp_down = FALSE;
	       ramp_up = FALSE;
               //printf("Ramp-up %f %d %d\n", ramp_up_tm, tm->par.max_active_nodes, tm->active_node_num);
	    }else if (ramp_up){
	       no_work_start = wall_clock(NULL);
	    }else{
	       ramp_down = TRUE;
	       no_work_start = wall_clock(NULL);
	    }

}
#pragma omp master
	    if(tm->par.node_selection_rule == DEPTH_FIRST_THEN_BEST_FIRST &&
	       tm->has_ub){
	      tm->par.node_selection_rule = LOWEST_LP_FIRST;
	    }

	    if (i != NEW_NODE__STARTED)
	       break;

#ifdef COMPILE_IN_LP
#ifdef _OPENMP
	    if (tm->par.verbosity > 1)
	       printf("Thread %i now processing node %i\n", thread_num,
		      tm->lpp[thread_num]->bc_index);
#endif

	    ret = process_chain(tm->lpp[thread_num]); 

	    switch(ret){

	     case FUNCTION_TERMINATED_NORMALLY:
	       break;
	       
	     case ERROR__NO_BRANCHING_CANDIDATE:
OPENMP_ATOMIC_WRITE
	       tm->termcode = TM_ERROR__NO_BRANCHING_CANDIDATE;
	       break;
	       
	     case ERROR__ILLEGAL_RETURN_CODE:
OPENMP_ATOMIC_WRITE
	       tm->termcode = TM_ERROR__ILLEGAL_RETURN_CODE;
	       break;
	       
	     case ERROR__NUMERICAL_INSTABILITY:
OPENMP_ATOMIC_WRITE
	       tm->termcode = TM_ERROR__NUMERICAL_INSTABILITY;
	       break;
	       
	     case ERROR__COMM_ERROR:
OPENMP_ATOMIC_WRITE
	       tm->termcode = TM_ERROR__COMM_ERROR;
	       
	     case ERROR__USER:
OPENMP_ATOMIC_WRITE
	       tm->termcode = TM_ERROR__USER;
	       break;

	     case ERROR__DUAL_INFEASIBLE:
	       if(tm->lpp[thread_num]->bc_index < 1 ) {		  
OPENMP_ATOMIC_WRITE
		  tm->termcode = TM_UNBOUNDED;
	       }else{
OPENMP_ATOMIC_WRITE
		  tm->termcode = TM_ERROR__NUMERICAL_INSTABILITY;
	       }
	       break;	       
	    }
#endif
	 }

	 if (tm->termcode != TM_UNFINISHED){
	    break;
	 }
	 
#pragma omp master
{
	 if (c_count > 0){
	    tm->termcode = TM_SIGNAL_CAUGHT;
	    c_count = 0;
	 }
}

         if (tm->par.time_limit >= 0.0 &&
	     wall_clock(NULL) - start_time > tm->par.time_limit){
	    tm->termcodes[thread_num] = TM_TIME_LIMIT_EXCEEDED;
	 }else if (tm->par.node_limit >= 0 &&
		   tm->stat.analyzed >= tm->par.node_limit){
	    if (tm->active_node_num + tm->samephase_candnum > 0){
	       tm->termcode = tm->termcodes[thread_num] = TM_NODE_LIMIT_EXCEEDED;
	    }else{
	       tm->termcodes[thread_num] = TM_FINISHED;
	    }
	 }else if (tm->par.find_first_feasible && tm->has_ub && tm->lp_stat.ip_sols){
	    tm->termcodes[thread_num] = TM_FINISHED;
	 }else if (tm->has_ub && (tm->par.gap_limit >= 0.0)){
	    find_tree_lb(tm);
	    if (d_gap(tm->ub, tm->lb, tm->obj_offset, tm->obj_sense) <= tm->par.gap_limit){
	       if (tm->lb < tm->ub){
		  tm->termcodes[thread_num] = TM_TARGET_GAP_ACHIEVED;
	       }else{
		  tm->termcodes[thread_num] = TM_FINISHED;
	       }
	    }
	 }
	 if (tm->par.rs_mode_enabled && tm->lp_stat.lp_iter_num > tm->par.rs_lp_iter_limit){
	    tm->termcodes[thread_num] = TM_ITERATION_LIMIT_EXCEEDED;
	 }

         if (tm->termcodes[thread_num] != TM_UNFINISHED){
OPENMP_ATOMIC_WRITE
	    tm->termcode = tm->termcodes[thread_num];
	    break;
	 }

         if (tm->termcode != TM_UNFINISHED){
	    tm->termcodes[thread_num] = tm->termcode;
	    break;
	 }
	 
	 if (i == NEW_NODE__ERROR){
OPENMP_ATOMIC_WRITE
	    tm->termcode = tm->termcodes[thread_num] = SOMETHING_DIED;
	 }

         if (tm->samephase_candnum == 0 && tm->active_node_num == 0 &&
	     tm->stat.analyzed > 0){
	    tm->termcodes[thread_num] = TM_FINISHED;
	    break;
	 }
	 
#ifndef COMPILE_IN_LP
	 struct timeval timeout = {5, 0};
	 r_bufid = treceive_msg(ANYONE, ANYTHING, &timeout);
	 if (r_bufid && !process_messages(tm, r_bufid)){
            find_tree_lb(tm);
	    tm->termcode = SOMETHING_DIED;
	    break;
	 }
#endif
#pragma omp master
{	    
	 now = wall_clock(NULL);
	 if (now - then > timeout4){
	    if (!processes_alive(tm)){
               find_tree_lb(tm);
               tm->termcode = SOMETHING_DIED;
	    }
	    then = now;
	 }
#if 0
         for (i = 0; i < tm->par.max_active_nodes; i++){
	    if (tm->active_nodes[i]){
	       break;
	    }
	 }
	 if (i == tm->par.max_active_nodes){
	    tm->active_node_num = 0;
	 }
#endif
	 if (now - then2 > timeout2){
	    if(tm->par.verbosity >=0 ){
	       print_tree_status(tm);
	    }
	    then2 = now;
	 }
	 if (now - then3 > timeout3){
	    write_log_files(tm);
	    then3 = now;
	 }
} // pragma
      }
} // pragma

      if (tm->termcode == TM_UNBOUNDED){
	 break;
      }

      if (tm->samephase_candnum == 0 && tm->active_node_num == 0 &&
	  tm->stat.analyzed > 0){
	 int ii = tm->par.max_active_nodes;
        for (ii = 1; ii < tm->par.max_active_nodes; ii++){
	   if (tm->termcodes[ii]  == TM_UNFINISHED){
	      break;
	   }
	}
	if (ii == tm->par.max_active_nodes){
OPENMP_ATOMIC_WRITE
	   tm->termcode = TM_FINISHED;
	}else{
	   if (now - then2 > timeout2){
	      if(tm->par.verbosity >=0 ){
		 printf("Waiting for all threads to exit...");
		 print_tree_status(tm);
	      }
	      then2 = now;
	   }
	}
      }

      if (tm->nextphase_candnum == 0)
	 break;

      if (tm->termcode != TM_UNFINISHED)
	 break;
   }
   find_tree_lb(tm);
   tm->comp_times.ramp_up_tm = ramp_up_tm;
   tm->comp_times.ramp_down_time = ramp_down_time;
   write_log_files(tm);

   return(tm->termcode);
}

/*===========================================================================*/

/*==========================================================================*\
 * Write out the log files
\*==========================================================================*/

void write_log_files(tm_prob *tm)
{
#if !defined(COMPILE_IN_LP) || !defined(COMPILE_IN_CP)
   int s_bufid;
#endif

   if (tm->par.logging){
      write_tm_info(tm, tm->par.tree_log_file_name, NULL, FALSE);
      write_subtree(tm->rootnode, tm->par.tree_log_file_name, NULL, TRUE,
		    tm->par.logging);
      if (tm->par.logging != VBC_TOOL)
	 write_tm_cut_list(tm, tm->par.cut_log_file_name, FALSE);
   }

   if (tm->par.max_cp_num > 0 && tm->par.cp_logging){
#if defined(COMPILE_IN_LP) && defined(COMPILE_IN_CP)
      write_cp_cut_list(tm->cpp[0], tm->cpp[0]->par.log_file_name,
			FALSE);
#else
      s_bufid = init_send(DataInPlace);
      send_msg(tm->cp.procs[0], WRITE_LOG_FILE);
#endif
   }
}

/*===========================================================================*/

/*==========================================================================*\
 * Prints out the current size of the tree and the gap                      *
\*==========================================================================*/

void print_tree_status(tm_prob *tm)
{
   double elapsed_time;
   double obj_ub = SYM_INFINITY, obj_lb = -SYM_INFINITY;

#ifdef SHOULD_SHOW_MEMORY_USAGE
   int i;
   int pid;
   int tmp_int;
   long unsigned vsize;
   char tmp_str[100], proc_filename[100];
   FILE *proc_file;
   double vsize_in_mb;
#endif

#if 0
   int *widths;
   double *gamma;
   int last_full_level = 0, max_width = 0, num_nodes_estimate = 1;
   int first_waist_level = 0, last_waist_level = 0, waist_level = 0;
   double average_node_time, estimated_time_remaining, user_time = 0.0;

   widths = (int *) calloc (tm->stat.max_depth + 1, ISIZE);
   gamma = (double *) calloc (tm->stat.max_depth + 1, DSIZE);
   
   calculate_widths(tm->rootnode, widths);

   last_full_level = tm->stat.max_depth;
   for (i = tm->stat.max_depth - 1; i > 0; i--){
      if ((double)(widths[i])/(double)(widths[i - 1]) < 2){
	 last_full_level = i - 1;
      }
      if (widths[i] > max_width){
	 max_width = widths[i];
	 last_waist_level = i;
	 first_waist_level = i;
      }
      if (widths[i] == max_width){
	 first_waist_level = i;
      }
   }

   waist_level = (first_waist_level + last_waist_level)/2;
   
   for (i = 0; i < tm->stat.max_depth; i++){
      if (i < last_full_level){
	 gamma[i] = 2.0;
      }else if (i < waist_level){
	 gamma[i] = 2.0 - (double)((i - last_full_level + 1))/
	    (double)((waist_level - last_full_level + 1));
      }else{
	 gamma[i] = 1.0 - (double)(i - waist_level + 1)/
	    (double)(tm->stat.max_depth - waist_level + 1);
      }
   }

   for (i = 1; i < tm->stat.max_depth; i++){
      gamma[i] *= gamma[i - 1];
      num_nodes_estimate += (int)(gamma[i] + 0.5);
   }

   elapsed_time = wall_clock(NULL) - tm->start_time;
   average_node_time = elapsed_time/tm->stat.analyzed;

   estimated_time_remaining =
      MAX(average_node_time*(num_nodes_estimate - tm->stat.analyzed), 0);
#else

   elapsed_time = wall_clock(NULL) - tm->start_time;

#endif

#ifdef SHOULD_SHOW_MEMORY_USAGE
   pid = getpid();
   //printf("process id = %d\n",pid);
   sprintf(proc_filename,"/proc/%d/stat",pid);
   proc_file = fopen (proc_filename, "r");
   fscanf (proc_file, "%d %s %s", &tmp_int, tmp_str, tmp_str); 
   for (i=0; i<19;i++) {
      fscanf (proc_file, "%d", &tmp_int);
   }
   fscanf (proc_file, "%lu", &vsize);
   fclose(proc_file);
   //printf("vsize = %lu\n",vsize);
   vsize_in_mb = vsize/1024.0/1024.0;
   if (tm->stat.max_vsize<vsize_in_mb) {
      tm->stat.max_vsize = vsize_in_mb;
   }
#endif

   if (tm->par.output_mode > 0) {
     if (tm->stat.print_stats_cnt < 1 || tm->par.verbosity > 1) {
       printf("%7s ","Time");     
#ifdef SHOULD_SHOW_MEMORY_USAGE 
       printf("%9s ","Mem(MB)");
#endif
       printf("%10s ","Done");
#ifdef _OPENMP
       if (tm->par.max_active_nodes > 1){
	  printf("%10s ","Active");
       }
#endif
       printf("%10s ","Queued");
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 printf("%19s ","UB");
	 printf("%19s ","LB");
       } else{
	 printf("%19s ","LB");
	 printf("%19s ","UB");	 
       }
       printf("%7s ","Gap");
       printf("\n");
     }
     (tm->stat.print_stats_cnt)++;
     printf("%7i ", (int)(elapsed_time));
#ifdef SHOULD_SHOW_MEMORY_USAGE 
     printf("%9.2f ", vsize_in_mb);
#endif
     printf("%10i ", tm->stat.analyzed);
#ifdef _OPENMP
     if (tm->par.max_active_nodes > 1){
	printf("%10i ", tm->active_node_num);
     }
#endif
     printf("%10i ", tm->samephase_candnum);     
     find_tree_lb(tm);
     if (tm->lb > -SYM_INFINITY) {
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 obj_ub = -tm->printed_lb + tm->obj_offset;
	 printf("%19.2f ", obj_ub);
       } else {
	 obj_lb = tm->printed_lb + tm->obj_offset;
	 printf("%19.2f ", obj_lb);
       }
     } else {
       if (tm->obj_sense == SYM_MAXIMIZE) {  
	 printf("%19s ","");
       } else {
	 printf("%19s ","");
       }
     }

     if (tm->has_ub) {
       if (tm->obj_sense == SYM_MAXIMIZE) {
         obj_lb = -tm->ub + tm->obj_offset;
	 printf("%19.2f ", obj_lb);
       } else {
         obj_ub = tm->ub + tm->obj_offset;
	 printf("%19.2f ", obj_ub);
       }
     } else {
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 printf("%19s ","");
       } else {
	 printf("%19s ","");
       }
     }

     if (tm->has_ub && tm->ub && tm->lb > -SYM_INFINITY){
       printf("%7.2f ", fabs(100*(obj_ub-obj_lb)/obj_ub));
     } else {
       printf("%9s ","");
     }   
     printf("\n");
   } else {     
#ifdef SHOULD_SHOW_MEMORY_USAGE
     printf("memory: %.2f MB ", vsize_in_mb);
#endif
     printf("done: %i ", tm->stat.analyzed);
     printf("left: %i ", tm->samephase_candnum+tm->active_node_num);
     if (tm->has_ub) {
       if (tm->obj_sense == SYM_MAXIMIZE) {
         obj_lb = -tm->ub + tm->obj_offset;
	 printf("lb: %.2f ", obj_lb);
       }else {
         obj_ub = tm->ub + tm->obj_offset;
	 printf("ub: %.2f ", obj_ub);	 
       }
     } else {
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 printf("lb: ?? ");
       }else {
	 printf("ub: ?? ");	 
       }
     }
     find_tree_lb(tm);
     if (tm->lb > -SYM_INFINITY) {
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 obj_ub = -tm->lb + tm->obj_offset;
	 printf("ub: %.2f ", obj_ub);
       } else {
	 obj_lb = tm->lb + tm->obj_offset;
	 printf("lb: %.2f ", obj_lb);
       }
     } else {
       if (tm->obj_sense == SYM_MAXIMIZE) {
	 printf("ub: ?? ");
       } else {
	 printf("lb: ?? ");
       }
     }
     if (tm->has_ub && tm->ub && tm->lb > -SYM_INFINITY) {
       printf("gap: %.2f ", fabs(100*(obj_ub-obj_lb)/obj_ub));
     }
     printf("time: %i\n", (int)(elapsed_time));
   }


#if 0
   printf("Estimated nodes remaining:         %i\n", num_nodes_estimate);
   printf("Estimated time remaining:          %i\n",
	  (int)(estimated_time_remaining));
#endif
   
   if (tm->par.vbc_emulation == VBC_EMULATION_FILE){
      FILE *f;
#pragma omp critical(write_vbc_emulation_file)
      if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	 printf("\nError opening vbc emulation file\n\n");
      }else{
	 PRINT_TIME(tm, f);
	 fprintf(f, "L %.2f \n", tm->printed_lb);
	 fclose(f); 
      }
   }else if (tm->par.vbc_emulation == VBC_EMULATION_LIVE){
      printf("$L %.2f\n", tm->printed_lb);
   }

#if 0
   FREE(widths);
   FREE(gamma);
#endif

}

/*===========================================================================*/

void calculate_widths(bc_node *node, int* widths)
{
   int i;
   
   widths[node->bc_level] += 1;
   for (i = 0; i < node->bobj.child_num; i ++){ 
      calculate_widths(node->children[i], widths);
   }
}
/*===========================================================================*/

/*===========================================================================*\
 * This function picks the "best" node off the active node list
\*===========================================================================*/

int start_node(tm_prob *tm, int thread_num)
{
   int lp_ind, get_next, ind;
   bc_node *best_node = NULL;
   double time;

   if (tm->termcode != TM_UNFINISHED){
      return NEW_NODE__STOP;
   }

   time = wall_clock(NULL);
   
   /*------------------------------------------------------------------------*\
    * First choose the "best" node from the list of candidate nodes.
    * If the list for the current phase is empty then we return NEW_NODE__NONE.
    * Also, if the lower bound on the "best" node is above the current UB then
    * we just move that node the list of next phase candidates.
   \*------------------------------------------------------------------------*/

   get_next = TRUE;
   while (get_next){
#pragma omp critical (tree_update)
      best_node = del_best_node(tm);
      if (best_node == NULL)
	 return(NEW_NODE__NONE);

      if (best_node->node_status == NODE_STATUS__WARM_STARTED){
	 if(best_node->lower_bound >= MAXDOUBLE) 
	    break;
      }
      
      /* if no UB yet or lb is lower than UB then go ahead */
      if (!tm->has_ub ||
	  (tm->has_ub && best_node->lower_bound < tm->ub-tm->par.granularity))
	 break;
      /* ok, so we do have an UB and lb is higher than the UB. */
      /* in this switch we assume that there are only two phases! */
      switch (((best_node->desc.nf_status) << 8) + tm->phase){
       case (NF_CHECK_NOTHING << 8) + 0: /* prune these */
       case (NF_CHECK_NOTHING << 8) + 1:
	  if(!tm->par.sensitivity_analysis){
	     if (tm->par.max_cp_num > 0 && best_node->cp){
#ifdef COMPILE_IN_CP
		ind = best_node->cp;
#else
		ind = find_process_index(&tm->cp, best_node->cp);
#endif
		tm->nodes_per_cp[ind]--;
		if (tm->nodes_per_cp[ind] + tm->active_nodes_per_cp[ind] == 0)
		   tm->cp.free_ind[tm->cp.free_num++] = ind;
	     }
	     best_node->node_status = NODE_STATUS__PRUNED;
	     best_node->feasibility_status = OVER_UB_PRUNED;

	     if(tm->br_inf_down != NULL && tm->br_inf_up != NULL &&
                best_node->parent){
	       for(int i = 0; i < best_node->parent->bobj.child_num; i++){
		 if(best_node->parent->children[i] == best_node){
		   if(best_node->parent->bobj.sense[i] == 'L'){
		     tm->br_inf_down[best_node->parent->bobj.name]++;
		   }else{
		     tm->br_inf_up[best_node->parent->bobj.name]++;		     
		   }
		 }
	       }
	     }
	 
	     if (tm->par.verbosity > 1){
		printf("++++++++++++++++++++++++++++++++++++++++++++++++++\n");
		printf("+ TM: Pruning NODE %i LEVEL %i instead of sending it.\n",
		       best_node->bc_index, best_node->bc_level);
		printf("++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	     }
	     if (tm->par.keep_description_of_pruned == KEEP_ON_DISK_VBC_TOOL ||
		 tm->par.keep_description_of_pruned == KEEP_ON_DISK_FULL ||
		 tm->par.keep_description_of_pruned == DISCARD){
		if (tm->par.keep_description_of_pruned ==
		    KEEP_ON_DISK_VBC_TOOL ||
		    tm->par.keep_description_of_pruned == KEEP_ON_DISK_FULL){
#pragma omp critical (write_pruned_node_file)
		   write_pruned_nodes(tm, best_node);
		}
#if 0
		if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
		   purge_pruned_nodes(tm, best_node, VBC_PRUNED_FATHOMED);
		} else {
		   purge_pruned_nodes(tm, best_node, VBC_PRUNED);
		}
#else
#pragma omp critical (tree_update)
		purge_pruned_nodes(tm, best_node, VBC_PRUNED);
#endif
	     }
	     break;
	  }
       case (NF_CHECK_ALL            << 8) + 1: /* work on these */
       case (NF_CHECK_UNTIL_LAST     << 8) + 1:
       case (NF_CHECK_AFTER_LAST     << 8) + 1:
	 get_next = FALSE;
	 break;

       default:
	 /* i.e., phase == 0 and nf_status != NF_CHECK_NOTHING */
#pragma omp critical (tree_update)
	  if (!(tm->par.colgen_strat[0] & FATHOM__GENERATE_COLS__RESOLVE)){
	     REALLOC(tm->nextphase_cand, bc_node *, tm->nextphase_cand_size,
		     tm->nextphase_candnum+1, BB_BUNCH);
	     tm->nextphase_candnum++;
	     tm->nextphase_cand[tm->nextphase_candnum] = best_node;
	  }else{
	     get_next = FALSE;
	  }
	  break;
      }
   }

   /* Assign a free lp process */
#ifdef COMPILE_IN_LP
   lp_ind = thread_num;
#else
   lp_ind = tm->lp.free_ind[--tm->lp.free_num];
   best_node->lp = tm->lp.procs[lp_ind];
   best_node->cg = tm->par.use_cg ? tm->cg.procs[lp_ind] : 0;
#endif
   /* assign pools, too */

   best_node->cp = assign_pool(tm, best_node->cp, &tm->cp,
			       tm->active_nodes_per_cp, tm->nodes_per_cp);
   if (best_node->cp < 0) return(NEW_NODE__ERROR);


   /* It's time to put together the node and send it out */
   tm->active_nodes[lp_ind] = best_node;
OPENMP_ATOMIC_UPDATE
   tm->active_node_num++;

   send_active_node(tm,best_node,tm->par.colgen_strat[tm->phase],thread_num);

OPENMP_ATOMIC_UPDATE
   tm->comp_times.start_node += wall_clock(NULL) - time;

   return(NEW_NODE__STARTED);
}

/*===========================================================================*/

/*===========================================================================*\
 * Returns the "best" active node and deletes it from the list
\*===========================================================================*/

bc_node *del_best_node(tm_prob *tm)
{
   bc_node **list = tm->samephase_cand;
   int size = tm->samephase_candnum;
   bc_node *temp = NULL, *best_node;
   int pos, ch;
   int rule = tm->par.node_selection_rule;

   if (size == 0)
      return(NULL);

#ifdef DO_TESTS
   for (int i = tm->samephase_candnum; i >= 1; i--){
      //printf("Node %i, LB: %f\n", tm->samephase_cand[i]->bc_index,
      //     tm->samephase_cand[i]->lower_bound);
      if ((2*i <= tm->samephase_candnum &&
           tm->samephase_cand[i]->lower_bound >
           tm->samephase_cand[2*i]->lower_bound) ||
          (2*i+1 <= tm->samephase_candnum &&
           tm->samephase_cand[i]->lower_bound >
           tm->samephase_cand[2*i+1]->lower_bound)){
         printf("#####Warning: heap corruption detected\n");
      }
   }
#endif

   best_node = list[1];
   
   temp = list[1] = list[size];
   
   tm->samephase_candnum = --size;

   if (tm->par.verbosity > 10)
      if (tm->samephase_candnum % 10 == 0)
	 printf("\nTM: tree size: %i , %i\n\n",
		tm->samephase_candnum, tm->nextphase_candnum);

   pos = 1;
   while ((ch=2*pos) < size){
      if (node_compar(tm, rule, list[ch], list[ch+1]))
	 ch++;
      if (node_compar(tm, rule, list[ch], temp)){
	 list[pos] = temp;
	 return(best_node);
      }
      list[pos] = list[ch];
      pos = ch;
   }
   if (ch == size){
      if (node_compar(tm, rule, temp, list[ch])){
	 list[pos] = list[ch];
	 pos = ch;
      }
   }
   list[pos] = temp;

#ifdef DO_TESTS
   for (int i = tm->samephase_candnum; i >= 1; i--){
      //printf("Node %i, LB: %f\n", tm->samephase_cand[i]->bc_index,
      //     tm->samephase_cand[i]->lower_bound);
      if ((2*i <= tm->samephase_candnum &&
           tm->samephase_cand[i]->lower_bound >
           tm->samephase_cand[2*i]->lower_bound) ||
          (2*i+1 <= tm->samephase_candnum &&
           tm->samephase_cand[i]->lower_bound >
           tm->samephase_cand[2*i+1]->lower_bound)){
         printf("#####Warning: heap corruption detected\n");
      }
   }
#endif

   return(best_node);
}

/*===========================================================================*/

/*===========================================================================*\
 * Insert a new active node into the active node list (kept as a binary tree)
\*===========================================================================*/

void insert_new_node(tm_prob *tm, bc_node *node)
{
   if (tm->termcode == TM_UNFINISHED){
      if (node->node_status == NODE_STATUS__TIME_LIMIT){
OPENMP_ATOMIC_WRITE
	 tm->termcode = TM_TIME_LIMIT_EXCEEDED;
#ifdef _OPENMP
	 tm->termcodes[omp_get_thread_num()] = TM_TIME_LIMIT_EXCEEDED;
#else
	 tm->termcodes[0] = TM_TIME_LIMIT_EXCEEDED;
#endif
      }else if (node->node_status == NODE_STATUS__ITERATION_LIMIT){
OPENMP_ATOMIC_WRITE
	 tm->termcode = TM_ITERATION_LIMIT_EXCEEDED;
#ifdef _OPENMP
	 tm->termcodes[omp_get_thread_num()] = TM_ITERATION_LIMIT_EXCEEDED;
#else
	 tm->termcodes[0] = TM_ITERATION_LIMIT_EXCEEDED;
#endif
      }
   }
  
#pragma omp critical (tree_update)
{
   int pos, ch, size = tm->samephase_candnum;
   bc_node **list = tm->samephase_cand;
   int rule = tm->par.node_selection_rule;
   
#ifdef DO_TESTS
   if (size > 0){
      for (int i = size; i >= 1; i--){
         //printf("Node %i, LB: %f\n", list[i]->bc_index, list[i]->lower_bound);
         if ((2*i <= size && list[i]->lower_bound > list[2*i]->lower_bound) ||
             (2*i+1 <= size && list[i]->lower_bound > list[2*i+1]->lower_bound)){
            printf("#####Warning: heap corruption detected\n");
         }
      }
   }
#endif
   
   tm->samephase_candnum = pos = ++size;

   if (tm->par.verbosity > 10)
      if (tm->samephase_candnum % 10 == 0)
	 printf("\nTM: tree size: %i , %i\n\n",
		tm->samephase_candnum, tm->nextphase_candnum);

   REALLOC(tm->samephase_cand, bc_node *,
	   tm->samephase_cand_size, size + 1, BB_BUNCH);

   list = tm->samephase_cand;
   
   while ((ch=pos>>1) != 0){
      if (node_compar(tm, rule, list[ch], node)){
	 list[pos] = list[ch];
	 pos = ch;
      }else{
	 break;
      }
   }
   list[pos] = node;

#ifdef DO_TESTS
   for (int i = size; i >= 1; i--){
      if ((2*i <= size && list[i]->lower_bound > list[2*i]->lower_bound) ||
          (2*i+1 <= size && list[i]->lower_bound > list[2*i+1]->lower_bound)){
         printf("#####Warning: heap corruption detected\n");
      }
   }
#endif
   
} /* End critical Region */

}

/*===========================================================================*/

/*===========================================================================*\
 * This is the node comparison function used to order the list of active
 * Nodes are ordered differently depending on what the comparison rule is
\*===========================================================================*/

int node_compar(tm_prob *tm, int rule, bc_node *node0, bc_node *node1)
{

   int ret_ind = 0;

#if 0
   double n0_rhs, n1_rhs;
   int n0_ind, n1_ind;
   n1_ind = node1->parent->bobj.name;
   n0_ind = node0->parent->bobj.name;
   double n0_frac, n1_frac; 
   if(node0->parent->children[0] == node0){
      n0_rhs = node0->parent->bobj.rhs[0];
   }else{
      n0_rhs = node0->parent->bobj.rhs[1];
   }

   if(node1->parent->children[0] == node1){
      n1_rhs = node1->parent->bobj.rhs[0];
   }else{
      n1_rhs = node1->parent->bobj.rhs[1];
   }

   n0_frac = fabs(node0->parent->bobj.value - n0_rhs);
   n1_frac = fabs(node1->parent->bobj.value - n1_rhs);
#endif
   
   /* solves acc3 without swap 
      if(node1->lower_bound < node0->lower_bound - 1e-4) ret_ind = 1;
      else if (node1->lower_bound < node0->lower_bound + 1e-4) {
	 if(node1->bc_level > node0->bc_level) ret_ind = 1;
      }
      //return(node1->lower_bound < node0->lower_bound ? 1:0);
      return ret_ind;
   */

   switch(rule){
    case LOWEST_LP_FIRST:      
      if(node1->lower_bound < node0->lower_bound) ret_ind = 1;
      //if(node1->lower_bound < node0->lower_bound - 1e-4) ret_ind = 1;
      //else if (node1->lower_bound < node0->lower_bound + 1e-4) {
      // if(node1->bc_level > node0->bc_level) ret_ind = 1;
      // else if(node1->bc_level == node0->bc_level)
      //    if(node1->frac_cnt < node0->frac_cnt) ret_ind = 1;
	 ////if(tm->has_ub){
	 //// if(node1->bc_level > node0->bc_level) ret_ind = 1;
	 //// else if(node1->bc_level == node0->bc_level)
	 ////    if(node1->frac_cnt < node0->frac_cnt) ret_ind = 1;
	 ////}else
	 ////  if(node1->frac_cnt < node0->frac_cnt) ret_ind = 1;
	 ////  else if(node1->frac_cnt == node0->frac_cnt) 
	 ////     if (node1->bc_level > node0->bc_level) ret_ind = 1;
      //}
      //return(node1->lower_bound < node0->lower_bound ? 1:0);
      return ret_ind;
      //switch(rule){
      //case LOWEST_LP_FIRST:
      //if(node1->lower_bound < node0->lower_bound - 1e-4)
      //	 return 1;
      //else if(node1->lower_bound < node0->lower_bound + 1e-4)
      // if(node1->bc_level < node0->bc_level) return 1;
      // else return 0;
      //else return 0;
      // return(node1->lower_bound < node0->lower_bound ? 1:0);
    case HIGHEST_LP_FIRST:
      return(node1->lower_bound > node0->lower_bound ? 1:0);
    case BREADTH_FIRST_SEARCH:
      return(node1->bc_level < node0->bc_level ? 1:0);
    case DEPTH_FIRST_SEARCH:
    case DEPTH_FIRST_THEN_BEST_FIRST:
      return(node1->bc_level > node0->bc_level ? 1:0);
   }
   return(0); /* fake return */
}

/*===========================================================================*/

/*===========================================================================*\
 * Nodes by default inherit their parent's pools. However if there is a free
 * pool then the node is moved over to the free pool.
\*===========================================================================*/

int assign_pool(tm_prob *tm, int oldpool, process_set *pools,
		int *active_nodes_per_pool, int *nodes_per_pool)
{
   int oldind = -1, ind, pool;
#ifndef COMPILE_IN_CP
   int s_bufid, r_bufid;
   struct timeval timeout = {5, 0};
#endif
   
   if (pools->free_num == 0){
      /* No change in the pool assigned to this node */
      return(oldpool);
   }

   if (oldpool > 0){
#ifdef COMPILE_IN_CP
      oldind = oldpool;
#else
      oldind = find_process_index(pools, oldpool);
#endif
      if (nodes_per_pool[oldind] == 1){
	 nodes_per_pool[oldind]--;
	 active_nodes_per_pool[oldind]++;
	 return(oldpool);
      }
   }

   ind = pools->free_ind[--pools->free_num];
#ifdef COMPILE_IN_CP
   pool = ind;
#else
   pool = pools->procs[ind];
#endif
   
   if (! oldpool){
      /* If no pool is assigned yet then just assign the free one */
      active_nodes_per_pool[ind] = 1;
      return(pool);
   }

   /* finally when we really move the node from one pool to another */
   nodes_per_pool[oldind]--;
   active_nodes_per_pool[ind] = 1;
#ifdef COMPILE_IN_CP
   /*FIXME: Multiple Pools won't work in shared memory mode until I fill this
     in.*/
#else
   s_bufid = init_send(DataInPlace);
   send_int_array(&oldpool, 1);
   send_msg(pool, POOL_YOU_ARE_USELESS);
   s_bufid = init_send(DataInPlace);
   send_int_array(&pool, 1);
   send_msg(oldpool, POOL_COPY_YOURSELF);
   freebuf(s_bufid);

   do{
      r_bufid = treceive_msg(pool, POOL_USELESSNESS_ACKNOWLEDGED, &timeout);
      if (r_bufid == 0)
	 if (pstat(pool) != PROCESS_OK) return(NEW_NODE__ERROR);
   }while (r_bufid == 0);
   freebuf(r_bufid);
#endif

   return(pool);
}

/*===========================================================================*/

/*===========================================================================*\
 * Takes the branching object description and sets up data structures
 * for the resulting children and adds them to the list of candidates.
\*===========================================================================*/

int generate_children(tm_prob *tm, bc_node *node, branch_obj *bobj,
		      double *objval, int *feasible, char *action,
		      int olddive, int *keep, int new_branching_cut)
{
   node_desc *desc;
   int np_cp = 0, np_sp = 0;
   int dive = DO_NOT_DIVE, i;
   bc_node *child;
   int child_num, node_status;
#ifdef TRACE_PATH
   int optimal_path = -1;
#endif
   
   /* before we start to generate the children we must figure out if we'll
    * dive so that we can put the kept child into the right location */
   if (*keep >= 0 && (olddive == CHECK_BEFORE_DIVE || olddive == DO_DIVE))
      dive = olddive == DO_DIVE ? DO_DIVE : shall_we_dive(tm, objval[*keep]);
   
   node->children = (bc_node **) calloc(bobj->child_num, sizeof(bc_node *));
   if (node->bc_level == tm->stat.max_depth)
      tm->stat.max_depth++;
   child_num = bobj->child_num;
#ifdef TRACE_PATH
   if (node->optimal_path && tm->feas_sol_size){
      for (i = 0; i < tm->feas_sol_size; i++)
	 if (tm->feas_sol[i] == bobj->name)
	    break;
      if (i < tm->feas_sol_size)
	 optimal_path = 1;
      else
	 optimal_path = 0;
      printf("\n\nNode %i is on the optimal path\n\n",
	     tm->stat.tree_size + optimal_path);
   }
#endif
   for (i = 0; i < child_num; i++){
      child = node->children[i] = (bc_node *) calloc(1, sizeof(bc_node));
      child->bc_index = tm->stat.tree_size++;
      child->bc_level = node->bc_level + 1;
      child->lower_bound = objval[i];
#ifdef DO_TESTS
      if (child->lower_bound < tm->lb - .001 ||
          child->lower_bound < node->lower_bound - .001){
         printf("#####Warning: lower bound decrease detected after branching\n");
         printf("     From parent: %f\n", node->lower_bound - child->lower_bound);
         printf("     From global: %f\n", tm->lb - child->lower_bound);
      }
#endif
      child->frac_cnt = node->frac_cnt; 
      child->frac_avg = node->frac_avg; 
#ifdef COMPILE_IN_LP
      child->update_pc = bobj->is_est[i] ? TRUE : FALSE;
#endif
      child->parent = node;
      if (tm->par.verbosity > 10){
	 printf("Generating node %i from %i...\n", child->bc_index,
		node->bc_index);
      }
      if (tm->par.vbc_emulation == VBC_EMULATION_FILE){
	 FILE *f;
#pragma omp critical(write_vbc_emulation_file)
	 if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	    printf("\nError opening vbc emulation file\n\n");
	 }else{
	    PRINT_TIME(tm, f);
	    fprintf(f, "N %i %i %i\n", node->bc_index+1, child->bc_index+1,
                    feasible[i] ? VBC_FEAS_SOL_FOUND :
                    ((dive != DO_NOT_DIVE && *keep == i) ?
                     VBC_ACTIVE_NODE : VBC_CAND_NODE));
	 }
      } else if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
	 FILE *f;
#pragma omp critical(write_vbc_emulation_file)
	 if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	    printf("\nError opening vbc emulation file\n\n");
	 }else{
	    PRINT_TIME2(tm, f);
	    char reason[50];
	    char branch_dir = 'M';
	    sprintf (reason, "%s %i %i", "candidate", child->bc_index+1,
		     node->bc_index+1);
	    if (child->bc_index>0){
	       if (node->children[0]==child) {
		  branch_dir = node->bobj.sense[0];
		  /*branch_dir = 'L';*/
	       } else {
		  branch_dir = node->bobj.sense[1];
		  /*branch_dir = 'R';*/
	       }
	       if (branch_dir == 'G') {
		  branch_dir = 'R';
	       }
	    }
	    if (action[i] == PRUNE_THIS_CHILD_FATHOMABLE ||
		action[i] == PRUNE_THIS_CHILD_INFEASIBLE){
	       sprintf(reason,"%s %c", reason, branch_dir);
	    }else{
	       sprintf(reason,"%s %c %f", reason, branch_dir,
		       child->lower_bound);
	    }
	    fprintf(f,"%s\n",reason);
	    fclose(f);
	 }
      }else if (tm->par.vbc_emulation == VBC_EMULATION_LIVE){
	 printf("$N %i %i %i\n", node->bc_index+1, child->bc_index+1,
		feasible[i] ? VBC_FEAS_SOL_FOUND :
		((dive != DO_NOT_DIVE && *keep == i) ?
		 VBC_ACTIVE_NODE: VBC_CAND_NODE));
      }
#ifdef TRACE_PATH
      if (optimal_path == i)
	 child->optimal_path = TRUE;
#endif
      tm->stat.created++;
#ifndef ROOT_NODE_ONLY
      if (action[i] == PRUNE_THIS_CHILD ||
	  action[i] == PRUNE_THIS_CHILD_FATHOMABLE ||
	  action[i] == PRUNE_THIS_CHILD_INFEASIBLE ||
	  (tm->has_ub && tm->ub - tm->par.granularity < objval[i] &&
	   node->desc.nf_status == NF_CHECK_NOTHING)){
	 /* this last can happen if the TM got the new bound but it hasn't
          * been propagated to the LP yet */
#else /*We only want to process the root node in this case - discard others*/
      if (TRUE){	 
#endif
	 if (tm->par.verbosity > 1){
	    printf("++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	    printf("+ TM: Pruning NODE %i LEVEL %i while generating it.\n",
		   child->bc_index, child->bc_level);
	    printf("++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	 }
         
	 node_status = child->node_status = NODE_STATUS__PRUNED;
         
#ifdef TRACE_PATH
	 if (child->optimal_path){
	    printf("\n\nAttempting to prune the optimal path!!!!!!!!!\n\n");
	    sleep(600);
	    if (tm->par.logging){
	       write_tm_info(tm, tm->par.tree_log_file_name, NULL, FALSE);
	       write_subtree(tm->rootnode, tm->par.tree_log_file_name, NULL,
			     TRUE, tm->par.logging);
	       write_tm_cut_list(tm, tm->par.cut_log_file_name, FALSE);
	    }
	    exit(1);
	 }
#endif
	 if (tm->par.keep_description_of_pruned == DISCARD ||
	     tm->par.keep_description_of_pruned == KEEP_ON_DISK_VBC_TOOL ||
             tm->par.keep_description_of_pruned == KEEP_ON_DISK_FULL){
	    child->parent = node;
	    if (tm->par.keep_description_of_pruned != DISCARD)
#pragma omp critical (write_pruned_node_file)
	       write_pruned_nodes(tm, child);
#pragma omp critical (tree_update)
	    if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
	       int vbc_node_pr_reason;
	       switch (action[i]) {
		case PRUNE_THIS_CHILD_INFEASIBLE:
		  vbc_node_pr_reason = VBC_PRUNED_INFEASIBLE;
		  break;
		case PRUNE_THIS_CHILD_FATHOMABLE:
		  vbc_node_pr_reason = VBC_PRUNED_FATHOMED;
		  break;
		default:
		  vbc_node_pr_reason = VBC_PRUNED;
	       }
	       purge_pruned_nodes(tm, child, vbc_node_pr_reason);
	    } else {
	       purge_pruned_nodes(tm, child, feasible[i] ? VBC_FEAS_SOL_FOUND :
				  VBC_PRUNED);
	    }
	 }else{ /* tm->par.keep_description_of_pruned == KEEP_IN_MEMORY */
	    child->feasibility_status = OVER_UB_PRUNED;	   
	    if (feasible[i]){
	       child->sol_size = bobj->sol_sizes[i];
	       child->sol_ind = bobj->sol_inds[i];
	       bobj->sol_inds[i] = 0;
	       child->sol = bobj->solutions[i];
	       bobj->solutions[i] = 0;
	       child->feasibility_status = FEASIBLE_PRUNED;	   	    
	    }
	    if (action[i] == PRUNE_THIS_CHILD_INFEASIBLE){
	       child->feasibility_status = INFEASIBLE_PRUNED;
	    }
         }
      }else{
	 node_status = child->node_status = NODE_STATUS__CANDIDATE;
	 /* child->lp = child->cg = 0;   zeroed out by calloc */
	 child->cp = node->cp;
         if (feasible[i]){
            if(tm->par.keep_description_of_pruned == KEEP_IN_MEMORY){
               child->sol_size = bobj->sol_sizes[i];
               child->sol_ind = bobj->sol_inds[i];
               bobj->sol_inds[i]=0;
               child->sol = bobj->solutions[i];
               bobj->solutions[i] = 0;
               child->feasibility_status = NOT_PRUNED_HAS_CAN_SOLUTION;
            }
         }
      }
      
      if (node_status == NODE_STATUS__CANDIDATE ||
          tm->par.keep_description_of_pruned != DISCARD){
         desc = &child->desc;
         
         if (node->desc.basis.basis_exists){
            desc->basis.basis_exists = TRUE;
         }
         
         /* If we have a non-base, new branching cut then few more things
            might have to be fixed */
         if (new_branching_cut && bobj->name >= 0){
            /* Fix cutind and the basis description */
            desc->cutind.size = 1;
            desc->cutind.added = 1;
            desc->cutind.list = (int *) malloc(ISIZE);
            desc->cutind.list[0] = bobj->name;
            if (desc->basis.basis_exists){
               desc->basis.extrarows.size = 1;
               desc->basis.extrarows.list = (int *) malloc(ISIZE);
               desc->basis.extrarows.list[0] = bobj->name;
               desc->basis.extrarows.stat = (int *) malloc(ISIZE);
               desc->basis.extrarows.stat[0] = SLACK_BASIC;
            }
         }
         
         desc->desc_size = node->desc.desc_size;
         desc->desc = node->desc.desc;
         desc->nf_status = node->desc.nf_status;
         
#ifdef SENSITIVITY_ANALYSIS
         //Anahita
         child->intcpt = bobj->intcpt[i];
         if (tm->par.sensitivity_rhs){ //&& //Anahita 
            //action[i] != PRUNE_THIS_CHILD_INFEASIBLE){
            child->duals = bobj->duals[i];
            bobj->duals[i] = 0;
            //Anahita
            if (bobj->rays){
               child->rays = bobj->rays[i];
               bobj->rays[i] = 0;
            }
         }
         if (tm->par.sensitivity_bounds){
            //Ted
            child->dj = bobj->dj[i];
            bobj->dj[i] = 0;
         }      
#endif
      } else if (node_status == NODE_STATUS__PRUNED){
         if (--child_num == 0){
            *keep = -1;
            return(DO_NOT_DIVE);
         }
         if (*keep == child_num){
            *keep = i;
         }else if (*keep == i){
            /*We pruned the child we were supposed to keep*/
            dive = DO_NOT_DIVE;
         }
#ifdef TRACE_PATH
         if (optimal_path == child_num){
            optimal_path = i;
         }
#endif
         action[i] = action[child_num];
         objval[i] = objval[child_num];
         feasible[i--] = feasible[child_num];
	 continue;
      }
      
      if (tm->phase == 0 &&
	  !(tm->par.colgen_strat[0] & FATHOM__GENERATE_COLS__RESOLVE) &&
	  (feasible[i] == LP_D_UNBOUNDED ||
	   (tm->has_ub && tm->ub - tm->par.granularity < child->lower_bound))){
	 /* it is kept for the next phase  (==> do not dive) */
	 if (*keep == i)
	    dive = DO_NOT_DIVE;
	 REALLOC(tm->nextphase_cand, bc_node *,
                 tm->nextphase_cand_size, tm->nextphase_candnum+1, BB_BUNCH);
	 tm->nextphase_cand[tm->nextphase_candnum++] = child;
	 np_cp++;
	 np_sp++;
      }else{
	 /* it will be processed in this phase (==> insert it if not kept) */
	 if (*keep != i || dive == DO_NOT_DIVE){
	    insert_new_node(tm, child);
	    np_cp++;
	    np_sp++;
	 }
      }
   }

   if (node->cp)
#ifdef COMPILE_IN_CP
      tm->nodes_per_cp[node->cp] += np_cp;
#else
      tm->nodes_per_cp[find_process_index(&tm->cp, node->cp)] += np_cp;
#endif
   
   return(dive);
}

/*===========================================================================*/

/*===========================================================================*\
 * Determines whether or not the LP process should keep one of the
 * children resulting from branching or whether it should get a new node
 * from the candidate list.
\*===========================================================================*/

char shall_we_dive(tm_prob *tm, double objval)
{
   char dive;
   int i, k;
   double rand_num, average_lb;
   double cutoff = 0;
   double etol = 1e-3;

   if (tm->par.time_limit >= 0.0 &&
	wall_clock(NULL) - tm->start_time >= tm->par.time_limit){
      return(FALSE);
   }

   if (tm->par.node_limit >= 0 && tm->stat.analyzed >= tm->par.node_limit){
      return(FALSE);
   }

   double d_threshold = tm->par.diving_threshold;
   double dual_gap = 100.00;

   if (tm->samephase_candnum > 0 || tm->active_node_num > 1) {
      find_tree_lb(tm);
   }else{
      tm->lb = objval;
   }

   if (tm->has_ub && (tm->par.gap_limit >= 0.0)){
      double tm_dual_gap = d_gap(tm->ub, tm->lb, tm->obj_offset, tm->obj_sense);
      if (tm_dual_gap <= tm->par.gap_limit){
	 return(FALSE);
      }
   }
   
   if(tm->has_ub){
      dual_gap = d_gap(tm->ub, objval, tm->obj_offset, tm->obj_sense);
   }
   
   if(dual_gap < 100.00){
      d_threshold *=etol*etol;
   }else{
      d_threshold = 0.5; 
   }

   d_threshold = MIN(d_threshold, 0.5);
   
   rand_num = ((double)(RANDOM()))/((double)(MAXINT));
   if (tm->par.unconditional_dive_frac > 1 - rand_num){
      dive = CHECK_BEFORE_DIVE;
   }else{
      switch(tm->par.diving_strategy){
       case BEST_ESTIMATE:
	 if (tm->has_ub_estimate){
	    if (objval > tm->ub_estimate){
	       dive = DO_NOT_DIVE;
	       tm->stat.diving_halts++;
	    }else{
	       dive = CHECK_BEFORE_DIVE;
	    }
	    break;
	 }
       case COMP_BEST_K:
	 average_lb = 0;
#pragma omp critical (tree_update) 
	 for (k = 0, i = MIN(tm->samephase_candnum, tm->par.diving_k);
	      i > 0; i--)
	    if (tm->samephase_cand[i]->lower_bound < MAXDOUBLE/2){
	       average_lb += tm->samephase_cand[i]->lower_bound;
	       k++;
	    }
	 if (k){
	    average_lb /= k;
	 }else{
	    dive = CHECK_BEFORE_DIVE;
	    break;
	 }
         if (fabs(average_lb) < etol) {
            average_lb = (average_lb >= 0) ? etol : -etol;
            if (fabs(objval) < etol) {
               objval = (objval >= 0) ? etol : -etol;
            }
         }
	 cutoff = fabs((d_threshold)*average_lb);
	 if (objval > average_lb + cutoff){
	    //if (fabs((objval/average_lb)-1) > d_threshold){
	    dive = DO_NOT_DIVE;
	    tm->stat.diving_halts++;
	 }else{
	    dive = CHECK_BEFORE_DIVE;
	 }
	 break;
       case COMP_BEST_K_GAP:
	 average_lb = 0;
	 for (k = 0, i = MIN(tm->samephase_candnum, tm->par.diving_k);
	      i > 0; i--)
	    if (tm->samephase_cand[i]->lower_bound < MAXDOUBLE/2){
	       average_lb += tm->samephase_cand[i]->lower_bound;
	       k++;
	    }
	 if (k){
	    average_lb /= k;
	 }else{
	    dive = CHECK_BEFORE_DIVE;
	    break;
	 }
	 if (tm->has_ub)
	    cutoff = d_threshold*(tm->ub - average_lb);
	 else
	    cutoff = fabs((d_threshold)*average_lb);
	 if (objval > average_lb + cutoff){
	    dive = DO_NOT_DIVE;
	    tm->stat.diving_halts++;
	 }else{
	    dive = CHECK_BEFORE_DIVE;
	 }
	 break;
       default:
	 printf("Unknown diving strategy -- diving by default\n");
	 dive = DO_DIVE;
	 break;
      }
   }
   return(dive);
}

/*===========================================================================*/

/*===========================================================================*\
 * This routine is entirely for saving memory. If there is no need to
 * keep the description of the pruned nodes in memory, they are freed as
 * soon as they are no longer needed. This can set off a chain reaction
 * of other nodes that are no longer needed.
\*===========================================================================*/

 int purge_pruned_nodes(tm_prob *tm, bc_node *node, int category)
{
   int i, new_child_num;
   branch_obj *bobj = &node->parent->bobj;
   char reason[30];
   char branch_dir = 'M';

   if (tm->par.vbc_emulation != VBC_EMULATION_FILE_NEW && 
	 (category == VBC_PRUNED_INFEASIBLE || category == VBC_PRUNED_FATHOMED 
	  || category == VBC_IGNORE)) {
      printf("Error in purge_pruned_nodes.");
      printf("category refers to VBC_EMULATION_FILE_NEW");
      printf("when it is not used.\n");
      exit(456);
   }

   if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
      switch (category) {
       case VBC_PRUNED_INFEASIBLE:
	 sprintf(reason, "%s", "infeasible");
	 sprintf(reason,"%s %i", reason, node->bc_index+1);
	 if (node->bc_index>0) {
	    sprintf(reason, "%s %i", reason, node->parent->bc_index+1);
	    if (node->parent->children[0]==node) {
	       branch_dir = node->parent->bobj.sense[0];
	       /*branch_dir = 'L';*/
	    } else {
	       branch_dir = node->parent->bobj.sense[1];
	       /*branch_dir = 'R';*/
	    }
	    if (branch_dir == 'G') {
	       branch_dir = 'R';
	    }
	 }else{
	    sprintf(reason," 0");
	 }
	    
	 sprintf(reason,"%s %c %s", reason, branch_dir, "\n");
	 break;
       case VBC_PRUNED_FATHOMED:
	 sprintf(reason, "%s", "fathomed");
	 sprintf(reason, "%s %i", reason, node->bc_index+1);
	 if (node->bc_index>0) {
	    sprintf(reason,"%s %i", reason, node->parent->bc_index+1);
	    if (node->parent->children[0]==node) {
	       branch_dir = node->parent->bobj.sense[0];
	       /*branch_dir = 'L';*/
	    } else {
	       branch_dir = node->parent->bobj.sense[1];
	       /*branch_dir = 'R';*/
	    }
	    if (branch_dir == 'G') {
	       branch_dir = 'R';
	    }
	 }else{
	    sprintf(reason," 0");
	 }
	 sprintf(reason,"%s %c %s", reason, branch_dir, "\n");
	 break;
       case VBC_FEAS_SOL_FOUND:
	 /* This case has already been dealt in install_new_ub(), hence
	  * commented out
	  */
/*	 sprintf(reason,"%s","integer");
	 sprintf(reason,"%s %i %i",reason, node->bc_index+1,
		 node->parent->bc_index+1);
	 if (node->parent->children[0]==node) {
	    branch_dir = 'L';
	 } else {
	    branch_dir = 'R';
	 }
	 sprintf(reason,"%s %c %f\n", reason, branch_dir, tm->ub);
	 break;
 */
       default:
	 category = VBC_IGNORE;
	 break;
      }
   }
   
   if (node->parent == NULL){
      return(1);
   }
   if (category == VBC_IGNORE) {
#if 0
      PRINT(tm->par.verbosity, 1,
	    ("ignoring vbc update in purge_pruned_nodes"));
#endif
   } else if (tm->par.vbc_emulation == VBC_EMULATION_FILE){
      FILE *f;
#pragma omp critical(write_vbc_emulation_file)
      if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	 printf("\nError opening vbc emulation file\n\n");
      }else{
	 PRINT_TIME(tm, f);
	 fprintf(f, "P %i %i\n", node->bc_index+1, category);
	 fclose(f);
      }
   } else if (tm->par.vbc_emulation == VBC_EMULATION_LIVE){
      printf("$P %i %i\n", node->bc_index+1, category);
   } else if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
      FILE *f;
#pragma omp critical(write_vbc_emulation_file)
      if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	 printf("\nError opening vbc emulation file\n\n");
      }else{
	 PRINT_TIME2(tm, f);
	 fprintf(f, "%s", reason);
	 fclose(f);
      }
   }
   
   if ((new_child_num = --bobj->child_num) == 0){
      if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
	 purge_pruned_nodes(tm, node->parent, VBC_IGNORE);
      } else {
	 purge_pruned_nodes(tm, node->parent, category);
      }
   }else{
      for (i = 0; i <= bobj->child_num; i++){
	 if (node->parent->children[i] == node){
	    if (i == new_child_num){
	       node->parent->children[i] = NULL;
	    }else{
	       node->parent->children[i]=node->parent->children[new_child_num];
	       bobj->sense[i] = bobj->sense[new_child_num];
	       bobj->rhs[i] = bobj->rhs[new_child_num];
	       bobj->range[i] = bobj->range[new_child_num];
	       bobj->branch[i] = bobj->branch[new_child_num];
	       bobj->sos_cnt[i] = bobj->sos_cnt[new_child_num];
	       int *swap_si = bobj->sos_ind[i];
	       bobj->sos_ind[i] = bobj->sos_ind[new_child_num];
	       bobj->sos_ind[new_child_num] = swap_si;
#ifdef COMPILE_IN_LP
	       bobj->is_est[i] = bobj->is_est[new_child_num];
	       bobj->termcode[i] = bobj->termcode[new_child_num];
	       bobj->iterd[i] = bobj->iterd[new_child_num];
#endif
	    }
	 }
      }
   }

   free_tree_node(node);
#ifdef COMPILE_IN_LP
#ifdef _OPENMP
   int thread_num = omp_get_thread_num();
#else
   int thread_num = 0;
#endif
   if (node == tm->active_nodes[thread_num]){
      // We have to remove this node from the list now, since
      // it might otherwise be referenced in find_tree_lb
      // before it is finally removed in fathom_node
      tm->active_nodes[thread_num] = NULL;
      // For some reason, this seems to cause problems,
      // even though you would think it should be done here.
      //OPENMP_ATOMIC_UPDATE
      //tm->active_node_num--;
   }
#endif
   return(1);
}

/*===========================================================================*\
 * This routine is for writing the pruned nodes to disk before deleting them
 * from memory. 
\*===========================================================================*/

int write_pruned_nodes(tm_prob *tm, bc_node *node)
{
   FILE *f = NULL;
   branch_obj *bobj = &node->parent->bobj;
   
   if (tm->par.keep_description_of_pruned == KEEP_ON_DISK_FULL ||
       tm->par.keep_description_of_pruned == KEEP_ON_DISK_VBC_TOOL){
      if (!(f = fopen(tm->par.pruned_node_file_name, "a"))){
	 printf("\nError opening pruned node file\n\n");
	 return(0);
      }
   }
   
   if (node->parent == NULL){
      return(1);
   }
   
   if (bobj->child_num == 1){
      write_pruned_nodes(tm, node->parent);
   }
   
   if (tm->par.keep_description_of_pruned == KEEP_ON_DISK_VBC_TOOL){
      if (node->parent)
	 fprintf(f, "%i %i\n", node->parent->bc_index + 1, node->bc_index + 1);
      fclose(f);
   }else if (tm->par.keep_description_of_pruned == KEEP_ON_DISK_FULL){
      write_node(node, tm->par.pruned_node_file_name, f, TRUE);
      fclose(f);
   }

   return(1);
}

/*===========================================================================*/

/*===========================================================================*\
 * Find the index of a particular process in a list
\*===========================================================================*/

int find_process_index(process_set *pset, int tid)
{
   int i = pset->procnum-1, *procs = pset->procs;

   for ( ; i >= 0 && procs[i] != tid; i--);
#ifdef DO_TESTS
   if (i == -1){
      printf("TM: process index not found !!!\n\n");
      exit(-5);
   }
#endif   
   return(i);
}

/*===========================================================================*/

void mark_lp_process_free(tm_prob *tm, int lp, int cp)
{
   int ind;
   if (tm->cp.procnum > 0){
#ifdef COMPILE_IN_CP
      ind = cp;
#else
      ind = find_process_index(&tm->cp, cp);
#endif
      tm->active_nodes_per_cp[ind]--;
      if (tm->nodes_per_cp[ind] + tm->active_nodes_per_cp[ind] == 0)
	 tm->cp.free_ind[tm->cp.free_num++] = ind;
   }
   tm->active_nodes[lp] = NULL;
   tm->lp.free_ind[tm->lp.free_num++] = lp;
   tm->active_node_num--;
}

/*===========================================================================*/

int add_cut_to_list(tm_prob *tm, cut_data *cut)
{
#pragma omp critical (cut_pool)
   {
      REALLOC(tm->cuts, cut_data *, tm->allocated_cut_num, tm->cut_num + 1,
	      (tm->cut_num / tm->stat.created + 5) * BB_BUNCH);
      cut->name = tm->cut_num;
      tm->cuts[tm->cut_num++] = cut;
   }
   return(cut->name);
}

/*===========================================================================*/

/*===========================================================================*\
 * Installs a new upper bound and cleans up the candidate list
\*===========================================================================*/

void install_new_ub(tm_prob *tm, double new_ub, int opt_thread_num,
		    int bc_index, char branching, int feasible){
   bc_node *node, *temp, **list;
   int rule, pos, prev_pos, last, i, j;
   int changed_bound = TRUE;
   
{
   if (!tm->has_ub || (tm->has_ub && new_ub < tm->ub)){
      tm->has_ub = TRUE;
      tm->ub = new_ub;
   }else{
      changed_bound = FALSE;
   }
#ifdef COMPILE_IN_LP
   for (i = 0; i < tm->par.max_active_nodes; i ++){
      tm->lpp[i]->has_ub = tm->has_ub;
      tm->lpp[i]->ub = tm->ub;
   }
#endif
}
   if (!changed_bound){
      return;
   }
#ifdef COMPILE_IN_LP
   tm->opt_thread_num = opt_thread_num;
#endif
   if (tm->par.vbc_emulation == VBC_EMULATION_FILE){
      FILE *f;
#pragma omp critical(write_vbc_emulation_file)
      if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	 printf("\nError opening vbc emulation file\n\n");
      }else{
	 PRINT_TIME(tm, f);
	 fprintf(f, "U %.2f\n", new_ub);
	 fclose(f);
      }
   }else if (tm->par.vbc_emulation == VBC_EMULATION_LIVE){
      printf("$U %.2f\n", new_ub);
   }else if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW &&
	     (feasible == IP_FEASIBLE || feasible == IP_HEUR_FEASIBLE)){
      FILE *f;
      //      char reason[30];
      char branch_dir = 'M';
      if (!(f = fopen(tm->par.vbc_emulation_file_name, "a"))){
	 printf("\nError opening vbc emulation file\n\n");
      }else if ((feasible == IP_FEASIBLE && branching) ||
		(feasible == IP_HEUR_FEASIBLE)) {
#pragma omp critical(write_vbc_emulation_file)
	 PRINT_TIME2(tm, f);
	 fprintf(f, "%s %f %i\n", "heuristic", new_ub, bc_index+1);
      }else if (feasible == IP_FEASIBLE && !branching){
	 node = tm->active_nodes[opt_thread_num];
	 if (node->bc_index>0) {
	    if (node->parent->children[0]==node) {
	       branch_dir = node->parent->bobj.sense[0];
	       /*branch_dir = 'L';*/
	    } else {
	       branch_dir = node->parent->bobj.sense[1];
	       /*branch_dir = 'R';*/
	    }
	    if (branch_dir == 'G') {
	       branch_dir = 'R';
	    }
	 }
	 PRINT_TIME2(tm, f);
	 if (node->bc_index){
	    fprintf (f, "%s %i %i %c %f\n", "integer", node->bc_index+1,
		     node->parent->bc_index+1, branch_dir, new_ub);
	 }else{
	    fprintf (f, "%s %i %i %c %f\n", "integer", 1, 0, 'M', new_ub);
	 }
	    
      }
      if (f){
	 fclose(f);
      }
   }

   /* Remove nodes that can now be fathomed from the list */
#pragma omp critical (tree_update)
   {
      int var_index;
   
      rule = tm->par.node_selection_rule;
      list = tm->samephase_cand;
      char has_exchanged = FALSE;
      for (last = i = tm->samephase_candnum; i > 0; i--){
	 has_exchanged = FALSE;
	 node = list[i];
	 if (tm->has_ub &&
	     node->lower_bound >= tm->ub-tm->par.granularity){
#ifdef COMPILE_IN_LP
	    if (node->parent && node->parent->bobj.type == BRANCHING_VARIABLE){
	       for (j = 0; j < node->parent->bobj.child_num; j++){
		  if (node->parent->children[j] == node){
		     var_index = (node->parent->bobj.name < 0 ?
			/* base variable : extra variable */
				  (-node->parent->bobj.name-1) :
				  (node->parent->bobj.name + tm->bvarnum));
		     if (node->parent->bobj.sense[j] == 'L'){
			tm->br_inf_down[var_index]++;
		     }else{
			tm->br_inf_up[var_index]++;
		     }
		  }
	       }
	    }
#endif
	    if (i != last){
	       list[i] = list[last];
	       for (prev_pos = i, pos = i/2; pos >= 1;
		    prev_pos = pos, pos /= 2){
		  if (node_compar(tm, rule, list[pos], list[prev_pos])){
		     temp = list[prev_pos];
		     list[prev_pos] = list[pos];
		     list[pos] = temp;
		     has_exchanged = TRUE;
		  }else{
		     break;
		  }
	       }
	    }
	    tm->samephase_cand[last] = NULL;
	    last--;
	    if (tm->par.verbosity > 1){
	       printf("+++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	       printf("+ TM: Pruning NODE %i LEVEL %i after new incumbent.\n",
		      node->bc_index, node->bc_level);
	       printf("+++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	    }
	    if (tm->par.keep_description_of_pruned == DISCARD ||
		tm->par.keep_description_of_pruned ==
		KEEP_ON_DISK_VBC_TOOL){
	       if (tm->par.keep_description_of_pruned ==
		   KEEP_ON_DISK_VBC_TOOL)
#pragma omp_critical (write_pruned_node_file)
		  write_pruned_nodes(tm, node);
	       if (tm->par.vbc_emulation == VBC_EMULATION_FILE_NEW) {
		  purge_pruned_nodes(tm, node, VBC_PRUNED_FATHOMED);
	       } else {
		  purge_pruned_nodes(tm, node, VBC_PRUNED);
	       }
	    }
	    if (tm->par.sensitivity_analysis){
	       node->feasibility_status = OVER_UB_PRUNED;
	    }
	 }
	 if (has_exchanged) {
	    /*
	     * if exchanges have taken place, node[i] should be
	     * checked again for pruning
	     */
	    i++;
	 }
      }
      tm->samephase_candnum = last;
   }
}

/*===========================================================================*/

/*===========================================================================*\
 * This routine takes the description of the node after processing and
 * compares it to the description before processing. Then it updates
 * data structures appropriately. The other functions below are all
 * related to this one.
\*===========================================================================*/

void merge_descriptions(node_desc *old_node, node_desc *new_node)
{
   if (old_node->basis.basis_exists && new_node->basis.basis_exists){
      merge_base_stat(&old_node->basis.basevars, &new_node->basis.basevars);
      merge_extra_array_and_stat(&old_node->uind, &old_node->basis.extravars,
				 &new_node->uind, &new_node->basis.extravars);
      merge_base_stat(&old_node->basis.baserows, &new_node->basis.baserows);
      merge_extra_array_and_stat(&old_node->cutind,&old_node->basis.extrarows,
				 &new_node->cutind,&new_node->basis.extrarows);
   }else{
      old_node->basis = new_node->basis;
      merge_arrays(&old_node->uind, &new_node->uind);
      merge_arrays(&old_node->cutind, &new_node->cutind);
#ifdef COMPILE_IN_LP
      memset((char *)&(new_node->basis), 0, sizeof(basis_desc));
#endif
   }
   old_node->nf_status = new_node->nf_status;
   if (new_node->nf_status == NF_CHECK_AFTER_LAST ||
       new_node->nf_status == NF_CHECK_UNTIL_LAST){
      merge_arrays(&old_node->not_fixed, &new_node->not_fixed);
   }else{
      FREE(old_node->not_fixed.list);
   }
}

/*===========================================================================*/

void merge_base_stat(double_array_desc *dad, double_array_desc *moddad)
{
   if (moddad->type == EXPLICIT_LIST){
      FREE(dad->list);
      FREE(dad->stat);
      *dad = *moddad;
#ifdef COMPILE_IN_LP
      moddad->stat = NULL;
#endif
      return;
   }

   /* we've got a diff list against the old list */
   if (moddad->size > 0) { /* if there is a change */
      if (dad->type == EXPLICIT_LIST){
	 /* just overwrite the changes */
	 int i;
	 int *oldstat = dad->stat;
	 int  newsize = moddad->size;
	 int *newlist = moddad->list;
	 int *newstat = moddad->stat;
	 for (i = newsize - 1; i >= 0; i--)
	    oldstat[newlist[i]] = newstat[i];
      }else{
	 merge_double_array_descs(dad, moddad);
      }
   }
}

/*===========================================================================*/

void merge_extra_array_and_stat(array_desc *ad, double_array_desc *dad,
				array_desc *modad, double_array_desc *moddad)
{
   if (moddad->type != WRT_PARENT){
      /* If moddad is explicit then just use it */
      FREE(dad->list);
      FREE(dad->stat);
      *dad = *moddad;
#ifdef COMPILE_IN_LP
      moddad->stat = NULL;
#endif
   }else{
      /* So moddad is WRT. Then dad must be WRT, too!! */
      /* First throw out from dad everything that's just been deleted */
      int  newdeled = modad->size - modad->added;
      int *newtodel = modad->list + modad->added;
      int i, j, k, nextdel;
      if (newdeled > 0 && dad->size > 0){
	 int dsize = dad->size;
	 int *dlist = dad->list;
	 int *dstat = dad->stat;
	 k = j = 0;
	 for (i = 0; i < newdeled; ){
	    nextdel = newtodel[i];
	    for (; j < dsize && dlist[j] < nextdel; ){
	       dlist[k] = dlist[j];
	       dstat[k++] = dstat[j++];
	    }
	    if (j == dsize){
	       break;
	    }else{ /* in this case dlist[j] >= nextdel */
	       i++;
	       if (dlist[j] == nextdel)
		  j++;
	    }
	 }
	 while (j < dsize){
	    dlist[k] = dlist[j];
	    dstat[k++] = dstat[j++];
	 }
	 dad->size = k;
      }

      /* Now merge the remaining dad and moddad together */
      merge_double_array_descs(dad, moddad);
   }

   /* dad is fine now. Update ad */

   merge_arrays(ad, modad);
}

/*===========================================================================*/

/*===========================================================================*\
 * Merge the old and new changes together, in case of identical
 *  userindices the newer value overrides the older
\*===========================================================================*/

void merge_double_array_descs(double_array_desc *dad,
			      double_array_desc *moddad)
{
   if (moddad->size != 0){
      if (dad->size == 0){
	 *dad = *moddad;
#ifdef COMPILE_IN_LP
      moddad->stat = moddad->list = NULL;
#endif
      }else{
	 int i, j, k;
	 int  oldsize = dad->size;
	 int *oldlist = dad->list;
	 int *oldstat = dad->stat;
	 int newsize = moddad->size;
	 int *newlist = moddad->list;
	 int *newstat = moddad->stat;
	 int *dlist = dad->list = (int *) malloc((oldsize+newsize) * ISIZE);
	 int *dstat = dad->stat = (int *) malloc((oldsize+newsize) * ISIZE);

	 for (k = 0, i = j = 0; i < oldsize && j < newsize; ){
	    if (oldlist[i] < newlist[j]){
	       dlist[k] = oldlist[i];
	       dstat[k++] = oldstat[i++];
	    }else{
	       if (oldlist[i] == newlist[j])
		  i++;
	       dlist[k] = newlist[j];
	       dstat[k++] = newstat[j++];
	    }
	 }
	 while (i < oldsize){
	    dlist[k] = oldlist[i];
	    dstat[k++] = oldstat[i++];
	 }
	 while (j < newsize){
	    dlist[k] = newlist[j];
	    dstat[k++] = newstat[j++];
	 }
	 dad->size = k;
	 FREE(oldlist);
	 FREE(oldstat);
	 FREE(moddad->list);
	 FREE(moddad->stat);
      }
   }
}

/*===========================================================================*/

void merge_arrays(array_desc *array, array_desc *adesc)
{
   if (adesc->type != WRT_PARENT){
      /* Replace the old with the new one */
      FREE(array->list);
      *array = *adesc;
#ifdef COMPILE_IN_LP
      adesc->list = NULL;
#endif
      return;
   }

   if (adesc->size == 0){
      /* No change, leave the old description alone. */
      return;
   }

   if (array->size == 0){
      /* The old size is 0 (the new type is still WRT_PARENT).
	 If the old type was WRT_PARENT then we can simply replace it with
	 the new WRT_PARENT data.
	 If it was EXPLICIT_LIST with nothing in it, then... contradiction!
	 it would be shorter to explicitly list the new stuff then wrt an
	 empty explicit list. */
      *array = *adesc;
#ifdef COMPILE_IN_LP
      adesc->list = NULL;
#endif
   }else{
      /* OK, array is either WRT or EXP.
	 But!!! If array is EXP then array->added is set to array->size, so
	 we can handle it exactly as if it were WRT!
	 */
      /* Now comes the ugly part... we had an old WRT list and got a new
	 one wrt to the old (none of them is empty)... Create a correct one
	 now.
	 For extra vars/rows the basis status further complicates things.
	 If we had the basis stati stored as EXP, then we don't have to worry
	 about it, since we are going to receive an EXP. But if it was WRT
	 then we must delete the from the basis stati list the extras to be
	 deleted according to the new description! */
      int i, j, k, *list;

      int  newsize = adesc->size;
      int  newadded = adesc->added;
      int *newtoadd = adesc->list;
      int  newdeled = newsize - newadded;
      int *newtodel = newtoadd + newadded;
      int  oldadded = array->added;
      int *oldtoadd = array->list;
      int  olddeled = array->size - oldadded;
      int *oldtodel = oldtoadd + oldadded;

      /* cancel those both in oldtoadd and newdeled; also those both in
	 newtoadd and olddeled.
	 Then the unions of oldtoadd-newtodel and oldtodel-newtoadd will be
	 the correct list. */
      /* First count the number of collisions and mark the colliding ones */
      k = 0;
      for (i = j = 0; i < oldadded && j < newdeled; ){
	 if      (oldtoadd[i] < newtodel[j])  i++;
	 else if (oldtoadd[i] > newtodel[j])  j++;
	 else{
	    oldtoadd[i] = newtodel[j] = -1;
	    i++;
	    j++;
	    k++;
	 }
      }
      for (i = j = 0; i < newadded && j < olddeled; ){
	 if      (newtoadd[i] < oldtodel[j])  i++;
	 else if (newtoadd[i] > oldtodel[j])  j++;
	 else{
	    newtoadd[i] = oldtodel[j] = -1;
	    i++;
	    j++;
	    k++;
	 }
      }
      array->size = array->size + newsize - 2 * k;
      if (array->size == 0){
	 /* Nothing is left, great! */
	 array->added = 0;
	 FREE(adesc->list);
	 FREE(array->list);
      }else{
	 array->list = list = (int *) malloc(array->size * ISIZE);
	 for (i = j = k = 0; i < oldadded && j < newadded; ){
	    if      (oldtoadd[i] == -1)  i++;
	    else if (newtoadd[j] == -1)  j++;
	    else if (oldtoadd[i] < newtoadd[j])  list[k++] = oldtoadd[i++];
	    else /* can't be == */               list[k++] = newtoadd[j++];
	 }
	 for ( ; i < oldadded; i++)
	    if (oldtoadd[i] != -1) list[k++] = oldtoadd[i];
	 for ( ; j < newadded; j++)
	    if (newtoadd[j] != -1) list[k++] = newtoadd[j];
	 array->added = k;

	 for (i = j = 0; i < olddeled && j < newdeled; ){
	    if      (oldtodel[i] == -1)  i++;
	    else if (newtodel[j] == -1)  j++;
	    else if (oldtodel[i] < newtodel[j])  list[k++] = oldtodel[i++];
	    else /* can't be == */               list[k++] = newtodel[j++];
	 }
	 for ( ; i < olddeled; i++)
	    if (oldtodel[i] != -1) list[k++] = oldtodel[i];
	 for ( ; j < newdeled; j++)
	    if (newtodel[j] != -1) list[k++] = newtodel[j];

	 FREE(adesc->list); /* adesc->list */
	 FREE(oldtoadd); /* the old array->list */
      }
   }
}

/*===========================================================================*/

/*===========================================================================*\
 * This routine modifies a list of integers, by deleting those listed in
 * todel and adding those listed in toadd. todel is a subset of iarray,
 * toadd has no common element with iarray.
\*===========================================================================*/

void modify_list(array_desc *origad, array_desc *modad)
{
   int j, k, l, nextdel;
   int added = modad->added;
   int *toadd = modad->list;
   int deled = modad->size - modad->added;
   int *todel = toadd + added;
   int size = origad->size;
   int *origlist = origad->list;

   if (deled){
      /* l is the location where we copy to; and k is where we copy from */
      l = k = 0;
      for (j = 0; j < deled; k++, j++){
	 nextdel = todel[j];
	 for (; origlist[k] != nextdel; origlist[l++] = origlist[k++]);
      }
      for (; k < size; origlist[l++] = origlist[k++]);
      size = l;
   }
   if (added){
      /* add toadd to origlist */
      for (l = size+added-1, k = added-1, j = size-1; k >= 0 && j >= 0; ){
	 if (origlist[j] > toadd[k])
	    origlist[l--] = origlist[j--];
	 else
	    origlist[l--] = toadd[k--];
      }
      if (k >= 0)
	 memcpy(origlist, toadd, (k+1) * ISIZE);
      size += added;
   }
   origad->size = size;
}

/*===========================================================================*/

void modify_list_and_stat(array_desc *origad, int *origstat,
			  array_desc *modad, double_array_desc *moddad)
{
   int i, j, k, l, nextdel;
   int added = modad->added;
   int *toadd = modad->list;
   int deled = modad->size - modad->added;
   int *todel = toadd + added;
   int size = origad->size;
   int *origlist = origad->list;

   /* First modify origad, and at the same time delete the appropriate entries
      from origstat as well as insert phony ones where needed */

   if (deled){
      /* l is the location where we copy to; and k is where we copy from */
      l = k = 0;
      for (j = 0; j < deled; k++, j++){
	 nextdel = todel[j];
	 for (; origlist[k] != nextdel; ){
	    origstat[l] = origstat[k];
	    origlist[l++] = origlist[k++];
	 }
      }
      for (; k < size; ){
	 origstat[l] = origstat[k];
	 origlist[l++] = origlist[k++];
      }
      size = l;
   }
   if (added){
      /* add toadd to origlist */
      for (l = size+added-1, k = added-1, j = size-1; k >= 0 && j >= 0; )
	 if (origlist[j] > toadd[k]){
	    origstat[l] = origstat[j];
	    origlist[l--] = origlist[j--];
	 }else{
	    origstat[l] = INVALID_BASIS_STATUS;
	    origlist[l--] = toadd[k--];
	 }
      if (k >= 0){
	 for ( ; k >= 0; ){
	    origstat[l] = INVALID_BASIS_STATUS;
	    origlist[l--] = toadd[k--];
	 }
      }
      size += added;
   }
   origad->size = size;

#ifdef DO_TM_BASIS_TESTS
   if (origad->size == 0 && moddad->size > 0){
      printf("TM: Problem with storing the basis!!\n\n");
      exit(990);
   }
#endif

   /* Now adjust the basis stati */
   if (origad->size > 0 && moddad->size > 0){
      int *modlist = moddad->list;
      int *modstat = moddad->stat;

      for (i = moddad->size - 1, j = origad->size - 1; i >= 0 && j >= 0; j--){
	 if (origlist[j] == modlist[i])
	    origstat[j] = modstat[i--];
#ifdef DO_TM_BASIS_TESTS
	 else if (origlist[j] < modlist[i]){
	    printf("TM: Problem with storing the basis!!\n\n");
	    exit(990);
	 }
#endif
      }
#ifdef DO_TM_BASIS_TESTS
      if (i >= 0){
	 printf("TM: Problem with storing the basis!!\n\n");
	 exit(990);
      }
#endif
   }
#ifdef DO_TM_BASIS_TESTS
   for (j = origad->size - 1; j >= 0; j--){
      if (origstat[j] == INVALID_BASIS_STATUS){
	 printf("TM: Problem with storing the basis!!\n\n");
	 exit(990);
      }
   }
#endif
}

/*===========================================================================*/

/*===========================================================================*\
 * Do some tasks before phase 2. Thes are:
 * - price out variables in the root if requested
 * - build up the samephase_cand binary tree
 * - inform everyone about it
\*===========================================================================*/

int tasks_before_phase_two(tm_prob *tm)
{
#if !defined(COMPILE_IN_TM)||(defined(COMPILE_IN_TM)&&!defined(COMPILE_IN_LP))
   int s_bufid;
#endif
#ifdef COMPILE_IN_LP
   int num_threads = 1;
#else
   int r_bufid, msgtag, bytes, sender, not_fixed_size;
#endif
   int i;
   bc_node *n;
   int termcode = 0;

#ifdef COMPILE_IN_LP
#ifdef _OPENMP
   num_threads =  omp_get_num_threads();
#endif
   for (i = 0; i < num_threads; i++){
      free_node_desc(&tm->lpp[i]->desc);
      tm->lpp[i]->phase = 1;
   }
#else
   /* Notify the LPs about the start of the second phase and get back the
      timing data for the first phase */
   s_bufid = init_send(DataInPlace);
   msend_msg(tm->lp.procs, tm->lp.procnum, LP__SECOND_PHASE_STARTS);
#endif

   if (tm->par.price_in_root && tm->has_ub){
      /* put together a message and send it out to an LP process. At this
	 point tm->rootnode->{lp,cg,cp,sp} should still be set to wherever
	 it was first processed */
#ifdef DO_TESTS
      if ((!tm->rootnode->lp) ||
	  (!tm->rootnode->cg && tm->par.use_cg) ||
	 (!tm->rootnode->cp && tm->cp.procnum > 0)){
	 printf("When trying to send root for repricing, the root doesn't\n");
	 printf("   have some process id correctly set!\n\n");
	 exit(-100);
      }
#endif

      send_active_node(tm, tm->rootnode, COLGEN_REPRICING, 0);
   }

   /* trim the tree */
   tm->stat.leaves_before_trimming = tm->nextphase_candnum;
   if (tm->par.trim_search_tree && tm->has_ub)
      tm->stat.tree_size -= trim_subtree(tm, tm->rootnode);

   /* while the LP is working, build up the samephase_cand binary tree */
   REALLOC(tm->samephase_cand, bc_node *,
	   tm->samephase_cand_size, tm->nextphase_candnum + 1, BB_BUNCH);
   for (i = 0; i < tm->nextphase_candnum; i++){
      if ((n = tm->nextphase_cand[i])){
	 if (n->bc_index >= 0){
	    insert_new_node(tm, n);
	 }else{
	    free_tree_node(n);
	 }
      }
   }
   tm->stat.leaves_after_trimming = tm->samephase_candnum;

   if ((termcode = receive_lp_timing(tm)) < 0)
      return(SOMETHING_DIED);

   if (tm->par.price_in_root && tm->has_ub){
      /* receive what the LP has to say, what is the new not_fixed list.
       * also, incorporate that list into the not_fixed field of everything */
#ifdef COMPILE_IN_LP
      switch(process_chain(tm->lpp[0])){
	 
      case FUNCTION_TERMINATED_NORMALLY:
	 break;
	 
      case ERROR__NO_BRANCHING_CANDIDATE:
	 return(TM_ERROR__NO_BRANCHING_CANDIDATE);
	 
      case ERROR__ILLEGAL_RETURN_CODE:
	 return(TM_ERROR__ILLEGAL_RETURN_CODE);
	 
      case ERROR__NUMERICAL_INSTABILITY:
	 return(TM_ERROR__NUMERICAL_INSTABILITY);
	 
      case ERROR__USER:
	 return(TM_ERROR__USER);
	 
      }
#else
      char go_on;
      int nsize, nf_status;
      
      do{
	 r_bufid = receive_msg(tm->rootnode->lp, ANYTHING);
	 bufinfo(r_bufid, &bytes, &msgtag, &sender);
	 switch (msgtag){
	  case LP__NODE_DESCRIPTION:
	    n = (bc_node *) calloc(1, sizeof(bc_node));
	    receive_node_desc(tm, n);
	    tm->stat.root_lb = n->lower_bound;
	    if (n->node_status == NODE_STATUS__PRUNED){
	       /* Field day! Proved optimality! */
	       free_subtree(tm->rootnode);
	       tm->rootnode = n;
	       tm->samephase_candnum = tm->nextphase_candnum = 0;
	       return (FUNCTION_TERMINATED_NORMALLY);
	    }
	    /* Otherwise in 'n' we have the new description of the root node.
	       We don't care about the cuts, just the not fixed variables.
	       We got to pay attention to changes in uind and not_fixed.
	       We won't change the uind in root but put every change into
	       not_fixed.
	       The new not_fixed list will comprise of the vars added to uind
	       and whatever is in the new not_fixed. */
	    if (n->desc.uind.size > 0){
	       array_desc *uind = &n->desc.uind;
	       array_desc *ruind = &tm->rootnode->desc.uind;
	       int usize = uind->size;
	       int rusize = ruind->size;
	       int *ulist = uind->list;
	       int *rulist = ruind->list;
	       int j, k;
	       /* Kick out from uind those in root's uind */
	       for (i = 0, j = 0, k = 0; i < usize && j < rusize; ){
		  if (ulist[i] < rulist[j]){
		     /* a new element in uind */
		     ulist[k++] = ulist[i++];
		  }else if (ulist[i] < rulist[j]){
		     /* something got kicked out of ruind */
		     j++;
		  }else{ /* ulist[i] == rulist[j] */
		     /* It just stayed there peacefully */
		     i++; j++;
		  }
	       }
	       if (i < usize){
		  /* The rest are new */
		  for ( ; i < usize; i++, k++)
		     ulist[k] = ulist[i];
	       }

	       if ((usize = k) > 0){
		  if ((nsize = n->desc.not_fixed.size) == 0){
		     /* All we got is from uind */
		     n->desc.not_fixed.size = usize;
		     n->desc.not_fixed.list = ulist;
		     uind->list = NULL;
		  }else{
		     /* Now merge whatever is left in ulist with not_fixed.
			Note that the two lists are disjoint. */
		     int *not_fixed = (int *) malloc((usize + nsize) * ISIZE);
		     int *nlist = n->desc.not_fixed.list;
		     for (not_fixed_size=i=j=k=0; i < usize && j < nsize;
			  not_fixed_size++){
			if (ulist[i] < nlist[j]){
			   not_fixed[k++] = ulist[i++];
			}else if (ulist[i] > nlist[j]){
			   not_fixed[k++] = nlist[j++];
			}else{
			   not_fixed[k++] = nlist[j++];
			   i++;
			}
		     }
		     if (i < usize)
			memcpy(not_fixed+k, ulist+i, (usize-i)*ISIZE);
		     if (j < nsize)
			memcpy(not_fixed+k, nlist+j, (nsize-j)*ISIZE);
		     FREE(nlist);
		     n->desc.not_fixed.size = not_fixed_size;
		     n->desc.not_fixed.list = not_fixed;
		  }
	       }
	    }

	    /* OK, now every new thingy is in n->desc.not_fixed */
	    nsize = n->desc.not_fixed.size;
	    if (nsize == 0){
	       /* Field day! Proved optimality!
		  Caveats:
		  This proves optimality, but the current tree may not contain
		  this proof, since the cuts used in pricing out might be
		  different from those originally in the root.
		  For now just accept this sad fact and report optimality.
		  Later, when the tree could be written out on disk, take care
		  of writing out BOTH root descriptions to prove optimality.
		  FIXME */
	       if (tm->par.keep_description_of_pruned){
		  /* We got to write it out here. */
	       }
	       free_tree_node(n);
	       tm->samephase_candnum = tm->nextphase_candnum = 0;
	       return(FUNCTION_TERMINATED_NORMALLY);
	    }else{
	       tm->rootnode->desc.not_fixed.list = n->desc.not_fixed.list;
	       n->desc.not_fixed.list = NULL;
	       if (nsize > tm->par.not_fixed_storage_size){
		  tm->rootnode->desc.not_fixed.size =
		     tm->par.not_fixed_storage_size;
		  nf_status = NF_CHECK_AFTER_LAST;
	       }else{
		  tm->rootnode->desc.not_fixed.size = nsize;
		  nf_status = NF_CHECK_UNTIL_LAST;
	       }
	    }
	    propagate_nf_status(tm->rootnode, nf_status);
	    tm->stat.nf_status = nf_status;
	    tm->stat.vars_not_priced = tm->rootnode->desc.not_fixed.size;
	    free_tree_node(n);
	    go_on = FALSE;
	    break;

	  case UPPER_BOUND:
	    process_ub_message(tm);
	    go_on = TRUE;
	    break;

	  case LP__CUT_NAMES_REQUESTED:
	    unpack_cut_set(tm, sender, 0, NULL);
	    go_on = TRUE;
	    break;

	  default: /* We shouldn't get anything else */
	    printf("Unexpected message at repricing! (%i)\n\n", msgtag);
	    return(ERROR__COMM_ERROR);
	 }
      }while (go_on);
#endif
   }
   
#ifdef COMPILE_IN_TM
   if (tm->samephase_candnum > 0){
      printf( "\n");
      printf( "**********************************************\n");
      printf( "* Branch and Cut First Phase Finished!!!!    *\n");
      printf( "* Now displaying stats and best solution...  *\n");
      printf( "**********************************************\n\n");
      
      print_statistics(&(tm->comp_times), &(tm->stat), &(tm->lp_stat), 
                       tm->ub, tm->lb, 0,
		       tm->start_time, wall_clock(NULL),
		       tm->obj_offset,
		       tm->obj_sense, tm->has_ub,NULL, tm->par.output_mode);
   }
#else
   /* Report to the master all kind of statistics */
   s_bufid = init_send(DataInPlace);
   send_char_array((char *)&tm->comp_times, sizeof(node_times));
   send_char_array((char *)&tm->lp_stat, sizeof(lp_stat_desc));
   send_dbl_array(&tm->lb, 1);
   send_char_array((char *)&tm->stat, sizeof(tm_stat));
   send_msg(tm->master, TM_FIRST_PHASE_FINISHED);
#endif
   
   tm->nextphase_candnum = 0;

   return(FUNCTION_TERMINATED_NORMALLY);
}

/*===========================================================================*/

/*===========================================================================*\
 * For now we'll trim the tree only between phases when nothing is processed.
 * Reason: It's kinda ugly to cut off a subtree when several of its nodes
 * might be processed. Nevertheless, this should be done sooner or later.
 *
 * FIXME!
 *
\*===========================================================================*/

int trim_subtree(tm_prob *tm, bc_node *n)
{
   int i, deleted = 0, not_pruned = 0;

   /* Theer isn't anything to do if this is a leaf. */
   if (n->bobj.child_num == 0)
      return(0);

   /* There isn't anything to do if all children are pruned, and we are
      better off to go down if only one is not pruned. */
   for (i = n->bobj.child_num - 1; i >= 0; i--)
      if (n->children[i]->node_status != NODE_STATUS__PRUNED)
	 if (++not_pruned > 1)
	    break;
   if (not_pruned == 0)
      return(0);
   if (not_pruned == 1){
      for (i = n->bobj.child_num - 1; i >= 0; i--)
	 if (n->children[i]->node_status != NODE_STATUS__PRUNED){
	    deleted = trim_subtree(tm, n->children[i]);
	    break;
	 }
      return(deleted);
   }

   /* So there are at least two not pruned. */
   for (i = n->bobj.child_num - 1; i >= 0; i--)
      if (n->children[i]->lower_bound + tm->par.granularity < tm->ub)
	 break;

   /* if all children have high objval */
   if (i < 0){
      /* put back this node into the nodes_per_... stuff */
      if (tm->par.max_cp_num > 0 && n->cp)
#ifdef COMPILE_IN_CP
	 tm->nodes_per_cp[n->cp]++;
#else
	 tm->nodes_per_cp[find_process_index(&tm->cp, n->cp)]++;
#endif
      /* also put the node on the nextphase list */
      REALLOC(tm->nextphase_cand, bc_node *,
	      tm->nextphase_cand_size, tm->nextphase_candnum+1, BB_BUNCH);
      tm->nextphase_cand[tm->nextphase_candnum++] = n;
      /* get rid of the children */
      for (i = n->bobj.child_num - 1; i >= 0; i--)
	 deleted += mark_subtree(tm, n->children[i]);
      /* free the children description */
      FREE(n->children);
      n->bobj.child_num = 0;
#ifndef MAX_CHILDREN_NUM
      FREE(n->bobj.sense);
      FREE(n->bobj.rhs);
      FREE(n->bobj.range);
      FREE(n->bobj.branch);
#endif
      FREE(n->bobj.solutions); //added by asm4
   }else{
      /* try to trim every child */
      for (i = n->bobj.child_num - 1; i >= 0; i--)
	 deleted += trim_subtree(tm, n->children[i]);
   }
   return(deleted);
}

/*===========================================================================*/

int mark_subtree(tm_prob *tm, bc_node *n)
{
   int i, deleted = 0;
   /* mark the node trimmed */
   if (n->bobj.child_num == 0){
      /* if this was a leaf and it is not pruned,
	 delete it from the nodes_per_... stuff */
      if (n->node_status != NODE_STATUS__PRUNED){
	 if (tm->par.max_cp_num > 0 && n->cp){
#ifdef COMPILE_IN_CP
	    i = n->cp;
#else
	    i = find_process_index(&tm->cp, n->cp);
#endif
	    tm->nodes_per_cp[i]--;
	    if (tm->nodes_per_cp[i] + tm->active_nodes_per_cp[i] == 0)
	       tm->cp.free_ind[tm->cp.free_num++] = i;
	 }
	 n->bc_index = -1;
      }else{
	 /* if it was pruned already the free it now */
	 free_tree_node(n);
      }
   }else{
      /* if non-leaf then process the children recursively and then free
         the node */
      for (i = n->bobj.child_num - 1; i >= 0; i--)
	 deleted += mark_subtree(tm, n->children[i]);
      free_tree_node(n);
   }
   return(++deleted);
}

/*===========================================================================*/

void propagate_nf_status(bc_node *n, int nf_status)
{
   int i;

   for (i = n->bobj.child_num - 1; i >= 0; i--)
      propagate_nf_status(n->children[i], nf_status);
   n->desc.nf_status = nf_status;
}


/*===========================================================================*/

/*===========================================================================*\
 * The functions below are for logging. They write out all necessary
 * data to a file so that a warmstart can be made.
\*===========================================================================*/

int write_node(bc_node *node, char *file, FILE* f, char append)
{
   int i;
   char close = FALSE;
   
   if (!f){
      if (!(f = fopen(file, append ? "a" : "w"))){
	 printf("\nError opening node file\n\n");
	 return(0);
      }
      close = TRUE;
   }

   if (append)
      fprintf(f, "\n");
   fprintf(f, "NODE INDEX:      %i\n", node->bc_index);
   fprintf(f, "NODE LEVEL:      %i\n", node->bc_level);
   fprintf(f, "LOWER BOUND:     %f\n", node->lower_bound);
   fprintf(f, "NODE STATUS:     %i\n", (int)node->node_status);
#ifdef TRACE_PATH
   fprintf(f, "OPTIMAL PATH:    %i\n", (int)node->optimal_path);
#endif
   if (node->parent)
      fprintf(f, "PARENT INDEX:    %i\n", node->parent->bc_index);
   else
      fprintf(f, "PARENT INDEX:    -1\n");
   fprintf(f, "CHILDREN:        %i %i %i\n", (int)node->bobj.type,
	   node->bobj.name, node->bobj.child_num);
   for (i = 0; i < node->bobj.child_num; i++)
      fprintf(f, "%i %c %f %f %i\n", node->children[i]->bc_index,
	      node->bobj.sense[i], node->bobj.rhs[i],
	      node->bobj.range[i], node->bobj.branch[i]);
   fprintf(f, "NODE DESCRIPTION: %i\n", node->desc.nf_status);
   fprintf(f, "USER INDICES:    %i %i %i\n", (int)node->desc.uind.type,
	   node->desc.uind.size, node->desc.uind.added);
   for (i = 0; i < node->desc.uind.size; i++)
      fprintf(f, "%i\n", node->desc.uind.list[i]);
   fprintf(f, "NOT FIXED:       %i %i %i\n", (int)node->desc.not_fixed.type,
	   node->desc.not_fixed.size, node->desc.not_fixed.added);
   for (i = 0; i < node->desc.not_fixed.size; i++)
      fprintf(f, "%i\n", node->desc.not_fixed.list[i]);
   fprintf(f, "CUT INDICES:     %i %i %i\n", (int)node->desc.cutind.type,
	   node->desc.cutind.size, node->desc.cutind.added);
   for (i = 0; i < node->desc.cutind.size; i++)
      fprintf(f, "%i\n", node->desc.cutind.list[i]);
   fprintf(f, "BASIS: %i\n", (int)node->desc.basis.basis_exists);
   fprintf(f, "BASE VARIABLES:  %i %i\n", (int)node->desc.basis.basevars.type,
	   node->desc.basis.basevars.size);
   if (node->desc.basis.basevars.type == WRT_PARENT)
      for (i = 0; i < node->desc.basis.basevars.size; i++)
	 fprintf(f, "%i %i\n", node->desc.basis.basevars.list[i],
		 node->desc.basis.basevars.stat[i]);
   else
      for (i = 0; i < node->desc.basis.basevars.size; i++)
	 fprintf(f, "%i\n", node->desc.basis.basevars.stat[i]);
   fprintf(f, "EXTRA VARIABLES: %i %i\n", (int)node->desc.basis.extravars.type,
	   node->desc.basis.extravars.size);
   if (node->desc.basis.extravars.type == WRT_PARENT)
      for (i = 0; i < node->desc.basis.extravars.size; i++)
	 fprintf(f, "%i %i\n", node->desc.basis.extravars.list[i],
		 node->desc.basis.extravars.stat[i]);
   else
      for (i = 0; i < node->desc.basis.extravars.size; i++)
	 fprintf(f, "%i\n", node->desc.basis.extravars.stat[i]);
   fprintf(f, "BASE ROWS:       %i %i\n", (int)node->desc.basis.baserows.type,
	   node->desc.basis.baserows.size);
   if (node->desc.basis.baserows.type == WRT_PARENT)
      for (i = 0; i < node->desc.basis.baserows.size; i++)
	 fprintf(f, "%i %i\n", node->desc.basis.baserows.list[i],
		 node->desc.basis.baserows.stat[i]);
   else
      for (i = 0; i < node->desc.basis.baserows.size; i++)
	 fprintf(f, "%i\n", node->desc.basis.baserows.stat[i]);
   fprintf(f, "EXTRA ROWS:      %i %i\n", (int)node->desc.basis.extrarows.type,
	   node->desc.basis.extrarows.size);
   if (node->desc.basis.extrarows.type == WRT_PARENT)
      for (i = 0; i < node->desc.basis.extrarows.size; i++)
	 fprintf(f, "%i %i\n", node->desc.basis.extrarows.list[i],
		 node->desc.basis.extrarows.stat[i]);
   else
      for (i = 0; i < node->desc.basis.extrarows.size; i++)
	 fprintf(f, "%i\n", node->desc.basis.extrarows.stat[i]);
      
   if (close)
      fclose(f);

   return(1);
}

/*===========================================================================*/

int read_node(tm_prob *tm, bc_node *node, FILE *f, int **children)
{
   int i, parent = 0, tmp = 0;
   char str1[10], str2[10];

   if (f){
      fscanf(f, "%s %s %i", str1, str2, &node->bc_index);
      fscanf(f, "%s %s %i", str1, str2, &node->bc_level);
      fscanf(f, "%s %s %lf", str1, str2, &node->lower_bound);
      fscanf(f, "%s %s %i", str1, str2, &tmp);
      node->node_status = (char)tmp;
#ifdef TRACE_PATH
      fscanf(f, "%s %s %i\n", str1, str2, &tmp);
      node->optimal_path = (char)tmp;
#endif
      fscanf(f, "%s %s %i", str1, str2, &parent);
      fscanf(f, "%s %i %i %i", str1, &tmp,
	     &node->bobj.name, &node->bobj.child_num);
      node->bobj.type = (char)tmp;
      if (node->bobj.child_num){
#ifndef MAX_CHILDREN_NUM
	 node->bobj.sense = malloc(node->bobj.child_num*sizeof(char));
	 node->bobj.rhs = (double *) malloc(node->bobj.child_num*DSIZE);
	 node->bobj.range = (double *) malloc(node->bobj.child_num*DSIZE);
	 node->bobj.branch = (int *) malloc(node->bobj.child_num*ISIZE);
#endif
	 *children = (int *) malloc(node->bobj.child_num*ISIZE);
	 for (i = 0; i < node->bobj.child_num; i++)
	    fscanf(f, "%i %c %lf %lf %i", *children+i, node->bobj.sense+i,
		   node->bobj.rhs+i, node->bobj.range+i, node->bobj.branch+i);
      }
      fscanf(f, "%s %s %i", str1, str2, &node->desc.nf_status);
      fscanf(f, "%s %s %i %i %i", str1, str2, &tmp, &node->desc.uind.size,
	     &node->desc.uind.added);
      node->desc.uind.type = (char)tmp;
      if (node->desc.uind.size){
	 node->desc.uind.list = (int *) malloc(node->desc.uind.size*ISIZE);
	 for (i = 0; i < node->desc.uind.size; i++)
	    fscanf(f, "%i", node->desc.uind.list+i);
      }
      fscanf(f, "%s %s %i %i %i", str1, str2, &tmp,
	     &node->desc.not_fixed.size, &node->desc.not_fixed.added);
      node->desc.not_fixed.type = (char)tmp;
      if (node->desc.not_fixed.size){
	 node->desc.not_fixed.list =
	    (int *) malloc(node->desc.not_fixed.size*ISIZE);
	 for (i = 0; i < node->desc.not_fixed.size; i++)
	    fscanf(f, "%i", node->desc.not_fixed.list+i);
      }
      fscanf(f, "%s %s %i %i %i", str1, str2, &tmp,
	     &node->desc.cutind.size, &node->desc.cutind.added);
      node->desc.cutind.type = (char)tmp;
      if (node->desc.cutind.size){
	 node->desc.cutind.list = (int *) malloc(node->desc.cutind.size*ISIZE);
	 for (i = 0; i < node->desc.cutind.size; i++)
	    fscanf(f, "%i", node->desc.cutind.list+i);
      }
      fscanf(f, "%s %i", str1, &tmp);
      node->desc.basis.basis_exists = (char)tmp;
      fscanf(f, "%s %s %i %i", str1, str2, &tmp,
	     &node->desc.basis.basevars.size);
      node->desc.basis.basevars.type = (char)tmp;
      if (node->desc.basis.basevars.size){
	 node->desc.basis.basevars.stat =
	    (int *) malloc(node->desc.basis.basevars.size*ISIZE);
	 if (node->desc.basis.basevars.type == WRT_PARENT){
	    node->desc.basis.basevars.list = 
	       (int *) malloc(node->desc.basis.basevars.size*ISIZE);   
	    for (i = 0; i < node->desc.basis.basevars.size; i++)
	       fscanf(f, "%i %i", node->desc.basis.basevars.list+i,
		      node->desc.basis.basevars.stat+i);
	 }else{
	    for (i = 0; i < node->desc.basis.basevars.size; i++)
	       fscanf(f, "%i", node->desc.basis.basevars.stat+i);
	 }
      }
      fscanf(f, "%s %s %i %i", str1, str2, &tmp,
	     &node->desc.basis.extravars.size);
      node->desc.basis.extravars.type = (char)tmp;
      if (node->desc.basis.extravars.size){
	 node->desc.basis.extravars.stat =
	    (int *) malloc(node->desc.basis.extravars.size*ISIZE);
	 if (node->desc.basis.extravars.type == WRT_PARENT){
	    node->desc.basis.extravars.list = 
	       (int *) malloc(node->desc.basis.extravars.size*ISIZE);   
	    for (i = 0; i < node->desc.basis.extravars.size; i++)
	       fscanf(f, "%i %i", node->desc.basis.extravars.list+i,
		      node->desc.basis.extravars.stat+i);
	 }else{
	    for (i = 0; i < node->desc.basis.extravars.size; i++)
	       fscanf(f, "%i", node->desc.basis.extravars.stat+i);
	 }
      }
      fscanf(f, "%s %s %i %i", str1, str2, &tmp,
	     &node->desc.basis.baserows.size);
      node->desc.basis.baserows.type = (char)tmp;
      if (node->desc.basis.baserows.size){
	 node->desc.basis.baserows.stat =
	    (int *) malloc(node->desc.basis.baserows.size*ISIZE);
	 if (node->desc.basis.baserows.type == WRT_PARENT){
	    node->desc.basis.baserows.list = 
	       (int *) malloc(node->desc.basis.baserows.size*ISIZE);   
	    for (i = 0; i < node->desc.basis.baserows.size; i++)
	       fscanf(f, "%i %i", node->desc.basis.baserows.list+i,
		      node->desc.basis.baserows.stat+i);
	 }else{
	    for (i = 0; i < node->desc.basis.baserows.size; i++)
	       fscanf(f, "%i", node->desc.basis.baserows.stat+i);
	 }
      }
      fscanf(f, "%s %s %i %i", str1, str2, &tmp,
	     &node->desc.basis.extrarows.size);
      node->desc.basis.extrarows.type = (char)tmp;
      if (node->desc.basis.extrarows.size){
	 node->desc.basis.extrarows.stat =
	    (int *) malloc(node->desc.basis.extrarows.size*ISIZE);
	 if (node->desc.basis.extrarows.type == WRT_PARENT){
	    node->desc.basis.extrarows.list = 
	       (int *) malloc(node->desc.basis.extrarows.size*ISIZE);   
	    for (i = 0; i < node->desc.basis.extrarows.size; i++)
	       fscanf(f, "%i %i", node->desc.basis.extrarows.list+i,
		      node->desc.basis.extrarows.stat+i);
	 }else{
	    for (i = 0; i < node->desc.basis.extrarows.size; i++)
	       fscanf(f, "%i", node->desc.basis.extrarows.stat+i);
	 }
      }
   }
   
   switch (node->node_status){
    case NODE_STATUS__HELD:
      REALLOC(tm->nextphase_cand, bc_node *,
	      tm->nextphase_cand_size, tm->nextphase_candnum+1, BB_BUNCH);
      tm->nextphase_cand[tm->nextphase_candnum++] = node;
      /* update the nodes_per_... stuff */
      /* the active_nodes_per_... will be updated when the LP__IS_FREE
	 message comes */
      if (node->cp)
#ifdef COMPILE_IN_CP
	 tm->nodes_per_cp[node->cp]++;
#else
	 tm->nodes_per_cp[find_process_index(&tm->cp, node->cp)]++;
#endif
      break;
    case NODE_STATUS__ROOT:
      tm->rootnode = node;
      break;
    case NODE_STATUS__WARM_STARTED:
    case NODE_STATUS__CANDIDATE:
      insert_new_node(tm, node);
      break;
   }

   return(parent);
}

/*===========================================================================*/
   
int write_subtree(bc_node *root, char *file, FILE *f, char append, int logging)
{
   int i;
   char close = FALSE;
   
   if (!f){
      if (!(f = fopen(file, append ? "a" : "w"))){
	 printf("\nError opening subtree file\n\n");
	 return(0);
      }
      close = TRUE;
   }

   if (logging == VBC_TOOL){
      if (root->parent)
	 fprintf(f, "%i %i\n", root->parent->bc_index + 1, root->bc_index + 1);
   }else{
      write_node(root, file, f, append);
   }
   for (i = 0; i < root->bobj.child_num; i++)
      write_subtree(root->children[i], file, f, TRUE, logging);

   if (close)
      fclose(f);
	 
   return(1);
}

/*===========================================================================*/

int read_subtree(tm_prob *tm, bc_node *root, FILE *f)
{
   int parent, i;
   int *children;
   
   parent = read_node(tm, root, f, &children);
   if (f && root->bobj.child_num){
      root->children = (bc_node **)
	 malloc(root->bobj.child_num*sizeof(bc_node *));
      for (i = 0; i < root->bobj.child_num; i++){
	 root->children[i] = (bc_node *) calloc(1, sizeof(bc_node));
	 root->children[i]->parent = root;
      }
   }
   for (i = 0; i < root->bobj.child_num; i++){
      read_subtree(tm, root->children[i], f);
   }

   return(parent);
}
			 
/*===========================================================================*/

int write_tm_cut_list(tm_prob *tm, char *file, char append)
{
   FILE *f;
   int i, j;

   if (!(f = fopen(file, append ? "a" : "w"))){
      printf("\nError opening cut file\n\n");
      return(0);
   }

   fprintf(f, "CUTNUM: %i %i\n", tm->cut_num, tm->allocated_cut_num);
   for (i = 0; i < tm->cut_num; i++){
      fprintf(f, "%i %i %i %c %i %f %f\n", tm->cuts[i]->name,
	      tm->cuts[i]->size, (int)tm->cuts[i]->type, tm->cuts[i]->sense,
	      (int)tm->cuts[i]->branch, tm->cuts[i]->rhs, tm->cuts[i]->range);
      for (j = 0; j < tm->cuts[i]->size; j++)
	 fprintf(f, "%i ", (int)tm->cuts[i]->coef[j]);
      fprintf(f, "\n");
   }

   fclose(f);

   return(1);
}

/*===========================================================================*/

int read_tm_cut_list(tm_prob *tm, char *file)
{
   FILE *f;
   int i, j, tmp1 = 0, tmp2 = 0;
   char str[20];
   
   if (!(f = fopen(file, "r"))){
      printf("\nError opening cut file\n\n");
      return(0);
   }

   fscanf(f, "%s %i %i", str, &tm->cut_num, &tm->allocated_cut_num);
   tm->cuts = (cut_data **) malloc(tm->allocated_cut_num*sizeof(cut_data *));
   for (i = 0; i < tm->cut_num; i++){
      tm->cuts[i] = (cut_data *) malloc(sizeof(cut_data));
      fscanf(f, "%i %i %i %c %i %lf %lf", &tm->cuts[i]->name,
	     &tm->cuts[i]->size, &tmp1, &tm->cuts[i]->sense,
	     &tmp2, &tm->cuts[i]->rhs, &tm->cuts[i]->range);
      tm->cuts[i]->type = (char)tmp1;
      tm->cuts[i]->branch = (char)tmp2;
      tm->cuts[i]->coef = (char *) malloc(tm->cuts[i]->size*sizeof(char));
      for (j = 0; j < tm->cuts[i]->size; j++){
	 fscanf(f, "%i ", &tmp1);
	 tm->cuts[i]->coef[j] = (char)tmp1;
      }
   }

   fclose(f);

   return(1);
}   

/*===========================================================================*/

int write_tm_info(tm_prob *tm, char *file, FILE* f, char append)
{
   char close = FALSE;
   
   if (!f){
      if (!(f = fopen(file, append ? "a" : "w"))){
	 printf("\nError opening TM info file\n\n");
	 return(0);
      }
      close = TRUE;
   }

   if (tm->par.logging == VBC_TOOL){
      fprintf(f, "#TYPE: COMPLETE TREE\n");
      fprintf(f, "#TIME: NOT\n");
      fprintf(f, "#BOUNDS: NONE\n");
      fprintf(f, "#INFORMATION: EXCEPTION\n");
      fprintf(f, "#NODE_NUMBER: NONE\n");
      if (close)
	 fclose(f);
      
      return(1);
   }
   
   fprintf(f, "UPPER BOUND: ");
   if (tm->has_ub)
      fprintf(f, "   %f\n", tm->ub);
   else
      fprintf(f, "\n");
   fprintf(f, "LOWER BOUND:    %f\n", tm->lb);
   fprintf(f, "PHASE:          %i\n", tm->phase);
   fprintf(f, "ROOT LB:        %f\n", tm->stat.root_lb);
   fprintf(f, "MAX DEPTH:      %i\n", tm->stat.max_depth);
   fprintf(f, "CHAINS:         %i\n", tm->stat.chains);
   fprintf(f, "DIVING HALTS:   %i\n", tm->stat.diving_halts);
   fprintf(f, "TREE SIZE:      %i\n", tm->stat.tree_size);
   fprintf(f, "NODES CREATED:  %i\n", tm->stat.created);
   fprintf(f, "NODES ANALYZED: %i\n", tm->stat.analyzed);
   fprintf(f, "LEAVES BEFORE:  %i\n", tm->stat.leaves_before_trimming);
   fprintf(f, "LEAVES AFTER:   %i\n", tm->stat.leaves_after_trimming);
   fprintf(f, "NF STATUS:      %i\n", (int)tm->stat.nf_status);
   fprintf(f, "TIMING:\n");
   fprintf(f, " COMM:          %f\n", tm->comp_times.communication);
   fprintf(f, " LP:            %f\n", tm->comp_times.lp);
   fprintf(f, " SEPARATION:    %f\n", tm->comp_times.separation);
   fprintf(f, " FIXING:        %f\n", tm->comp_times.fixing);
   fprintf(f, " PRICING:       %f\n", tm->comp_times.pricing);
   fprintf(f, " BRANCHING:     %f\n", tm->comp_times.strong_branching);
   fprintf(f, " CUT POOL:      %f\n", tm->comp_times.cut_pool);
   fprintf(f, " REAL TIME:     %f\n", wall_clock(NULL) - tm->start_time);
   
   if (close)
      fclose(f);
   
   return(1);
}

/*===========================================================================*/

int read_tm_info(tm_prob *tm, FILE *f)
{
   char str1[20], str2[20];
   int tmp = 0;
   double previous_elapsed_time = 0;

   if (!f)
      return(0);
      
   fscanf(f, "%s %s", str1, str2);
   if (fscanf(f, "%lf", &tm->ub) != 0)
      tm->has_ub = TRUE;
   fscanf(f, "%s %s %lf", str1, str2, &tm->lb);
   fscanf(f, "%s %i", str1, &tm->phase);
   fscanf(f, "%s %s %lf", str1, str2, &tm->stat.root_lb);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.max_depth);
   fscanf(f, "%s %i", str1, &tm->stat.chains);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.diving_halts);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.tree_size);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.created);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.analyzed);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.leaves_before_trimming);
   fscanf(f, "%s %s %i", str1, str2, &tm->stat.leaves_after_trimming);
   fscanf(f, "%s %s %i", str1, str2, &tmp);
   tm->stat.nf_status = (char)tmp;
   fscanf(f, "%s", str1);
   fscanf(f, "%s %lf", str1, &tm->comp_times.communication);
   fscanf(f, "%s %lf", str1, &tm->comp_times.lp);
   fscanf(f, "%s %lf", str1, &tm->comp_times.separation);
   fscanf(f, "%s %lf", str1, &tm->comp_times.fixing);
   fscanf(f, "%s %lf", str1, &tm->comp_times.pricing);
   fscanf(f, "%s %lf", str1, &tm->comp_times.strong_branching);
   fscanf(f, "%s %s %lf", str1, str2, &tm->comp_times.cut_pool);
   fscanf(f, "%s %s %lf\n", str1, str2, &previous_elapsed_time);
   tm->start_time -= previous_elapsed_time;

   return(1);
}

/*===========================================================================*/

int write_base(base_desc *base, char *file, FILE *f, char append)
{
   int i;
   char close = FALSE;
   
   if (!f){
      if (!(f = fopen(file, append ? "a" : "w"))){
	 printf("\nError opening base file\n\n");
	 return(0);
      }
      close = TRUE;
   }

   fprintf(f, "BASE DESCRIPTION: %i %i\n", base->varnum, base->cutnum);
   for (i = 0; i < base->varnum; i++)
      fprintf(f, "%i\n", base->userind[i]);

   if (close)
      fclose(f);

   return(1);
}

/*===========================================================================*/

int read_base(base_desc *base, FILE *f)
{
   char str1[20], str2[20];
   int i;
   
   fscanf(f, "%s %s %i %i", str1, str2, &base->varnum, &base->cutnum);
   base->userind = (int *) malloc(base->varnum*ISIZE);
   for (i = 0; i < base->varnum; i++)
      fscanf(f, "%i", base->userind+i);

   return(1);
}

/*===========================================================================*/

/*===========================================================================*\
 * Cleanup. Free the datastructure.
\*===========================================================================*/

void free_tm(tm_prob *tm)
{
   int i;
   cut_data **cuts = tm->cuts;
#ifdef _OPENMP
   int num_threads = tm->par.max_active_nodes;
#else
   int num_threads = 1;
#endif
   
#if defined(COMPILE_IN_TM) && defined(COMPILE_IN_LP)
   for (i = 0; i < num_threads; i++)
      free_lp(tm->lpp[i]);
   FREE(tm->lpp);
#ifdef COMPILE_IN_CG
   FREE(tm->cgp);
#endif
#endif
   
   if (tm->par.lp_machs){
      FREE(tm->par.lp_machs[0]);
      FREE(tm->par.lp_machs);
   }
   if (tm->par.cg_machs){
      FREE(tm->par.cg_machs[0]);
      FREE(tm->par.cg_machs);
   }
   if (tm->par.cp_machs){
      FREE(tm->par.cp_machs[0]);
      FREE(tm->par.cp_machs);
   }
   FREE(tm->lp.procs);
   FREE(tm->lp.free_ind);
   FREE(tm->cg.procs);
   FREE(tm->cg.free_ind);
   FREE(tm->cp.procs);
   FREE(tm->cp.free_ind);
   FREE(tm->nodes_per_cp);
   FREE(tm->active_nodes_per_cp);

   FREE(tm->active_nodes);
   FREE(tm->samephase_cand);
   FREE(tm->nextphase_cand);
   FREE(tm->termcodes);

#ifndef COMPILE_IN_TM
   /* Go over the tree and free the nodes */
   free_subtree(tm->rootnode);
#endif
   
   /* Go over the cuts stored and free them all */
#pragma omp critical (cut_pool)
   if (cuts){
      for (i = tm->cut_num - 1; i >= 0; i--)
	 if (cuts[i]){
	    FREE(cuts[i]->coef);
	    FREE(cuts[i]);
	 }
      FREE(tm->cuts);
   }

#pragma omp critical (tmp_memory)
   {
      FREE(tm->tmp.i);
      FREE(tm->tmp.c);
      FREE(tm->tmp.d);
   }

   /*get rid of the added pointers for sens.analysis*/

   for (i = 0; i < num_threads; i++){
      if(tm->rpath[i])
	 if(tm->rpath[i][0])
	    tm->rpath[i][0] = NULL;
      FREE(tm->bpath[i]);
      FREE(tm->rpath[i]);
   }

   FREE(tm->rpath);
   FREE(tm->rpath_size);
   FREE(tm->bpath);
   FREE(tm->bpath_size);

   if (tm->reduced_costs) {
      for (i=0; i<tm->reduced_costs->num_rcs; i++) {
         FREE(tm->reduced_costs->indices[i]);
         FREE(tm->reduced_costs->values[i]);
         FREE(tm->reduced_costs->lb[i]);
         FREE(tm->reduced_costs->ub[i]);
      }
      FREE(tm->reduced_costs->indices);
      FREE(tm->reduced_costs->values);
      FREE(tm->reduced_costs->lb);
      FREE(tm->reduced_costs->ub);
      FREE(tm->reduced_costs->cnt);
      FREE(tm->reduced_costs->obj);
      FREE(tm->reduced_costs);
   }

   if (tm->pcost_down) {
      FREE(tm->pcost_down);
      FREE(tm->pcost_up);
      FREE(tm->br_rel_down);
      FREE(tm->br_rel_up);
      FREE(tm->br_rel_cand_list);
      FREE(tm->br_rel_down_min_level);
      FREE(tm->br_rel_up_min_level);
   }
   FREE(tm->br_inf_down);
   FREE(tm->br_inf_up);
   FREE(tm->var_rank);
   FREE(tm->root_lp);
   
   FREE(tm);
}

/*===========================================================================*/

void free_subtree(bc_node *n)
{
   int i;

   if (n == NULL) return;
   
   for (i = n->bobj.child_num - 1; i >= 0; i--)
      free_subtree(n->children[i]);
   free_tree_node(n);
}

/*===========================================================================*/

void free_tree_node(bc_node *n)
{

   FREE(n->sol);
   FREE(n->sol_ind);
#ifdef SENSITIVITY_ANALYSIS
   FREE(n->duals);
   //Anahita
   FREE(n->duals);
   //Ted
   FREE(n->dj);
#endif
   FREE(n->children);

   for(int i=0; i < n->bobj.child_num; i++){
      FREE(n->bobj.sos_ind[i]);
   }

#ifndef MAX_CHILDREN_NUM
   FREE(n->bobj.sense);
   FREE(n->bobj.rhs);
   FREE(n->bobj.range);
   FREE(n->bobj.branch);
   FREE(n->bobj.sos_cnt);
   FREE(n->bobj.sos_ind);
#endif
   // Anahita
   //FREE(n->bobj.solutions); //added by asm4

   FREE(n->desc.uind.list);
   free_basis(&n->desc.basis);
   FREE(n->desc.not_fixed.list);
   FREE(n->desc.cutind.list);
   FREE(n->desc.desc);
   if (n->desc.bnd_change) {
      FREE(n->desc.bnd_change->index);
      FREE(n->desc.bnd_change->lbub);
      FREE(n->desc.bnd_change->value);
      FREE(n->desc.bnd_change);
   }

   if(n->desc.frac_vars) FREE(n->desc.frac_vars);

   FREE(n);
}

/*===========================================================================*/

void free_basis(basis_desc *basis)
{
   FREE(basis->basevars.list);
   FREE(basis->basevars.stat);
   FREE(basis->extravars.list);
   FREE(basis->extravars.stat);
   FREE(basis->baserows.list);
   FREE(basis->baserows.stat);
   FREE(basis->extrarows.list);
   FREE(basis->extrarows.stat);
}

/*===========================================================================*/

/*===========================================================================*\
 * This function shuts down the treemanager
\*===========================================================================*/

int tm_close(tm_prob *tm, int termcode)
{
#ifndef COMPILE_IN_TM
   int s_bufid;
#endif
#ifdef COMPILE_IN_LP
   lp_prob **lp = tm->lpp;
#endif
#ifndef COMPILE_IN_CP
   int r_bufid = 0, new_cuts;
   struct timeval timeout = {5, 0};
   double new_time;
#endif
   int i;
   
#if defined(DO_TESTS) && 0
   if (tm->cp.free_num != tm->cp.procnum)
      printf(" Something is fishy! tm->cp.freenum != tm->cp.procnum\n");
#endif

   if (tm->par.vbc_emulation == VBC_EMULATION_LIVE){
      printf("$#END_OF_OUTPUT");
   }
      
   /*------------------------------------------------------------------------*\
    * Kill the processes. Some of them will send back statistics.
   \*------------------------------------------------------------------------*/
#ifndef COMPILE_IN_LP
   stop_processes(&tm->lp);
#endif
#ifndef COMPILE_IN_CG
   stop_processes(&tm->cg);
#endif
#ifndef COMPILE_IN_CP
   stop_processes(&tm->cp);
#endif

   /*------------------------------------------------------------------------*\
    * Receive statistics from the cutpools
   \*------------------------------------------------------------------------*/
#ifdef COMPILE_IN_CP
   if (tm->cpp){
      for (i = 0; i < tm->par.max_cp_num; i++){
	 tm->comp_times.cut_pool += tm->cpp[i]->cut_pool_time;
	 tm->stat.cuts_in_pool += tm->cpp[i]->cut_num;
      	 if (!tm->par.keep_cut_pools){
      	    tm->cpp[i]->msgtag = YOU_CAN_DIE;
      	    cp_close(tm->cpp[i]);
      	 }else{
      	    tm->cpp[i]->cut_pool_time = 0;
      	 }
      }
      if (!tm->par.keep_cut_pools){
      	 FREE(tm->cpp);
      }
   }
#else
   for (i = 0; i < tm->par.max_cp_num;){
      r_bufid = treceive_msg(tm->cp.procs[i], POOL_TIME, &timeout);
      if (r_bufid > 0){
	 receive_dbl_array(&new_time, 1);
	 receive_int_array(&new_cuts, 1);
	 tm->comp_times.cut_pool += new_time;
	 tm->stat.cuts_in_pool += new_cuts;
	 i++;
      }else{
	 if (pstat(tm->cp.procs[i]) != PROCESS_OK)
	    i++;
      }
   }
   freebuf(r_bufid);
#endif
   /* Receive timing from the LPs */

   if (receive_lp_timing(tm) < 0){
      printf("\nWarning: problem receiving LP timing. LP process is dead\n\n");
   }
   
#ifdef COMPILE_IN_LP
   for (i = 0; i < tm->par.max_active_nodes; i ++){
      lp_close(lp[i]);
   }
#endif
   
   tm->stat.root_lb = tm->rootnode->lower_bound;
   find_tree_lb(tm);
   return(termcode);

#ifndef COMPILE_IN_TM
   /*------------------------------------------------------------------------*\
    * Send back the statistics to the master
   \*------------------------------------------------------------------------*/

   s_bufid = init_send(DataInPlace);
   send_char_array((char *)&tm->comp_times, sizeof(node_times));
   send_char_array((char *)&tm->lp_stat, sizeof(lp_stat_desc));
   send_dbl_array(&tm->lb, 1);
   send_char_array((char *)&tm->stat, sizeof(tm_stat));
   send_msg(tm->master, termcode);
   freebuf(s_bufid);

   free_tm(tm);
   
#endif
   
   return(termcode);
}   
   
/*===========================================================================*/
/*===========================================================================*/

#if !defined(_MSC_VER) && !defined(__MNO_CYGWIN) && defined(SIGHANDLER)
void sym_catch_c(int num)
{

   sigset_t mask_set;
   sigset_t old_set;

   /* SIGTSTP ? */
   signal(SIGINT, sym_catch_c);

   char temp [MAX_LINE_LENGTH + 1];
   
   sigfillset(&mask_set);
   sigprocmask(SIG_SETMASK, &mask_set, &old_set);
   
   strcpy(temp, "");
   fprintf(stderr, "\nDo you want to abort immediately, exit gracefully (from the current solve call only), or continue? [a/e/c]: ");
   fflush(stderr);   
   fgets(temp, MAX_LINE_LENGTH, stdin);
   if(temp[1] == '\n' && (temp[0] == 'a' || temp[0] == 'A')){
      fprintf(stderr, "\nTerminating...\n");
      fflush(stderr);
      exit(0);
   }else if(temp[1] == '\n' && (temp[0] == 'e' || temp[0] == 'E')){    
      c_count++;	    
   } else{
      fprintf(stderr, "\nContinuing...\n");
      fflush(stderr);
      c_count = 0;      
   }

}
#endif

/*===========================================================================*/
/*
 * Find the lowerbound of the current branch-and-cut tree and save it in
 * tm->lb
 */

int find_tree_lb(tm_prob *tm)
{
   double lb = SYM_INFINITY;
   bc_node **samephase_cand;

#pragma omp critical (tree_update)
{
   if (tm->samephase_candnum > 0 || tm->active_node_num > 0) {
      if (tm->samephase_candnum > 0){
	 if (tm->par.node_selection_rule == LOWEST_LP_FIRST) {
	    lb = tm->samephase_cand[1]->lower_bound; /* [0] is a dummy */
#ifdef DO_TESTS
	    for (int i = tm->samephase_candnum; i >= 1; i--){
               //printf("Node %i, LB: %f\n", tm->samephase_cand[i]->bc_index,
               //     tm->samephase_cand[i]->lower_bound);
               if ((2*i <= tm->samephase_candnum &&
                    tm->samephase_cand[i]->lower_bound >
                    tm->samephase_cand[2*i]->lower_bound) ||
                   (2*i+1 <= tm->samephase_candnum &&
                    tm->samephase_cand[i]->lower_bound >
                    tm->samephase_cand[2*i+1]->lower_bound)){
                  printf("#####Warning: heap corruption detected\n");
               }
	    }
#endif
	 } else {
	    samephase_cand = tm->samephase_cand;
	    for (int i = tm->samephase_candnum; i >= 1; i--){
	       lb = MIN(lb, samephase_cand[i]->lower_bound);
	    }
	 }
      }
#ifdef COMPILE_IN_LP
      for (int i = tm->par.max_active_nodes - 1; i >= 0; i--){
	 if (tm->active_nodes[i]){
	    lb = MIN(lb, tm->active_nodes[i]->lower_bound);
	 }
      }
#endif
   } else {
      /* there are no more nodes left. */
      lb = tm->ub;
   }
   /*
   if (lb >= MAXDOUBLE / 2){ 
      lb = tm->ub;
   }
   */
#ifdef DO_TESTS
   if (lb < tm->lb - .001 && tm->stat.analyzed > 1){
      printf("#####Warning: global lower bound decrease detected: %f\n",
             tm->lb - lb);
   }
#endif
   tm->lb = lb;
   // In parallel execution, the lower bound can be slightly non-monotonic in rare
   // circumstances. This is algorithmically expected, but we keep the bound in the output
   // non-monotonic because this is what the user expects. 
   if (tm->stat.analyzed <= 1 || tm->printed_lb >= SYM_INFINITY){
      tm->printed_lb = lb;
   }else{
      tm->printed_lb = MAX(lb, tm->printed_lb);
   }
}
   return 0;
}

/*===========================================================================*/
/*===========================================================================*/

int tighten_root_bounds(tm_prob *tm)
{
   /* 
    * using the reduced costs that are saved from the root node, try to
    * improve variable bounds.
    * should be called whenever ub is updated.
    * change only bounds for root. not for the current node. the bounds for
    * current node are updated in tighten_bounds()
    */
   int                  i, j, k, l;
   rc_desc             *rc = tm->reduced_costs;
   double               gap, max_change;
   double              *dj, *lb, *ub;
   int                 *saved_ind;
   int                  cnt, total_changes = 0;
   int                 *ind;
   double              *bd;
   char                *lu;
#ifdef COMPILE_IN_LP
   double               lpetol = tm->lpp[0]->lp_data->lpetol;
#else
   double               lpetol = 9.9999999999999995e-07;
#endif
   bounds_change_desc  *bnd_change;
   int                 *new_ind;
   int                  num_new_bounds;
   int                  verbosity = tm->par.verbosity;
   int                 *oldindex;
   double              *oldvalue;
   char                *oldlu;

   if (!rc) {
      return 0;
   }

   if (!tm->has_ub) {
      PRINT(verbosity, -1, ("tighten_root_bounds: cant tighten bounds if ub "
            "does not exist!\n"));
      return 0;
   }

   int max_length = 0;
   for (i=0; i<rc->num_rcs;i++) {
      max_length = MAX(max_length, rc->cnt[i]);
   }

   REMALLOC(tm->tmp.c, char, tm->tmp.c_size, max_length, BB_BUNCH);
   REMALLOC(tm->tmp.i, int, tm->tmp.i_size, max_length, BB_BUNCH);
   REMALLOC(tm->tmp.d, double, tm->tmp.d_size, max_length, BB_BUNCH);
   ind = tm->tmp.i;
   bd = tm->tmp.d;
   lu = tm->tmp.c;
   new_ind = (int *)malloc(max_length*ISIZE);
   
   for (i=0; i<rc->num_rcs;i++) {
      gap = tm->ub - rc->obj[i] - tm->par.granularity;
      if (gap <= lpetol) {
         continue;
      }
      saved_ind = rc->indices[i];
      dj  = rc->values[i];
      lb = rc->lb[i];
      ub = rc->ub[i];
      cnt = 0;
      for (j=0; j<rc->cnt[i]; j++) {
         max_change = gap/dj[j];
         if (max_change > 0 && max_change < ub[j]-lb[j]){
            ind[cnt] = saved_ind[j];
            lu[cnt] = 'U';
            bd[cnt++] = floor(lb[j] + max_change);
         }else if (max_change < 0 && max_change > lb[j] - ub[j]){
            ind[cnt] = saved_ind[j];
            lu[cnt] = 'L';
            bd[cnt++] = ceil(ub[j] + max_change);
         }
      }
      PRINT(verbosity, 5, ("tighten_root_bounds: tightening %d "
               "bounds in root\n", cnt));
      if (cnt == 0) {
         continue;
      }
      /* add these changes to root node */
      if (tm->rootnode->desc.bnd_change) {
         bnd_change = tm->rootnode->desc.bnd_change;
      } else {
         tm->rootnode->desc.bnd_change = bnd_change = 
            (bounds_change_desc *)calloc(1,sizeof(bounds_change_desc));
      }
      if (bnd_change->num_changes>0) {
         /* 
          * update existing changes and store the new ones in a separate array
          */
         num_new_bounds=0;
         oldvalue = bnd_change->value;
         oldindex = bnd_change->index;
         oldlu    = bnd_change->lbub;
         for (k=0; k<cnt; k++) {
            for (j=0; j<bnd_change->num_changes; j++) {
               if (oldindex[j]==ind[k] && oldlu[j]==lu[k]){
                  if (lu[k]=='L' && oldvalue[j]<bd[k]) {
                     oldvalue[j]=bd[k];
                     total_changes++;
                  } else if (lu[k]=='U' && oldvalue[j]>bd[k]) {
                     oldvalue[j]=bd[k];
                     total_changes++;
                  }
                  break;
               }
            }
            if (j>=bnd_change->num_changes) {
               new_ind[num_new_bounds] = k;
               num_new_bounds++;
            }
         }
         /* those changes that dint already have an entry and stored now */
         if (num_new_bounds) {
            int new_cnt = num_new_bounds+bnd_change->num_changes;
            bnd_change->index = (int *)realloc(bnd_change->index,
                  ISIZE*new_cnt);
            bnd_change->lbub  = (char *)realloc(bnd_change->lbub,
                  CSIZE*new_cnt);
            bnd_change->value = (double *)realloc(bnd_change->value,
                  DSIZE*new_cnt);
            oldvalue = bnd_change->value;
            oldindex = bnd_change->index;
            oldlu    = bnd_change->lbub;
            l = bnd_change->num_changes;
            for (j=0; j<num_new_bounds; j++) {
               total_changes++;
               k = new_ind[j]; 
               oldindex[l] = ind[k];
               oldlu[l]    = lu[k];
               oldvalue[l] = bd[k];
               bnd_change->num_changes++;
               l++;
            }
         }
      } else {
         bnd_change->index = (int *)malloc(cnt*ISIZE);
         bnd_change->lbub  = (char *)malloc(cnt*CSIZE);
         bnd_change->value = (double *)malloc(cnt*DSIZE);
         bnd_change->index = (int *) memcpy(bnd_change->index, ind, ISIZE*cnt);
         bnd_change->lbub  = (char *) memcpy(bnd_change->lbub, lu, CSIZE*cnt);
         bnd_change->value = (double *) memcpy(bnd_change->value, bd, 
               DSIZE*cnt);
         bnd_change->num_changes = cnt;
      }
   }
   if (verbosity>5 && tm->rootnode->desc.bnd_change!=NULL) {
      printf("tighten_root_bounds: root now has %d changes\n",
            tm->rootnode->desc.bnd_change->num_changes);
   }
   FREE(new_ind);
   return 0;
}

/*===========================================================================*/
