/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*                  This file is part of the program and library             */
/*         SCIP --- Solving Constraint Integer Programs                      */
/*                                                                           */
/*  Copyright (c) 2002-2026 Zuse Institute Berlin (ZIB)                      */
/*                                                                           */
/*  Licensed under the Apache License, Version 2.0 (the "License");          */
/*  you may not use this file except in compliance with the License.         */
/*  You may obtain a copy of the License at                                  */
/*                                                                           */
/*      http://www.apache.org/licenses/LICENSE-2.0                           */
/*                                                                           */
/*  Unless required by applicable law or agreed to in writing, software      */
/*  distributed under the License is distributed on an "AS IS" BASIS,        */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/*  See the License for the specific language governing permissions and      */
/*  limitations under the License.                                           */
/*                                                                           */
/*  You should have received a copy of the Apache-2.0 license                */
/*  along with SCIP; see the file LICENSE. If not visit scipopt.org.         */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**@file   var.c
 * @ingroup OTHER_CFILES
 * @brief  methods for problem variables
 * @author Tobias Achterberg
 * @author Timo Berthold
 * @author Gerald Gamrath
 * @author Stefan Heinz
 * @author Marc Pfetsch
 * @author Michael Winkler
 * @author Kati Wolter
 * @author Stefan Vigerske
 *
 * @todo Possibly implement the access of bounds of multi-aggregated variables by accessing the
 * corresponding linear constraint if it exists. This seems to require some work, since the linear
 * constraint has to be stored. Moreover, it has even to be created in case the original constraint
 * was deleted after multi-aggregation, but the bounds of the multi-aggregated variable should be
 * changed. This has to be done with care in order to not loose the performance gains of
 * multi-aggregation.
 */

/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
#include "scip/cons.h"
#include "scip/certificate.h"
#include "scip/event.h"
#include "scip/history.h"
#include "scip/implics.h"
#include "scip/lp.h"
#include "scip/lpexact.h"
#include "scip/primal.h"
#include "scip/prob.h"
#include "scip/pub_cons.h"
#include "scip/pub_history.h"
#include "scip/pub_implics.h"
#include "scip/pub_lp.h"
#include "scip/pub_message.h"
#include "scip/pub_misc.h"
#include "scip/pub_misc_sort.h"
#include "scip/pub_prop.h"
#include "scip/pub_var.h"
#include "scip/relax.h"
#include "scip/scip_certificate.h"
#include "scip/scip_exact.h"
#include "scip/scip_prob.h"
#include "scip/scip_probing.h"
#include "scip/set.h"
#include "scip/sol.h"
#include "scip/stat.h"
#include "scip/struct_event.h"
#include "scip/struct_lp.h"
#include "scip/struct_lpexact.h"
#include "scip/struct_prob.h"
#include "scip/struct_set.h"
#include "scip/struct_stat.h"
#include "scip/struct_var.h"
#include "scip/tree.h"
#include "scip/var.h"
#include <string.h>

#define MAXIMPLSCLOSURE 100  /**< maximal number of descendants of implied variable for building closure
                              *   in implication graph */
#define MAXABSVBCOEF    1e+5 /**< maximal absolute coefficient in variable bounds added due to implications */


/*
 * Debugging variable release and capture
 *
 * Define DEBUGUSES_VARNAME to the name of the variable for which to print
 * a backtrace when it is captured and released.
 * Optionally define DEBUGUSES_PROBNAME to the name of a SCIP problem to consider.
 * Have DEBUGUSES_NOADDR2LINE defined if you do not have addr2line installed on your system.
 */
/* #define DEBUGUSES_VARNAME "t_t_b7" */
/* #define DEBUGUSES_PROBNAME "t_st_e35_rens" */
/* #define DEBUGUSES_NOADDR2LINE */

#ifdef DEBUGUSES_VARNAME
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include "scip/struct_scip.h"

/** obtains a backtrace and prints it to stdout. */
static
void print_backtrace(void)
{
   void* array[10];
   char** strings;
   int size;
   int i;

   size = backtrace(array, 10);
   strings = backtrace_symbols(array, size);
   if( strings == NULL )
      return;

   /* skip first entry, which is the print_backtrace function */
   for( i = 1; i < size; ++i )
   {
      /* if string is something like
       *  /path/to/scip/bin/../lib/shared/libscip-7.0.1.3.linux.x86_64.gnu.dbg.so(+0x2675dd3)
       * (that is, no function name because it is a inlined function), then call
       * addr2line -e <libname> <addr> to get func and code line
       * dladdr() may be an alternative
       */
      char* openpar;
      char* closepar = NULL;
#ifndef DEBUGUSES_NOADDR2LINE
      openpar = strchr(strings[i], '(');
      if( openpar != NULL && openpar[1] == '+' )
         closepar = strchr(openpar+2, ')');
#endif
      if( closepar != NULL )
      {
         char cmd[SCIP_MAXSTRLEN];
         (void) SCIPsnprintf(cmd, SCIP_MAXSTRLEN, "addr2line -f -p -e \"%.*s\" %.*s", openpar - strings[i], strings[i], closepar-openpar-1, openpar+1);
         printf("  ");
         fflush(stdout);
         system(cmd);
      }
      else
         printf("  %s\n", strings[i]);
    }

  free(strings);
}
#endif

/*
 * hole, holelist, and domain methods
 */

/** creates a new holelist element */
static
SCIP_RETCODE holelistCreate(
   SCIP_HOLELIST**       holelist,           /**< pointer to holelist to create */
   BMS_BLKMEM*           blkmem,             /**< block memory for target holelist */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right               /**< right bound of open interval in new hole */
   )
{
   assert(holelist != NULL);
   assert(blkmem != NULL);
   assert(SCIPsetIsLT(set, left, right));

   SCIPsetDebugMsg(set, "create hole list element (%.15g,%.15g) in blkmem %p\n", left, right, (void*)blkmem);

   SCIP_ALLOC( BMSallocBlockMemory(blkmem, holelist) );
   (*holelist)->hole.left = left;
   (*holelist)->hole.right = right;
   (*holelist)->next = NULL;

   return SCIP_OKAY;
}

/** frees all elements in the holelist */
static
void holelistFree(
   SCIP_HOLELIST**       holelist,           /**< pointer to holelist to free */
   BMS_BLKMEM*           blkmem              /**< block memory for target holelist */
   )
{
   assert(holelist != NULL);
   assert(blkmem != NULL);

   while( *holelist != NULL )
   {
      SCIP_HOLELIST* next;

      SCIPdebugMessage("free hole list element (%.15g,%.15g) in blkmem %p\n",
         (*holelist)->hole.left, (*holelist)->hole.right, (void*)blkmem);

      next = (*holelist)->next;
      BMSfreeBlockMemory(blkmem, holelist);
      assert(*holelist == NULL);

      *holelist = next;
   }
   assert(*holelist == NULL);
}

/** duplicates a list of holes */
static
SCIP_RETCODE holelistDuplicate(
   SCIP_HOLELIST**       target,             /**< pointer to target holelist */
   BMS_BLKMEM*           blkmem,             /**< block memory for target holelist */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_HOLELIST*        source              /**< holelist to duplicate */
   )
{
   assert(target != NULL);

   while( source != NULL )
   {
      assert(source->next == NULL || SCIPsetIsGE(set, source->next->hole.left, source->hole.right));
      SCIP_CALL( holelistCreate(target, blkmem, set, source->hole.left, source->hole.right) );
      source = source->next;
      target = &(*target)->next;
   }

   return SCIP_OKAY;
}

/** adds a hole to the domain */
static
SCIP_RETCODE domAddHole(
   SCIP_DOM*             dom,                /**< domain to add hole to */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right,              /**< right bound of open interval in new hole */
   SCIP_Bool*            added               /**< pointer to store whether the hole was added (variable didn't had that hole before), or NULL */
   )
{
   SCIP_HOLELIST** insertpos;
   SCIP_HOLELIST* next;

   assert(dom != NULL);
   assert(added != NULL);

   /* search for the position of the new hole */
   insertpos = &dom->holelist;
   while( *insertpos != NULL && (*insertpos)->hole.left < left )
      insertpos = &(*insertpos)->next;

   /* check if new hole already exists in the hole list or is a sub hole of an existing one */
   if( *insertpos != NULL && (*insertpos)->hole.left == left && (*insertpos)->hole.right >= right )  /*lint !e777 */
   {
      SCIPsetDebugMsg(set, "new hole (%.15g,%.15g) is redundant through known hole (%.15g,%.15g)\n",
         left, right, (*insertpos)->hole.left, (*insertpos)->hole.right);
      *added = FALSE;
      return SCIP_OKAY;
   }

   /* add hole */
   *added = TRUE;

   next = *insertpos;
   SCIP_CALL( holelistCreate(insertpos, blkmem, set, left, right) );
   (*insertpos)->next = next;

   return SCIP_OKAY;
}

/** merges overlapping holes into single holes, computes and moves lower and upper bound, respectively */
/**@todo  the domMerge() method is currently called if a lower or an upper bound locally or globally changed; this could
 *        be more efficient if performed with the knowledge if it was a lower or an upper bound which triggered this
 *        merge */
static
void domMerge(
   SCIP_DOM*             dom,                /**< domain to merge */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            newlb,              /**< pointer to store new lower bound */
   SCIP_Real*            newub               /**< pointer to store new upper bound */
   )
{
   SCIP_HOLELIST** holelistptr;
   SCIP_HOLELIST** lastnextptr;
   SCIP_Real* lastrightptr;

   assert(dom != NULL);
   assert(SCIPsetIsLE(set, dom->lb, dom->ub));

#ifndef NDEBUG
   {
      /* check if the holelist is sorted w.r.t. to the left interval bounds */
      SCIP_Real lastleft;

      holelistptr = &dom->holelist;

      lastleft = -SCIPsetInfinity(set);

      while( *holelistptr != NULL )
      {
         if( (*holelistptr)->next != NULL )
         {
            assert( SCIPsetIsLE(set, lastleft, (*holelistptr)->hole.left) );
            lastleft = (*holelistptr)->hole.left;
         }

         holelistptr = &(*holelistptr)->next;
      }
   }
#endif

   SCIPsetDebugMsg(set, "merge hole list\n");

   holelistptr = &dom->holelist;
   lastrightptr = &dom->lb;  /* lower bound is the right bound of the hole (-infinity,lb) */
   lastnextptr = holelistptr;

   while( *holelistptr != NULL )
   {
      SCIPsetDebugMsg(set, "check hole (%.15g,%.15g) last right interval was <%.15g>\n", (*holelistptr)->hole.left, (*holelistptr)->hole.right, *lastrightptr);

      /* check that the hole is not empty */
      assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right));

      if( SCIPsetIsGE(set, (*holelistptr)->hole.left, dom->ub) )
      {
         /* the remaining holes start behind the upper bound: remove them */
         SCIPsetDebugMsg(set, "remove remaining hole since upper bound <%.15g> is less then the left hand side of the current hole\n", dom->ub);
         holelistFree(holelistptr, blkmem);
         assert(*holelistptr == NULL);

         /* unlink this hole from the previous hole */
         *lastnextptr = NULL;
      }
      else if( SCIPsetIsGT(set, (*holelistptr)->hole.right, dom->ub) )
      {
         /* the hole overlaps the upper bound: decrease upper bound, remove this hole and all remaining holes */
         SCIPsetDebugMsg(set, "upper bound <%.15g> lays in current hole; store new upper bound and remove this and all remaining holes\n", dom->ub);

         assert(SCIPsetIsLT(set, (*holelistptr)->hole.left, dom->ub));

         /* adjust upper bound */
         dom->ub = (*holelistptr)->hole.left;

         if(newub != NULL )
            *newub = (*holelistptr)->hole.left;

         /* remove remaining hole list */
         holelistFree(holelistptr, blkmem);
         assert(*holelistptr == NULL);

         /* unlink this hole from the previous hole */
         *lastnextptr = NULL;
      }
      else if( SCIPsetIsGT(set, *lastrightptr, (*holelistptr)->hole.left) )
      {
         /* the right bound of the last hole is greater than the left bound of this hole: increase the right bound of
          * the last hole, delete this hole */
         SCIP_HOLELIST* nextholelist;

         if( SCIPsetIsEQ(set, *lastrightptr, dom->lb ) )
         {
            /* the reason for the overlap results from the lower bound hole (-infinity,lb); therefore, we can increase
             * the lower bound */
            SCIPsetDebugMsg(set, "lower bound <%.15g> lays in current hole; store new lower bound and remove hole\n", dom->lb);
            *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);

            /* adjust lower bound */
            dom->lb = *lastrightptr;

            if(newlb != NULL )
               *newlb = *lastrightptr;
         }
         else
         {
            SCIPsetDebugMsg(set, "current hole overlaps with the previous one (...,%.15g); merge to (...,%.15g)\n",
               *lastrightptr, MAX(*lastrightptr, (*holelistptr)->hole.right) );
            *lastrightptr = MAX(*lastrightptr, (*holelistptr)->hole.right);
         }
         nextholelist = (*holelistptr)->next;
         (*holelistptr)->next = NULL;
         holelistFree(holelistptr, blkmem);

         /* connect the linked list after removing the hole */
         *lastnextptr = nextholelist;

         /* get next hole */
         *holelistptr = nextholelist;
      }
      else
      {
         /* the holes do not overlap: update lastholelist and lastrightptr */
         lastrightptr = &(*holelistptr)->hole.right;
         lastnextptr = &(*holelistptr)->next;

         /* get next hole */
         holelistptr = &(*holelistptr)->next;
      }
   }

#ifndef NDEBUG
   {
      /* check that holes are merged */
      SCIP_Real lastright;

      lastright = dom->lb; /* lower bound is the right bound of the hole (-infinity,lb) */
      holelistptr = &dom->holelist;

      while( *holelistptr != NULL )
      {
         /* check the the last right interval is smaller or equal to the current left interval (none overlapping) */
         assert( SCIPsetIsLE(set, lastright, (*holelistptr)->hole.left) );

         /* check the hole property (check that the hole is not empty) */
         assert( SCIPsetIsLT(set, (*holelistptr)->hole.left, (*holelistptr)->hole.right) );
         lastright = (*holelistptr)->hole.right;

         /* get next hole */
         holelistptr = &(*holelistptr)->next;
      }

      /* check the the last right interval is smaller or equal to the upper bound (none overlapping) */
      assert( SCIPsetIsLE(set, lastright, dom->ub) );
   }
#endif
}

/*
 * domain change methods
 */

/** ensures, that bound change info array for lower bound changes can store at least num entries */
static
SCIP_RETCODE varEnsureLbchginfosSize(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   int                   num                 /**< minimum number of entries to store */
   )
{
   assert(var != NULL);
   assert(var->nlbchginfos <= var->lbchginfossize);
   assert(SCIPvarIsTransformed(var));

   if( num > var->lbchginfossize )
   {
      int newsize;

      newsize = SCIPsetCalcMemGrowSize(set, num);
      SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->lbchginfos, var->lbchginfossize, newsize) );
      var->lbchginfossize = newsize;
   }
   assert(num <= var->lbchginfossize);

   return SCIP_OKAY;
}

/** ensures, that bound change info array for upper bound changes can store at least num entries */
static
SCIP_RETCODE varEnsureUbchginfosSize(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   int                   num                 /**< minimum number of entries to store */
   )
{
   assert(var != NULL);
   assert(var->nubchginfos <= var->ubchginfossize);
   assert(SCIPvarIsTransformed(var));

   if( num > var->ubchginfossize )
   {
      int newsize;

      newsize = SCIPsetCalcMemGrowSize(set, num);
      SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->ubchginfos, var->ubchginfossize, newsize) );
      var->ubchginfossize = newsize;
   }
   assert(num <= var->ubchginfossize);

   return SCIP_OKAY;
}

/** adds domain change info to the variable's lower bound change info array */
static
SCIP_RETCODE varAddLbchginfo(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             oldbound,           /**< old value for bound */
   SCIP_Real             newbound,           /**< new value for bound */
   int                   depth,              /**< depth in the tree, where the bound change takes place */
   int                   pos,                /**< position of the bound change in its bound change array */
   SCIP_VAR*             infervar,           /**< variable that was changed (parent of var, or var itself) */
   SCIP_CONS*            infercons,          /**< constraint that inferred this bound change, or NULL */
   SCIP_PROP*            inferprop,          /**< propagator that deduced the bound change, or NULL */
   int                   inferinfo,          /**< user information for inference to help resolving the conflict */
   SCIP_BOUNDTYPE        inferboundtype,     /**< type of bound for inference var: lower or upper bound */
   SCIP_BOUNDCHGTYPE     boundchgtype        /**< bound change type: branching decision or inferred bound change */
   )
{
   assert(var != NULL);
   assert(SCIPsetIsLT(set, oldbound, newbound));
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, oldbound));
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));
   assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 0.0));
   assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 1.0));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
   assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);

   SCIPsetDebugMsg(set, "adding lower bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
      SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
      infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
      oldbound, newbound);

   SCIP_CALL( varEnsureLbchginfosSize(var, blkmem, set, var->nlbchginfos+1) );
   var->lbchginfos[var->nlbchginfos].oldbound = oldbound;
   var->lbchginfos[var->nlbchginfos].newbound = newbound;
   var->lbchginfos[var->nlbchginfos].var = var;
   var->lbchginfos[var->nlbchginfos].bdchgidx.depth = depth;
   var->lbchginfos[var->nlbchginfos].bdchgidx.pos = pos;
   var->lbchginfos[var->nlbchginfos].pos = var->nlbchginfos; /*lint !e732*/
   var->lbchginfos[var->nlbchginfos].boundchgtype = boundchgtype; /*lint !e641*/
   var->lbchginfos[var->nlbchginfos].boundtype = SCIP_BOUNDTYPE_LOWER; /*lint !e641*/
   var->lbchginfos[var->nlbchginfos].redundant = FALSE;
   var->lbchginfos[var->nlbchginfos].inferboundtype = inferboundtype; /*lint !e641*/
   var->lbchginfos[var->nlbchginfos].inferencedata.var = infervar;
   var->lbchginfos[var->nlbchginfos].inferencedata.info = inferinfo;

   /**@note The "pos" data member of the bound change info has a size of 27 bits */
   assert(var->nlbchginfos < 1 << 27);

   switch( boundchgtype )
   {
   case SCIP_BOUNDCHGTYPE_BRANCHING:
      break;
   case SCIP_BOUNDCHGTYPE_CONSINFER:
      assert(infercons != NULL);
      var->lbchginfos[var->nlbchginfos].inferencedata.reason.cons = infercons;
      break;
   case SCIP_BOUNDCHGTYPE_PROPINFER:
      var->lbchginfos[var->nlbchginfos].inferencedata.reason.prop = inferprop;
      break;
   default:
      SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
      return SCIP_INVALIDDATA;
   }

   var->nlbchginfos++;

   assert(var->nlbchginfos < 2
      || SCIPbdchgidxIsEarlier(&var->lbchginfos[var->nlbchginfos-2].bdchgidx,
         &var->lbchginfos[var->nlbchginfos-1].bdchgidx));

   return SCIP_OKAY;
}

/** adds domain change info to the variable's upper bound change info array */
static
SCIP_RETCODE varAddUbchginfo(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             oldbound,           /**< old value for bound */
   SCIP_Real             newbound,           /**< new value for bound */
   int                   depth,              /**< depth in the tree, where the bound change takes place */
   int                   pos,                /**< position of the bound change in its bound change array */
   SCIP_VAR*             infervar,           /**< variable that was changed (parent of var, or var itself) */
   SCIP_CONS*            infercons,          /**< constraint that inferred this bound change, or NULL */
   SCIP_PROP*            inferprop,          /**< propagator that deduced the bound change, or NULL */
   int                   inferinfo,          /**< user information for inference to help resolving the conflict */
   SCIP_BOUNDTYPE        inferboundtype,     /**< type of bound for inference var: lower or upper bound */
   SCIP_BOUNDCHGTYPE     boundchgtype        /**< bound change type: branching decision or inferred bound change */
   )
{
   assert(var != NULL);
   assert(SCIPsetIsGT(set, oldbound, newbound));
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, oldbound));
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));
   assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, oldbound, 1.0));
   assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, 0.0));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
   assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);

   SCIPsetDebugMsg(set, "adding upper bound change info to var <%s>[%g,%g]: depth=%d, pos=%d, infer%s=<%s>, inferinfo=%d, %g -> %g\n",
      SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, depth, pos, infercons != NULL ? "cons" : "prop",
      infercons != NULL ? SCIPconsGetName(infercons) : (inferprop != NULL ? SCIPpropGetName(inferprop) : "-"), inferinfo,
      oldbound, newbound);

   SCIP_CALL( varEnsureUbchginfosSize(var, blkmem, set, var->nubchginfos+1) );
   var->ubchginfos[var->nubchginfos].oldbound = oldbound;
   var->ubchginfos[var->nubchginfos].newbound = newbound;
   var->ubchginfos[var->nubchginfos].var = var;
   var->ubchginfos[var->nubchginfos].bdchgidx.depth = depth;
   var->ubchginfos[var->nubchginfos].bdchgidx.pos = pos;
   var->ubchginfos[var->nubchginfos].pos = var->nubchginfos; /*lint !e732*/
   var->ubchginfos[var->nubchginfos].boundchgtype = boundchgtype; /*lint !e641*/
   var->ubchginfos[var->nubchginfos].boundtype = SCIP_BOUNDTYPE_UPPER; /*lint !e641*/
   var->ubchginfos[var->nubchginfos].redundant = FALSE;
   var->ubchginfos[var->nubchginfos].inferboundtype = inferboundtype; /*lint !e641*/
   var->ubchginfos[var->nubchginfos].inferencedata.var = infervar;
   var->ubchginfos[var->nubchginfos].inferencedata.info = inferinfo;

   /**@note The "pos" data member of the bound change info has a size of 27 bits */
   assert(var->nubchginfos < 1 << 27);

   switch( boundchgtype )
   {
   case SCIP_BOUNDCHGTYPE_BRANCHING:
      break;
   case SCIP_BOUNDCHGTYPE_CONSINFER:
      assert(infercons != NULL);
      var->ubchginfos[var->nubchginfos].inferencedata.reason.cons = infercons;
      break;
   case SCIP_BOUNDCHGTYPE_PROPINFER:
      var->ubchginfos[var->nubchginfos].inferencedata.reason.prop = inferprop;
      break;
   default:
      SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
      return SCIP_INVALIDDATA;
   }

   var->nubchginfos++;

   assert(var->nubchginfos < 2
      || SCIPbdchgidxIsEarlier(&var->ubchginfos[var->nubchginfos-2].bdchgidx,
         &var->ubchginfos[var->nubchginfos-1].bdchgidx));

   return SCIP_OKAY;
}

/** applies single bound change */
static
SCIP_RETCODE boundchgApplyExact(
   SCIP_BOUNDCHG*        boundchg,           /**< bound change to apply */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   int                   depth,              /**< depth in the tree, where the bound change takes place */
   int                   pos,                /**< position of the bound change in its bound change array */
   SCIP_Bool*            cutoff              /**< pointer to store whether an infeasible bound change was detected */
   )
{
   SCIP_VAR* var;

   assert(boundchg != NULL);
   assert(stat != NULL);
   assert(depth > 0);
   assert(pos >= 0);
   assert(cutoff != NULL);
   assert(boundchg->newboundexact != NULL);

   *cutoff = FALSE;

   /* ignore redundant bound changes */
   if( boundchg->redundant )
      return SCIP_OKAY;

   var = boundchg->var;
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(boundchg->newboundexact));

   /* apply bound change */
   switch( boundchg->boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
      if( SCIPrationalIsGT(boundchg->newboundexact, SCIPvarGetLbLocalExact(var)) )
      {
         if( SCIPrationalIsLE(boundchg->newboundexact, SCIPvarGetUbLocalExact(var)) )
         {
            /* add the bound change info to the variable's bound change info array */
            switch( boundchg->boundchgtype )
            {
            case SCIP_BOUNDCHGTYPE_BRANCHING:
               SCIPsetDebugMsg(set, " -> branching: new lower bound of <%s>[%g,%g]: %g\n",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_LOWER, SCIP_BOUNDCHGTYPE_BRANCHING) );
               stat->lastbranchvar = var;
               stat->lastbranchdir = SCIP_BRANCHDIR_UPWARDS;
               stat->lastbranchvalue = boundchg->newbound;
               break;

            case SCIP_BOUNDCHGTYPE_CONSINFER:
               assert(boundchg->data.inferencedata.reason.cons != NULL);
               SCIPsetDebugMsg(set, " -> constraint <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
                  SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
               break;

            case SCIP_BOUNDCHGTYPE_PROPINFER:
               SCIPsetDebugMsg(set, " -> propagator <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
                  boundchg->data.inferencedata.reason.prop != NULL
                  ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
               break;

            default:
               SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
               return SCIP_INVALIDDATA;
            }
            if( SCIPshouldCertificateTrackBounds(set->scip) )
            {
               assert( var->exactdata->locdom.lbcertificateidx != -1 || SCIPsetIsInfinity(set, -var->locdom.lb) );
               var->lbchginfos[var->nlbchginfos - 1].oldcertificateindex = var->exactdata->locdom.lbcertificateidx;
               SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip), boundchg->certificateindex) );
            }
            /* change local bound of variable */
            SCIP_CALL( SCIPvarChgLbLocalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, boundchg->newboundexact) );
         }
         else
         {
            SCIPsetDebugMsg(set, " -> cutoff: new lower bound of <%s>[%g,%g]: %g\n",
               SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
            *cutoff = TRUE;
            boundchg->redundant = TRUE; /* bound change has not entered the lbchginfos array of the variable! */
         }
      }
      else
      {
         /* mark bound change to be inactive */
         SCIPsetDebugMsg(set, " -> inactive %s: new lower bound of <%s>[%g,%g]: %g\n",
            (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
            SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
         boundchg->redundant = TRUE;
      }
      break;

   case SCIP_BOUNDTYPE_UPPER:
      /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
      if( SCIPrationalIsLT(boundchg->newboundexact, SCIPvarGetUbLocalExact(var)) )
      {
         if( SCIPrationalIsGE(boundchg->newboundexact, SCIPvarGetLbLocalExact(var)) )
         {
            /* add the bound change info to the variable's bound change info array */
            switch( boundchg->boundchgtype )
            {
            case SCIP_BOUNDCHGTYPE_BRANCHING:
               SCIPsetDebugMsg(set, " -> branching: new upper bound of <%s>[%g,%g]: %g\n",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_UPPER, SCIP_BOUNDCHGTYPE_BRANCHING) );
               stat->lastbranchvar = var;
               stat->lastbranchdir = SCIP_BRANCHDIR_DOWNWARDS;
               stat->lastbranchvalue = boundchg->newbound;
               break;

            case SCIP_BOUNDCHGTYPE_CONSINFER:
               assert(boundchg->data.inferencedata.reason.cons != NULL);
               SCIPsetDebugMsg(set, " -> constraint <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
                  SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
               break;

            case SCIP_BOUNDCHGTYPE_PROPINFER:
               SCIPsetDebugMsg(set, " -> propagator <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
                  boundchg->data.inferencedata.reason.prop != NULL
                  ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
               break;

            default:
               SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
               return SCIP_INVALIDDATA;
            }

            if( SCIPshouldCertificateTrackBounds(set->scip) )
            {
               assert( var->exactdata->locdom.ubcertificateidx != -1 || SCIPsetIsInfinity(set, var->locdom.ub) );
               var->ubchginfos[var->nubchginfos - 1].oldcertificateindex = var->exactdata->locdom.ubcertificateidx;
               SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip), boundchg->certificateindex) );
            }
            /* change local bound of variable */
            SCIP_CALL( SCIPvarChgUbLocalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, boundchg->newboundexact) );
         }
         else
         {
            SCIPsetDebugMsg(set, " -> cutoff: new upper bound of <%s>[%g,%g]: %g\n",
               SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
            *cutoff = TRUE;
            boundchg->redundant = TRUE; /* bound change has not entered the ubchginfos array of the variable! */
         }
      }
      else
      {
         /* mark bound change to be inactive */
         SCIPsetDebugMsg(set, " -> inactive %s: new upper bound of <%s>[%g,%g]: %g\n",
            (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
            SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
         boundchg->redundant = TRUE;
      }
      break;

   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }

   /* update the branching and inference history */
   if( !boundchg->applied && !boundchg->redundant )
   {
      assert(var == boundchg->var);

      if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
      {
         SCIP_CALL( SCIPvarIncNBranchings(var, blkmem, set, stat,
               (SCIP_BOUNDTYPE)boundchg->boundtype == SCIP_BOUNDTYPE_LOWER
               ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS, boundchg->newbound, depth) );
      }
      else if( stat->lastbranchvar != NULL )
      {
         /**@todo if last branching variable is unknown, retrieve it from the nodes' boundchg arrays */
         SCIP_CALL( SCIPvarIncInferenceSum(stat->lastbranchvar, blkmem, set, stat, stat->lastbranchdir, stat->lastbranchvalue, 1.0) );
      }
      boundchg->applied = TRUE;
   }

   return SCIP_OKAY;
}


/** applies single bound change */
SCIP_RETCODE SCIPboundchgApply(
   SCIP_BOUNDCHG*        boundchg,           /**< bound change to apply */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   int                   depth,              /**< depth in the tree, where the bound change takes place */
   int                   pos,                /**< position of the bound change in its bound change array */
   SCIP_Bool*            cutoff              /**< pointer to store whether an infeasible bound change was detected */
   )
{
   SCIP_VAR* var;

   assert(boundchg != NULL);
   assert(stat != NULL);
   assert(depth > 0);
   assert(pos >= 0);
   assert(cutoff != NULL);

   *cutoff = FALSE;

   /* ignore redundant bound changes */
   if( boundchg->redundant )
      return SCIP_OKAY;

   if( boundchg->newboundexact != NULL)
      return boundchgApplyExact(boundchg, blkmem, set, stat, lp, branchcand, eventqueue, depth, pos, cutoff);

   var = boundchg->var;
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, boundchg->newbound));

   /* apply bound change */
   switch( boundchg->boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
      if( SCIPsetIsGT(set, boundchg->newbound, var->locdom.lb) )
      {
         if( SCIPsetIsLE(set, boundchg->newbound, var->locdom.ub) )
         {
            /* add the bound change info to the variable's bound change info array */
            switch( boundchg->boundchgtype )
            {
            case SCIP_BOUNDCHGTYPE_BRANCHING:
               SCIPsetDebugMsg(set, " -> branching: new lower bound of <%s>[%g,%g]: %g\n",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_LOWER, SCIP_BOUNDCHGTYPE_BRANCHING) );
               stat->lastbranchvar = var;
               stat->lastbranchdir = SCIP_BRANCHDIR_UPWARDS;
               stat->lastbranchvalue = boundchg->newbound;
               break;

            case SCIP_BOUNDCHGTYPE_CONSINFER:
               assert(boundchg->data.inferencedata.reason.cons != NULL);
               SCIPsetDebugMsg(set, " -> constraint <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
                  SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
               break;

            case SCIP_BOUNDCHGTYPE_PROPINFER:
               SCIPsetDebugMsg(set, " -> propagator <%s> inference: new lower bound of <%s>[%g,%g]: %g\n",
                  boundchg->data.inferencedata.reason.prop != NULL
                  ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddLbchginfo(var, blkmem, set, var->locdom.lb, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
               break;

            default:
               SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
               return SCIP_INVALIDDATA;
            }

            if( SCIPshouldCertificateTrackBounds(set->scip) )
            {
               assert( var->exactdata->locdom.lbcertificateidx != -1 || SCIPsetIsInfinity(set, -var->locdom.lb) );
               var->lbchginfos[var->nlbchginfos - 1].oldcertificateindex = var->exactdata->locdom.lbcertificateidx;
               SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip), boundchg->certificateindex) );
            }

            /* change local bound of variable */
            SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
         }
         else
         {
            SCIPsetDebugMsg(set, " -> cutoff: new lower bound of <%s>[%g,%g]: %g\n",
               SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
            *cutoff = TRUE;
            boundchg->redundant = TRUE; /* bound change has not entered the lbchginfos array of the variable! */
         }
      }
      else
      {
         /* mark bound change to be inactive */
         SCIPsetDebugMsg(set, " -> inactive %s: new lower bound of <%s>[%g,%g]: %g\n",
            (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
            SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
         boundchg->redundant = TRUE;
      }
      break;

   case SCIP_BOUNDTYPE_UPPER:
      /* check, if the bound change is still active (could be replaced by inference due to repropagation of higher node) */
      if( SCIPsetIsLT(set, boundchg->newbound, var->locdom.ub) )
      {
         if( SCIPsetIsGE(set, boundchg->newbound, var->locdom.lb) )
         {
            /* add the bound change info to the variable's bound change info array */
            switch( boundchg->boundchgtype )
            {
            case SCIP_BOUNDCHGTYPE_BRANCHING:
               SCIPsetDebugMsg(set, " -> branching: new upper bound of <%s>[%g,%g]: %g\n",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     NULL, NULL, NULL, 0, SCIP_BOUNDTYPE_UPPER, SCIP_BOUNDCHGTYPE_BRANCHING) );
               stat->lastbranchvar = var;
               stat->lastbranchdir = SCIP_BRANCHDIR_DOWNWARDS;
               stat->lastbranchvalue = boundchg->newbound;
               break;

            case SCIP_BOUNDCHGTYPE_CONSINFER:
               assert(boundchg->data.inferencedata.reason.cons != NULL);
               SCIPsetDebugMsg(set, " -> constraint <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
                  SCIPconsGetName(boundchg->data.inferencedata.reason.cons),
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, boundchg->data.inferencedata.reason.cons, NULL,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_CONSINFER) );
               break;

            case SCIP_BOUNDCHGTYPE_PROPINFER:
               SCIPsetDebugMsg(set, " -> propagator <%s> inference: new upper bound of <%s>[%g,%g]: %g\n",
                  boundchg->data.inferencedata.reason.prop != NULL
                  ? SCIPpropGetName(boundchg->data.inferencedata.reason.prop) : "-",
                  SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
               SCIP_CALL( varAddUbchginfo(var, blkmem, set, var->locdom.ub, boundchg->newbound, depth, pos,
                     boundchg->data.inferencedata.var, NULL, boundchg->data.inferencedata.reason.prop,
                     boundchg->data.inferencedata.info,
                     (SCIP_BOUNDTYPE)(boundchg->inferboundtype), SCIP_BOUNDCHGTYPE_PROPINFER) );
               break;

            default:
               SCIPerrorMessage("invalid bound change type %d\n", boundchg->boundchgtype);
               return SCIP_INVALIDDATA;
            }

            if( SCIPshouldCertificateTrackBounds(set->scip) )
            {
               assert( var->exactdata->locdom.ubcertificateidx != -1 || SCIPsetIsInfinity(set, var->locdom.ub) );
               var->ubchginfos[var->nubchginfos - 1].oldcertificateindex = var->exactdata->locdom.ubcertificateidx;
               SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip), boundchg->certificateindex) );
            }

            /* change local bound of variable */
            SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, boundchg->newbound) );
         }
         else
         {
            SCIPsetDebugMsg(set, " -> cutoff: new upper bound of <%s>[%g,%g]: %g\n",
               SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
            *cutoff = TRUE;
            boundchg->redundant = TRUE; /* bound change has not entered the ubchginfos array of the variable! */
         }
      }
      else
      {
         /* mark bound change to be inactive */
         SCIPsetDebugMsg(set, " -> inactive %s: new upper bound of <%s>[%g,%g]: %g\n",
            (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
            SCIPvarGetName(var), var->locdom.lb, var->locdom.ub, boundchg->newbound);
         boundchg->redundant = TRUE;
      }
      break;

   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }

   /* update the branching and inference history */
   if( !boundchg->applied && !boundchg->redundant )
   {
      assert(var == boundchg->var);

      if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
      {
         SCIP_CALL( SCIPvarIncNBranchings(var, blkmem, set, stat,
               (SCIP_BOUNDTYPE)boundchg->boundtype == SCIP_BOUNDTYPE_LOWER
               ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS, boundchg->newbound, depth) );
      }
      else if( stat->lastbranchvar != NULL )
      {
         /**@todo if last branching variable is unknown, retrieve it from the nodes' boundchg arrays */
         SCIP_CALL( SCIPvarIncInferenceSum(stat->lastbranchvar, blkmem, set, stat, stat->lastbranchdir, stat->lastbranchvalue, 1.0) );
      }
      boundchg->applied = TRUE;
   }

   return SCIP_OKAY;
}

/** undoes single bound change */
SCIP_RETCODE SCIPboundchgUndo(
   SCIP_BOUNDCHG*        boundchg,           /**< bound change to remove */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue          /**< event queue */
   )
{
   SCIP_VAR* var;

   assert(boundchg != NULL);
   assert(stat != NULL);

   /* ignore redundant bound changes */
   if( boundchg->redundant )
      return SCIP_OKAY;

   var = boundchg->var;
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);

   /* undo bound change: apply the previous bound change of variable */
   switch( boundchg->boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      var->nlbchginfos--;
      assert(var->nlbchginfos >= 0);
      assert(var->lbchginfos != NULL);
      assert( SCIPsetIsFeasEQ(set, var->lbchginfos[var->nlbchginfos].newbound, var->locdom.lb) ); /*lint !e777*/
      assert( SCIPsetIsFeasLE(set, boundchg->newbound, var->locdom.lb) ); /* current lb might be larger to intermediate global bound change */

      SCIPsetDebugMsg(set, "removed lower bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
         SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
         var->lbchginfos[var->nlbchginfos].bdchgidx.depth, var->lbchginfos[var->nlbchginfos].bdchgidx.pos,
         var->lbchginfos[var->nlbchginfos].oldbound, var->lbchginfos[var->nlbchginfos].newbound);

      /* in case certificate is used, set back the certificate line index */
      if( SCIPshouldCertificateTrackBounds(set->scip)
         && !SCIPsetIsInfinity(set, -var->lbchginfos[var->nlbchginfos].oldbound) )
      {
         SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip),
               var->lbchginfos[var->nlbchginfos].oldcertificateindex) );
      }

      if( set->exact_enable && !SCIPrationalIsFpRepresentable(SCIPvarGetLbGlobalExact(boundchg->var))
            && SCIPsetIsEQ(set, var->lbchginfos[var->nlbchginfos].oldbound, SCIPvarGetLbGlobal(boundchg->var) ) )
      {
         /* reinstall the exact global bound, if necessary */
         SCIP_CALL( SCIPvarChgLbLocalExact(boundchg->var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue,
               SCIPvarGetLbGlobalExact(boundchg->var)) );
      }
      else
      {
         /* reinstall the previous local bound */
         SCIP_CALL( SCIPvarChgLbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
               var->lbchginfos[var->nlbchginfos].oldbound) );
      }

      /* in case all bound changes are removed the local bound should match the global bound */
      assert(var->nlbchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.lb, var->glbdom.lb));

      break;

   case SCIP_BOUNDTYPE_UPPER:
      var->nubchginfos--;
      assert(var->nubchginfos >= 0);
      assert(var->ubchginfos != NULL);
      assert( SCIPsetIsFeasEQ(set, var->ubchginfos[var->nubchginfos].newbound, var->locdom.ub) ); /*lint !e777*/
      assert( SCIPsetIsFeasGE(set, boundchg->newbound, var->locdom.ub) ); /* current ub might be smaller to intermediate global bound change */

      SCIPsetDebugMsg(set, "removed upper bound change info of var <%s>[%g,%g]: depth=%d, pos=%d, %g -> %g\n",
         SCIPvarGetName(var), var->locdom.lb, var->locdom.ub,
         var->ubchginfos[var->nubchginfos].bdchgidx.depth, var->ubchginfos[var->nubchginfos].bdchgidx.pos,
         var->ubchginfos[var->nubchginfos].oldbound, var->ubchginfos[var->nubchginfos].newbound);

      if( SCIPshouldCertificateTrackBounds(set->scip)
         && !SCIPsetIsInfinity(set, var->ubchginfos[var->nubchginfos].oldbound) )
      {
         SCIP_CALL( SCIPcertificateSetLastBoundIndex(SCIPgetCertificate(set->scip),
               var->ubchginfos[var->nubchginfos].oldcertificateindex) );
      }

      if( set->exact_enable && !SCIPrationalIsFpRepresentable(SCIPvarGetUbGlobalExact(boundchg->var))
            && SCIPsetIsEQ(set, var->ubchginfos[var->nubchginfos].oldbound, SCIPvarGetUbGlobal(boundchg->var) ) )
      {
         /* reinstall the exact global bound, if necessary */
         SCIP_CALL( SCIPvarChgUbLocalExact(boundchg->var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue,
               SCIPvarGetUbGlobalExact(boundchg->var)) );
      }
      else
      {
         /* reinstall the previous local bound */
         SCIP_CALL( SCIPvarChgUbLocal(boundchg->var, blkmem, set, stat, lp, branchcand, eventqueue,
               var->ubchginfos[var->nubchginfos].oldbound) );
      }

      /* in case all bound changes are removed the local bound should match the global bound */
      assert(var->nubchginfos > 0 || SCIPsetIsFeasEQ(set, var->locdom.ub, var->glbdom.ub));

      /* in case certificate is used, set back the certificate line index */

      break;

   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }

   /* update last branching variable */
   if( (SCIP_BOUNDCHGTYPE)boundchg->boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING )
   {
      stat->lastbranchvar = NULL;
      stat->lastbranchvalue = SCIP_UNKNOWN;
   }

   return SCIP_OKAY;
}

/** applies single bound change to the global problem by changing the global bound of the corresponding variable */
static
SCIP_RETCODE boundchgApplyGlobal(
   SCIP_BOUNDCHG*        boundchg,           /**< bound change to apply */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Bool*            cutoff              /**< pointer to store whether an infeasible bound change was detected */
   )
{
   SCIP_VAR* var;
   SCIP_Real newbound;
   SCIP_BOUNDTYPE boundtype;

   assert(boundchg != NULL);
   assert(cutoff != NULL);

   *cutoff = FALSE;

   /* ignore redundant bound changes */
   if( boundchg->redundant )
      return SCIP_OKAY;

   var = SCIPboundchgGetVar(boundchg);
   newbound = SCIPboundchgGetNewbound(boundchg);
   boundtype = SCIPboundchgGetBoundtype(boundchg);

   /* check if the bound change is redundant which can happen due to a (better) global bound change which was performed
    * after that bound change was applied
    *
    * @note a global bound change is not captured by the redundant member of the bound change data structure
    */
   if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasLE(set, newbound, SCIPvarGetLbGlobal(var)))
      || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasGE(set, newbound, SCIPvarGetUbGlobal(var))) )
   {
      return SCIP_OKAY;
   }

   SCIPsetDebugMsg(set, "applying global bound change: <%s>[%g,%g] %s %g\n",
      SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var),
      boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", newbound);

   /* check for cutoff */
   if( (boundtype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, newbound, SCIPvarGetUbGlobal(var)))
      || (boundtype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, newbound, SCIPvarGetLbGlobal(var))) )
   {
      *cutoff = TRUE;
      return SCIP_OKAY;
   }

   if( SCIPisCertified(set->scip) )
   {
      if( boundtype == SCIP_BOUNDTYPE_LOWER )
      {
         var->exactdata->glbdom.lbcertificateidx = boundchg->certificateindex;
         var->exactdata->locdom.lbcertificateidx = boundchg->certificateindex;
      }
      else if( boundtype == SCIP_BOUNDTYPE_UPPER )
      {
         var->exactdata->glbdom.ubcertificateidx = boundchg->certificateindex;
         var->exactdata->locdom.ubcertificateidx = boundchg->certificateindex;
      }
#ifndef NDEBUG
      assert(SCIPcertificateEnsureLastBoundInfoConsistent(SCIPgetCertificate(set->scip), var, boundtype, newbound, TRUE));
#endif
   }

   /* apply bound change */
   SCIP_CALL( SCIPvarChgBdGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound, boundtype) );

   return SCIP_OKAY;
}

/** captures branching and inference data of bound change */
static
SCIP_RETCODE boundchgCaptureData(
   SCIP_BOUNDCHG*        boundchg            /**< bound change to remove */
   )
{
   assert(boundchg != NULL);

   /* capture variable associated with the bound change */
   assert(boundchg->var != NULL);
   SCIPvarCapture(boundchg->var);

   switch( boundchg->boundchgtype )
   {
   case SCIP_BOUNDCHGTYPE_BRANCHING:
   case SCIP_BOUNDCHGTYPE_PROPINFER:
      break;

   case SCIP_BOUNDCHGTYPE_CONSINFER:
      assert(boundchg->data.inferencedata.var != NULL);
      assert(boundchg->data.inferencedata.reason.cons != NULL);
      SCIPconsCapture(boundchg->data.inferencedata.reason.cons);
      break;

   default:
      SCIPerrorMessage("invalid bound change type\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** releases branching and inference data of bound change */
static
SCIP_RETCODE boundchgReleaseData(
   SCIP_BOUNDCHG*        boundchg,           /**< bound change to remove */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_LP*              lp                  /**< current LP data */

   )
{
   assert(boundchg != NULL);

   switch( boundchg->boundchgtype )
   {
   case SCIP_BOUNDCHGTYPE_BRANCHING:
   case SCIP_BOUNDCHGTYPE_PROPINFER:
      break;

   case SCIP_BOUNDCHGTYPE_CONSINFER:
      assert(boundchg->data.inferencedata.var != NULL);
      assert(boundchg->data.inferencedata.reason.cons != NULL);
      SCIP_CALL( SCIPconsRelease(&boundchg->data.inferencedata.reason.cons, blkmem, set) );
      break;

   default:
      SCIPerrorMessage("invalid bound change type\n");
      return SCIP_INVALIDDATA;
   }

   /* release variable */
   assert(boundchg->var != NULL);
   SCIP_CALL( SCIPvarRelease(&boundchg->var, blkmem, set, eventqueue, lp) );

   return SCIP_OKAY;
}

/** creates empty domain change data with dynamic arrays */
static
SCIP_RETCODE domchgCreate(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change data */
   BMS_BLKMEM*           blkmem              /**< block memory */
   )
{
   assert(domchg != NULL);
   assert(blkmem != NULL);

   SCIP_ALLOC( BMSallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN)) );
   (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
   (*domchg)->domchgdyn.nboundchgs = 0;
   (*domchg)->domchgdyn.boundchgs = NULL;
   (*domchg)->domchgdyn.nholechgs = 0;
   (*domchg)->domchgdyn.holechgs = NULL;
   (*domchg)->domchgdyn.boundchgssize = 0;
   (*domchg)->domchgdyn.holechgssize = 0;

   return SCIP_OKAY;
}

/** frees domain change data */
SCIP_RETCODE SCIPdomchgFree(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   assert(domchg != NULL);
   assert(blkmem != NULL);

   if( *domchg != NULL )
   {
      int i;

      /* release variables, branching and inference data associated with the bound changes */
      for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
      {
         SCIP_CALL( boundchgReleaseData(&(*domchg)->domchgbound.boundchgs[i], blkmem, set, eventqueue, lp) );
         if( (*domchg)->domchgbound.boundchgs[i].newboundexact != NULL )
            SCIPrationalFreeBlock(blkmem, &(*domchg)->domchgbound.boundchgs[i].newboundexact);
      }

      /* free memory for bound and hole changes */
      switch( (*domchg)->domchgdyn.domchgtype )
      {
      case SCIP_DOMCHGTYPE_BOUND:
         BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgbound.boundchgs, (*domchg)->domchgbound.nboundchgs);
         BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND));
         break;
      case SCIP_DOMCHGTYPE_BOTH:
         BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.boundchgs, (*domchg)->domchgboth.nboundchgs);
         BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgboth.holechgs, (*domchg)->domchgboth.nholechgs);
         BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH));
         break;
      case SCIP_DOMCHGTYPE_DYNAMIC:
         BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.boundchgs, (*domchg)->domchgdyn.boundchgssize);
         BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);
         BMSfreeBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN));
         break;
      default:
         SCIPerrorMessage("invalid domain change type\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** converts a static domain change data into a dynamic one */
static
SCIP_RETCODE domchgMakeDynamic(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change data */
   BMS_BLKMEM*           blkmem              /**< block memory */
   )
{
   assert(domchg != NULL);
   assert(blkmem != NULL);

   SCIPdebugMessage("making domain change data %p pointing to %p dynamic\n", (void*)domchg, (void*)*domchg);

   if( *domchg == NULL )
   {
      SCIP_CALL( domchgCreate(domchg, blkmem) );
   }
   else
   {
      switch( (*domchg)->domchgdyn.domchgtype )
      {
      case SCIP_DOMCHGTYPE_BOUND:
         SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOUND), sizeof(SCIP_DOMCHGDYN)) );
         (*domchg)->domchgdyn.nholechgs = 0;
         (*domchg)->domchgdyn.holechgs = NULL;
         (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
         (*domchg)->domchgdyn.holechgssize = 0;
         (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
         break;
      case SCIP_DOMCHGTYPE_BOTH:
         SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGDYN)) );
         (*domchg)->domchgdyn.boundchgssize = (int) (*domchg)->domchgdyn.nboundchgs;
         (*domchg)->domchgdyn.holechgssize = (*domchg)->domchgdyn.nholechgs;
         (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_DYNAMIC; /*lint !e641*/
         break;
      case SCIP_DOMCHGTYPE_DYNAMIC:
         break;
      default:
         SCIPerrorMessage("invalid domain change type\n");
         return SCIP_INVALIDDATA;
      }
   }
#ifndef NDEBUG
   {
      int i;
      for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
         assert(!SCIPvarIsIntegral((*domchg)->domchgbound.boundchgs[i].var)
            || EPSISINT((*domchg)->domchgbound.boundchgs[i].newbound, 1e-06));
   }
#endif

   return SCIP_OKAY;
}

/** converts a dynamic domain change data into a static one, using less memory than for a dynamic one */
SCIP_RETCODE SCIPdomchgMakeStatic(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   assert(domchg != NULL);
   assert(blkmem != NULL);

   SCIPsetDebugMsg(set, "making domain change data %p pointing to %p static\n", (void*)domchg, (void*)*domchg);

   if( *domchg != NULL )
   {
      switch( (*domchg)->domchgdyn.domchgtype )
      {
      case SCIP_DOMCHGTYPE_BOUND:
         if( (*domchg)->domchgbound.nboundchgs == 0 )
         {
            SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
         }
         break;
      case SCIP_DOMCHGTYPE_BOTH:
         if( (*domchg)->domchgboth.nholechgs == 0 )
         {
            if( (*domchg)->domchgbound.nboundchgs == 0 )
            {
               SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
            }
            else
            {
               SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGBOTH), sizeof(SCIP_DOMCHGBOUND)) );
               (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
            }
         }
         break;
      case SCIP_DOMCHGTYPE_DYNAMIC:
         if( (*domchg)->domchgboth.nholechgs == 0 )
         {
            if( (*domchg)->domchgbound.nboundchgs == 0 )
            {
               SCIP_CALL( SCIPdomchgFree(domchg, blkmem, set, eventqueue, lp) );
            }
            else
            {
               /* shrink dynamic size arrays to their minimal sizes */
               SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
                     (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
               BMSfreeBlockMemoryArrayNull(blkmem, &(*domchg)->domchgdyn.holechgs, (*domchg)->domchgdyn.holechgssize);

               /* convert into static domain change */
               SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOUND)) );
               (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOUND; /*lint !e641*/
            }
         }
         else
         {
            /* shrink dynamic size arrays to their minimal sizes */
            SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.boundchgs, \
                  (*domchg)->domchgdyn.boundchgssize, (*domchg)->domchgdyn.nboundchgs) ); /*lint !e571*/
            SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(*domchg)->domchgdyn.holechgs, \
                  (*domchg)->domchgdyn.holechgssize, (*domchg)->domchgdyn.nholechgs) );

            /* convert into static domain change */
            SCIP_ALLOC( BMSreallocBlockMemorySize(blkmem, domchg, sizeof(SCIP_DOMCHGDYN), sizeof(SCIP_DOMCHGBOTH)) );
            (*domchg)->domchgdyn.domchgtype = SCIP_DOMCHGTYPE_BOTH; /*lint !e641*/
         }
         break;
      default:
         SCIPerrorMessage("invalid domain change type\n");
         return SCIP_INVALIDDATA;
      }
#ifndef NDEBUG
      if( *domchg != NULL )
      {
         int i;
         for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
            assert(SCIPvarGetType((*domchg)->domchgbound.boundchgs[i].var) == SCIP_VARTYPE_CONTINUOUS
               || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
      }
#endif
   }

   return SCIP_OKAY;
}

/** ensures, that boundchgs array can store at least num entries */
static
SCIP_RETCODE domchgEnsureBoundchgsSize(
   SCIP_DOMCHG*          domchg,             /**< domain change data structure */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   int                   num                 /**< minimum number of entries to store */
   )
{
   assert(domchg != NULL);
   assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/

   if( num > domchg->domchgdyn.boundchgssize )
   {
      int newsize;

      newsize = SCIPsetCalcMemGrowSize(set, num);
      SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.boundchgs, domchg->domchgdyn.boundchgssize, newsize) );
      for( int i = domchg->domchgdyn.boundchgssize; i < newsize; ++i)
         domchg->domchgdyn.boundchgs[i].newboundexact = NULL;
      domchg->domchgdyn.boundchgssize = newsize;
   }
   assert(num <= domchg->domchgdyn.boundchgssize);

   return SCIP_OKAY;
}

/** ensures, that holechgs array can store at least num additional entries */
static
SCIP_RETCODE domchgEnsureHolechgsSize(
   SCIP_DOMCHG*          domchg,             /**< domain change data structure */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   int                   num                 /**< minimum number of additional entries to store */
   )
{
   assert(domchg != NULL);
   assert(domchg->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/

   if( num > domchg->domchgdyn.holechgssize )
   {
      int newsize;

      newsize = SCIPsetCalcMemGrowSize(set, num);
      SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &domchg->domchgdyn.holechgs, domchg->domchgdyn.holechgssize, newsize) );
      domchg->domchgdyn.holechgssize = newsize;
   }
   assert(num <= domchg->domchgdyn.holechgssize);

   return SCIP_OKAY;
}

/** applies domain change */
SCIP_RETCODE SCIPdomchgApply(
   SCIP_DOMCHG*          domchg,             /**< domain change to apply */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   int                   depth,              /**< depth in the tree, where the domain change takes place */
   SCIP_Bool*            cutoff              /**< pointer to store whether an infeasible domain change was detected */
   )
{
   int i;

   assert(cutoff != NULL);

   *cutoff = FALSE;

   SCIPsetDebugMsg(set, "applying domain changes at %p in depth %d\n", (void*)domchg, depth);

   if( domchg == NULL )
      return SCIP_OKAY;

   /* apply bound changes */
   for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
   {
      SCIP_CALL( SCIPboundchgApply(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
            branchcand, eventqueue, depth, i, cutoff) );
      if( *cutoff )
         break;
   }
   SCIPsetDebugMsg(set, " -> %u bound changes (cutoff %u)\n", domchg->domchgbound.nboundchgs, *cutoff);

   /* mark all bound changes after a cutoff redundant */
   for( ; i < (int)domchg->domchgbound.nboundchgs; ++i )
      domchg->domchgbound.boundchgs[i].redundant = TRUE;

   /* apply holelist changes */
   if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
   {
      for( i = 0; i < domchg->domchgboth.nholechgs; ++i )
         *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].newlist;
      SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
   }

   return SCIP_OKAY;
}

/** undoes domain change */
SCIP_RETCODE SCIPdomchgUndo(
   SCIP_DOMCHG*          domchg,             /**< domain change to remove */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue          /**< event queue */
   )
{
   int i;

   SCIPsetDebugMsg(set, "undoing domain changes at %p\n", (void*)domchg);
   if( domchg == NULL )
      return SCIP_OKAY;

   /* undo holelist changes */
   if( domchg->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_BOUND ) /*lint !e641*/
   {
      for( i = domchg->domchgboth.nholechgs-1; i >= 0; --i )
         *(domchg->domchgboth.holechgs[i].ptr) = domchg->domchgboth.holechgs[i].oldlist;
      SCIPsetDebugMsg(set, " -> %d hole changes\n", domchg->domchgboth.nholechgs);
   }

   /* undo bound changes */
   for( i = domchg->domchgbound.nboundchgs-1; i >= 0; --i )
   {
      SCIP_CALL( SCIPboundchgUndo(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp, branchcand, eventqueue) );
   }
   SCIPsetDebugMsg(set, " -> %u bound changes\n", domchg->domchgbound.nboundchgs);

   return SCIP_OKAY;
}

/** applies domain change to the global problem */
SCIP_RETCODE SCIPdomchgApplyGlobal(
   SCIP_DOMCHG*          domchg,             /**< domain change to apply */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Bool*            cutoff              /**< pointer to store whether an infeasible domain change was detected */
   )
{
   int i;

   assert(cutoff != NULL);

   *cutoff = FALSE;

   if( domchg == NULL )
      return SCIP_OKAY;

   SCIPsetDebugMsg(set, "applying domain changes at %p to the global problem\n", (void*)domchg);

   /* apply bound changes */
   for( i = 0; i < (int)domchg->domchgbound.nboundchgs; ++i )
   {
      SCIP_CALL( boundchgApplyGlobal(&domchg->domchgbound.boundchgs[i], blkmem, set, stat, lp,
            branchcand, eventqueue, cliquetable, cutoff) );
      if( *cutoff )
         break;
   }
   SCIPsetDebugMsg(set, " -> %u global bound changes\n", domchg->domchgbound.nboundchgs);

   /**@todo globally apply holelist changes - how can this be done without confusing pointer updates? */

   return SCIP_OKAY;
}

/** adds certificate line number to domain changes */
void SCIPdomchgAddCurrentCertificateIndex(
   SCIP_DOMCHG*          domchg,             /**< pointer to domain change data structure */
   SCIP_CERTIFICATE*     certificate         /**< certificate information */
   )
{
   SCIP_BOUNDCHG* change;

   if( !SCIPcertificateIsEnabled(certificate) )
      return;

   change = &(domchg->domchgdyn.boundchgs[domchg->domchgdyn.nboundchgs - 1]);

   #ifndef NDEBUG
      assert(SCIPcertificateEnsureLastBoundInfoConsistent(certificate, change->var, (SCIP_BOUNDTYPE) change->boundtype, change->newbound, FALSE));
   #endif

   change->certificateindex = SCIPcertificateGetCurrentIndex(certificate) - 1;
}

/** adds bound change to domain changes */
SCIP_RETCODE SCIPdomchgAddBoundchg(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change data structure */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR*             var,                /**< variable to change the bounds for */
   SCIP_Real             newbound,           /**< new value for bound */
   SCIP_RATIONAL*        newboundexact,      /**< new value for exact bound, or NULL if not needed */
   SCIP_BOUNDTYPE        boundtype,          /**< type of bound for var: lower or upper bound */
   SCIP_BOUNDCHGTYPE     boundchgtype,       /**< type of bound change: branching decision or inference */
   SCIP_Real             lpsolval,           /**< solval of variable in last LP on path to node, or SCIP_INVALID if unknown */
   SCIP_VAR*             infervar,           /**< variable that was changed (parent of var, or var itself), or NULL */
   SCIP_CONS*            infercons,          /**< constraint that deduced the bound change, or NULL */
   SCIP_PROP*            inferprop,          /**< propagator that deduced the bound change, or NULL */
   int                   inferinfo,          /**< user information for inference to help resolving the conflict */
   SCIP_BOUNDTYPE        inferboundtype      /**< type of bound for inference var: lower or upper bound */
   )
{
   SCIP_BOUNDCHG* boundchg;

   assert(domchg != NULL);
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(!SCIPvarIsBinary(var) || SCIPsetIsEQ(set, newbound, boundtype == SCIP_BOUNDTYPE_LOWER ? 1.0 : 0.0));
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING || infervar != NULL);
   assert((boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER) == (infercons != NULL));
   assert(boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER || inferprop == NULL);

   SCIPsetDebugMsg(set, "adding %s bound change <%s: %g> of variable <%s> to domain change at %p pointing to %p\n",
      boundtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper", boundchgtype == SCIP_BOUNDCHGTYPE_BRANCHING ? "branching" : "inference",
      newbound, var->name, (void*)domchg, (void*)*domchg);

   /* if domain change data doesn't exist, create it;
    * if domain change is static, convert it into dynamic change
    */
   if( *domchg == NULL )
   {
      SCIP_CALL( domchgCreate(domchg, blkmem) );
   }
   else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
   {
      SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
   }
   assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/

   /* get memory for additional bound change */
   SCIP_CALL( domchgEnsureBoundchgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nboundchgs+1) );

   /* fill in the bound change data */
   boundchg = &(*domchg)->domchgdyn.boundchgs[(*domchg)->domchgdyn.nboundchgs];
   boundchg->var = var;
   switch( boundchgtype )
   {
   case SCIP_BOUNDCHGTYPE_BRANCHING:
      boundchg->data.branchingdata.lpsolval = lpsolval;
      break;
   case SCIP_BOUNDCHGTYPE_CONSINFER:
      assert(infercons != NULL);
      boundchg->data.inferencedata.var = infervar;
      boundchg->data.inferencedata.reason.cons = infercons;
      boundchg->data.inferencedata.info = inferinfo;
      break;
   case SCIP_BOUNDCHGTYPE_PROPINFER:
      boundchg->data.inferencedata.var = infervar;
      boundchg->data.inferencedata.reason.prop = inferprop;
      boundchg->data.inferencedata.info = inferinfo;
      break;
   default:
      SCIPerrorMessage("invalid bound change type %d\n", boundchgtype);
      return SCIP_INVALIDDATA;
   }

   boundchg->newbound = newbound;
   boundchg->boundchgtype = boundchgtype; /*lint !e641*/
   boundchg->boundtype = boundtype; /*lint !e641*/
   boundchg->inferboundtype = inferboundtype; /*lint !e641*/
   boundchg->applied = FALSE;
   boundchg->redundant = FALSE;
   (*domchg)->domchgdyn.nboundchgs++;
   if( newboundexact != NULL )
   {
      if( boundchg->newboundexact == NULL )
         SCIP_CALL( SCIPrationalCopyBlock(blkmem, &boundchg->newboundexact, newboundexact) );
      else
         SCIPrationalSetRational(boundchg->newboundexact, newboundexact);
   }

   /* capture branching and inference data associated with the bound changes */
   SCIP_CALL( boundchgCaptureData(boundchg) );

#ifdef SCIP_DISABLED_CODE /* expensive debug check */
#ifdef SCIP_MORE_DEBUG
   {
      int i;
      for( i = 0; i < (int)(*domchg)->domchgbound.nboundchgs; ++i )
         assert(!SCIPvarIsIntegral((*domchg)->domchgbound.boundchgs[i].var)
            || SCIPsetIsFeasIntegral(set, (*domchg)->domchgbound.boundchgs[i].newbound));
   }
#endif
#endif

   return SCIP_OKAY;
}

/** adds hole change to domain changes */
SCIP_RETCODE SCIPdomchgAddHolechg(
   SCIP_DOMCHG**         domchg,             /**< pointer to domain change data structure */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_HOLELIST**       ptr,                /**< changed list pointer */
   SCIP_HOLELIST*        newlist,            /**< new value of list pointer */
   SCIP_HOLELIST*        oldlist             /**< old value of list pointer */
   )
{
   SCIP_HOLECHG* holechg;

   assert(domchg != NULL);
   assert(ptr != NULL);

   /* if domain change data doesn't exist, create it;
    * if domain change is static, convert it into dynamic change
    */
   if( *domchg == NULL )
   {
      SCIP_CALL( domchgCreate(domchg, blkmem) );
   }
   else if( (*domchg)->domchgdyn.domchgtype != SCIP_DOMCHGTYPE_DYNAMIC ) /*lint !e641*/
   {
      SCIP_CALL( domchgMakeDynamic(domchg, blkmem) );
   }
   assert(*domchg != NULL && (*domchg)->domchgdyn.domchgtype == SCIP_DOMCHGTYPE_DYNAMIC); /*lint !e641*/

   /* get memory for additional hole change */
   SCIP_CALL( domchgEnsureHolechgsSize(*domchg, blkmem, set, (*domchg)->domchgdyn.nholechgs+1) );

   /* fill in the hole change data */
   holechg = &(*domchg)->domchgdyn.holechgs[(*domchg)->domchgdyn.nholechgs];
   holechg->ptr = ptr;
   holechg->newlist = newlist;
   holechg->oldlist = oldlist;
   (*domchg)->domchgdyn.nholechgs++;

   return SCIP_OKAY;
}




/*
 * methods for variables
 */

/** returns adjusted lower bound value, which is rounded for integral variable types */
static
SCIP_Real adjustedLb(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_Real             lb                  /**< lower bound to adjust */
   )
{
   if( lb < 0.0 && SCIPsetIsInfinity(set, -lb) )
      return -SCIPsetInfinity(set);
   else if( lb > 0.0 && SCIPsetIsInfinity(set, lb) )
      return SCIPsetInfinity(set);
   else if( isintegral )
      return SCIPsetFeasCeil(set, lb);
   else if( lb > 0.0 && lb < SCIPsetEpsilon(set) )
      return 0.0;
   else
      return lb;
}

/** returns adjusted lower bound value, which is rounded for integral variable types */
static
SCIP_Real adjustedLbExactFloat(
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_Real             lb                  /**< lower bound to adjust */
   )
{
   if( isintegral )
      return ceil(lb);
   else
      return lb;
}

/** returns adjusted lower bound value, which is rounded for integral variable types */
static
void adjustedLbExact(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_RATIONAL*        lb                  /**< lower bound to adjust */
   )
{
   if( SCIPrationalIsNegative(lb) && SCIPsetIsInfinity(set, -SCIPrationalGetReal(lb)) )
      SCIPrationalSetNegInfinity(lb);
   else if( SCIPrationalIsPositive(lb) && SCIPsetIsInfinity(set, SCIPrationalGetReal(lb)) )
      SCIPrationalSetInfinity(lb);
   else if( isintegral )
      SCIPrationalRoundInteger(lb, lb, SCIP_R_ROUND_UPWARDS);
}

/** returns adjusted upper bound value, which is rounded for integral variable types */
static
SCIP_Real adjustedUb(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_Real             ub                  /**< upper bound to adjust */
   )
{
   if( ub > 0.0 && SCIPsetIsInfinity(set, ub) )
      return SCIPsetInfinity(set);
   else if( ub < 0.0 && SCIPsetIsInfinity(set, -ub) )
      return -SCIPsetInfinity(set);
   else if( isintegral )
      return SCIPsetFeasFloor(set, ub);
   else if( ub < 0.0 && ub > -SCIPsetEpsilon(set) )
      return 0.0;
   else
      return ub;
}

/** returns adjusted upperbound value, which is rounded for integral variable types */
static
SCIP_Real adjustedUbExactFloat(
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_Real             lb                  /**< lower bound to adjust */
   )
{
   if( isintegral )
      return floor(lb);
   else
      return lb;
}

/** returns adjusted lower bound value, which is rounded for integral variable types */
static
void adjustedUbExact(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             isintegral,         /**< is variable integral? */
   SCIP_RATIONAL*        ub                  /**< lower bound to adjust */
   )
{
   if( SCIPrationalIsNegative(ub) && SCIPsetIsInfinity(set, -SCIPrationalGetReal(ub)) )
      SCIPrationalSetNegInfinity(ub);
   else if( SCIPrationalIsPositive(ub) && SCIPsetIsInfinity(set, SCIPrationalGetReal(ub)) )
      SCIPrationalSetInfinity(ub);
   else if( isintegral )
      SCIPrationalRoundInteger(ub, ub, SCIP_R_ROUND_DOWNWARDS);
}

/** writes the approximate exact multi-aggregate data in the floating-point structs */
static
void overwriteMultAggrWithExactData(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR*             var                 /**< SCIP variable */
   )
{
   int i;

   if( !set->exact_enable || SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
      return;

   var->data.multaggr.constant = SCIPrationalGetReal(var->exactdata->multaggr.constant);
   for( i = 0; i < var->data.multaggr.nvars; i++ )
   {
      var->data.multaggr.scalars[i] = SCIPrationalGetReal(var->exactdata->multaggr.scalars[i]);
   }
}

/** removes (redundant) cliques, implications and variable bounds of variable from all other variables' implications and variable
 *  bounds arrays, and optionally removes them also from the variable itself
 */
SCIP_RETCODE SCIPvarRemoveCliquesImplicsVbs(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             irrelevantvar,      /**< has the variable become irrelevant? */
   SCIP_Bool             onlyredundant,      /**< should only the redundant implications and variable bounds be removed? */
   SCIP_Bool             removefromvar       /**< should the implications and variable bounds be removed from the var itself? */
   )
{
   SCIP_Real lb;
   SCIP_Real ub;

   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(SCIPvarIsActive(var) || SCIPvarGetType(var) != SCIP_VARTYPE_BINARY || SCIPvarIsImpliedIntegral(var));

   lb = SCIPvarGetLbGlobal(var);
   ub = SCIPvarGetUbGlobal(var);

   SCIPsetDebugMsg(set, "removing %s implications and vbounds of %s<%s>[%g,%g]\n",
      onlyredundant ? "redundant" : "all", irrelevantvar ? "irrelevant " : "", SCIPvarGetName(var), lb, ub);

   /* remove implications of (fixed) binary variable */
   if( var->implics != NULL && (!onlyredundant || lb > 0.5 || ub < 0.5) )
   {
      SCIP_Bool varfixing;

      assert(SCIPvarIsBinary(var));

      varfixing = FALSE;
      do
      {
         SCIP_VAR** implvars;
         SCIP_BOUNDTYPE* impltypes;
         int nimpls;
         int i;

         nimpls = SCIPimplicsGetNImpls(var->implics, varfixing);
         implvars = SCIPimplicsGetVars(var->implics, varfixing);
         impltypes = SCIPimplicsGetTypes(var->implics, varfixing);

         for( i = 0; i < nimpls; i++ )
         {
            SCIP_VAR* implvar;
            SCIP_BOUNDTYPE impltype;

            implvar = implvars[i];
            impltype = impltypes[i];
            assert(implvar != var);

            /* remove for all implications z == 0 / 1  ==>  x <= p / x >= p (x not binary)
             * the following variable bound from x's variable bounds
             *   x <= b*z+d (z in vubs of x)            , for z == 0 / 1  ==>  x <= p
             *   x >= b*z+d (z in vlbs of x)            , for z == 0 / 1  ==>  x >= p
             */
            if( impltype == SCIP_BOUNDTYPE_UPPER )
            {
               if( implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
               {
                  SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u  ==>  <%s> <= %g\n",
                     SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
                     SCIPimplicsGetBounds(var->implics, varfixing)[i]);
                  SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, varfixing) );
                  implvar->closestvblpcount = -1;
                  var->closestvblpcount = -1;
               }
            }
            else
            {
               if( implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
               {
                  SCIPsetDebugMsg(set, "deleting variable bound: <%s> == %u  ==>  <%s> >= %g\n",
                     SCIPvarGetName(var), varfixing, SCIPvarGetName(implvar),
                     SCIPimplicsGetBounds(var->implics, varfixing)[i]);
                  SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, !varfixing) );
                  implvar->closestvblpcount = -1;
                  var->closestvblpcount = -1;
               }
            }
         }
         varfixing = !varfixing;
      }
      while( varfixing == TRUE );

      if( removefromvar )
      {
         /* free the implications data structures */
         SCIPimplicsFree(&var->implics, blkmem);
      }
   }

   /* remove the (redundant) variable lower bounds */
   if( var->vlbs != NULL )
   {
      SCIP_VAR** vars;
      SCIP_Real* coefs;
      SCIP_Real* constants;
      int nvbds;
      int newnvbds;
      int i;

      nvbds = SCIPvboundsGetNVbds(var->vlbs);
      vars = SCIPvboundsGetVars(var->vlbs);
      coefs = SCIPvboundsGetCoefs(var->vlbs);
      constants = SCIPvboundsGetConstants(var->vlbs);

      /* remove for all variable bounds x >= b*z+d the following implication from z's implications
       *   z == ub  ==>  x >= b*ub + d           , if b > 0
       *   z == lb  ==>  x >= b*lb + d           , if b < 0
       */
      newnvbds = 0;
      for( i = 0; i < nvbds; i++ )
      {
         SCIP_VAR* implvar;
         SCIP_Real coef;

         assert(newnvbds <= i);

         implvar = vars[i];
         assert(implvar != NULL);

         coef = coefs[i];
         assert(!SCIPsetIsZero(set, coef));

         /* check, if we want to remove the variable bound */
         if( onlyredundant )
         {
            SCIP_Real vbound;

            vbound = MAX(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i];  /*lint !e666*/
            if( SCIPsetIsFeasGT(set, vbound, lb) )
            {
               /* the variable bound is not redundant: keep it */
               if( removefromvar )
               {
                  if( newnvbds < i )
                  {
                     vars[newnvbds] = implvar;
                     coefs[newnvbds] = coef;
                     constants[newnvbds] = constants[i];
                  }
                  newnvbds++;
               }
               continue;
            }
         }

         /* remove the corresponding implication */
         if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting implication: <%s> == %d  ==>  <%s> >= %g\n",
               SCIPvarGetName(implvar), (coef > 0.0), SCIPvarGetName(var), MAX(coef, 0.0) + constants[i]);
            SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef > 0.0), var, SCIP_BOUNDTYPE_LOWER) );
         }
         if( coef > 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
               SCIPvarGetName(implvar), SCIPvarGetName(var));
            SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, FALSE) );
            implvar->closestvblpcount = -1;
            var->closestvblpcount = -1;
         }
         else if( coef < 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
               SCIPvarGetName(implvar), SCIPvarGetName(var));
            SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, TRUE) );
            implvar->closestvblpcount = -1;
            var->closestvblpcount = -1;
         }
      }

      if( removefromvar )
      {
         /* update the number of variable bounds */
         SCIPvboundsShrink(&var->vlbs, blkmem, newnvbds);
         var->closestvblpcount = -1;
      }
   }

   /**@todo in general, variable bounds like x >= b*z + d corresponding to an implication like z = ub ==> x >= b*ub + d
    *       might be missing because we only add variable bounds with reasonably small value of b. thus, we currently
    *       cannot remove such variables x from z's implications.
    */

   /* remove the (redundant) variable upper bounds */
   if( var->vubs != NULL )
   {
      SCIP_VAR** vars;
      SCIP_Real* coefs;
      SCIP_Real* constants;
      int nvbds;
      int newnvbds;
      int i;

      nvbds = SCIPvboundsGetNVbds(var->vubs);
      vars = SCIPvboundsGetVars(var->vubs);
      coefs = SCIPvboundsGetCoefs(var->vubs);
      constants = SCIPvboundsGetConstants(var->vubs);

      /* remove for all variable bounds x <= b*z+d the following implication from z's implications
       *   z == lb  ==>  x <= b*lb + d           , if b > 0
       *   z == ub  ==>  x <= b*ub + d           , if b < 0
       */
      newnvbds = 0;
      for( i = 0; i < nvbds; i++ )
      {
         SCIP_VAR* implvar;
         SCIP_Real coef;

         assert(newnvbds <= i);

         implvar = vars[i];
         assert(implvar != NULL);

         coef = coefs[i];
         assert(!SCIPsetIsZero(set, coef));

         /* check, if we want to remove the variable bound */
         if( onlyredundant )
         {
            SCIP_Real vbound;

            vbound = MIN(coef * SCIPvarGetUbGlobal(implvar), coef * SCIPvarGetLbGlobal(implvar)) + constants[i];  /*lint !e666*/
            if( SCIPsetIsFeasLT(set, vbound, ub) )
            {
               /* the variable bound is not redundant: keep it */
               if( removefromvar )
               {
                  if( newnvbds < i )
                  {
                     vars[newnvbds] = implvar;
                     coefs[newnvbds] = coefs[i];
                     constants[newnvbds] = constants[i];
                  }
                  newnvbds++;
               }
               continue;
            }
         }

         /* remove the corresponding implication */
         if( implvar->implics != NULL ) /* variable may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting implication: <%s> == %d  ==>  <%s> <= %g\n",
               SCIPvarGetName(implvar), (coef < 0.0), SCIPvarGetName(var), MIN(coef, 0.0) + constants[i]);
            SCIP_CALL( SCIPimplicsDel(&implvar->implics, blkmem, set, (coef < 0.0), var, SCIP_BOUNDTYPE_UPPER) );
         }
         if( coef < 0.0 && implvar->vubs != NULL ) /* implvar may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting variable upper bound from <%s> involving variable %s\n",
               SCIPvarGetName(implvar), SCIPvarGetName(var));
            SCIP_CALL( SCIPvboundsDel(&implvar->vubs, blkmem, var, TRUE) );
            implvar->closestvblpcount = -1;
            var->closestvblpcount = -1;
         }
         else if( coef > 0.0 && implvar->vlbs != NULL ) /* implvar may have been aggregated in the mean time */
         {
            SCIPsetDebugMsg(set, "deleting variable lower bound from <%s> involving variable %s\n",
               SCIPvarGetName(implvar), SCIPvarGetName(var));
            SCIP_CALL( SCIPvboundsDel(&implvar->vlbs, blkmem, var, FALSE) );
            implvar->closestvblpcount = -1;
            var->closestvblpcount = -1;
         }
      }

      if( removefromvar )
      {
         /* update the number of variable bounds */
         SCIPvboundsShrink(&var->vubs, blkmem, newnvbds);
         var->closestvblpcount = -1;
      }
   }

   /* remove the variable from all cliques */
   if( SCIPvarIsBinary(var) )
      SCIPcliquelistRemoveFromCliques(var->cliquelist, cliquetable, var, irrelevantvar);

   /**@todo variable bounds like x <= b*z + d with z general integer are not removed from x's vbd arrays, because
    *       z has no link (like in the binary case) to x
    */

   return SCIP_OKAY;
}

/** sets the variable name */
static
SCIP_RETCODE varSetName(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL */
   const char*           name                /**< name of variable, or NULL for automatic name creation */
   )
{
   assert(blkmem != NULL);
   assert(var != NULL);

   if( name == NULL )
   {
      char s[SCIP_MAXSTRLEN];

      assert(stat != NULL);

      (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "_var%d_", stat->nvaridx);
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, s, strlen(s)+1) );
   }
   else
   {
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->name, name, strlen(name)+1) );
   }

   return SCIP_OKAY;
}

/** creates variable; if variable is of integral type, fractional bounds are automatically rounded; an integer variable
 *  with bounds zero and one is automatically converted into a binary variable
 */
static
SCIP_RETCODE varCreate(
   SCIP_VAR**            var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   const char*           name,               /**< name of variable, or NULL for automatic name creation */
   SCIP_Real             lb,                 /**< lower bound of variable */
   SCIP_Real             ub,                 /**< upper bound of variable */
   SCIP_Real             obj,                /**< objective function value */
   SCIP_VARTYPE          vartype,            /**< type of variable */
   SCIP_IMPLINTTYPE      impltype,           /**< implied integral type of the variable */
   SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
   SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
   SCIP_DECL_VARCOPY     ((*varcopy)),       /**< copies variable data if wanted to subscip, or NULL */
   SCIP_DECL_VARDELORIG  ((*vardelorig)),    /**< frees user data of original variable, or NULL */
   SCIP_DECL_VARTRANS    ((*vartrans)),      /**< creates transformed user data by transforming original user data, or NULL */
   SCIP_DECL_VARDELTRANS ((*vardeltrans)),   /**< frees user data of transformed variable, or NULL */
   SCIP_VARDATA*         vardata             /**< user data for this specific variable */
   )
{
   SCIP_Bool integral;
   int i;

   assert(var != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(vartype != SCIP_DEPRECATED_VARTYPE_IMPLINT);

   /* forbid infinite objective values */
   if( SCIPsetIsInfinity(set, REALABS(obj)) )
   {
      SCIPerrorMessage("invalid objective coefficient: value is infinite\n");
      return SCIP_INVALIDDATA;
   }

   /* exact bounds may follow later */
   if( !set->exact_enable )
   {
      /* adjust bounds of variable */
      integral = vartype != SCIP_VARTYPE_CONTINUOUS || impltype != SCIP_IMPLINTTYPE_NONE;
      lb = adjustedLb(set, integral, lb);
      ub = adjustedUb(set, integral, ub);

      /* convert [0,1]-integers into binary variables and check that binary variables have correct bounds */
      if( ( lb == 0.0 || lb == 1.0 ) && ( ub == 0.0 || ub == 1.0 ) ) /*lint !e777*/
      {
         if( vartype == SCIP_VARTYPE_INTEGER )
            vartype = SCIP_VARTYPE_BINARY;
      }
      else
      {
         if( vartype == SCIP_VARTYPE_BINARY )
         {
            SCIPerrorMessage("invalid bounds [%.2g,%.2g] for binary variable <%s>\n", lb, ub, name);
            return SCIP_INVALIDDATA;
         }
      }

      assert(vartype != SCIP_VARTYPE_BINARY || lb == 0.0 || lb == 1.0); /*lint !e777*/
      assert(vartype != SCIP_VARTYPE_BINARY || ub == 0.0 || ub == 1.0); /*lint !e777*/
      assert(vartype != SCIP_DEPRECATED_VARTYPE_IMPLINT);
   }

   SCIP_ALLOC( BMSallocBlockMemory(blkmem, var) );

   /* set variable's name */
   SCIP_CALL( varSetName(*var, blkmem, stat, name) );

#ifndef NDEBUG
   (*var)->scip = set->scip;
#endif
   (*var)->obj = obj;
   (*var)->unchangedobj = obj;
   (*var)->branchfactor = 1.0;
   (*var)->rootsol = 0.0;
   (*var)->bestrootsol = 0.0;
   (*var)->bestrootredcost = 0.0;
   (*var)->bestrootlpobjval = SCIP_INVALID;
   (*var)->relaxsol = 0.0;
   (*var)->nlpsol = 0.0;
   (*var)->primsolavg = 0.5 * (lb + ub);
   (*var)->conflictlb = SCIP_REAL_MIN;
   (*var)->conflictub = SCIP_REAL_MAX;
   (*var)->conflictrelaxedlb = (*var)->conflictlb;
   (*var)->conflictrelaxedub = (*var)->conflictub;
   (*var)->lazylb = -SCIPsetInfinity(set);
   (*var)->lazyub = SCIPsetInfinity(set);
   (*var)->glbdom.holelist = NULL;
   (*var)->glbdom.lb = lb;
   (*var)->glbdom.ub = ub;
   (*var)->locdom.holelist = NULL;
   (*var)->locdom.lb = lb;
   (*var)->locdom.ub = ub;
   (*var)->varcopy = varcopy;
   (*var)->vardelorig = vardelorig;
   (*var)->vartrans = vartrans;
   (*var)->vardeltrans = vardeltrans;
   (*var)->vardata = vardata;
   (*var)->parentvars = NULL;
   (*var)->negatedvar = NULL;
   (*var)->vlbs = NULL;
   (*var)->vubs = NULL;
   (*var)->implics = NULL;
   (*var)->cliquelist = NULL;
   (*var)->eventfilter = NULL;
   (*var)->lbchginfos = NULL;
   (*var)->ubchginfos = NULL;
   (*var)->index = stat->nvaridx;
   (*var)->probindex = -1;
   (*var)->pseudocandindex = -1;
   (*var)->eventqueueindexobj = -1;
   (*var)->eventqueueindexlb = -1;
   (*var)->eventqueueindexub = -1;
   (*var)->parentvarssize = 0;
   (*var)->nparentvars = 0;
   (*var)->nuses = 0;
   (*var)->branchpriority = 0;
   (*var)->branchdirection = SCIP_BRANCHDIR_AUTO; /*lint !e641*/
   (*var)->lbchginfossize = 0;
   (*var)->nlbchginfos = 0;
   (*var)->ubchginfossize = 0;
   (*var)->nubchginfos = 0;
   (*var)->conflictlbcount = 0;
   (*var)->conflictubcount = 0;
   (*var)->closestvlbidx = -1;
   (*var)->closestvubidx = -1;
   (*var)->closestvblpcount = -1;
   (*var)->initial = initial;
   (*var)->removable = removable;
   (*var)->deleted = FALSE;
   (*var)->donotaggr = FALSE;
   (*var)->donotmultaggr = FALSE;
   (*var)->vartype = (unsigned int)vartype;
   (*var)->varimpltype = (unsigned int)impltype;
   (*var)->pseudocostflag = FALSE;
   (*var)->eventqueueimpl = FALSE;
   (*var)->deletable = FALSE;
   (*var)->delglobalstructs = FALSE;
   (*var)->exactdata = NULL;
   (*var)->relaxationonly = FALSE;

   for( i = 0; i < NLOCKTYPES; i++ )
   {
      (*var)->nlocksdown[i] = 0;
      (*var)->nlocksup[i] = 0;
   }

   stat->nvaridx++;

   /* create branching and inference history entries */
   SCIP_CALL( SCIPhistoryCreate(&(*var)->history, blkmem) );
   SCIP_CALL( SCIPhistoryCreate(&(*var)->historycrun, blkmem) );

   /* the value based history is only created on demand */
   (*var)->valuehistory = NULL;

   return SCIP_OKAY;
}

/** creates and captures an original problem variable; an integer variable with bounds
 *  zero and one is automatically converted into a binary variable
 */
SCIP_RETCODE SCIPvarCreateOriginal(
   SCIP_VAR**            var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   const char*           name,               /**< name of variable, or NULL for automatic name creation */
   SCIP_Real             lb,                 /**< lower bound of variable */
   SCIP_Real             ub,                 /**< upper bound of variable */
   SCIP_Real             obj,                /**< objective function value */
   SCIP_VARTYPE          vartype,            /**< type of variable */
   SCIP_IMPLINTTYPE      impltype,           /**< implied integral type of the variable */
   SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
   SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
   SCIP_DECL_VARDELORIG  ((*vardelorig)),    /**< frees user data of original variable, or NULL */
   SCIP_DECL_VARTRANS    ((*vartrans)),      /**< creates transformed user data by transforming original user data, or NULL */
   SCIP_DECL_VARDELTRANS ((*vardeltrans)),   /**< frees user data of transformed variable, or NULL */
   SCIP_DECL_VARCOPY     ((*varcopy)),       /**< copies variable data if wanted to subscip, or NULL */
   SCIP_VARDATA*         vardata             /**< user data for this specific variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);

   /* create variable */
   SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, impltype, initial, removable,
         varcopy, vardelorig, vartrans, vardeltrans, vardata) );

   /* set variable status and data */
   (*var)->varstatus = SCIP_VARSTATUS_ORIGINAL; /*lint !e641*/
   (*var)->data.original.origdom.holelist = NULL;
   (*var)->data.original.origdom.lb = lb;
   (*var)->data.original.origdom.ub = ub;
   (*var)->data.original.transvar = NULL;

   /* capture variable */
   SCIPvarCapture(*var);

   return SCIP_OKAY;
}

/** creates and captures a loose variable belonging to the transformed problem; an integer variable with bounds
 *  zero and one is automatically converted into a binary variable
 */
SCIP_RETCODE SCIPvarCreateTransformed(
   SCIP_VAR**            var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   const char*           name,               /**< name of variable, or NULL for automatic name creation */
   SCIP_Real             lb,                 /**< lower bound of variable */
   SCIP_Real             ub,                 /**< upper bound of variable */
   SCIP_Real             obj,                /**< objective function value */
   SCIP_VARTYPE          vartype,            /**< type of variable */
   SCIP_IMPLINTTYPE      impltype,           /**< implied integral type of the variable */
   SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
   SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
   SCIP_DECL_VARDELORIG  ((*vardelorig)),    /**< frees user data of original variable, or NULL */
   SCIP_DECL_VARTRANS    ((*vartrans)),      /**< creates transformed user data by transforming original user data, or NULL */
   SCIP_DECL_VARDELTRANS ((*vardeltrans)),   /**< frees user data of transformed variable, or NULL */
   SCIP_DECL_VARCOPY     ((*varcopy)),       /**< copies variable data if wanted to subscip, or NULL */
   SCIP_VARDATA*         vardata             /**< user data for this specific variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);

   /* create variable */
   SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, impltype, initial, removable,
         varcopy, vardelorig, vartrans, vardeltrans, vardata) );

   /* create event filter for transformed variable */
   SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );

   /* set variable status and data */
   (*var)->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/
   (*var)->data.loose.minaggrcoef = 1.0;
   (*var)->data.loose.maxaggrcoef = 1.0;

   /* capture variable */
   SCIPvarCapture(*var);

   return SCIP_OKAY;
}

/** creates and sets the exact variable bounds and objective value (using floating-point data if value pointer is NULL)
 *
 *  @note an inactive integer variable with bounds zero and one is automatically converted into a binary variable
 *
 *  @note if exact data is provided, the corresponding floating-point data is overwritten
 */
SCIP_RETCODE SCIPvarAddExactData(
   SCIP_VAR*             var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_RATIONAL*        lb,                 /**< lower bound of variable, or NULL to use floating-point data */
   SCIP_RATIONAL*        ub,                 /**< upper bound of variable, or NULL to use floating-point data  */
   SCIP_RATIONAL*        obj                 /**< objective function value, or NULL to use floating-point data  */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);

   assert(var->exactdata == NULL);
   SCIP_ALLOC( BMSallocBlockMemory(blkmem, &(var->exactdata)) );

   if( lb != NULL )
   {
      var->data.original.origdom.lb = SCIPrationalRoundReal(lb, SCIP_R_ROUND_DOWNWARDS);
      var->glbdom.lb = var->data.original.origdom.lb;
      var->locdom.lb = var->data.original.origdom.lb;

      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->origdom.lb, lb) );
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->glbdom.lb, lb) );
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->locdom.lb, lb) );
   }
   else
   {
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->origdom.lb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->glbdom.lb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->locdom.lb) );

      SCIPrationalSetReal(var->exactdata->origdom.lb, var->data.original.origdom.lb);
      SCIPrationalSetReal(var->exactdata->glbdom.lb, var->glbdom.lb);
      SCIPrationalSetReal(var->exactdata->locdom.lb, var->locdom.lb);
   }

   if( ub != NULL )
   {
      var->data.original.origdom.ub = SCIPrationalRoundReal(ub, SCIP_R_ROUND_UPWARDS);
      var->glbdom.ub = var->data.original.origdom.ub;
      var->locdom.ub = var->data.original.origdom.ub;

      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->origdom.ub, ub) );
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->glbdom.ub, ub) );
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->locdom.ub, ub) );
   }
   else
   {
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->origdom.ub) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->glbdom.ub) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->locdom.ub) );

      SCIPrationalSetReal(var->exactdata->origdom.ub, var->data.original.origdom.ub);
      SCIPrationalSetReal(var->exactdata->glbdom.ub, var->glbdom.ub);
      SCIPrationalSetReal(var->exactdata->locdom.ub, var->locdom.ub);
   }

   if( obj != NULL )
   {
      var->unchangedobj = SCIPrationalGetReal(obj);
      var->obj = var->unchangedobj;

      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &var->exactdata->obj, obj) );
      SCIPintervalSetRational(&var->exactdata->objinterval, obj);
   }
   else
   {
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->obj) );

      SCIPrationalSetReal(var->exactdata->obj, var->obj);
      SCIPintervalSet(&var->exactdata->objinterval, var->obj);
   }

   var->exactdata->glbdom.lbcertificateidx = -1;
   var->exactdata->glbdom.ubcertificateidx = -1;
   var->exactdata->locdom.lbcertificateidx = -1;
   var->exactdata->locdom.ubcertificateidx = -1;
   var->exactdata->colexact = NULL;
   var->exactdata->varstatusexact = SCIPvarGetStatus(var);
   var->exactdata->certificateindex = -1;
   var->exactdata->multaggr.scalars = NULL;
   var->exactdata->multaggr.constant = NULL;
   var->exactdata->aggregate.constant = NULL;
   var->exactdata->aggregate.scalar = NULL;
   var->primsolavg = 0.5 * (var->data.original.origdom.lb + var->data.original.origdom.ub);

   /* convert inactive [0,1]-integers into binary variables and check that binary variables have correct bounds */
   if( ( SCIPrationalIsZero(var->exactdata->origdom.lb) || SCIPrationalIsEQReal(var->exactdata->origdom.lb, 1.0) )
      && ( SCIPrationalIsZero(var->exactdata->origdom.ub) || SCIPrationalIsEQReal(var->exactdata->origdom.ub, 1.0) ) )
   {
      if( (SCIP_VARTYPE)var->vartype == SCIP_VARTYPE_INTEGER && var->probindex == -1 )
         var->vartype = (unsigned int)SCIP_VARTYPE_BINARY;
   }
   else
   {
      if( (SCIP_VARTYPE)var->vartype == SCIP_VARTYPE_BINARY )
      {
         SCIPerrorMessage("invalid bounds [%.2g,%.2g] for binary variable <%s>\n", var->data.original.origdom.lb, var->data.original.origdom.ub, var->name);
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** copies exact variable data from one variable to another
 *
 *  @note This method cannot be integrated into SCIPvarCopy() because it is needed, e.g., when transforming vars.
 */
SCIP_RETCODE SCIPvarCopyExactData(
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_VAR*             targetvar,          /**< variable that gets the exact data */
   SCIP_VAR*             sourcevar,          /**< variable the data gets copied from */
   SCIP_Bool             negateobj           /**< should the objective be negated */
   )
{
   assert(blkmem != NULL);
   assert(targetvar != NULL);
   assert(sourcevar != NULL);

   if( sourcevar->exactdata == NULL )
      return SCIP_OKAY;

   assert(sourcevar->exactdata != NULL);

   SCIP_ALLOC( BMSallocBlockMemory(blkmem, &(targetvar->exactdata)) );
   targetvar->exactdata->glbdom = sourcevar->exactdata->glbdom;
   targetvar->exactdata->locdom = sourcevar->exactdata->locdom;
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->glbdom.lb, sourcevar->exactdata->glbdom.lb) );
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->glbdom.ub, sourcevar->exactdata->glbdom.ub) );
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->locdom.lb, sourcevar->exactdata->locdom.lb) );
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->locdom.ub, sourcevar->exactdata->locdom.ub) );
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->origdom.lb, sourcevar->exactdata->origdom.lb) );
   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->origdom.ub, sourcevar->exactdata->origdom.ub) );

   if( sourcevar->exactdata->aggregate.scalar != NULL )
   {
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->aggregate.scalar, sourcevar->exactdata->aggregate.scalar) );
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->aggregate.constant, sourcevar->exactdata->aggregate.constant) );
   }
   else
   {
      targetvar->exactdata->aggregate.constant = NULL;
      targetvar->exactdata->aggregate.scalar = NULL;
   }

   if( sourcevar->exactdata->multaggr.scalars != NULL )
   {
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->aggregate.constant, sourcevar->exactdata->multaggr.constant) );
      SCIP_CALL( SCIPrationalCopyBlockArray(blkmem, &targetvar->exactdata->multaggr.scalars, sourcevar->exactdata->multaggr.scalars, sourcevar->data.multaggr.nvars) );
   }
   else
   {
      targetvar->exactdata->multaggr.constant = NULL;
      targetvar->exactdata->multaggr.scalars = NULL;
   }

   SCIP_CALL( SCIPrationalCopyBlock(blkmem, &targetvar->exactdata->obj, sourcevar->exactdata->obj) );
   if( negateobj )
   {
      SCIPrationalNegate(targetvar->exactdata->obj, targetvar->exactdata->obj);
   }
   SCIPintervalSetRational(&(targetvar->exactdata->objinterval), targetvar->exactdata->obj);
   targetvar->exactdata->colexact = NULL;
   targetvar->exactdata->varstatusexact = SCIP_VARSTATUS_LOOSE;
   targetvar->exactdata->certificateindex = sourcevar->exactdata->certificateindex;

   return SCIP_OKAY;
}

/** copies and captures a variable from source to target SCIP; an integer variable with bounds zero and one is
 *  automatically converted into a binary variable; in case the variable data cannot be copied the variable is not
 *  copied at all
 */
SCIP_RETCODE SCIPvarCopy(
   SCIP_VAR**            var,                /**< pointer to store the target variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP*                 sourcescip,         /**< source SCIP data structure */
   SCIP_VAR*             sourcevar,          /**< source variable */
   SCIP_HASHMAP*         varmap,             /**< a hashmap to store the mapping of source variables corresponding
                                              *   target variables */
   SCIP_HASHMAP*         consmap,            /**< a hashmap to store the mapping of source constraints to the corresponding
                                              *   target constraints */
   SCIP_Bool             global              /**< should global or local bounds be used? */
   )
{
   SCIP_VARDATA* targetdata;
   SCIP_RESULT result;
   SCIP_Real lb;
   SCIP_Real ub;

   assert(set != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(sourcescip != NULL);
   assert(sourcevar != NULL);
   assert(var != NULL);
   assert(set->stage == SCIP_STAGE_PROBLEM);
   assert(varmap != NULL);
   assert(consmap != NULL);

   /** @todo copy hole lists */
   assert(global || SCIPvarGetHolelistLocal(sourcevar) == NULL);
   assert(!global || SCIPvarGetHolelistGlobal(sourcevar) == NULL);

   result = SCIP_DIDNOTRUN;
   targetdata = NULL;

   if( SCIPvarGetStatus(sourcevar) == SCIP_VARSTATUS_ORIGINAL )
   {
      lb = SCIPvarGetLbOriginal(sourcevar);
      ub = SCIPvarGetUbOriginal(sourcevar);
   }
   else
   {
      lb = global ? SCIPvarGetLbGlobal(sourcevar) : SCIPvarGetLbLocal(sourcevar);
      ub = global ? SCIPvarGetUbGlobal(sourcevar) : SCIPvarGetUbLocal(sourcevar);
   }

   /* creates and captures the variable in the target SCIP and initialize callback methods and variable data to NULL */
   SCIP_CALL( SCIPvarCreateOriginal(var, blkmem, set, stat, SCIPvarGetName(sourcevar),
         lb, ub, SCIPvarGetObj(sourcevar), SCIPvarGetType(sourcevar), SCIPvarGetImplType(sourcevar),
         SCIPvarIsInitial(sourcevar), SCIPvarIsRemovable(sourcevar),
         NULL, NULL, NULL, NULL, NULL) );
   assert(*var != NULL);

   /* directly copy donot(mult)aggr flag */
   (*var)->donotaggr = sourcevar->donotaggr;
   (*var)->donotmultaggr = sourcevar->donotmultaggr;

   /* insert variable into mapping between source SCIP and the target SCIP */
   assert(!SCIPhashmapExists(varmap, sourcevar));
   SCIP_CALL( SCIPhashmapInsert(varmap, sourcevar, *var) );

   /* in case there exists variable data and the variable data copy callback, try to copy variable data */
   if( sourcevar->vardata != NULL )
   {
      if( sourcevar->varcopy != NULL )
      {
         SCIP_CALL( sourcevar->varcopy(set->scip, sourcescip, sourcevar, sourcevar->vardata,
               varmap, consmap, (*var), &targetdata, &result) );

         /* evaluate result */
         if( result != SCIP_DIDNOTRUN && result != SCIP_SUCCESS )
         {
            SCIPerrorMessage("variable data copying method returned invalid result <%d>\n", result);
            return SCIP_INVALIDRESULT;
         }

         assert(targetdata == NULL || result == SCIP_SUCCESS);

         /* if copying was successful, add the created variable data to the variable as well as all callback methods */
         if( result == SCIP_SUCCESS )
         {
            (*var)->varcopy = sourcevar->varcopy;
            (*var)->vardelorig = sourcevar->vardelorig;
            (*var)->vartrans = sourcevar->vartrans;
            (*var)->vardeltrans = sourcevar->vardeltrans;
            (*var)->vardata = targetdata;
         }
      }
      else
      {
         /* if there is no copy callback, just copy data pointers */
         (*var)->vardata = sourcevar->vardata;
      }
   }

   /* we initialize histories of the variables by copying the source variable-information */
   if( set->history_allowtransfer )
   {
      SCIPvarMergeHistories((*var), sourcevar, stat);
   }

   /* in case the copying was successfully, add the created variable data to the variable as well as all callback
    * methods
    */
   if( result == SCIP_SUCCESS )
   {
      (*var)->varcopy = sourcevar->varcopy;
      (*var)->vardelorig = sourcevar->vardelorig;
      (*var)->vartrans = sourcevar->vartrans;
      (*var)->vardeltrans = sourcevar->vardeltrans;
      (*var)->vardata = targetdata;
   }

   SCIPsetDebugMsg(set, "created copy <%s> of variable <%s>\n", SCIPvarGetName(*var), SCIPvarGetName(sourcevar));

   return SCIP_OKAY;
}

/** parse given string for a value */
static
SCIP_RETCODE parseValue(
   SCIP_SET*             set,                /**< global SCIP settings */
   const char*           str,                /**< string to parse */
   SCIP_Real*            value,              /**< pointer to store the parsed value */
   SCIP_RATIONAL*        valueexact          /**< pointer to store the parsed exact value */
   )
{
   assert(value == NULL || valueexact == NULL);

   /* parse exact value */
   if( valueexact != NULL )
   {
      /* check for rationality */
      if( SCIPrationalIsString(str) )
      {
         SCIPrationalSetString(valueexact, str);
         SCIPrationalCanonicalize(valueexact);
      }
      else
      {
         SCIPerrorMessage("expected exact value: %s\n", str);
         return SCIP_READERROR;
      }

      SCIPrationalDebugMessage("parsed exact value: %q\n", valueexact);
   }
   /* parse real value */
   else if( value != NULL )
   {
      char* endptr;

      /* check for infinity */
      if( strncmp(str, "+inf", 4) == 0 )
      {
         *value = SCIPsetInfinity(set);
      }
      else if( strncmp(str, "-inf", 4) == 0 )
      {
         *value = -SCIPsetInfinity(set);
      }
      else if( !SCIPstrToRealValue(str, value, &endptr) || *endptr != '\0' )
      {
         SCIPerrorMessage("expected real value: %s\n", str);
         return SCIP_READERROR;
      }

      SCIPsetDebugMsg(set, "parsed real value: %g\n", *value);
   }

   return SCIP_OKAY;
}

/** parse the characters as bounds */
static
SCIP_RETCODE parseBounds(
   SCIP_SET*             set,                /**< global SCIP settings */
   const char*           str,                /**< string to parse */
   char*                 type,               /**< bound type (global, local, or lazy) */
   SCIP_Real*            lb,                 /**< pointer to store the lower bound */
   SCIP_Real*            ub,                 /**< pointer to store the upper bound */
   SCIP_RATIONAL*        lbexact,            /**< pointer to store the exact lower bound */
   SCIP_RATIONAL*        ubexact,            /**< pointer to store the exact upper bound */
   char**                endptr              /**< pointer to store the final string position if successfully parsed (or NULL if an error occured) */
   )
{
   char token[SCIP_MAXSTRLEN];

   SCIPsetDebugMsg(set, "parsing bounds: '%s'\n", str);

   /* get bound type */
   SCIPstrCopySection(str, ' ', ' ', type, SCIP_MAXSTRLEN, endptr);
   if ( *endptr == str
        || ( strncmp(type, "original", 8) != 0 && strncmp(type, "global", 6) != 0 && strncmp(type, "local", 5) != 0 && strncmp(type, "lazy", 4) != 0 ) )
   {
      SCIPsetDebugMsg(set, "unkown bound type\n");
      *endptr = NULL;
      return SCIP_OKAY;
   }

   SCIPsetDebugMsg(set, "parsed bound type <%s>\n", type);

   /* get lower bound */
   SCIPstrCopySection(str, '[', ',', token, SCIP_MAXSTRLEN, endptr);
   SCIP_CALL( parseValue(set, token, lb, lbexact) );

   str = *endptr - 1;

   /* get upper bound */
   SCIPstrCopySection(str, ',', ']', token, SCIP_MAXSTRLEN, endptr);
   SCIP_CALL( parseValue(set, token, ub, ubexact) );

   /* skip end of bounds */
   if( **endptr == ',' )
      ++(*endptr);

   return SCIP_OKAY;
}

/** parses a given string for a variable informations */
static
SCIP_RETCODE varParse(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   const char*           str,                /**< string to parse */
   char*                 name,               /**< pointer to store the variable name */
   SCIP_Real*            lb,                 /**< pointer to store the lower bound */
   SCIP_Real*            ub,                 /**< pointer to store the upper bound */
   SCIP_Real*            obj,                /**< pointer to store the objective coefficient */
   SCIP_RATIONAL*        lbexact,            /**< pointer to store the exact lower bound */
   SCIP_RATIONAL*        ubexact,            /**< pointer to store the exact upper bound */
   SCIP_RATIONAL*        objexact,           /**< pointer to store the exact objective coefficient */
   SCIP_VARTYPE*         vartype,            /**< pointer to store the variable type */
   SCIP_IMPLINTTYPE*     impltype,           /**< pointer to store the implied integral type */
   SCIP_Real*            lazylb,             /**< pointer to store if the lower bound is lazy */
   SCIP_Real*            lazyub,             /**< pointer to store if the upper bound is lazy */
   SCIP_RATIONAL*        lazylbexact,        /**< pointer to store if the exact lower bound is lazy */
   SCIP_RATIONAL*        lazyubexact,        /**< pointer to store if the exact upper bound is lazy */
   SCIP_Bool             local,              /**< should the local bound be applied */
   char**                endptr,             /**< pointer to store the final string position if successfully */
   SCIP_Bool*            success             /**< pointer store if the paring process was successful */
   )
{
   SCIP_Bool lazyread = FALSE;
   char token[SCIP_MAXSTRLEN];
   char* strptr;
   int i;

   assert(vartype != NULL);
   assert(success != NULL);

   (*success) = TRUE;

   /* copy variable type */
   SCIPstrCopySection(str, '[', ']', token, SCIP_MAXSTRLEN, endptr);
   assert(*endptr != str);
   SCIPsetDebugMsg(set, "parsed variable type <%s>\n", token);

   (*impltype) = SCIP_IMPLINTTYPE_NONE;
   /* get variable type */
   if( strncmp(token, "binary", 3) == 0 )
      (*vartype) = SCIP_VARTYPE_BINARY;
   else if( strncmp(token, "integer", 3) == 0 )
      (*vartype) = SCIP_VARTYPE_INTEGER;
   else if( strncmp(token, "implicit", 3) == 0 )
   {
      (*vartype) = SCIP_VARTYPE_CONTINUOUS;
      (*impltype) = SCIP_IMPLINTTYPE_WEAK;
   }
   else if( strncmp(token, "continuous", 3) == 0 )
      (*vartype) = SCIP_VARTYPE_CONTINUOUS;
   else
   {
      SCIPmessagePrintWarning(messagehdlr, "unknown variable type\n");
      (*success) = FALSE;
      return SCIP_OKAY;
   }

   /* move string pointer behind variable type */
   str = *endptr;

   /* get variable name */
   SCIPstrCopySection(str, '<', '>', name, SCIP_MAXSTRLEN, endptr);
   assert(*endptr != str);
   SCIPsetDebugMsg(set, "parsed variable name <%s>\n", name);

   /* move string pointer behind variable name */
   str = *endptr;

   /* get objective coefficient */
   SCIPstrCopySection(str, '=', ',', token, SCIP_MAXSTRLEN, endptr);
   SCIP_CALL( parseValue(set, token, obj, objexact) );

   /* move string pointer behind objective coefficient */
   str = *endptr;

   /* parse global/original bounds */
   SCIP_CALL( parseBounds(set, str, token, lb, ub, lbexact, ubexact, endptr) );
   if( *endptr == NULL )
   {
      SCIPerrorMessage("Expected bound type: %s.\n", token);
      return SCIP_READERROR;
   }
   assert(strncmp(token, "global", 6) == 0 || strncmp(token, "original", 8) == 0);

   /* store pointer */
   strptr = *endptr;

   /* possibly parse optional local and lazy bounds */
   for( i = 0; i < 2 && *endptr != NULL && **endptr != '\0'; ++i )
   {
      /* start after previous bounds */
      strptr = *endptr;

      /* parse variable bounds */
      SCIP_CALL( parseBounds(set, strptr, token, lazylb, lazyub, lazylbexact, lazyubexact, endptr) );

      /* set local bounds */
      if( strncmp(token, "local", 5) == 0 )
      {
         if( local )
         {
            if( lb != NULL )
            {
               assert(lazylb != NULL);
               *lb = *lazylb;
            }

            if( ub != NULL )
            {
               assert(lazyub != NULL);
               *ub = *lazyub;
            }

            if( lbexact != NULL )
            {
               assert(lazylbexact != NULL);
               SCIPrationalSetRational(lbexact, lazylbexact);
            }

            if( ubexact != NULL )
            {
               assert(lazyubexact != NULL);
               SCIPrationalSetRational(ubexact, lazyubexact);
            }
         }
      }
      /* set lazy bounds */
      else if( strncmp(token, "lazy", 4) == 0 )
      {
         lazyread = TRUE;
         break;
      }

      /* stop if parsing of bounds failed */
      if( *endptr == NULL )
         break;
   }

   /* reset lazy bounds */
   if( !lazyread )
   {
      if( lazylb != NULL )
         *lazylb = -SCIPsetInfinity(set);

      if( lazyub != NULL )
         *lazyub = SCIPsetInfinity(set);

      if( lazylbexact != NULL )
         SCIPrationalSetNegInfinity(lazylbexact);

      if( lazyubexact != NULL )
         SCIPrationalSetInfinity(lazyubexact);
   }

   /* check bounds for binary variables */
   if( (*vartype) == SCIP_VARTYPE_BINARY )
   {
      if( lb != NULL && *lb < 0.0 )
      {
         SCIPerrorMessage("Parsed invalid lower bound for binary variable <%s>: %f.\n", name, *lb);
         return SCIP_READERROR;
      }

      if( ub != NULL && *ub > 1.0 )
      {
         SCIPerrorMessage("Parsed invalid upper bound for binary variable <%s>: %f.\n", name, *ub);
         return SCIP_READERROR;
      }

      if( lbexact != NULL && SCIPrationalIsNegative(lbexact) )
      {
         SCIPerrorMessage("Parsed invalid exact lower bound for binary variable <%s>: %f.\n",
               name, SCIPrationalRoundReal(lbexact, SCIP_R_ROUND_DOWNWARDS));
         return SCIP_READERROR;
      }

      if( ubexact != NULL && SCIPrationalIsGTReal(ubexact, 1.0) )
      {
         SCIPerrorMessage("Parsed invalid exact upper bound for binary variable <%s>: %f.\n",
               name, SCIPrationalRoundReal(ubexact, SCIP_R_ROUND_UPWARDS));
         return SCIP_READERROR;
      }

      if( lazyread )
      {
         if( lazylb != NULL && *lazylb < 0.0 )
         {
            SCIPerrorMessage("Parsed invalid lazy lower bound for binary variable <%s>: %f.\n", name, *lazylb);
            return SCIP_READERROR;
         }

         if( lazyub != NULL && *lazyub > 1.0 )
         {
            SCIPerrorMessage("Parsed invalid lazy upper bound for binary variable <%s>: %f.\n", name, *lazyub);
            return SCIP_READERROR;
         }

         if( lazylbexact != NULL && SCIPrationalIsNegative(lazylbexact) )
         {
            SCIPerrorMessage("Parsed invalid exact lazy lower bound for binary variable <%s>: %f.\n",
                  name, SCIPrationalRoundReal(lazylbexact, SCIP_R_ROUND_DOWNWARDS));
            return SCIP_READERROR;
         }

         if( lazyubexact != NULL && SCIPrationalIsGTReal(lazyubexact, 1.0) )
         {
            SCIPerrorMessage("Parsed invalid exact lazy upper bound for binary variable <%s>: %f.\n",
                  name, SCIPrationalRoundReal(lazyubexact, SCIP_R_ROUND_UPWARDS));
            return SCIP_READERROR;
         }
      }
   }

   /* update string pointer */
   if( *endptr != NULL )
      strptr = *endptr;

   /* detect implied declaration */
   SCIPstrCopySection(strptr, ' ', ':', token, SCIP_MAXSTRLEN, endptr);

   /* no further declaration */
   if( *endptr == strptr )
      return SCIP_OKAY;

   /* get implied type */
   if( strncmp(token, "implied", 7) == 0 )
   {
      strptr = *endptr;
      SCIP_CALL( SCIPskipSpace(&strptr) );

      if( strncmp(strptr, "strong", 6) == 0 )
      {
         (*impltype) = SCIP_IMPLINTTYPE_STRONG;
         *endptr = strptr + 6;
      }
      else if( strncmp(strptr, "weak", 4) == 0 )
      {
         (*impltype) = SCIP_IMPLINTTYPE_WEAK;
         *endptr = strptr + 4;
      }
      else if( strncmp(strptr, "none", 4) == 0 )
      {
         (*impltype) = SCIP_IMPLINTTYPE_NONE;
         *endptr = strptr + 4;
      }
      else
      {
         SCIPerrorMessage("Expected implied integral type 'none', 'weak', or 'strong', got: '%s'.\n", strptr);
         return SCIP_READERROR;
      }
   }
   /* keep other declarations */
   else
      *endptr = strptr;

   return SCIP_OKAY;
}

/** parses variable information (in cip format) out of a string; if the parsing process was successful an original
 *  variable is created and captured; if variable is of integral type, fractional bounds are automatically rounded; an
 *  integer variable with bounds zero and one is automatically converted into a binary variable
 */
SCIP_RETCODE SCIPvarParseOriginal(
   SCIP_VAR**            var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   SCIP_STAT*            stat,               /**< problem statistics */
   const char*           str,                /**< string to parse */
   SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
   SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
   SCIP_DECL_VARCOPY     ((*varcopy)),       /**< copies variable data if wanted to subscip, or NULL */
   SCIP_DECL_VARDELORIG  ((*vardelorig)),    /**< frees user data of original variable */
   SCIP_DECL_VARTRANS    ((*vartrans)),      /**< creates transformed user data by transforming original user data */
   SCIP_DECL_VARDELTRANS ((*vardeltrans)),   /**< frees user data of transformed variable */
   SCIP_VARDATA*         vardata,            /**< user data for this specific variable */
   char**                endptr,             /**< pointer to store the final string position if successfully */
   SCIP_Bool*            success             /**< pointer store if the paring process was successful */
   )
{
   char name[SCIP_MAXSTRLEN];
   SCIP_VARTYPE vartype;
   SCIP_IMPLINTTYPE impltype;

   assert(var != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(endptr != NULL);
   assert(success != NULL);

   /* parse exact variable */
   if( set->exact_enable )
   {
      SCIP_RATIONAL* lb;
      SCIP_RATIONAL* ub;
      SCIP_RATIONAL* obj;
      SCIP_RATIONAL* lazylb;
      SCIP_RATIONAL* lazyub;

      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &ub) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &obj) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lazylb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lazyub) );

      /* parse string in cip format for exact variable information */
      SCIP_CALL( varParse(set, messagehdlr, str, name, NULL, NULL, NULL, lb, ub, obj, &vartype, &impltype,
            NULL, NULL, lazylb, lazyub, FALSE, endptr, success) );

      if( *success ) /*lint !e774*/
      {
         /* create variable */
         SCIP_CALL( varCreate(var, blkmem, set, stat, name, 0.0, 0.0, 0.0, vartype, impltype, initial, removable,
               varcopy, vardelorig, vartrans, vardeltrans, vardata) );

         /* set variable status */
         SCIPvarAdjustLbExact(*var, set, lb);
         SCIPvarAdjustUbExact(*var, set, ub);
         (*var)->varstatus = (unsigned int)SCIP_VARSTATUS_ORIGINAL;
         (*var)->data.original.origdom.holelist = NULL;
         (*var)->data.original.transvar = NULL;

         /* add exact data */
         SCIP_CALL( SCIPvarAddExactData(*var, blkmem, lb, ub, obj) );

         /**@todo implement lazy bounds in exact solving mode (and adjust values before setting them) */
         if( !SCIPrationalIsNegInfinity(lazylb) || !SCIPrationalIsInfinity(lazyub) )
         {
            SCIPerrorMessage("exact lazy bounds not supported yet\n");
            return SCIP_READERROR;
         }

         /* capture variable */
         SCIPvarCapture(*var);
      }

      SCIPrationalFreeBlock(blkmem, &lazyub);
      SCIPrationalFreeBlock(blkmem, &lazylb);
      SCIPrationalFreeBlock(blkmem, &obj);
      SCIPrationalFreeBlock(blkmem, &ub);
      SCIPrationalFreeBlock(blkmem, &lb);
   }
   else
   {
      SCIP_Real lb;
      SCIP_Real ub;
      SCIP_Real obj;
      SCIP_Real lazylb;
      SCIP_Real lazyub;

      /* parse string in cip format for variable information */
      SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, NULL, NULL, NULL, &vartype, &impltype,
            &lazylb, &lazyub, NULL, NULL, FALSE, endptr, success) );

      if( *success ) /*lint !e774*/
      {
         /* create variable */
         SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, impltype, initial, removable,
               varcopy, vardelorig, vartrans, vardeltrans, vardata) );

         /* set variable status */
         assert(*var != NULL);
         (*var)->varstatus = (unsigned int)SCIP_VARSTATUS_ORIGINAL;
         (*var)->data.original.origdom.holelist = NULL;
         (*var)->data.original.origdom.lb = (*var)->glbdom.lb;
         (*var)->data.original.origdom.ub = (*var)->glbdom.ub;
         (*var)->data.original.transvar = NULL;
         SCIPvarAdjustLb(*var, set, &lazylb);
         SCIPvarAdjustUb(*var, set, &lazyub);
         (*var)->lazylb = lazylb;
         (*var)->lazyub = lazyub;

         /* capture variable */
         SCIPvarCapture(*var);
      }
   }

   return SCIP_OKAY;
}

/** parses variable information (in cip format) out of a string; if the parsing process was successful a loose variable
 *  belonging to the transformed problem is created and captured; if variable is of integral type, fractional bounds are
 *  automatically rounded; an integer variable with bounds zero and one is automatically converted into a binary
 *  variable
 */
SCIP_RETCODE SCIPvarParseTransformed(
   SCIP_VAR**            var,                /**< pointer to variable data */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   SCIP_STAT*            stat,               /**< problem statistics */
   const char*           str,                /**< string to parse */
   SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
   SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
   SCIP_DECL_VARCOPY     ((*varcopy)),       /**< copies variable data if wanted to subscip, or NULL */
   SCIP_DECL_VARDELORIG  ((*vardelorig)),    /**< frees user data of original variable */
   SCIP_DECL_VARTRANS    ((*vartrans)),      /**< creates transformed user data by transforming original user data */
   SCIP_DECL_VARDELTRANS ((*vardeltrans)),   /**< frees user data of transformed variable */
   SCIP_VARDATA*         vardata,            /**< user data for this specific variable */
   char**                endptr,             /**< pointer to store the final string position if successfully */
   SCIP_Bool*            success             /**< pointer store if the paring process was successful */
   )
{
   char name[SCIP_MAXSTRLEN];
   SCIP_VARTYPE vartype;
   SCIP_IMPLINTTYPE impltype;

   assert(var != NULL);
   assert(blkmem != NULL);
   assert(endptr != NULL);
   assert(success != NULL);

   /* parse exact variable */
   if( set->exact_enable )
   {
      SCIP_RATIONAL* lb;
      SCIP_RATIONAL* ub;
      SCIP_RATIONAL* obj;
      SCIP_RATIONAL* lazylb;
      SCIP_RATIONAL* lazyub;

      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &ub) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &obj) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lazylb) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &lazyub) );

      /* parse string in cip format for exact variable information */
      SCIP_CALL( varParse(set, messagehdlr, str, name, NULL, NULL, NULL, lb, ub, obj, &vartype, &impltype,
            NULL, NULL, lazylb, lazyub, TRUE, endptr, success) );

      if( *success ) /*lint !e774*/
      {
         /* create variable */
         SCIP_CALL( varCreate(var, blkmem, set, stat, name, 0.0, 0.0, 0.0, vartype, impltype, initial, removable,
               varcopy, vardelorig, vartrans, vardeltrans, vardata) );

         /* set variable status */
         SCIPvarAdjustLbExact(*var, set, lb);
         SCIPvarAdjustUbExact(*var, set, ub);
         (*var)->varstatus = (unsigned int)SCIP_VARSTATUS_LOOSE;
         (*var)->data.loose.minaggrcoef = 1.0;
         (*var)->data.loose.maxaggrcoef = 1.0;

         /* add exact data */
         SCIP_CALL( SCIPvarAddExactData(*var, blkmem, lb, ub, obj) );

         /**@todo implement lazy bounds in exact solving mode */
         if( !SCIPrationalIsNegInfinity(lazylb) || !SCIPrationalIsInfinity(lazyub) )
         {
            SCIPerrorMessage("exact lazy bounds not supported yet\n");
            return SCIP_READERROR;
         }

         /* create event filter for transformed variable */
         SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );

         /* capture variable */
         SCIPvarCapture(*var);
      }

      SCIPrationalFreeBlock(blkmem, &lazyub);
      SCIPrationalFreeBlock(blkmem, &lazylb);
      SCIPrationalFreeBlock(blkmem, &obj);
      SCIPrationalFreeBlock(blkmem, &ub);
      SCIPrationalFreeBlock(blkmem, &lb);
   }
   /* parse real variable */
   else
   {
      SCIP_Real lb;
      SCIP_Real ub;
      SCIP_Real obj;
      SCIP_Real lazylb;
      SCIP_Real lazyub;

      /* parse string in cip format for variable information */
      SCIP_CALL( varParse(set, messagehdlr, str, name, &lb, &ub, &obj, NULL, NULL, NULL, &vartype, &impltype,
            &lazylb, &lazyub, NULL, NULL, TRUE, endptr, success) );

      if( *success ) /*lint !e774*/
      {
         /* create variable */
         SCIP_CALL( varCreate(var, blkmem, set, stat, name, lb, ub, obj, vartype, impltype, initial, removable,
               varcopy, vardelorig, vartrans, vardeltrans, vardata) );

         /* set variable status */
         assert(*var != NULL);
         (*var)->varstatus = (unsigned int)SCIP_VARSTATUS_LOOSE;
         (*var)->data.loose.minaggrcoef = 1.0;
         (*var)->data.loose.maxaggrcoef = 1.0;
         (*var)->lazylb = lazylb;
         (*var)->lazyub = lazyub;

         /* create event filter for transformed variable */
         SCIP_CALL( SCIPeventfilterCreate(&(*var)->eventfilter, blkmem) );

         /* capture variable */
         SCIPvarCapture(*var);
      }
   }

   return SCIP_OKAY;
}

/** ensures, that parentvars array of var can store at least num entries */
static
SCIP_RETCODE varEnsureParentvarsSize(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   int                   num                 /**< minimum number of entries to store */
   )
{
   assert(var->nparentvars <= var->parentvarssize);

   if( num > var->parentvarssize )
   {
      int newsize;

      newsize = SCIPsetCalcMemGrowSize(set, num);
      SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &var->parentvars, var->parentvarssize, newsize) );
      var->parentvarssize = newsize;
   }
   assert(num <= var->parentvarssize);

   return SCIP_OKAY;
}

/** adds variable to parent list of a variable and captures parent variable */
static
SCIP_RETCODE varAddParent(
   SCIP_VAR*             var,                /**< variable to add parent to */
   BMS_BLKMEM*           blkmem,             /**< block memory of transformed problem */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR*             parentvar           /**< parent variable to add */
   )
{
   assert(var != NULL);
   assert(parentvar != NULL);

   /* the direct original counterpart must be stored as first parent */
   assert(var->nparentvars == 0 || SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL);

   SCIPsetDebugMsg(set, "adding parent <%s>[%p] to variable <%s>[%p] in slot %d\n",
      parentvar->name, (void*)parentvar, var->name, (void*)var, var->nparentvars);

   SCIP_CALL( varEnsureParentvarsSize(var, blkmem, set, var->nparentvars+1) );

   var->parentvars[var->nparentvars] = parentvar;
   var->nparentvars++;

   SCIPvarCapture(parentvar);

   return SCIP_OKAY;
}

/** deletes and releases all variables from the parent list of a variable, frees the memory of parents array */
static
SCIP_RETCODE varFreeParents(
   SCIP_VAR**            var,                /**< pointer to variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue (or NULL, if it's an original variable) */
   SCIP_LP*              lp                  /**< current LP data (or NULL, if it's an original variable) */
   )
{
   SCIP_VAR* parentvar;
   int i;

   SCIPsetDebugMsg(set, "free parents of <%s>\n", (*var)->name);

   /* release the parent variables and remove the link from the parent variable to the child */
   for( i = 0; i < (*var)->nparentvars; ++i )
   {
      assert((*var)->parentvars != NULL);
      parentvar = (*var)->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         assert(parentvar->data.original.transvar == *var);
         assert(&parentvar->data.original.transvar != var);
         parentvar->data.original.transvar = NULL;
         break;

      case SCIP_VARSTATUS_AGGREGATED:
         assert(parentvar->data.aggregate.var == *var);
         assert(&parentvar->data.aggregate.var != var);
         parentvar->data.aggregate.var = NULL;
         break;

#ifdef SCIP_DISABLED_CODE
      /* The following code is unclear: should the current variable be removed from its parents? */
      case SCIP_VARSTATUS_MULTAGGR:
         assert(parentvar->data.multaggr.vars != NULL);
         for( v = 0; v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] != *var; ++v )
         {}
         assert(v < parentvar->data.multaggr.nvars && parentvar->data.multaggr.vars[v] == *var);
         if( v < parentvar->data.multaggr.nvars-1 )
         {
            parentvar->data.multaggr.vars[v] = parentvar->data.multaggr.vars[parentvar->data.multaggr.nvars-1];
            parentvar->data.multaggr.scalars[v] = parentvar->data.multaggr.scalars[parentvar->data.multaggr.nvars-1];
         }
         parentvar->data.multaggr.nvars--;
         break;
#endif

      case SCIP_VARSTATUS_NEGATED:
         assert(parentvar->negatedvar == *var);
         assert((*var)->negatedvar == parentvar);
         parentvar->negatedvar = NULL;
         (*var)->negatedvar = NULL;
         break;

      default:
         SCIPerrorMessage("parent variable is neither ORIGINAL, AGGREGATED nor NEGATED\n");
         return SCIP_INVALIDDATA;
      }  /*lint !e788*/

      SCIP_CALL( SCIPvarRelease(&(*var)->parentvars[i], blkmem, set, eventqueue, lp) );
   }

   /* free parentvars array */
   BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->parentvars, (*var)->parentvarssize);

   return SCIP_OKAY;
}

/** free exact variable data, if it exists */
static
SCIP_RETCODE varFreeExactData(
   SCIP_VAR*             var,                /**< variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   assert(blkmem != NULL);
   assert(var != NULL);

   if( !set->exact_enable )
   {
      assert( var->exactdata == NULL );
      return SCIP_OKAY;
   }

   /* free exact variable data if it was created */
   if( var->exactdata != NULL )
   {
      if( SCIPvarGetStatusExact(var) ==  SCIP_VARSTATUS_COLUMN )
      {
         SCIP_CALL( SCIPcolExactFree(&(var->exactdata->colexact), blkmem) );
      }

      if( var->exactdata->aggregate.scalar != NULL )
      {
         SCIPrationalFreeBlock(blkmem, &(var)->exactdata->aggregate.constant);
         SCIPrationalFreeBlock(blkmem, &(var)->exactdata->aggregate.scalar);
      }

      if( var->exactdata->multaggr.scalars != NULL )
      {
         SCIPrationalFreeBlock(blkmem, &(var)->exactdata->multaggr.constant);
         SCIPrationalFreeBlockArray(blkmem, &(var)->exactdata->multaggr.scalars, var->data.multaggr.varssize);
      }

      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->glbdom.lb);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->glbdom.ub);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->locdom.lb);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->locdom.ub);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->origdom.lb);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->origdom.ub);
      SCIPrationalFreeBlock(blkmem, &(var)->exactdata->obj );

      BMSfreeBlockMemory(blkmem, &(var)->exactdata);
      assert((var)->exactdata == NULL);
   }

   return SCIP_OKAY;
}

/** frees a variable */
static
SCIP_RETCODE varFree(
   SCIP_VAR**            var,                /**< pointer to variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue (may be NULL, if it's not a column variable) */
   SCIP_LP*              lp                  /**< current LP data (may be NULL, if it's not a column variable) */
   )
{
   assert(var != NULL);
   assert(*var != NULL);
   assert(SCIPvarGetStatus(*var) != SCIP_VARSTATUS_COLUMN || &(*var)->data.col->var != var);
   assert((*var)->nuses == 0);
   assert((*var)->probindex == -1);
   assert((*var)->nlocksup[SCIP_LOCKTYPE_MODEL] == 0);
   assert((*var)->nlocksdown[SCIP_LOCKTYPE_MODEL] == 0);

   SCIPsetDebugMsg(set, "free variable <%s> with status=%d\n", (*var)->name, SCIPvarGetStatus(*var));

   switch( SCIPvarGetStatus(*var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert((*var)->data.original.transvar == NULL); /* cannot free variable, if transformed variable is still existing */
      holelistFree(&(*var)->data.original.origdom.holelist, blkmem);
      assert((*var)->data.original.origdom.holelist == NULL);
      break;
   case SCIP_VARSTATUS_LOOSE:
      break;
   case SCIP_VARSTATUS_COLUMN:
      SCIP_CALL( SCIPcolFree(&(*var)->data.col, blkmem, set, eventqueue, lp) );  /* free corresponding LP column */
      break;
   case SCIP_VARSTATUS_FIXED:
   case SCIP_VARSTATUS_AGGREGATED:
      break;
   case SCIP_VARSTATUS_MULTAGGR:
      BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.vars, (*var)->data.multaggr.varssize);
      BMSfreeBlockMemoryArray(blkmem, &(*var)->data.multaggr.scalars, (*var)->data.multaggr.varssize);
      break;
   case SCIP_VARSTATUS_NEGATED:
      break;
   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   /* release all parent variables and free the parentvars array */
   SCIP_CALL( varFreeParents(var, blkmem, set, eventqueue, lp) );

   /* free user data */
   if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_ORIGINAL )
   {
      if( (*var)->vardelorig != NULL )
      {
         SCIP_CALL( (*var)->vardelorig(set->scip, *var, &(*var)->vardata) );
      }
   }
   else
   {
      if( (*var)->vardeltrans != NULL )
      {
         SCIP_CALL( (*var)->vardeltrans(set->scip, *var, &(*var)->vardata) );
      }
   }

   /* free event filter */
   if( (*var)->eventfilter != NULL )
   {
      SCIP_CALL( SCIPeventfilterFree(&(*var)->eventfilter, blkmem, set) );
   }
   assert((*var)->eventfilter == NULL);

   /* free hole lists */
   holelistFree(&(*var)->glbdom.holelist, blkmem);
   holelistFree(&(*var)->locdom.holelist, blkmem);
   assert((*var)->glbdom.holelist == NULL);
   assert((*var)->locdom.holelist == NULL);

   /* free variable bounds data structures */
   SCIPvboundsFree(&(*var)->vlbs, blkmem);
   SCIPvboundsFree(&(*var)->vubs, blkmem);

   /* free implications data structures */
   SCIPimplicsFree(&(*var)->implics, blkmem);

   /* free clique list data structures */
   SCIPcliquelistFree(&(*var)->cliquelist, blkmem);

   /* free bound change information arrays */
   BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->lbchginfos, (*var)->lbchginfossize);
   BMSfreeBlockMemoryArrayNull(blkmem, &(*var)->ubchginfos, (*var)->ubchginfossize);

   /* free branching and inference history entries */
   SCIPhistoryFree(&(*var)->history, blkmem);
   SCIPhistoryFree(&(*var)->historycrun, blkmem);
   SCIPvaluehistoryFree(&(*var)->valuehistory, blkmem);

   /* free exact data if it exists */
   SCIP_CALL( varFreeExactData(*var, blkmem, set) );

   /* free variable data structure */
   BMSfreeBlockMemoryArray(blkmem, &(*var)->name, strlen((*var)->name)+1);
   BMSfreeBlockMemory(blkmem, var);

   return SCIP_OKAY;
}

/** increases usage counter of variable */
void SCIPvarCapture(
   SCIP_VAR*             var                 /**< variable */
   )
{
   assert(var != NULL);
   assert(var->nuses >= 0);

   SCIPdebugMessage("capture variable <%s> with nuses=%d\n", var->name, var->nuses);
   var->nuses++;

#ifdef DEBUGUSES_VARNAME
   if( strcmp(var->name, DEBUGUSES_VARNAME) == 0
#ifdef DEBUGUSES_PROBNAME
      && ((var->scip->transprob != NULL && strcmp(SCIPprobGetName(var->scip->transprob), DEBUGUSES_PROBNAME) == 0) ||
          strcmp(SCIPprobGetName(var->scip->origprob), DEBUGUSES_PROBNAME) == 0)
#endif
   )
   {
      printf("Captured variable " DEBUGUSES_VARNAME " in SCIP %p, now %d uses; captured at\n", (void*)var->scip, var->nuses);  /* cppcheck-suppress syntaxError */
      print_backtrace();
   }
#endif
}

/** decreases usage counter of variable, and frees memory if necessary */
SCIP_RETCODE SCIPvarRelease(
   SCIP_VAR**            var,                /**< pointer to variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_LP*              lp                  /**< current LP data (or NULL, if it's an original variable) */
   )
{
   assert(var != NULL);
   assert(*var != NULL);
   assert((*var)->nuses >= 1);
   assert(blkmem != NULL);
   assert((*var)->scip == set->scip);

   SCIPsetDebugMsg(set, "release variable <%s> with nuses=%d\n", (*var)->name, (*var)->nuses);
   (*var)->nuses--;

#ifdef DEBUGUSES_VARNAME
   if( strcmp((*var)->name, DEBUGUSES_VARNAME) == 0
#ifdef DEBUGUSES_PROBNAME
      && (((*var)->scip->transprob != NULL && strcmp(SCIPprobGetName((*var)->scip->transprob), DEBUGUSES_PROBNAME) == 0) ||
          strcmp(SCIPprobGetName((*var)->scip->origprob), DEBUGUSES_PROBNAME) == 0)
#endif
   )
   {
      printf("Released variable " DEBUGUSES_VARNAME " in SCIP %p, now %d uses; released at\n", (void*)(*var)->scip, (*var)->nuses);
      print_backtrace();
   }
#endif

   if( (*var)->nuses == 0 )
   {
      SCIP_CALL( varFree(var, blkmem, set, eventqueue, lp) );
   }

   *var = NULL;

   return SCIP_OKAY;
}

/** change variable name */
SCIP_RETCODE SCIPvarChgName(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   const char*           name                /**< name of variable */
   )
{
   assert(name != NULL);

   /* remove old variable name */
   BMSfreeBlockMemoryArray(blkmem, &var->name, strlen(var->name)+1);

   /* set new variable name */
   SCIP_CALL( varSetName(var, blkmem, NULL, name) );

   return SCIP_OKAY;
}

/** initializes variable data structure for solving */
void SCIPvarInitSolve(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   SCIPhistoryReset(var->historycrun);
   var->conflictlbcount = 0;
   var->conflictubcount = 0;
}

/** outputs the given bounds into the file stream */
static
void printBounds(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   FILE*                 file,               /**< output file (or NULL for standard output) */
   SCIP_Real             lb,                 /**< lower bound */
   SCIP_Real             ub,                 /**< upper bound */
   const char*           name                /**< bound type name */
   )
{
   assert(set != NULL);

   SCIPmessageFPrintInfo(messagehdlr, file, ", %s=[", name);
   if( SCIPsetIsInfinity(set, lb) )
      SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
   else if( SCIPsetIsInfinity(set, -lb) )
      SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
   else
      SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", lb);
   SCIPmessageFPrintInfo(messagehdlr, file, ",");
   if( SCIPsetIsInfinity(set, ub) )
      SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
   else if( SCIPsetIsInfinity(set, -ub) )
      SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
   else
      SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", ub);
   SCIPmessageFPrintInfo(messagehdlr, file, "]");
}

/** outputs the given exact bounds into the file stream */
static
void printBoundsExact(
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   FILE*                 file,               /**< output file (or NULL for standard output) */
   SCIP_RATIONAL*        lb,                 /**< exact lower bound */
   SCIP_RATIONAL*        ub,                 /**< exact upper bound */
   const char*           name                /**< bound type name */
   )
{
   assert(lb != NULL);
   assert(ub != NULL);

   SCIPmessageFPrintInfo(messagehdlr, file, ", %s=[", name);
   if( SCIPrationalIsInfinity(lb) )
      SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
   else if( SCIPrationalIsNegInfinity(lb) )
      SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
   else
      SCIPrationalMessage(messagehdlr, file, lb);
   SCIPmessageFPrintInfo(messagehdlr, file, ",");
   if( SCIPrationalIsInfinity(ub) )
      SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
   else if( SCIPrationalIsNegInfinity(ub) )
      SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
   else
      SCIPrationalMessage(messagehdlr, file, ub);
   SCIPmessageFPrintInfo(messagehdlr, file, "]");
}

/** prints hole list to file stream */
static
void printHolelist(
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   FILE*                 file,               /**< output file (or NULL for standard output) */
   SCIP_HOLELIST*        holelist,           /**< hole list pointer to hole of interest */
   const char*           name                /**< hole type name */
   )
{  /*lint --e{715}*/
   SCIP_Real left;
   SCIP_Real right;

   if( holelist == NULL )
      return;

   left = SCIPholelistGetLeft(holelist);
   right = SCIPholelistGetRight(holelist);

   /* display first hole */
   SCIPmessageFPrintInfo(messagehdlr, file, ", %s=(%g,%g)", name, left, right);
   holelist = SCIPholelistGetNext(holelist);

   while(holelist != NULL  )
   {
      left = SCIPholelistGetLeft(holelist);
      right = SCIPholelistGetRight(holelist);

      /* display hole */
      SCIPmessageFPrintInfo(messagehdlr, file, "(%g,%g)", left, right);

      /* get next hole */
      holelist = SCIPholelistGetNext(holelist);
   }
}

/** outputs variable information into file stream */
SCIP_RETCODE SCIPvarPrint(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
   FILE*                 file                /**< output file (or NULL for standard output) */
   )
{
   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(set != NULL);
   assert(set->write_implintlevel >= -2);
   assert(set->write_implintlevel <= 2);

   SCIP_VARTYPE vartype = SCIPvarGetType(var);
   SCIP_IMPLINTTYPE impltype = SCIPvarGetImplType(var);
   int i;

   /* change integrality constraints of implied integral variables based on the writing settings */
   if( vartype == SCIP_VARTYPE_CONTINUOUS )
   {
      if( (int)impltype > 2 - set->write_implintlevel )
         vartype = SCIP_VARTYPE_INTEGER;
   }
   else
   {
      if( (int)impltype > 2 + set->write_implintlevel )
         vartype = SCIP_VARTYPE_CONTINUOUS;
   }

   /* type of variable */
   switch( vartype )
   {
      case SCIP_VARTYPE_BINARY:
         SCIPmessageFPrintInfo(messagehdlr, file, "  [binary]");
         break;
      case SCIP_VARTYPE_INTEGER:
         SCIPmessageFPrintInfo(messagehdlr, file, "  [integer]");
         break;
      case SCIP_VARTYPE_CONTINUOUS:
         SCIPmessageFPrintInfo(messagehdlr, file, "  [continuous]");
         break;
      case SCIP_DEPRECATED_VARTYPE_IMPLINT:
      default:
         SCIPerrorMessage("unknown variable type\n");
         return SCIP_INVALIDDATA;
   } /*lint !e788*/

   /* name */
   SCIPmessageFPrintInfo(messagehdlr, file, " <%s>:", var->name);

   if( var->exactdata != NULL )
   {
      assert(set->exact_enable);

      SCIP_RATIONAL* lb;
      SCIP_RATIONAL* ub;

      /* exact objective value */
      assert(var->exactdata->obj != NULL);
      SCIPmessageFPrintInfo(messagehdlr, file, " obj=");
      SCIPrationalMessage(messagehdlr, file, var->exactdata->obj);

      /* exact bounds (global bounds for transformed variables, original bounds for original variables) */
      if( !SCIPvarIsTransformed(var) )
      {
         /* output exact original bounds */
         lb = SCIPvarGetLbOriginalExact(var);
         ub = SCIPvarGetUbOriginalExact(var);
         printBoundsExact(messagehdlr, file, lb, ub, "original bounds");

         /**@todo get exact lazy bounds */
         /**@todo output exact lazy bounds */
         if( !SCIPsetIsInfinity(set, -SCIPvarGetLbLazy(var)) || !SCIPsetIsInfinity(set, SCIPvarGetUbLazy(var)) )
         {
            SCIPerrorMessage("exact lazy bounds not supported yet\n");
            return SCIP_INVALIDDATA;
         }

         assert(SCIPvarGetHolelistOriginal(var) == NULL);
      }
      else
      {
         /* output exact global bounds */
         lb = SCIPvarGetLbGlobalExact(var);
         ub = SCIPvarGetUbGlobalExact(var);
         printBoundsExact(messagehdlr, file, lb, ub, "global bounds");

         /* output exact local bounds */
         lb = SCIPvarGetLbLocalExact(var);
         ub = SCIPvarGetUbLocalExact(var);
         printBoundsExact(messagehdlr, file, lb, ub, "local bounds");

         /**@todo get exact lazy bounds */
         /**@todo output exact lazy bounds */
         if( !SCIPsetIsInfinity(set, -SCIPvarGetLbLazy(var)) || !SCIPsetIsInfinity(set, SCIPvarGetUbLazy(var)) )
         {
            SCIPerrorMessage("exact lazy bounds not supported yet\n");
            return SCIP_INVALIDDATA;
         }

         assert(SCIPvarGetHolelistGlobal(var) == NULL);
         assert(SCIPvarGetHolelistLocal(var) == NULL);
      }
   }
   else
   {
      assert(!set->exact_enable);

      SCIP_Real lb;
      SCIP_Real ub;

      /* objective value */
      SCIPmessageFPrintInfo(messagehdlr, file, " obj=%.15g", var->obj);

      /* bounds (global bounds for transformed variables, original bounds for original variables) */
      if( !SCIPvarIsTransformed(var) )
      {
         /* output original bounds */
         lb = SCIPvarGetLbOriginal(var);
         ub = SCIPvarGetUbOriginal(var);
         printBounds(set, messagehdlr, file, lb, ub, "original bounds");

         /* output lazy bounds */
         lb = SCIPvarGetLbLazy(var);
         ub = SCIPvarGetUbLazy(var);

         /* only display the lazy bounds if they are different from [-infinity,infinity] */
         if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
            printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");

         /* original hole list */
         printHolelist(messagehdlr, file, SCIPvarGetHolelistOriginal(var), "original holes");
      }
      else
      {
         /* output global bounds */
         lb = SCIPvarGetLbGlobal(var);
         ub = SCIPvarGetUbGlobal(var);
         printBounds(set, messagehdlr, file, lb, ub, "global bounds");

         /* output local bounds */
         lb = SCIPvarGetLbLocal(var);
         ub = SCIPvarGetUbLocal(var);
         printBounds(set, messagehdlr, file, lb, ub, "local bounds");

         /* output lazy bounds */
         lb = SCIPvarGetLbLazy(var);
         ub = SCIPvarGetUbLazy(var);

         /* only display the lazy bounds if they are different from [-infinity,infinity] */
         if( !SCIPsetIsInfinity(set, -lb) || !SCIPsetIsInfinity(set, ub) )
            printBounds(set, messagehdlr, file, lb, ub, "lazy bounds");

         /* global hole list */
         printHolelist(messagehdlr, file, SCIPvarGetHolelistGlobal(var), "global holes");

         /* local hole list */
         printHolelist(messagehdlr, file, SCIPvarGetHolelistLocal(var), "local holes");
      }
   }

   /* implication of variable */
   switch( impltype )
   {
      case SCIP_IMPLINTTYPE_NONE:
         break;
      case SCIP_IMPLINTTYPE_WEAK:
         SCIPmessageFPrintInfo(messagehdlr, file, ", implied: weak");
         break;
      case SCIP_IMPLINTTYPE_STRONG:
         SCIPmessageFPrintInfo(messagehdlr, file, ", implied: strong");
         break;
      default:
         SCIPerrorMessage("unknown implied type\n");
         return SCIP_INVALIDDATA;
   }

   /* fixings and aggregations */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPmessageFPrintInfo(messagehdlr, file, ", fixed:");
      if( SCIPsetIsInfinity(set, var->glbdom.lb) )
         SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
      else if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
         SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
      else
         SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", var->glbdom.lb);
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
      if( !SCIPsetIsZero(set, var->data.aggregate.constant) )
         SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.aggregate.constant);
      SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.aggregate.scalar, SCIPvarGetName(var->data.aggregate.var));
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPmessageFPrintInfo(messagehdlr, file, ", aggregated:");
      if( var->data.multaggr.nvars == 0 || !SCIPsetIsZero(set, var->data.multaggr.constant) )
         SCIPmessageFPrintInfo(messagehdlr, file, " %.15g", var->data.multaggr.constant);
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g<%s>", var->data.multaggr.scalars[i], SCIPvarGetName(var->data.multaggr.vars[i]));
      break;

   case SCIP_VARSTATUS_NEGATED:
      SCIPmessageFPrintInfo(messagehdlr, file, ", negated: %.15g - <%s>", var->data.negate.constant, SCIPvarGetName(var->negatedvar));
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   SCIPmessageFPrintInfo(messagehdlr, file, "\n");

   return SCIP_OKAY;
}

/** issues a VARUNLOCKED event on the given variable */
static
SCIP_RETCODE varEventVarUnlocked(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue          /**< event queue */
   )
{
   SCIP_EVENT* event;

   assert(var != NULL);
   assert(var->nlocksdown[SCIP_LOCKTYPE_MODEL] <= 1 && var->nlocksup[SCIP_LOCKTYPE_MODEL] <= 1);
   assert(var->scip == set->scip);

   /* issue VARUNLOCKED event on variable */
   SCIP_CALL( SCIPeventCreateVarUnlocked(&event, blkmem, var) );
   SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );

   return SCIP_OKAY;
}

/** modifies lock numbers for rounding */
SCIP_RETCODE SCIPvarAddLocks(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_LOCKTYPE         locktype,           /**< type of the variable locks */
   int                   addnlocksdown,      /**< increase in number of rounding down locks */
   int                   addnlocksup         /**< increase in number of rounding up locks */
   )
{
   SCIP_VAR* lockvar;

   assert(var != NULL);
   assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
   assert(var->nlocksup[locktype] >= 0);
   assert(var->nlocksdown[locktype] >= 0);
   assert(var->scip == set->scip);

   if( addnlocksdown == 0 && addnlocksup == 0 )
      return SCIP_OKAY;

#ifdef SCIP_DEBUG
   SCIPsetDebugMsg(set, "add rounding locks %d/%d to variable <%s> (locks=%d/%d, type=%u)\n",
         addnlocksdown, addnlocksup, var->name, var->nlocksdown[locktype], var->nlocksup[locktype], locktype);
#endif

   lockvar = var;

   while( TRUE ) /*lint !e716 */
   {
      assert(lockvar != NULL);

      switch( SCIPvarGetStatus(lockvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( lockvar->data.original.transvar != NULL )
         {
            lockvar = lockvar->data.original.transvar;
            break;
         }
         else
         {
            lockvar->nlocksdown[locktype] += addnlocksdown;
            lockvar->nlocksup[locktype] += addnlocksup;

            assert(lockvar->nlocksdown[locktype] >= 0);
            assert(lockvar->nlocksup[locktype] >= 0);

            return SCIP_OKAY;
         }
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_FIXED:
         lockvar->nlocksdown[locktype] += addnlocksdown;
         lockvar->nlocksup[locktype] += addnlocksup;

         assert(lockvar->nlocksdown[locktype] >= 0);
         assert(lockvar->nlocksup[locktype] >= 0);

         if( locktype == SCIP_LOCKTYPE_MODEL && lockvar->nlocksdown[locktype] <= 1
            && lockvar->nlocksup[locktype] <= 1 )
         {
            SCIP_CALL( varEventVarUnlocked(lockvar, blkmem, set, eventqueue) );
         }

         return SCIP_OKAY;
      case SCIP_VARSTATUS_AGGREGATED:
         assert(!lockvar->donotaggr);

         if( lockvar->data.aggregate.scalar < 0.0 )
         {
            int tmp = addnlocksup;

            addnlocksup = addnlocksdown;
            addnlocksdown = tmp;
         }

         lockvar = lockvar->data.aggregate.var;
         break;
      case SCIP_VARSTATUS_MULTAGGR:
      {
         int v;

         assert(!lockvar->donotmultaggr);

         lockvar->nlocksdown[locktype] += addnlocksdown;
         lockvar->nlocksup[locktype] += addnlocksup;

         assert(lockvar->nlocksdown[locktype] >= 0);
         assert(lockvar->nlocksup[locktype] >= 0);

         for( v = lockvar->data.multaggr.nvars - 1; v >= 0; --v )
         {
            if( lockvar->data.multaggr.scalars[v] > 0.0 )
            {
               SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksdown,
                     addnlocksup) );
            }
            else
            {
               SCIP_CALL( SCIPvarAddLocks(lockvar->data.multaggr.vars[v], blkmem, set, eventqueue, locktype, addnlocksup,
                     addnlocksdown) );
            }
         }
         return SCIP_OKAY;
      }
      case SCIP_VARSTATUS_NEGATED:
      {
         int tmp = addnlocksup;

         assert(lockvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(lockvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(lockvar->negatedvar->negatedvar == lockvar);

         addnlocksup = addnlocksdown;
         addnlocksdown = tmp;

         lockvar = lockvar->negatedvar;
         break;
      }
      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }
}

/** gets number of locks for rounding down of a special type */
int SCIPvarGetNLocksDownType(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_LOCKTYPE         locktype            /**< type of variable locks */
   )
{
   int nlocks;
   int i;

   assert(var != NULL);
   assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
   assert(var->nlocksdown[locktype] >= 0);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
         return SCIPvarGetNLocksDownType(var->data.original.transvar, locktype);
      else
         return var->nlocksdown[locktype];

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      return var->nlocksdown[locktype];

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);
      else
         return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      nlocks = 0;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         if( var->data.multaggr.scalars[i] > 0.0 )
            nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
         else
            nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
      }
      return nlocks;

   case SCIP_VARSTATUS_NEGATED:
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return SCIPvarGetNLocksUpType(var->negatedvar, locktype);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return INT_MAX; /*lint !e527*/
   }
}

/** gets number of locks for rounding up of a special type */
int SCIPvarGetNLocksUpType(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_LOCKTYPE         locktype            /**< type of variable locks */
   )
{
   int nlocks;
   int i;

   assert(var != NULL);
   assert((int)locktype >= 0 && (int)locktype < (int)NLOCKTYPES); /*lint !e685 !e568 !e587 !e650*/
   assert(var->nlocksup[locktype] >= 0);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
         return SCIPvarGetNLocksUpType(var->data.original.transvar, locktype);
      else
         return var->nlocksup[locktype];

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      return var->nlocksup[locktype];

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNLocksUpType(var->data.aggregate.var, locktype);
      else
         return SCIPvarGetNLocksDownType(var->data.aggregate.var, locktype);

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      nlocks = 0;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         if( var->data.multaggr.scalars[i] > 0.0 )
            nlocks += SCIPvarGetNLocksUpType(var->data.multaggr.vars[i], locktype);
         else
            nlocks += SCIPvarGetNLocksDownType(var->data.multaggr.vars[i], locktype);
      }
      return nlocks;

   case SCIP_VARSTATUS_NEGATED:
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return SCIPvarGetNLocksDownType(var->negatedvar, locktype);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return INT_MAX; /*lint !e527*/
   }
}

/** gets number of locks for rounding down
 *
 *  @note This method will always return variable locks of type model
 *
 *  @note It is recommented to use SCIPvarGetNLocksDownType()
 */
int SCIPvarGetNLocksDown(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL);
}

/** gets number of locks for rounding up
 *
 *  @note This method will always return variable locks of type model
 *
 *  @note It is recommented to use SCIPvarGetNLocksUpType()
 */
int SCIPvarGetNLocksUp(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL);
}

/** is it possible, to round variable down and stay feasible?
 *
 *  @note This method will always check w.r.t variable locks of type model
 */
SCIP_Bool SCIPvarMayRoundDown(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return (SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == 0);
}

/** is it possible, to round variable up and stay feasible?
 *
 *  @note This method will always check w.r.t. variable locks of type model
 */
SCIP_Bool SCIPvarMayRoundUp(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return (SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 0);
}

/** gets and captures transformed variable of a given variable; if the variable is not yet transformed,
 *  a new transformed variable for this variable is created
 */
SCIP_RETCODE SCIPvarTransform(
   SCIP_VAR*             origvar,            /**< original problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory of transformed problem */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_OBJSENSE         objsense,           /**< objective sense of original problem; transformed is always MINIMIZE */
   SCIP_VAR**            transvar            /**< pointer to store the transformed variable */
   )
{
   char name[SCIP_MAXSTRLEN];

   assert(origvar != NULL);
   assert(origvar->scip == set->scip);
   assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL);
   assert(SCIPsetIsEQ(set, origvar->glbdom.lb, origvar->locdom.lb));
   assert(SCIPsetIsEQ(set, origvar->glbdom.ub, origvar->locdom.ub));
   assert(origvar->vlbs == NULL);
   assert(origvar->vubs == NULL);
   assert(transvar != NULL);

   /* check if variable is already transformed */
   if( origvar->data.original.transvar != NULL )
   {
      *transvar = origvar->data.original.transvar;
      SCIPvarCapture(*transvar);
   }
   else
   {
      int i;

      /* create transformed variable */
      (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "t_%s", origvar->name);
      SCIP_CALL( SCIPvarCreateTransformed(transvar, blkmem, set, stat, name,
            origvar->glbdom.lb, origvar->glbdom.ub, (SCIP_Real)objsense * origvar->obj,
            SCIPvarGetType(origvar), SCIPvarGetImplType(origvar), origvar->initial, origvar->removable,
            origvar->vardelorig, origvar->vartrans, origvar->vardeltrans, origvar->varcopy, NULL) );

      /* copy the branch factor and priority */
      (*transvar)->branchfactor = origvar->branchfactor;
      (*transvar)->branchpriority = origvar->branchpriority;
      (*transvar)->branchdirection = origvar->branchdirection; /*lint !e732*/

      /* duplicate hole lists */
      SCIP_CALL( holelistDuplicate(&(*transvar)->glbdom.holelist, blkmem, set, origvar->glbdom.holelist) );
      SCIP_CALL( holelistDuplicate(&(*transvar)->locdom.holelist, blkmem, set, origvar->locdom.holelist) );

      /* link original and transformed variable */
      origvar->data.original.transvar = *transvar;
      SCIP_CALL( varAddParent(*transvar, blkmem, set, origvar) );

      /* copy rounding locks */
      for( i = 0; i < NLOCKTYPES; i++ )
      {
         (*transvar)->nlocksdown[i] = origvar->nlocksdown[i];
         (*transvar)->nlocksup[i] = origvar->nlocksup[i];
         assert((*transvar)->nlocksdown[i] >= 0);
         assert((*transvar)->nlocksup[i] >= 0);
      }

      /* copy donot(mult)aggr status */
      (*transvar)->donotaggr = origvar->donotaggr;
      (*transvar)->donotmultaggr = origvar->donotmultaggr;

      /* copy lazy bounds */
      (*transvar)->lazylb = origvar->lazylb;
      (*transvar)->lazyub = origvar->lazyub;

      /* transfer eventual variable statistics; do not update global statistics, because this has been done
       * when original variable was created
       */
      SCIPhistoryUnite((*transvar)->history, origvar->history, FALSE);

      /* transform user data */
      if( origvar->vartrans != NULL )
      {
         SCIP_CALL( origvar->vartrans(set->scip, origvar, origvar->vardata, *transvar, &(*transvar)->vardata) );
      }
      else
         (*transvar)->vardata = origvar->vardata;
   }

   SCIPsetDebugMsg(set, "transformed variable: <%s>[%p] -> <%s>[%p]\n", origvar->name, (void*)origvar, (*transvar)->name, (void*)*transvar);

   return SCIP_OKAY;
}

/** gets corresponding transformed variable of an original or negated original variable */
SCIP_RETCODE SCIPvarGetTransformed(
   SCIP_VAR*             origvar,            /**< original problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory of transformed problem */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_VAR**            transvar            /**< pointer to store the transformed variable, or NULL if not existing yet */
   )
{
   assert(origvar != NULL);
   assert(SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED);
   assert(origvar->scip == set->scip);

   if( SCIPvarGetStatus(origvar) == SCIP_VARSTATUS_NEGATED )
   {
      assert(origvar->negatedvar != NULL);
      assert(SCIPvarGetStatus(origvar->negatedvar) == SCIP_VARSTATUS_ORIGINAL);

      if( origvar->negatedvar->data.original.transvar == NULL )
         *transvar = NULL;
      else
      {
         SCIP_CALL( SCIPvarNegate(origvar->negatedvar->data.original.transvar, blkmem, set, stat, transvar) );
      }
   }
   else
      *transvar = origvar->data.original.transvar;

   return SCIP_OKAY;
}

/** converts loose transformed variable into column variable, creates LP column */
SCIP_RETCODE SCIPvarColumn(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(var->scip == set->scip);

   SCIPsetDebugMsg(set, "creating column for variable <%s>\n", var->name);

   /* switch variable status */
   var->varstatus = SCIP_VARSTATUS_COLUMN; /*lint !e641*/

   /* create column of variable */
   SCIP_CALL( SCIPcolCreate(&var->data.col, blkmem, set, stat, var, 0, NULL, NULL, var->removable) );

   if( var->probindex != -1 )
   {
      /* inform problem about the variable's status change */
      SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );

      /* inform LP, that problem variable is now a column variable and no longer loose */
      SCIP_CALL( SCIPlpUpdateVarColumn(lp, set, var) );
   }

   return SCIP_OKAY;
}

/** converts loose transformed variable into column variable, creates LP column */
SCIP_RETCODE SCIPvarColumnExact(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lp                  /**< current LP data */
   )
{
   if( !set->exact_enable )
      return SCIP_OKAY;

   assert(var != NULL);
   assert(var->exactdata->colexact == NULL);
   assert(var->scip == set->scip);
   assert(var->exactdata != NULL);

   SCIPsetDebugMsg(set, "creating exact column for variable <%s>\n", var->name);

   /* switch variable status */
   var->exactdata->varstatusexact = SCIP_VARSTATUS_COLUMN; /*lint !e641*/

   /* create column of variable */
   SCIP_CALL( SCIPcolExactCreate(&(var->exactdata->colexact), SCIPvarGetCol(var), blkmem, set, stat, var, 0, NULL, NULL, var->removable) );

   if( var->probindex != -1 )
   {
      /* inform LP, that problem variable is now a column variable and no longer loose */
      SCIP_CALL( SCIPlpExactUpdateVarColumn(lp, set, var) );
   }

   return SCIP_OKAY;
}

/** converts column transformed variable back into loose variable, frees LP column */
SCIP_RETCODE SCIPvarLoose(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(var->scip == set->scip);
   assert(var->data.col != NULL);
   assert(var->data.col->lppos == -1);
   assert(var->data.col->lpipos == -1);

   SCIPsetDebugMsg(set, "deleting column for variable <%s>\n", var->name);

   /* free column of variable */
   SCIP_CALL( SCIPcolFree(&var->data.col, blkmem, set, eventqueue, lp) );

   /* switch variable status */
   var->varstatus = SCIP_VARSTATUS_LOOSE; /*lint !e641*/

   if( var->probindex != -1 )
   {
      /* inform problem about the variable's status change */
      SCIP_CALL( SCIPprobVarChangedStatus(prob, blkmem, set, NULL, NULL, var) );

      /* inform LP, that problem variable is now a loose variable and no longer a column */
      SCIP_CALL( SCIPlpUpdateVarLoose(lp, set, var) );
   }

   /* initialize variable data */
   var->data.loose.minaggrcoef = 1.0;
   var->data.loose.maxaggrcoef = 1.0;

   return SCIP_OKAY;
}

/** issues a VARFIXED event on the given variable and all its parents (except ORIGINAL parents);
 *  the event issuing on the parents is necessary, because unlike with bound changes, the parent variables
 *  are not informed about a fixing of an active variable they are pointing to
 */
static
SCIP_RETCODE varEventVarFixed(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   int                   fixeventtype        /**< is this event a fixation(0), an aggregation(1), or a
                                              *   multi-aggregation(2)
                                              */
   )
{
   SCIP_EVENT* event;
   SCIP_VARSTATUS varstatus;
   int i;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(0 <= fixeventtype && fixeventtype <= 2);

   /* issue VARFIXED event on variable */
   SCIP_CALL( SCIPeventCreateVarFixed(&event, blkmem, var) );
   SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );

#ifndef NDEBUG
   for( i = var->nparentvars -1; i >= 0; --i )
   {
      assert(SCIPvarGetStatus(var->parentvars[i]) != SCIP_VARSTATUS_MULTAGGR);
   }
#endif

   switch( fixeventtype )
   {
   case 0:
      /* process all parents of a fixed variable */
      for( i = var->nparentvars - 1; i >= 0; --i )
      {
         varstatus = SCIPvarGetStatus(var->parentvars[i]);

         assert(varstatus != SCIP_VARSTATUS_FIXED);

         /* issue event on all not yet fixed parent variables, (that should already issued this event) except the original
          * one
          */
         if( varstatus != SCIP_VARSTATUS_ORIGINAL )
         {
            SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
         }
      }
      break;
   case 1:
      /* process all parents of a aggregated variable */
      for( i = var->nparentvars - 1; i >= 0; --i )
      {
         varstatus = SCIPvarGetStatus(var->parentvars[i]);

         assert(varstatus != SCIP_VARSTATUS_FIXED);

         /* issue event for not aggregated parent variable, because for these and its parents the var event was already
          * issued(, except the original one)
          *
          * @note that even before an aggregated parent variable, there might be variables, for which the vent was not
          *       yet issued
          */
         if( varstatus == SCIP_VARSTATUS_AGGREGATED )
            continue;

         if( varstatus != SCIP_VARSTATUS_ORIGINAL )
         {
            SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
         }
      }
      break;
   case 2:
      /* process all parents of a aggregated variable */
      for( i = var->nparentvars - 1; i >= 0; --i )
      {
         varstatus = SCIPvarGetStatus(var->parentvars[i]);

         assert(varstatus != SCIP_VARSTATUS_FIXED);

         /* issue event on all parent variables except the original one */
         if( varstatus != SCIP_VARSTATUS_ORIGINAL )
         {
            SCIP_CALL( varEventVarFixed(var->parentvars[i], blkmem, set, eventqueue, fixeventtype) );
         }
      }
      break;
   default:
      SCIPerrorMessage("unknown variable fixation event origin\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** converts variable into fixed variable */
SCIP_RETCODE SCIPvarFix(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             fixedval,           /**< value to fix variable at */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the fixing is infeasible */
   SCIP_Bool*            fixed               /**< pointer to store whether the fixing was performed (variable was unfixed) */
   )
{
   SCIP_Real obj;
   SCIP_Real childfixedval;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(SCIPsetIsEQ(set, var->glbdom.lb, var->locdom.lb));
   assert(SCIPsetIsEQ(set, var->glbdom.ub, var->locdom.ub));
   assert(!SCIPsetIsInfinity(set, REALABS(fixedval)));
   assert(infeasible != NULL);
   assert(fixed != NULL);

   SCIPsetDebugMsg(set, "fix variable <%s>[%g,%g] to %g\n", var->name, var->glbdom.lb, var->glbdom.ub, fixedval);

   *infeasible = FALSE;
   *fixed = FALSE;

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED )
   {
      *infeasible = !SCIPsetIsFeasEQ(set, fixedval, var->locdom.lb);
      SCIPsetDebugMsg(set, " -> variable already fixed to %g (fixedval=%g): infeasible=%u\n", var->locdom.lb, fixedval, *infeasible);
      return SCIP_OKAY;
   }
   else if( ( SCIPvarIsIntegral(var) && !SCIPsetIsFeasIntegral(set, fixedval) )
      || SCIPsetIsFeasLT(set, fixedval, var->locdom.lb)
      || SCIPsetIsFeasGT(set, fixedval, var->locdom.ub) )
   {
      SCIPsetDebugMsg(set, " -> fixing infeasible: locdom=[%g,%g], fixedval=%g\n", var->locdom.lb, var->locdom.ub, fixedval);
      *infeasible = TRUE;
      return SCIP_OKAY;
   }

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot fix an untransformed original variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarFix(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
            lp, branchcand, eventqueue, eventfilter, cliquetable, fixedval, infeasible, fixed) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */

      /* set the fixed variable's objective value to 0.0 */
      obj = var->obj;
      SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );

      /* since we change the variable type form loose to fixed, we have to adjust the number of loose
       * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
       * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
       * objective of this variable is set to zero
       */
      SCIPlpDecNLoosevars(lp);

      /* free hole lists */
      holelistFree(&var->glbdom.holelist, blkmem);
      holelistFree(&var->locdom.holelist, blkmem);

      /* adjust fixed value */
      if( SCIPvarIsIntegral(var) )
         fixedval = SCIPsetRound(set, fixedval);

      /* change variable bounds to fixed value */
      SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );
      SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, fixedval) );

      /* explicitly set variable's bounds if the fixed value was in epsilon range of the old bound (so above call didn't set bound) */
      var->glbdom.lb = fixedval;
      var->glbdom.ub = fixedval;

      /* ensure local domain is fixed to same value as global domain */
      var->locdom.lb = fixedval;
      var->locdom.ub = fixedval;

      /* delete implications and variable bounds information */
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
      assert(var->vlbs == NULL);
      assert(var->vubs == NULL);
      assert(var->implics == NULL);

      /* clear the history of the variable */
      SCIPhistoryReset(var->history);
      SCIPhistoryReset(var->historycrun);

      /* convert variable into fixed variable */
      var->varstatus = SCIP_VARSTATUS_FIXED; /*lint !e641*/

      /* inform problem about the variable's status change */
      if( var->probindex != -1 )
      {
         SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
      }

      /* reset the objective value of the fixed variable, thus adjusting the problem's objective offset */
      SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventqueue, eventfilter, obj) );

      /* issue VARFIXED event */
      SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 0) );

      *fixed = TRUE;
      break;

   case SCIP_VARSTATUS_COLUMN:
      SCIPerrorMessage("cannot fix a column variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot fix a fixed variable again\n");  /*lint !e527*/
      SCIPABORT(); /* case is already handled in earlier if condition */
      return SCIP_INVALIDDATA;  /*lint !e527*/

   case SCIP_VARSTATUS_AGGREGATED:
      /* fix aggregation variable y in x = a*y + c, instead of fixing x directly */
      assert(SCIPsetIsZero(set, var->obj));
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      childfixedval = (fixedval - var->data.aggregate.constant) / var->data.aggregate.scalar;
      SCIP_CALL( SCIPvarFix(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
            branchcand, eventqueue, eventfilter, cliquetable, childfixedval, infeasible, fixed) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot fix a multiple aggregated variable\n");
      SCIPABORT();
      return SCIP_INVALIDDATA;  /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED:
      /* fix negation variable x in x' = offset - x, instead of fixing x' directly */
      assert(SCIPsetIsZero(set, var->obj));
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarFix(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
            branchcand, eventqueue, eventfilter, cliquetable, var->data.negate.constant - fixedval, infeasible, fixed) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** converts variable into fixed variable */
SCIP_RETCODE SCIPvarFixExact(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        fixedval,           /**< value to fix variable at */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the fixing is infeasible */
   SCIP_Bool*            fixed               /**< pointer to store whether the fixing was performed (variable was unfixed) */
   )
{
   SCIP_RATIONAL* obj;
   SCIP_RATIONAL* childfixedval;
   SCIP_RATIONAL* tmpval;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(set->exact_enable);

   *infeasible = FALSE;
   *fixed = FALSE;

   if( !set->exact_enable )
      return SCIP_OKAY;

   assert(SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->locdom.lb));
   assert(SCIPrationalIsEQ(var->exactdata->glbdom.ub, var->exactdata->locdom.ub));
   assert(infeasible != NULL);
   assert(fixed != NULL);

   SCIPrationalDebugMessage("fix variable <%s>[%g,%g] to %q\n", var->name, var->glbdom.lb, var->glbdom.ub, fixedval);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &obj) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childfixedval) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );

   assert(SCIPvarGetStatusExact(var) == SCIPvarGetStatus(var));

   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_FIXED )
   {
      *infeasible = !SCIPrationalIsEQ(fixedval, var->exactdata->locdom.lb);
      SCIPrationalDebugMessage(" -> variable already fixed to %q (fixedval=%q): infeasible=%u\n", var->exactdata->locdom.lb, fixedval, *infeasible);
      goto terminate;
   }
   else if( (SCIPvarIsIntegral(var) && !SCIPrationalIsIntegral(fixedval))
      || SCIPrationalIsLT(fixedval, var->exactdata->locdom.lb)
      || SCIPrationalIsGT(fixedval, var->exactdata->locdom.ub) )
   {
      SCIPrationalDebugMessage(" -> fixing infeasible: locdom=[%q,%q], fixedval=%q\n", var->exactdata->locdom.lb, var->exactdata->locdom.ub, fixedval);
      *infeasible = TRUE;
      goto terminate;
   }

   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot fix an untransformed original variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarFixExact(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
            lp, branchcand, eventqueue, eventfilter, cliquetable, fixedval, infeasible, fixed) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */

      /* set the fixed variable's objective value to 0.0 */
      SCIPrationalSetRational(obj, var->exactdata->obj);
      SCIP_CALL( SCIPvarChgObjExact(var, blkmem, set, transprob, primal, lp->lpexact, eventqueue, tmpval) );

      /* since we change the variable type form loose to fixed, we have to adjust the number of loose
       * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
       * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
       * objective of this variable is set to zero
       */
      SCIPlpDecNLoosevars(lp);

      /* free fole lists */
      holelistFree(&var->glbdom.holelist, blkmem);
      holelistFree(&var->locdom.holelist, blkmem);

      /* no need to adjust fixed value as in floating-point code */
      assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(fixedval));

      /* change variable bounds to fixed value */
      SCIP_CALL( SCIPvarChgLbGlobalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, fixedval) );
      SCIP_CALL( SCIPvarChgUbGlobalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, fixedval) );

      /* delete implications and variable bounds information */
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
      assert(var->vlbs == NULL);
      assert(var->vubs == NULL);
      assert(var->implics == NULL);
      assert(var->cliquelist == NULL);

      /* clear the history of the variable */
      SCIPhistoryReset(var->history);
      SCIPhistoryReset(var->historycrun);

      /* convert variable into fixed variable */
      var->varstatus = SCIP_VARSTATUS_FIXED; /*lint !e641*/
      var->exactdata->varstatusexact = SCIP_VARSTATUS_FIXED; /*lint !e641*/

      /* inform problem about the variable's status change */
      if( var->probindex != -1 )
      {
         SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
      }

      /* reset the objective value of the fixed variable, thus adjusting the problem's objective offset */
      SCIP_CALL( SCIPvarAddObjExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventqueue, eventfilter, obj) );

      /* issue VARFIXED event */
      SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 0) );

      *fixed = TRUE;
      break;

   case SCIP_VARSTATUS_COLUMN:
      SCIPerrorMessage("cannot fix a column variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot fix a fixed variable again\n");  /*lint !e527*/
      SCIPABORT(); /* case is already handled in earlier if condition */
      return SCIP_INVALIDDATA;  /*lint !e527*/

   case SCIP_VARSTATUS_AGGREGATED:
      /* fix aggregation variable y in x = a*y + c, instead of fixing x directly */
      assert(SCIPsetIsZero(set, var->obj));
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      if( SCIPrationalIsInfinity(fixedval) || SCIPrationalIsNegInfinity(fixedval) )
         SCIPrationalIsNegative(var->exactdata->aggregate.scalar) ? SCIPrationalNegate(childfixedval, fixedval) : SCIPrationalSetRational(childfixedval, fixedval);
      else
      {
         SCIPrationalDiff(tmpval, fixedval, var->exactdata->aggregate.constant);
         SCIPrationalDiv(childfixedval, tmpval, var->exactdata->aggregate.scalar);
      }
      SCIP_CALL( SCIPvarFixExact(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
            branchcand, eventqueue, eventfilter, cliquetable, childfixedval, infeasible, fixed) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot fix a multiple aggregated variable\n");
      SCIPABORT();
      return SCIP_INVALIDDATA;  /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED:
      /* fix negation variable x in x' = offset - x, instead of fixing x' directly */
      assert(SCIPrationalIsZero(var->exactdata->obj));
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIPrationalDiffReal(fixedval, fixedval, var->data.negate.constant);
      SCIPrationalNegate(fixedval, fixedval);
      SCIP_CALL( SCIPvarFixExact(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
            branchcand, eventqueue, eventfilter, cliquetable, fixedval, infeasible, fixed) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

terminate:
   SCIPrationalFreeBuffer(set->buffer, &tmpval);
   SCIPrationalFreeBuffer(set->buffer, &childfixedval);
   SCIPrationalFreeBuffer(set->buffer, &obj);

   return SCIP_OKAY;
}

/** transforms given variables, scalars and constant to the corresponding active variables, scalars and constant
 *
 * If the number of needed active variables is greater than the available slots in the variable array, nothing happens
 * except that an upper bound on the required size is stored in the variable requiredsize; otherwise, the active
 * variable representation is stored in the arrays.
 *
 * The reason for this approach is that we cannot reallocate memory, since we do not know how the memory has been
 * allocated (e.g., by a C++ 'new' or SCIP functions). Note that requiredsize is an upper bound due to possible
 * cancelations.
 */
SCIP_RETCODE SCIPvarGetActiveRepresentatives(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR**            vars,               /**< variable array to get active variables */
   SCIP_Real*            scalars,            /**< scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
   int*                  nvars,              /**< pointer to number of variables and values in vars and scalars array */
   int                   varssize,           /**< available slots in vars and scalars array */
   SCIP_Real*            constant,           /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c  */
   int*                  requiredsize        /**< pointer to store an uppper bound on the required size for the active variables */
   )
{
   SCIP_VAR** activevars;
   SCIP_Real activeconstant;
   SCIP_Bool activeconstantinf;
   int activevarssize;
   int nactivevars;

   SCIP_VAR* var;
   SCIP_Real scalar;
   int v;

   SCIP_VAR** tmpvars;
   SCIP_VAR** multvars;
   SCIP_Real* tmpscalars;
   SCIP_Real* multscalars;
   int tmpvarssize;
   int ntmpvars;
   int nmultvars;

   SCIP_VAR* multvar;
   SCIP_Real multscalar;
   SCIP_Real multconstant;
   SCIP_VARSTATUS varstatus;
   int ntotalvars;

   assert(set != NULL);
   assert(nvars != NULL);
   assert(constant != NULL);
   assert(requiredsize != NULL);
   assert(*nvars <= varssize);

   *requiredsize = 0;

   if( *nvars == 0 )
      return SCIP_OKAY;

   assert(vars != NULL);
   assert(scalars != NULL);

   /* handle the "easy" case of just one variable and avoid memory allocation if the variable is already active */
   if( *nvars == 1 && (vars[0]->varstatus == ((int) SCIP_VARSTATUS_COLUMN) || vars[0]->varstatus == ((int) SCIP_VARSTATUS_LOOSE)) )
   {
      *requiredsize = 1;

      return SCIP_OKAY;
   }

   /* allocate temporary list of variables */
   tmpvarssize = *nvars;
   SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpvars, tmpvarssize) );

   /* allocate memory for list of active variables */
   activevarssize = MAX(10, 2 * (*nvars));  /* take the maximum to avoid small reallocations */
   SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );

   /* allocate dense array for storing scalars (to avoid checking for duplicate variables) */
   ntotalvars = SCIPgetNTotalVars(set->scip);
   SCIP_CALL( SCIPsetAllocCleanBufferArray(set, &tmpscalars, ntotalvars) );

   /* perform one round of replacing variables by their active, fixed or multi-aggregated counterparts */
   activeconstant = 0.0;
   nactivevars = 0;
   ntmpvars = 0;
   for( v = 0; v < *nvars; ++v )
   {
      var = vars[v];
      assert(var != NULL);
      scalar = scalars[v];

      /* Transforms variable, scalar and constant to corresponding active, fixed, or multi-aggregated variable, scalar
       * and constant; activeconstant collects the sum of all constants (even for variables with scalar == 0.0). */
      SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &scalar, &activeconstant) );
      assert(var != NULL);

      assert(SCIPsetIsInfinity(set, activeconstant) == (activeconstant == SCIPsetInfinity(set))); /*lint !e777*/
      assert(SCIPsetIsInfinity(set, -activeconstant) == (activeconstant == -SCIPsetInfinity(set))); /*lint !e777*/

      varstatus = SCIPvarGetStatus(var);
      assert( varstatus == SCIP_VARSTATUS_LOOSE || varstatus == SCIP_VARSTATUS_COLUMN
         || varstatus == SCIP_VARSTATUS_MULTAGGR || varstatus == SCIP_VARSTATUS_FIXED);

      /* enter nonzero scalars into dense array and list */
      if( scalar != 0.0 )
      {
         assert(0 <= var->index && var->index < ntotalvars);
         if( tmpscalars[var->index] == 0.0 )
         {
            if( varstatus == SCIP_VARSTATUS_LOOSE || varstatus == SCIP_VARSTATUS_COLUMN )
               activevars[nactivevars++] = var; /* store active variables */
            else if( varstatus == SCIP_VARSTATUS_MULTAGGR )
               tmpvars[ntmpvars++] = var; /* enter mutli-aggregated variables in list to be processed below */
         }
         tmpscalars[var->index] += scalar;
      }
   }
   assert(ntmpvars <= *nvars);
   assert(nactivevars <= *nvars);

   /* store whether the constant is infinite */
   activeconstantinf = SCIPsetIsInfinity(set, activeconstant) || SCIPsetIsInfinity(set, -activeconstant);

   /* collect for each variable the representation in active variables */
   while( ntmpvars >= 1 )
   {
      --ntmpvars;

      var = tmpvars[ntmpvars];
      assert(var != NULL);
      assert(0 <= var->index && var->index < ntotalvars);
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);

      scalar = tmpscalars[var->index];
      /* the scalar can be 0 if the variable has been treated before and is zeroed below */
      if( scalar == 0.0 )
         continue;

      /* x = a_1*y_1 + ... + a_n*y_n + c */
      nmultvars = var->data.multaggr.nvars;
      multvars = var->data.multaggr.vars;
      multscalars = var->data.multaggr.scalars;

      /* mark variable as treated */
      tmpscalars[var->index] = 0.0;

      /* loop through variables of multi-aggregation */
      for( v = 0; v < nmultvars; ++v )
      {
         multvar = multvars[v];
         multscalar = multscalars[v];
         multconstant = 0.0;

         assert(multvar != NULL);
         SCIP_CALL( SCIPvarGetProbvarSum(&multvar, set, &multscalar, &multconstant) );
         assert(multvar != NULL);

         /* handle constant */
         if( !activeconstantinf )
         {
            assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));

            if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
            {
               assert(scalar != 0.0);
               if( scalar * multconstant > 0.0 )
               {
                  activeconstant = SCIPsetInfinity(set);
                  activeconstantinf = TRUE;
               }
               else
               {
                  activeconstant = -SCIPsetInfinity(set);
                  activeconstantinf = TRUE;
               }
            }
            else
               activeconstant += scalar * multconstant;
         }
#ifndef NDEBUG
         else
         {
            assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
                  (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
            assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
                  (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
         }
#endif

         /* Note that the variable can have a nonzero constant but 0 scalar. */
         if( multscalar == 0.0 )
            continue;

         /* enter variable into list if not already present */
         assert(0 <= multvar->index && multvar->index < ntotalvars);
         if( tmpscalars[multvar->index] == 0.0 )
         {
            varstatus = SCIPvarGetStatus(multvar);
            if( varstatus == SCIP_VARSTATUS_LOOSE || varstatus == SCIP_VARSTATUS_COLUMN )
            {
               /* store active variables */
               if( nactivevars >= activevarssize )
               {
                  activevarssize *= 2;
                  SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
                  assert(nactivevars < activevarssize);
               }
               activevars[nactivevars++] = multvar;
            }
            else if( varstatus == SCIP_VARSTATUS_MULTAGGR )
            {
               /* ensure storage */
               if( ntmpvars >= tmpvarssize )
               {
                  tmpvarssize *= 2;
                  SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
                  assert(ntmpvars <= tmpvarssize);
               }
               tmpvars[ntmpvars++] = multvar;
            }
         }

         /* transfer new scalar to dense array in any case */
         tmpscalars[multvar->index] += scalar * multscalar;
         assert(scalar * multscalar != 0.0);
      }

      /* handle constant of multi-aggregation */
      if( !activeconstantinf )
      {
         assert(!SCIPsetIsInfinity(set, scalar) && !SCIPsetIsInfinity(set, -scalar));

         multconstant = SCIPvarGetMultaggrConstant(var);

         if( SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant) )
         {
            assert(scalar != 0.0);
            if( scalar * multconstant > 0.0 )
            {
               activeconstant = SCIPsetInfinity(set);
               activeconstantinf = TRUE;
            }
            else
            {
               activeconstant = -SCIPsetInfinity(set);
               activeconstantinf = TRUE;
            }
         }
         else
            activeconstant += scalar * multconstant;
      }
#ifndef NDEBUG
      else
      {
         multconstant = SCIPvarGetMultaggrConstant(var);
         assert(!SCIPsetIsInfinity(set, activeconstant) || !(scalar * multconstant < 0.0 &&
               (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
         assert(!SCIPsetIsInfinity(set, -activeconstant) || !(scalar * multconstant > 0.0 &&
               (SCIPsetIsInfinity(set, multconstant) || SCIPsetIsInfinity(set, -multconstant))));
      }
#endif
   }

   /* Here, nactivevars is an upper bound on the required size, because of possible cancellation. We could compute the
    * actual size, but this would need another loop through the active variables. We therefore take the upper bound. */

   /* return results */
   if( varssize >= nactivevars )
   {
      assert(vars != NULL);

      if( !SCIPsetIsInfinity(set, *constant) && !SCIPsetIsInfinity(set, -(*constant)) )
      {
         /* if activeconstant is infinite, the constant pointer gets the same value, otherwise add the value */
         if( activeconstantinf )
            *constant = activeconstant;
         else
            *constant += activeconstant;
      }
#ifndef NDEBUG
      else
      {
         assert(!SCIPsetIsInfinity(set, (*constant)) || !SCIPsetIsInfinity(set, -activeconstant));
         assert(!SCIPsetIsInfinity(set, -(*constant)) || !SCIPsetIsInfinity(set, activeconstant));
      }
#endif

      /* copy active variable and scalar array to the given arrays */
      *nvars = 0;
      for( v = 0; v < nactivevars; ++v )
      {
         var = activevars[v];

         assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
         assert(0 <= var->index && var->index < ntotalvars);

         /* due to cancelation, the scalar could become 0 */
         if( ! SCIPsetIsZero(set, tmpscalars[var->index]) )
         {
            vars[*nvars] = var;
            scalars[*nvars] = tmpscalars[var->index];
            assert(scalars[*nvars] != 0.0);
            ++(*nvars);
         }

         /* clean buffer again */
         tmpscalars[var->index] = 0.0;
      }
      /* set requiredsize to space actually needed */
      *requiredsize = *nvars;
   }
   else
   {
      /* clean buffer again */
      for( v = 0; v < nactivevars; ++v )
      {
         var = activevars[v];
         assert( 0 <= var->index && var->index < ntotalvars );
         tmpscalars[var->index] = 0.0;
      }
      *requiredsize = nactivevars;
   }

   assert(SCIPsetIsInfinity(set, *constant) == ((*constant) == SCIPsetInfinity(set))); /*lint !e777*/
   assert(SCIPsetIsInfinity(set, -(*constant)) == ((*constant) == -SCIPsetInfinity(set))); /*lint !e777*/

   SCIPsetFreeCleanBufferArray(set, &tmpscalars);
   SCIPsetFreeBufferArray(set, &activevars);
   SCIPsetFreeBufferArray(set, &tmpvars);

   return SCIP_OKAY;
}

/** transforms given variables, scalars and constant to the corresponding active variables, scalars and constant
 *
 * If the number of needed active variables is greater than the available slots in the variable array, nothing happens except
 * that the required size is stored in the corresponding variable; hence, if afterwards the required size is greater than the
 * available slots (varssize), nothing happens; otherwise, the active variable representation is stored in the arrays.
 *
 * The reason for this approach is that we cannot reallocate memory, since we do not know how the
 * memory has been allocated (e.g., by a C++ 'new' or SCIP functions).
 *
 * @todo Reimplement this method as was done with SCIPvarGetActiveRepresentatives() using a clean buffer array for rationals.
 */
SCIP_RETCODE SCIPvarGetActiveRepresentativesExact(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR**            vars,               /**< variable array to get active variables */
   SCIP_RATIONAL**       scalars,            /**< scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
   int*                  nvars,              /**< pointer to number of variables and values in vars and vals array */
   int                   varssize,           /**< available slots in vars and scalars array */
   SCIP_RATIONAL*        constant,           /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c  */
   int*                  requiredsize,       /**< pointer to store the required array size for the active variables */
   SCIP_Bool             mergemultiples      /**< should multiple occurrences of a var be replaced by a single coeff? */
   )
{
   SCIP_VAR** activevars;
   SCIP_RATIONAL** activescalars;
   int nactivevars;
   SCIP_RATIONAL* activeconstant;
   SCIP_Bool activeconstantinf;
   int activevarssize;

   SCIP_VAR* var;
   SCIP_RATIONAL* scalar;
   int v;
   int k;

   SCIP_VAR** tmpvars;
   SCIP_VAR** multvars;
   SCIP_RATIONAL** tmpscalars;
   SCIP_RATIONAL** multscalars;
   int tmpvarssize;
   int ntmpvars;
   int nmultvars;
   int ntmpvarsnew;

   SCIP_VAR* multvar;
   SCIP_RATIONAL* multscalar;
   SCIP_RATIONAL* multconstant;
   int pos;

   int noldtmpvars;

   SCIP_VAR** tmpvars2;
   SCIP_RATIONAL** tmpscalars2;
   int tmpvarssize2;
   int ntmpvars2;

   SCIP_Bool sortagain = FALSE;

   assert(set != NULL);
   assert(nvars != NULL);
   assert(scalars != NULL || *nvars == 0);
   assert(constant != NULL);
   assert(requiredsize != NULL);
   assert(*nvars <= varssize);

   *requiredsize = 0;

   if( *nvars == 0 )
      return SCIP_OKAY;

   assert(vars != NULL);

   /* handle the "easy" case of just one variable and avoid memory allocation if the variable is already active */
   if( *nvars == 1 && (vars[0]->varstatus == ((int) SCIP_VARSTATUS_COLUMN) || vars[0]->varstatus == ((int) SCIP_VARSTATUS_LOOSE)) )
   {
      *requiredsize = 1;

      return SCIP_OKAY;
   }

   nactivevars = 0;
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &scalar) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &activeconstant) );
   activeconstantinf = FALSE;
   activevarssize = (*nvars) * 2;
   ntmpvars = *nvars;
   tmpvarssize = *nvars;

   tmpvarssize2 = 1;

   /* allocate temporary memory */
   SCIP_CALL( SCIPsetAllocBufferArray(set, &tmpvars2, tmpvarssize2) );
   SCIP_CALL( SCIPrationalCreateBufferArray(set->buffer, &tmpscalars2, tmpvarssize2) );
   SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
   SCIP_CALL( SCIPrationalCreateBufferArray(set->buffer, &activescalars, activevarssize) );
   SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );
   SCIP_CALL( SCIPrationalCopyBufferArray(set->buffer, &tmpscalars, scalars, *nvars) );

   /* to avoid unnecessary expanding of variable arrays while disaggregating several variables multiple times combine same variables
    * first, first get all corresponding variables with status loose, column, multaggr or fixed
    */
   for( v = ntmpvars - 1; v >= 0; --v )
   {
      var = tmpvars[v];
      SCIPrationalSetRational(scalar, tmpscalars[v]);

      assert(var != NULL);
      /* transforms given variable, scalar and constant to the corresponding active, fixed, or
       * multi-aggregated variable, scalar and constant; if the variable resolves to a fixed
       * variable, "scalar" will be 0.0 and the value of the sum will be stored in "constant".
       */
      SCIP_CALL( SCIPvarGetProbvarSumExact(&var, scalar, activeconstant) );
      assert(var != NULL);

      activeconstantinf = SCIPrationalIsInfinity(activeconstant) || SCIPrationalIsNegInfinity(activeconstant);

      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);

      tmpvars[v] = var;
      SCIPrationalSetRational(tmpscalars[v], scalar);
   }
   noldtmpvars = ntmpvars;

   /* sort all variables to combine equal variables easily */
   SCIPsortPtrPtr((void**)tmpvars, (void**)tmpscalars, SCIPvarComp, noldtmpvars);
   ntmpvars = 0;
   for( v = 1; v < noldtmpvars; ++v )
   {
      /* combine same variables */
      if( SCIPvarCompare(tmpvars[v], tmpvars[ntmpvars]) == 0 )
      {
         SCIPrationalAdd(tmpscalars[ntmpvars], tmpscalars[ntmpvars], tmpscalars[v]);
      }
      else
      {
         ++ntmpvars;
         if( v > ntmpvars )
         {
            SCIPrationalSetRational(tmpscalars[ntmpvars], tmpscalars[v]);
            tmpvars[ntmpvars] = tmpvars[v];
         }
      }
   }
   ++ntmpvars;

#ifdef SCIP_MORE_DEBUG
   for( v = 1; v < ntmpvars; ++v )
      assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
#endif

   /* collect for each variable the representation in active variables */
   while( ntmpvars >= 1 )
   {
      --ntmpvars;
      ntmpvars2 = 0;
      var = tmpvars[ntmpvars];
      SCIPrationalSetRational(scalar, tmpscalars[ntmpvars]);

      assert(var != NULL);

      /* TODO: maybe we should test here on SCIPsetIsZero() instead of 0.0 */
      if( SCIPrationalIsZero(scalar) )
         continue;

      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR
         || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);

      switch( SCIPvarGetStatus(var) )
      {
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         /* x = a*y + c */
         if( nactivevars >= activevarssize )
         {
            int newactivevarssize = activevarssize * 2;
            SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, newactivevarssize) );
            SCIP_CALL( SCIPrationalReallocBufferArray(set->buffer, &activescalars, activevarssize, newactivevarssize) );
            activevarssize = newactivevarssize;
            assert(nactivevars < activevarssize);
         }
         activevars[nactivevars] = var;
         SCIPrationalSetRational(activescalars[nactivevars], scalar);
         nactivevars++;
         break;

      case SCIP_VARSTATUS_MULTAGGR:
         /* x = a_1*y_1 + ... + a_n*y_n + c */
         nmultvars = var->data.multaggr.nvars;
         multvars = var->data.multaggr.vars;
         multscalars = var->exactdata->multaggr.scalars;
         sortagain = TRUE;

         if( nmultvars + ntmpvars > tmpvarssize )
         {
            ntmpvarsnew = tmpvarssize;
            while( nmultvars + ntmpvars > ntmpvarsnew )
               ntmpvarsnew *= 2;
            SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, ntmpvarsnew) );
            SCIP_CALL( SCIPrationalReallocBufferArray(set->buffer, &tmpscalars, tmpvarssize, ntmpvarsnew) );
            assert(nmultvars + ntmpvars <= ntmpvarsnew);
            tmpvarssize = ntmpvarsnew;
         }

         if( nmultvars > tmpvarssize2 )
         {
            ntmpvarsnew = tmpvarssize2;
            while( nmultvars > ntmpvarsnew )
               ntmpvarsnew *= 2;
            SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars2, ntmpvarsnew) );
            SCIP_CALL( SCIPrationalReallocBufferArray(set->buffer, &tmpscalars2, tmpvarssize2, ntmpvarsnew) );
            assert(nmultvars <= ntmpvarsnew);
            tmpvarssize2 = ntmpvarsnew;
         }

         --nmultvars;

         for( ; nmultvars >= 0; --nmultvars )
         {
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &multconstant) );
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &multscalar) );

            multvar = multvars[nmultvars];
            SCIPrationalSetRational(multscalar, multscalars[nmultvars]);

            assert(multvar != NULL);
            SCIP_CALL( SCIPvarGetProbvarSumExact(&multvar, multscalar, multconstant) );
            assert(multvar != NULL);

            assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_LOOSE
               || SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_COLUMN
               || SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_MULTAGGR
               || SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_FIXED);

            if( !activeconstantinf )
            {
               assert(!SCIPrationalIsAbsInfinity(scalar));

               if( SCIPrationalIsAbsInfinity(multconstant) )
               {
                  assert(!SCIPrationalIsZero(scalar));
                  if( SCIPrationalGetSign(scalar) == SCIPrationalGetSign(multconstant) && !SCIPrationalIsZero(scalar) )
                  {
                     SCIPrationalSetInfinity(activeconstant);
                     activeconstantinf = TRUE;
                  }
                  else
                  {
                     SCIPrationalSetNegInfinity(activeconstant);
                     activeconstantinf = TRUE;
                  }
               }
               else
                  SCIPrationalAddProd(activeconstant, scalar, multconstant);
            }

            if( SCIPsortedvecFindPtr((void**)tmpvars, SCIPvarComp, multvar, ntmpvars, &pos) )
            {
               assert(SCIPvarCompare(tmpvars[pos], multvar) == 0);
               SCIPrationalAddProd(tmpscalars[pos], scalar, multscalar);
            }
            else
            {
               tmpvars2[ntmpvars2] = multvar;
               SCIPrationalMult(tmpscalars2[ntmpvars2], scalar, multscalar);
               ++(ntmpvars2);
               assert(ntmpvars2 <= tmpvarssize2);
            }

            SCIPrationalFreeBuffer(set->buffer, &multscalar);
            SCIPrationalFreeBuffer(set->buffer, &multconstant);
         }

         if( ntmpvars2 > 0 )
         {
            /* sort all variables to combine equal variables easily */
            SCIPsortPtrPtr((void**)tmpvars2, (void**)tmpscalars2, SCIPvarComp, ntmpvars2);
            pos = 0;
            for( v = 1; v < ntmpvars2; ++v )
            {
               /* combine same variables */
               if( SCIPvarCompare(tmpvars2[v], tmpvars2[pos]) == 0 )
               {
                  SCIPrationalAdd(tmpscalars2[pos], tmpscalars2[pos], tmpscalars2[v]);
               }
               else
               {
                  ++pos;
                  if( v > pos )
                  {
                     SCIPrationalSetRational(tmpscalars2[pos], tmpscalars2[v]);
                     tmpvars2[pos] = tmpvars2[v];
                  }
               }
            }
            ntmpvars2 = pos + 1;
#ifdef SCIP_MORE_DEBUG
            for( v = 1; v < ntmpvars2; ++v )
            {
               assert(SCIPvarCompare(tmpvars2[v], tmpvars2[v-1]) > 0);
            }
            for( v = 1; v < ntmpvars; ++v )
            {
               assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
            }
#endif
            v = ntmpvars - 1;
            k = ntmpvars2 - 1;
            pos = ntmpvars + ntmpvars2 - 1;
            ntmpvars += ntmpvars2;

            while( v >= 0 && k >= 0 )
            {
               assert(pos >= 0);
               assert(SCIPvarCompare(tmpvars[v], tmpvars2[k]) != 0);
               if( SCIPvarCompare(tmpvars[v], tmpvars2[k]) >= 0 )
               {
                  tmpvars[pos] = tmpvars[v];
                  SCIPrationalSetRational(tmpscalars[pos], tmpscalars[v]);
                  --v;
               }
               else
               {
                  tmpvars[pos] = tmpvars2[k];
                  SCIPrationalSetRational(tmpscalars[pos], tmpscalars2[k]);
                  --k;
               }
               --pos;
               assert(pos >= 0);
            }
            while( v >= 0 )
            {
               assert(pos >= 0);
               tmpvars[pos] = tmpvars[v];
               SCIPrationalSetRational(tmpscalars[pos], tmpscalars[v]);
               --v;
               --pos;
            }
            while( k >= 0 )
            {
               assert(pos >= 0);
               tmpvars[pos] = tmpvars2[k];
               SCIPrationalSetRational(tmpscalars[pos], tmpscalars2[k]);
               --k;
               --pos;
            }
         }
#ifdef SCIP_MORE_DEBUG
         for( v = 1; v < ntmpvars; ++v )
         {
            assert(SCIPvarCompare(tmpvars[v], tmpvars[v-1]) > 0);
         }
#endif

         if( !activeconstantinf )
         {
            assert(!SCIPrationalIsAbsInfinity(scalar));

            multconstant = SCIPvarGetMultaggrConstantExact(var);

            if( SCIPrationalIsAbsInfinity(multconstant) )
            {
               assert(!SCIPrationalIsZero(scalar));
               if( SCIPrationalGetSign(scalar) == SCIPrationalGetSign(multconstant) && !SCIPrationalIsZero(scalar) )
               {
                  SCIPrationalSetInfinity(activeconstant);
                  activeconstantinf = TRUE;
               }
               else
               {
                  SCIPrationalSetNegInfinity(activeconstant);
                  activeconstantinf = TRUE;
               }
            }
            else
               SCIPrationalAddProd(activeconstant, scalar, multconstant);
         }

         break;

      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_ORIGINAL:
      case SCIP_VARSTATUS_AGGREGATED:
      case SCIP_VARSTATUS_NEGATED:
      default:
         /* case x = c, but actually we should not be here, since SCIPvarGetProbvarSum() returns a scalar of 0.0 for
          * fixed variables and is handled already
          */
         assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED);
         assert(SCIPsetIsZero(set, var->glbdom.lb) && SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub));
      }
   }

   if( mergemultiples )
   {
      if( sortagain )
      {
         /* sort variable and scalar array by variable index */
         SCIPsortPtrPtr((void**)activevars, (void**)activescalars, SCIPvarComp, nactivevars);

         /* eliminate duplicates and count required size */
         v = nactivevars - 1;
         while( v > 0 )
         {
            /* combine both variable since they are the same */
            if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
            {
               SCIPrationalNegate(scalar, activescalars[v]);
               if( !SCIPrationalIsEQ(activescalars[v - 1], scalar) )
               {
                  SCIPrationalAdd(activescalars[v - 1], activescalars[v - 1], activescalars[v]);
                  --nactivevars;
                  activevars[v] = activevars[nactivevars];
                  SCIPrationalSetRational(activescalars[v], activescalars[nactivevars]);
               }
               else
               {
                  --nactivevars;
                  activevars[v] = activevars[nactivevars];
                  SCIPrationalSetRational(activescalars[v], activescalars[nactivevars]);
                  --nactivevars;
                  --v;
                  activevars[v] = activevars[nactivevars];
                  SCIPrationalSetRational(activescalars[v], activescalars[nactivevars]);
               }
            }
            --v;
         }
      }
      /* the variables were added in reverse order, we revert the order now;
       * this should not be necessary, but not doing this changes the behavior sometimes
       */
      else
      {
         SCIP_VAR* tmpvar;

         for( v = 0; v < nactivevars / 2; ++v )
         {
            tmpvar = activevars[v];
            SCIPrationalSetRational(scalar, activescalars[v]);
            activevars[v] = activevars[nactivevars - 1 - v];
            SCIPrationalSetRational(activescalars[v], activescalars[nactivevars - 1 - v]);
            activevars[nactivevars - 1 - v] = tmpvar;
            SCIPrationalSetRational(activescalars[nactivevars - 1 - v], scalar);
         }
      }
   }
   *requiredsize = nactivevars;

   if( varssize >= *requiredsize )
   {
      assert(vars != NULL);

      *nvars = *requiredsize;

      if( !SCIPrationalIsAbsInfinity(constant) )
      {
         /* if the activeconstant is infinite, the constant pointer gets the same value, otherwise add the value */
         if( activeconstantinf )
            SCIPrationalSetRational(constant, activeconstant);
         else
            SCIPrationalAdd(constant, constant, activeconstant);
      }

      /* copy active variable and scalar array to the given arrays */
      for( v = 0; v < *nvars; ++v )
      {
         vars[v] = activevars[v];
         SCIPrationalSetRational(scalars[v], activescalars[v]); /*lint !e613*/
      }
   }

   SCIPrationalFreeBufferArray(set->buffer, &tmpscalars, tmpvarssize);
   SCIPsetFreeBufferArray(set, &tmpvars);
   SCIPrationalFreeBufferArray(set->buffer, &activescalars, activevarssize);
   SCIPsetFreeBufferArray(set, &activevars);
   SCIPrationalFreeBufferArray(set->buffer, &tmpscalars2, tmpvarssize2);
   SCIPsetFreeBufferArray(set, &tmpvars2);

   SCIPrationalFreeBuffer(set->buffer, &activeconstant);
   SCIPrationalFreeBuffer(set->buffer, &scalar);

   return SCIP_OKAY;
}

/** flattens aggregation graph of multi-aggregated variable in order to avoid exponential recursion later on */
SCIP_RETCODE SCIPvarFlattenAggregationGraph(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue          /**< event queue */
   )
{
   int nlocksup[NLOCKTYPES];
   int nlocksdown[NLOCKTYPES];
   SCIP_Real multconstant;
   int multvarssize;
   int nmultvars;
   int multrequiredsize;
   int i;

   assert( var != NULL );
   assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR );
   assert(var->scip == set->scip);

   /* in order to update the locks on the active representation of the multi-aggregated variable, we remove all locks
    * on the current representation now and re-add the locks once the variable graph has been flattened, which
    * may lead to duplicate occurences of the same variable being merged
    *
    * Here is an example. Assume we have the multi-aggregation z = x + y.
    * z occures with positive coefficient in a <= constraint c1, so it has an uplock from there.
    * When the multi-aggregation is performed, all locks are added to the active representation,
    * so x and y both get an uplock from c1. However, z was not yet replaced by x + y in c1.
    * Next, a negation y = 1 - x is identified. Again, locks are moved, so that the uplock of y originating
    * from c1 is added to x as a downlock. Thus, x has both an up- and downlock from c1.
    * The multi-aggregation changes to z = x + 1 - x, which corresponds to the locks.
    * However, before z is replaced by that sum, SCIPvarFlattenAggregationGraph() is called
    * which changes z = x + y = x + 1 - x = 1, since it merges multiple occurences of the same variable.
    * The up- and downlock of x, however, is not removed when replacing z in c1 by its active representation,
    * because it is just 1 now. Therefore, we need to update locks when flattening the aggregation graph.
    * For this, the multi-aggregated variable knows its locks in addition to adding them to the active
    * representation, which corresponds to the locks from constraints where the variable was not replaced yet.
    * By removing the locks here, based on the old representation and adding them again after flattening,
    * we ensure that the locks are correct afterwards if coefficients were merged.
    */
   for( i = 0; i < NLOCKTYPES; ++i )
   {
      nlocksup[i] = var->nlocksup[i];
      nlocksdown[i] = var->nlocksdown[i];

      SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, -nlocksdown[i], -nlocksup[i]) );
   }

   multconstant = var->data.multaggr.constant;
   nmultvars = var->data.multaggr.nvars;
   multvarssize = var->data.multaggr.varssize;

   if( !set->exact_enable )
   {
      SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize) );

      if( multrequiredsize > multvarssize )
      {
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.vars), multvarssize, multrequiredsize) );
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.scalars), multvarssize, multrequiredsize) );
         multvarssize = multrequiredsize;
         SCIP_CALL( SCIPvarGetActiveRepresentatives(set, var->data.multaggr.vars, var->data.multaggr.scalars, &nmultvars, multvarssize, &multconstant, &multrequiredsize) );

         assert( multrequiredsize <= multvarssize );
      }

      /**@note After the flattening the multi aggregation might resolve to be in fact an aggregation (or even a fixing?).
       * This issue is not resolved right now, since var->data.multaggr.nvars < 2 should not cause troubles. However, one
       * may loose performance hereby, since aggregated variables are easier to handle.
       *
       * Note, that there are two cases where SCIPvarFlattenAggregationGraph() is called: The easier one is that it is
       * called while installing the multi-aggregation. in principle, the described issue could be handled straightforward
       * in this case by aggregating or fixing the variable instead.  The more complicated case is the one, when the
       * multi-aggregation is used, e.g., in linear presolving (and the variable is already declared to be multi-aggregated).
       *
       * By now, it is not allowed to fix or aggregate multi-aggregated variables which would be necessary in this case.
       *
       * The same issue appears in the SCIPvarGetProbvar...() methods.
       */

      var->data.multaggr.constant = multconstant;
   }
   else
   {
      SCIP_CALL( SCIPvarGetActiveRepresentativesExact(set, var->data.multaggr.vars, var->exactdata->multaggr.scalars,
         &nmultvars, multvarssize, var->exactdata->multaggr.constant, &multrequiredsize, TRUE) );

      var->data.multaggr.nvars = nmultvars;
      var->data.multaggr.varssize = multvarssize;
      overwriteMultAggrWithExactData(set, var);

      if( multrequiredsize > multvarssize )
      {
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.vars), multvarssize, multrequiredsize) );
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &(var->data.multaggr.scalars), multvarssize, multrequiredsize) );
         SCIP_CALL( SCIPrationalReallocBlockArray(blkmem, &(var->exactdata->multaggr.scalars), multvarssize, multrequiredsize) );
         multvarssize = multrequiredsize;
         SCIP_CALL( SCIPvarGetActiveRepresentativesExact(set, var->data.multaggr.vars, var->exactdata->multaggr.scalars,
            &nmultvars, multvarssize, var->exactdata->multaggr.constant, &multrequiredsize, TRUE) );

         var->data.multaggr.nvars = nmultvars;
         var->data.multaggr.varssize = multvarssize;

         overwriteMultAggrWithExactData(set, var);

         assert( multrequiredsize <= multvarssize );
      }
   }

   var->data.multaggr.nvars = nmultvars;
   var->data.multaggr.varssize = multvarssize;

   for( i = 0; i < NLOCKTYPES; ++i )
   {
      SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
   }

   return SCIP_OKAY;
}

/** merge two variable histories together; a typical use case is that \p othervar is an image of the target variable
 *  in a SCIP copy. Method should be applied with care, especially because no internal checks are performed whether
 *  the history merge is reasonable
 *
 *  @note Do not use this method if the two variables originate from two SCIP's with different objective functions, since
 *        this corrupts the variable pseudo costs
 *  @note Apply with care; no internal checks are performed if the two variables should be merged
 */
void SCIPvarMergeHistories(
   SCIP_VAR*             targetvar,          /**< the variable that should contain both histories afterwards */
   SCIP_VAR*             othervar,           /**< the variable whose history is to be merged with that of the target variable */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   /* merge only the history of the current run into the target history */
   SCIPhistoryUnite(targetvar->history, othervar->historycrun, FALSE);

   /* apply the changes also to the global history */
   SCIPhistoryUnite(stat->glbhistory, othervar->historycrun, FALSE);
}

/** sets the history of a variable; this method is typically used within reoptimization to keep and update the variable
 *  history over several iterations
 */
void SCIPvarSetHistory(
   SCIP_VAR*             var,                /**< variable */
   SCIP_HISTORY*         history,            /**< the history which is to set */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   /* merge only the history of the current run into the target history */
   SCIPhistoryUnite(var->history, history, FALSE);

   /* apply the changes also to the global history */
   SCIPhistoryUnite(stat->glbhistory, history, FALSE);
}

/** update min/maxaggrcoef of a loose variable */
static
void varUpdateMinMaxAggrCoef(
   SCIP_VAR*             var,                /**< problem variable that is used in aggregation */
   SCIP_VAR*             aggvar,             /**< variable that is aggregated */
   SCIP_Real             aggscalar           /**< coefficient that is used for var in the aggregation of aggvar */
   )
{
   SCIP_Real minscalar;
   SCIP_Real maxscalar;

   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(aggvar != NULL);
   assert(SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_LOOSE);
   assert(aggscalar != 0.0);  /*lint !e777*/

   maxscalar = minscalar = REALABS(aggscalar);
   minscalar *= aggvar->data.loose.minaggrcoef;
   maxscalar *= aggvar->data.loose.maxaggrcoef;
   if( var->data.loose.minaggrcoef > minscalar )
      var->data.loose.minaggrcoef = minscalar;
   if( var->data.loose.maxaggrcoef < maxscalar )
      var->data.loose.maxaggrcoef = maxscalar;
}


/** tightens the bounds of both variables in aggregation x = a*y + c */
static
SCIP_RETCODE varUpdateAggregationBounds(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_VAR*             aggvar,             /**< variable y in aggregation x = a*y + c */
   SCIP_Real             scalar,             /**< multiplier a in aggregation x = a*y + c */
   SCIP_Real             constant,           /**< constant shift c in aggregation x = a*y + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            fixed               /**< pointer to store whether the variables were fixed */
   )
{
   SCIP_Real varlb;
   SCIP_Real varub;
   SCIP_Real aggvarlb;
   SCIP_Real aggvarub;
   SCIP_Bool aggvarbdschanged;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(aggvar != NULL);
   assert(!SCIPsetIsZero(set, scalar));
   assert(infeasible != NULL);
   assert(fixed != NULL);

   *infeasible = FALSE;
   *fixed = FALSE;

   SCIPsetDebugMsg(set, "updating bounds of variables in aggregation <%s> == %g*<%s> %+g\n", var->name, scalar, aggvar->name, constant);
   SCIPsetDebugMsg(set, "  old bounds: <%s> [%g,%g]   <%s> [%g,%g]\n",
      var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);

   /* loop as long additional changes may be found */
   do
   {
      aggvarbdschanged = FALSE;

      /* update the bounds of the aggregated variable x in x = a*y + c */
      if( scalar > 0.0 )
      {
         if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
            varlb = -SCIPsetInfinity(set);
         else
            varlb = aggvar->glbdom.lb * scalar + constant;
         if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
            varub = SCIPsetInfinity(set);
         else
            varub = aggvar->glbdom.ub * scalar + constant;
      }
      else
      {
         if( SCIPsetIsInfinity(set, -aggvar->glbdom.lb) )
            varub = SCIPsetInfinity(set);
         else
            varub = aggvar->glbdom.lb * scalar + constant;
         if( SCIPsetIsInfinity(set, aggvar->glbdom.ub) )
            varlb = -SCIPsetInfinity(set);
         else
            varlb = aggvar->glbdom.ub * scalar + constant;
      }
      varlb = MAX(varlb, var->glbdom.lb);
      varub = MIN(varub, var->glbdom.ub);
      SCIPvarAdjustLb(var, set, &varlb);
      SCIPvarAdjustUb(var, set, &varub);

      /* check the new bounds */
      if( SCIPsetIsGT(set, varlb, varub) )
      {
         /* the aggregation is infeasible */
         *infeasible = TRUE;
         return SCIP_OKAY;
      }
      else if( SCIPsetIsEQ(set, varlb, varub) )
      {
         /* the aggregated variable is fixed -> fix both variables */
         SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, varlb, infeasible, fixed) );
         if( !(*infeasible) )
         {
            SCIP_Bool aggfixed;

            SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
                  eventqueue, eventfilter, cliquetable, (varlb-constant)/scalar, infeasible, &aggfixed) );
            assert(*fixed == aggfixed);
         }
         return SCIP_OKAY;
      }
      else
      {
         if( SCIPsetIsGT(set, varlb, var->glbdom.lb) )
         {
            SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varlb) );
         }
         if( SCIPsetIsLT(set, varub, var->glbdom.ub) )
         {
            SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, varub) );
         }

         /* update the hole list of the aggregation variable */
         /**@todo update hole list of aggregation variable */
      }

      /* update the bounds of the aggregation variable y in x = a*y + c  ->  y = (x-c)/a */
      if( scalar > 0.0 )
      {
         if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
            aggvarlb = -SCIPsetInfinity(set);
         else
            aggvarlb = (var->glbdom.lb - constant) / scalar;
         if( SCIPsetIsInfinity(set, var->glbdom.ub) )
            aggvarub = SCIPsetInfinity(set);
         else
            aggvarub = (var->glbdom.ub - constant) / scalar;
      }
      else
      {
         if( SCIPsetIsInfinity(set, -var->glbdom.lb) )
            aggvarub = SCIPsetInfinity(set);
         else
            aggvarub = (var->glbdom.lb - constant) / scalar;
         if( SCIPsetIsInfinity(set, var->glbdom.ub) )
            aggvarlb = -SCIPsetInfinity(set);
         else
            aggvarlb = (var->glbdom.ub - constant) / scalar;
      }
      aggvarlb = MAX(aggvarlb, aggvar->glbdom.lb);
      aggvarub = MIN(aggvarub, aggvar->glbdom.ub);
      SCIPvarAdjustLb(aggvar, set, &aggvarlb);
      SCIPvarAdjustUb(aggvar, set, &aggvarub);

      /* check the new bounds */
      if( SCIPsetIsGT(set, aggvarlb, aggvarub) )
      {
         /* the aggregation is infeasible */
         *infeasible = TRUE;
         return SCIP_OKAY;
      }
      else if( SCIPsetIsEQ(set, aggvarlb, aggvarub) )
      {
         /* the aggregation variable is fixed -> fix both variables */
         SCIP_CALL( SCIPvarFix(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, aggvarlb, infeasible, fixed) );
         if( !(*infeasible) )
         {
            SCIP_Bool varfixed;

            SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
                  eventqueue, eventfilter, cliquetable, aggvarlb * scalar + constant, infeasible, &varfixed) );
            assert(*fixed == varfixed);
         }
         return SCIP_OKAY;
      }
      else
      {
         SCIP_Real oldbd;
         if( SCIPsetIsGT(set, aggvarlb, aggvar->glbdom.lb) )
         {
            oldbd = aggvar->glbdom.lb;
            SCIP_CALL( SCIPvarChgLbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarlb) );
            aggvarbdschanged = !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.lb);
         }
         if( SCIPsetIsLT(set, aggvarub, aggvar->glbdom.ub) )
         {
            oldbd = aggvar->glbdom.ub;
            SCIP_CALL( SCIPvarChgUbGlobal(aggvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, aggvarub) );
            aggvarbdschanged = aggvarbdschanged || !SCIPsetIsEQ(set, oldbd, aggvar->glbdom.ub);
         }

         /* update the hole list of the aggregation variable */
         /**@todo update hole list of aggregation variable */
      }
   }
   while( aggvarbdschanged );

   SCIPsetDebugMsg(set, "  new bounds: <%s> [%g,%g]   <%s> [%g,%g]\n",
      var->name, var->glbdom.lb, var->glbdom.ub, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub);

   return SCIP_OKAY;
}

/** tightens the bounds of both variables in aggregation x = a*y + c */
static
SCIP_RETCODE varUpdateAggregationBoundsExact(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_VAR*             aggvar,             /**< variable y in aggregation x = a*y + c */
   SCIP_RATIONAL*        scalar,             /**< multiplier a in aggregation x = a*y + c */
   SCIP_RATIONAL*        constant,           /**< constant shift c in aggregation x = a*y + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            fixed               /**< pointer to store whether the variables were fixed */
   )
{
   SCIP_RATIONAL* varlb;
   SCIP_RATIONAL* varub;
   SCIP_RATIONAL* aggvarlb;
   SCIP_RATIONAL* aggvarub;
   SCIP_Bool aggvarbdschanged;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(aggvar != NULL);
   assert(!SCIPrationalIsZero(scalar));
   assert(infeasible != NULL);
   assert(fixed != NULL);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &varlb) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &varub) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &aggvarlb) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &aggvarub) );

   *infeasible = FALSE;
   *fixed = FALSE;

   SCIPrationalDebugMessage("updating bounds of variables in aggregation <%s> == %q*<%s> %+q\n", var->name, scalar, aggvar->name, constant);
   SCIPrationalDebugMessage("  old bounds: <%s> [%q,%q]   <%s> [%q,%q]\n",
      var->name, var->exactdata->glbdom.lb, var->exactdata->glbdom.ub, aggvar->name, aggvar->exactdata->glbdom.lb, aggvar->exactdata->glbdom.ub);

   /* loop as long additional changes may be found */
   do
   {
      aggvarbdschanged = FALSE;

      /* update the bounds of the aggregated variable x in x = a*y + c */
      if( SCIPrationalIsPositive(scalar) )
      {
         if( SCIPrationalIsNegInfinity(aggvar->exactdata->glbdom.lb) )
            SCIPrationalSetNegInfinity(varlb);
         else
         {
            SCIPrationalMult(varlb, aggvar->exactdata->glbdom.lb, scalar);
            SCIPrationalAdd(varlb, varlb, constant);
         }
         if( SCIPrationalIsInfinity(aggvar->exactdata->glbdom.ub) )
            SCIPrationalSetInfinity(varub);
         else
         {
            SCIPrationalMult(varub, aggvar->exactdata->glbdom.ub, scalar);
            SCIPrationalAdd(varub, varub, constant);
         }
      }
      else
      {
         if( SCIPrationalIsNegInfinity(aggvar->exactdata->glbdom.lb) )
            SCIPrationalSetInfinity(varub);
         else
         {
            SCIPrationalMult(varub, aggvar->exactdata->glbdom.lb, scalar);
            SCIPrationalAdd(varub, varub, constant);
         }
         if( SCIPrationalIsInfinity(aggvar->exactdata->glbdom.ub) )
            SCIPrationalSetNegInfinity(varlb);
         else
         {
            SCIPrationalMult(varlb, aggvar->exactdata->glbdom.ub, scalar);
            SCIPrationalAdd(varlb, varlb, constant);
         }
      }
      SCIPrationalMax(varlb, varlb, var->exactdata->glbdom.lb);
      SCIPrationalMin(varub, varub, var->exactdata->glbdom.ub);
      SCIPvarAdjustLbExact(var, set, varlb);
      SCIPvarAdjustUbExact(var, set, varub);

      /* check the new bounds */
      if( SCIPrationalIsGT(varlb, varub) )
      {
         /* the aggregation is infeasible */
         *infeasible = TRUE;
         break;
      }
      else if( SCIPrationalIsEQ(varlb, varub) )
      {
         /* the aggregated variable is fixed -> fix both variables */
         SCIP_CALL( SCIPvarFixExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, varlb, infeasible, fixed) );

         if( !(*infeasible) )
         {
            SCIP_Bool aggfixed;

            SCIPrationalDiff(varlb, varlb, constant);
            SCIPrationalDiv(varlb, varlb, scalar);

            SCIP_CALL( SCIPvarFixExact(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
                  eventqueue, eventfilter, cliquetable, varlb, infeasible, &aggfixed) );
            assert(*fixed == aggfixed);
         }
         break;
      }
      else
      {
         if( SCIPrationalIsGT(varlb, var->exactdata->glbdom.lb) )
         {
            SCIP_CALL( SCIPvarChgLbGlobalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, varlb) );
         }
         if( SCIPrationalIsLT(varub, var->exactdata->glbdom.ub) )
         {
            SCIP_CALL( SCIPvarChgUbGlobalExact(var, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, varub) );
         }

         /* update the hole list of the aggregation variable */
         /**@todo update hole list of aggregation variable */
      }

      /* update the bounds of the aggregation variable y in x = a*y + c  ->  y = (x-c)/a */
      if( SCIPrationalIsPositive(scalar) )
      {
         if( SCIPrationalIsNegInfinity(var->exactdata->glbdom.lb) )
            SCIPrationalSetNegInfinity(aggvarlb);
         else
         {
            SCIPrationalDiff(aggvarlb, var->exactdata->glbdom.lb, constant);
            SCIPrationalDiv(aggvarlb, aggvarlb, scalar);
         }
         if( SCIPrationalIsInfinity(var->exactdata->glbdom.ub) )
            SCIPrationalSetInfinity(aggvarub);
         else
         {
            SCIPrationalDiff(aggvarub, var->exactdata->glbdom.ub, constant);
            SCIPrationalDiv(aggvarub, aggvarub, scalar);
         }
      }
      else
      {
         if( SCIPrationalIsNegInfinity(var->exactdata->glbdom.lb) )
            SCIPrationalSetInfinity(aggvarub);
         else
         {
            SCIPrationalDiff(aggvarub, var->exactdata->glbdom.lb, constant);
            SCIPrationalDiv(aggvarub, aggvarub, scalar);
         }
         if( SCIPrationalIsInfinity(var->exactdata->glbdom.ub) )
            SCIPrationalSetNegInfinity(aggvarlb);
         else
         {
            SCIPrationalDiff(aggvarlb, var->exactdata->glbdom.ub, constant);
            SCIPrationalDiv(aggvarlb, aggvarlb, scalar);
         }
      }
      SCIPrationalMax(aggvarlb, aggvarlb, aggvar->exactdata->glbdom.lb);
      SCIPrationalMin(aggvarub, aggvarub, aggvar->exactdata->glbdom.ub);
      SCIPvarAdjustLbExact(aggvar, set, aggvarlb);
      SCIPvarAdjustUbExact(aggvar, set, aggvarub);

      /* check the new bounds */
      if( SCIPrationalIsGT(aggvarlb, aggvarub) )
      {
         /* the aggregation is infeasible */
         *infeasible = TRUE;
         break;
      }
      else if( SCIPrationalIsEQ(aggvarlb, aggvarub) )
      {
         /* the aggregation variable is fixed -> fix both variables */
         SCIP_CALL( SCIPvarFixExact(aggvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, aggvarlb, infeasible, fixed) );

         if( !(*infeasible) )
         {
            SCIP_Bool varfixed;

            SCIPrationalMult(aggvarlb, aggvarlb, scalar);
            SCIPrationalAdd(aggvarlb, aggvarlb, constant);

            SCIP_CALL( SCIPvarFixExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
                  eventqueue, eventfilter, cliquetable, aggvarlb, infeasible, &varfixed) );
            assert(*fixed == varfixed);
         }
         break;
      }
      else
      {
         if( SCIPrationalIsGT(aggvarlb, aggvar->exactdata->glbdom.lb) )
         {
            SCIPrationalSetRational(varlb, aggvar->exactdata->glbdom.lb);
            SCIP_CALL( SCIPvarChgLbGlobalExact(aggvar, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, aggvarlb) );
            aggvarbdschanged = !SCIPrationalIsEQ(varlb, aggvar->exactdata->glbdom.lb);
         }

         if( SCIPrationalIsLT(aggvarub, aggvar->exactdata->glbdom.ub) )
         {
            SCIPrationalSetRational(varub, aggvar->exactdata->glbdom.ub);
            SCIP_CALL( SCIPvarChgUbGlobalExact(aggvar, blkmem, set, stat, lp->lpexact, branchcand, eventqueue, cliquetable, aggvarub) );
            aggvarbdschanged = aggvarbdschanged || !SCIPrationalIsEQ(varub, aggvar->exactdata->glbdom.ub);
         }

         /* update the hole list of the aggregation variable */
         /**@todo update hole list of aggregation variable */
      }
   }
   while( aggvarbdschanged );

   SCIPrationalDebugMessage("  new bounds: <%s> [%q,%q]   <%s> [%q,%q]\n",
      var->name, var->exactdata->glbdom.lb, var->exactdata->glbdom.ub, aggvar->name, aggvar->exactdata->glbdom.lb, aggvar->exactdata->glbdom.ub);

   SCIPrationalFreeBuffer(set->buffer, &aggvarub);
   SCIPrationalFreeBuffer(set->buffer, &aggvarlb);
   SCIPrationalFreeBuffer(set->buffer, &varub);
   SCIPrationalFreeBuffer(set->buffer, &varlb);

   return SCIP_OKAY;
}

/** converts loose variable into aggregated variable */
SCIP_RETCODE SCIPvarAggregate(
   SCIP_VAR*             var,                /**< loose problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             aggvar,             /**< loose variable y in aggregation x = a*y + c */
   SCIP_Real             scalar,             /**< multiplier a in aggregation x = a*y + c */
   SCIP_Real             constant,           /**< constant shift c in aggregation x = a*y + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_VAR** vars;
   SCIP_Real* coefs;
   SCIP_Real* constants;
   SCIP_Real obj;
   SCIP_Real branchfactor;
   SCIP_Bool fixed;
   int branchpriority;
   int nlocksdown[NLOCKTYPES];
   int nlocksup[NLOCKTYPES];
   int nvbds;
   int i;
   int j;

   assert(var != NULL);
   assert(aggvar != NULL);
   assert(var->scip == set->scip);
   assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
   assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
   assert(infeasible != NULL);
   assert(aggregated != NULL);

   *infeasible = FALSE;
   *aggregated = FALSE;

   /* get active problem variable of aggregation variable */
   SCIP_CALL( SCIPvarGetProbvarSum(&aggvar, set, &scalar, &constant) );

   /* aggregation is a fixing, if the scalar is zero */
   if( SCIPsetIsZero(set, scalar) )
   {
      SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand, eventqueue,
            eventfilter, cliquetable, constant, infeasible, aggregated) );
      goto TERMINATE;
   }

   /* don't perform the aggregation if the aggregation variable is multi-aggregated itself */
   if( SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_MULTAGGR )
      return SCIP_OKAY;

   /**@todo currently we don't perform the aggregation if the aggregation variable has a non-empty hole list; this
    *  should be changed in the future
    */
   if( SCIPvarGetHolelistGlobal(var) != NULL )
      return SCIP_OKAY;

   /* if the variable is not allowed to be aggregated */
   if( SCIPvarDoNotAggr(var) )
   {
      SCIPsetDebugMsg(set, "variable is not allowed to be aggregated.\n");
      return SCIP_OKAY;
   }

   assert(aggvar->glbdom.lb == aggvar->locdom.lb); /*lint !e777*/
   assert(aggvar->glbdom.ub == aggvar->locdom.ub); /*lint !e777*/
   assert(SCIPvarGetStatus(aggvar) == SCIP_VARSTATUS_LOOSE);

   SCIPsetDebugMsg(set, "aggregate variable <%s>[%g,%g] == %g*<%s>[%g,%g] %+g\n", var->name, var->glbdom.lb, var->glbdom.ub,
      scalar, aggvar->name, aggvar->glbdom.lb, aggvar->glbdom.ub, constant);

   /* if variable and aggregation variable are equal, the variable can be fixed: x == a*x + c  =>  x == c/(1-a) */
   if( var == aggvar )
   {
      if( SCIPsetIsEQ(set, scalar, 1.0) )
         *infeasible = !SCIPsetIsZero(set, constant);
      else
      {
         SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, constant/(1.0-scalar), infeasible, aggregated) );
      }
      goto TERMINATE;
   }

   /* tighten the bounds of aggregated and aggregation variable */
   SCIP_CALL( varUpdateAggregationBounds(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
         branchcand, eventqueue, eventfilter, cliquetable, aggvar, scalar, constant, infeasible, &fixed) );
   if( *infeasible || fixed )
   {
      *aggregated = fixed;
      goto TERMINATE;
   }

   /* delete implications and variable bounds of the aggregated variable from other variables, but keep them in the
    * aggregated variable
    */
   SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, FALSE) );

   /* set the aggregated variable's objective value to 0.0 */
   obj = var->obj;
   SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );

   /* unlock all locks */
   for( i = 0; i < NLOCKTYPES; i++ )
   {
      nlocksdown[i] = var->nlocksdown[i];
      nlocksup[i] = var->nlocksup[i];

      var->nlocksdown[i] = 0;
      var->nlocksup[i] = 0;
   }

   /* update aggregation bounds (argument names of varUpdateMinMaxAggrCoef are swapped) */
   varUpdateMinMaxAggrCoef(aggvar, var, scalar);

   /* check, if variable should be used as NEGATED variable of the aggregation variable */
   if( SCIPvarIsBinary(var) && SCIPvarIsBinary(aggvar)
      && var->negatedvar == NULL && aggvar->negatedvar == NULL
      && SCIPsetIsEQ(set, scalar, -1.0) && SCIPsetIsEQ(set, constant, 1.0) )
   {
      /* link both variables as negation pair */
      var->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
      var->data.negate.constant = 1.0;
      var->negatedvar = aggvar;
      aggvar->negatedvar = var;

      /* copy donot(mult)aggr status */
      aggvar->donotaggr |= var->donotaggr;
      aggvar->donotmultaggr |= var->donotmultaggr;

      /* mark both variables to be non-deletable */
      SCIPvarMarkNotDeletable(var);
      SCIPvarMarkNotDeletable(aggvar);
   }
   else
   {
      /* convert variable into aggregated variable */
      var->varstatus = SCIP_VARSTATUS_AGGREGATED; /*lint !e641*/
      var->data.aggregate.var = aggvar;
      var->data.aggregate.scalar = scalar;
      var->data.aggregate.constant = constant;

      /* copy donot(mult)aggr status */
      aggvar->donotaggr |= var->donotaggr;
      aggvar->donotmultaggr |= var->donotmultaggr;

      /* mark both variables to be non-deletable */
      SCIPvarMarkNotDeletable(var);
      SCIPvarMarkNotDeletable(aggvar);
   }

   /* make aggregated variable a parent of the aggregation variable */
   SCIP_CALL( varAddParent(aggvar, blkmem, set, var) );

   /* relock the variable, thus increasing the locks of the aggregation variable */
   for( i = 0; i < NLOCKTYPES; i++ )
   {
      SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
   }

   /* move the variable bounds to the aggregation variable:
    *  - add all variable bounds again to the variable, thus adding it to the aggregation variable
    *  - free the variable bounds data structures
    */
   if( var->vlbs != NULL )
   {
      nvbds = SCIPvboundsGetNVbds(var->vlbs);
      vars = SCIPvboundsGetVars(var->vlbs);
      coefs = SCIPvboundsGetCoefs(var->vlbs);
      constants = SCIPvboundsGetConstants(var->vlbs);
      for( i = 0; i < nvbds && !(*infeasible); ++i )
      {
         SCIP_CALL( SCIPvarAddVlb(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
               eventqueue, eventfilter, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
      }
   }
   if( var->vubs != NULL )
   {
      nvbds = SCIPvboundsGetNVbds(var->vubs);
      vars = SCIPvboundsGetVars(var->vubs);
      coefs = SCIPvboundsGetCoefs(var->vubs);
      constants = SCIPvboundsGetConstants(var->vubs);
      for( i = 0; i < nvbds && !(*infeasible); ++i )
      {
         SCIP_CALL( SCIPvarAddVub(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
               eventqueue, eventfilter, vars[i], coefs[i], constants[i], FALSE, infeasible, NULL) );
      }
   }
   SCIPvboundsFree(&var->vlbs, blkmem);
   SCIPvboundsFree(&var->vubs, blkmem);

   /* move the implications to the aggregation variable:
    *  - add all implications again to the variable, thus adding it to the aggregation variable
    *  - free the implications data structures
    */
   if( var->implics != NULL && SCIPvarGetType(aggvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(aggvar) )
   {
      assert(SCIPvarIsBinary(var));
      for( i = 0; i < 2; ++i )
      {
         SCIP_VAR** implvars;
         SCIP_BOUNDTYPE* impltypes;
         SCIP_Real* implbounds;
         int nimpls;

         nimpls = SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i);
         implvars = SCIPimplicsGetVars(var->implics, (SCIP_Bool)i);
         impltypes = SCIPimplicsGetTypes(var->implics, (SCIP_Bool)i);
         implbounds = SCIPimplicsGetBounds(var->implics, (SCIP_Bool)i);

         for( j = 0; j < nimpls && !(*infeasible); ++j )
         {
            /* @todo can't we omit transitive closure, because it should already have been done when adding the
             *       implication to the aggregated variable?
             */
            SCIP_CALL( SCIPvarAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                  branchcand, eventqueue, eventfilter, (SCIP_Bool)i, implvars[j], impltypes[j], implbounds[j], FALSE,
                  infeasible, NULL) );
            assert(nimpls == SCIPimplicsGetNImpls(var->implics, (SCIP_Bool)i));
         }
      }
   }
   SCIPimplicsFree(&var->implics, blkmem);

   /* add the history entries to the aggregation variable and clear the history of the aggregated variable */
   SCIPhistoryUnite(aggvar->history, var->history, scalar < 0.0);
   SCIPhistoryUnite(aggvar->historycrun, var->historycrun, scalar < 0.0);
   SCIPhistoryReset(var->history);
   SCIPhistoryReset(var->historycrun);

   /* update flags of aggregation variable */
   aggvar->removable &= var->removable;

   /* update branching factors and priorities of both variables to be the maximum of both variables */
   branchfactor = MAX(aggvar->branchfactor, var->branchfactor);
   branchpriority = MAX(aggvar->branchpriority, var->branchpriority);
   SCIP_CALL( SCIPvarChgBranchFactor(aggvar, set, branchfactor) );
   SCIP_CALL( SCIPvarChgBranchPriority(aggvar, branchpriority) );
   SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
   SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );

   /* update branching direction of both variables to agree to a single direction */
   if( scalar >= 0.0 )
   {
      if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, (SCIP_BRANCHDIR)aggvar->branchdirection) );
      }
      else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(aggvar, (SCIP_BRANCHDIR)var->branchdirection) );
      }
      else if( var->branchdirection != aggvar->branchdirection )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
      }
   }
   else
   {
      if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIPbranchdirOpposite((SCIP_BRANCHDIR)aggvar->branchdirection)) );
      }
      else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(aggvar, SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection)) );
      }
      else if( var->branchdirection != aggvar->branchdirection )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
      }
   }

   if( var->probindex != -1 )
   {
      /* inform problem about the variable's status change */
      SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
   }

   /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
    * variable and the problem's objective offset
    */
   SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventqueue, eventfilter, obj) );

   /* issue VARFIXED event */
   SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 1) );

   *aggregated = TRUE;

TERMINATE:
   /* check aggregation on debugging solution */
   if( *infeasible || *aggregated )
      SCIP_CALL( SCIPdebugCheckAggregation(set, var, &aggvar, &scalar, constant, 1) ); /*lint !e506 !e774*/

   return SCIP_OKAY;
}

/** converts loose variable into aggregated variable */
SCIP_RETCODE SCIPvarAggregateExact(
   SCIP_VAR*             var,                /**< loose problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             aggvar,             /**< loose variable y in aggregation x = a*y + c */
   SCIP_RATIONAL*        scalar,             /**< multiplier a in aggregation x = a*y + c */
   SCIP_RATIONAL*        constant,           /**< constant shift c in aggregation x = a*y + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_RATIONAL* obj;
   SCIP_RATIONAL* tmpval;
   SCIP_Real branchfactor;
   SCIP_Bool fixed;
   int branchpriority;
   int nlocksdown[NLOCKTYPES];
   int nlocksup[NLOCKTYPES];
   int i;

   assert(var != NULL);
   assert(aggvar != NULL);
   assert(var->scip == set->scip);
   assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
   assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_LOOSE);
   assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */
   assert(infeasible != NULL);
   assert(aggregated != NULL);

   *infeasible = FALSE;
   *aggregated = FALSE;

   /* get active problem variable of aggregation variable */
   SCIP_CALL( SCIPvarGetProbvarSumExact(&aggvar, scalar, constant) );

   /* aggregation is a fixing, if the scalar is zero */
   if( SCIPrationalIsZero(scalar) )
   {
      SCIP_CALL( SCIPvarFixExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
            eventqueue, eventfilter, cliquetable, constant, infeasible, aggregated) );
      return SCIP_OKAY;
   }

   /* don't perform the aggregation if the aggregation variable is multi-aggregated itself */
   if( SCIPvarGetStatusExact(aggvar) == SCIP_VARSTATUS_MULTAGGR )
      return SCIP_OKAY;

   /**@todo currently we don't perform the aggregation if the aggregation variable has a non-empty hole list; this
    *  should be changed in the future
    */
   if( SCIPvarGetHolelistGlobal(var) != NULL )
      return SCIP_OKAY;

   /* if the variable is not allowed to be aggregated */
   if( SCIPvarDoNotAggr(var) )
   {
      SCIPsetDebugMsg(set, "variable is not allowed to be aggregated.\n");
      return SCIP_OKAY;
   }

   assert(aggvar->glbdom.lb == aggvar->locdom.lb); /*lint !e777*/
   assert(aggvar->glbdom.ub == aggvar->locdom.ub); /*lint !e777*/
   assert(SCIPvarGetStatusExact(aggvar) == SCIP_VARSTATUS_LOOSE);

   SCIPrationalDebugMessage("aggregate variable <%s>[%q,%q] == %q*<%s>[%q,%q] +%q\n", var->name, var->exactdata->glbdom.lb, var->exactdata->glbdom.ub,
      scalar, aggvar->name, aggvar->exactdata->glbdom.lb, aggvar->exactdata->glbdom.ub, constant);

   /* if variable and aggregation variable are equal, the variable can be fixed: x == a*x + c  =>  x == c/(1-a) */
   if( var == aggvar )
   {
      if( SCIPrationalIsEQReal(scalar, 1.0) )
         *infeasible = !SCIPrationalIsZero(constant);
      else
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );
         /* fix to constant/(1-scalar) */
         SCIPrationalDiffReal(tmpval, scalar, 1.0);
         SCIPrationalNegate(tmpval, tmpval);
         SCIPrationalDiv(tmpval, constant, tmpval);
         SCIP_CALL( SCIPvarFixExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, tmpval, infeasible, aggregated) );

         SCIPrationalFreeBuffer(set->buffer, &tmpval);
      }
      return SCIP_OKAY;
   }

   /* tighten the bounds of aggregated and aggregation variable */
   SCIP_CALL( varUpdateAggregationBoundsExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
         branchcand, eventqueue, eventfilter, cliquetable, aggvar, scalar, constant, infeasible, &fixed) );
   if( *infeasible || fixed )
   {
      *aggregated = fixed;
      return SCIP_OKAY;
   }

   /* delete implications and variable bounds of the aggregated variable from other variables, but keep them in the
    * aggregated variable
    */
   SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, FALSE) );
   assert(var->cliquelist == NULL);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &obj) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );

   /* set the aggregated variable's objective value to 0.0 */
   SCIPrationalSetRational(obj, var->exactdata->obj);
   SCIPrationalSetReal(tmpval, 0.0);
   SCIP_CALL( SCIPvarChgObjExact(var, blkmem, set, transprob, primal, lp->lpexact, eventqueue, tmpval) );

   SCIPrationalFreeBuffer(set->buffer, &tmpval);

   /* unlock all locks */
   for( i = 0; i < NLOCKTYPES; i++ )
   {
      nlocksdown[i] = var->nlocksdown[i];
      nlocksup[i] = var->nlocksup[i];

      var->nlocksdown[i] = 0;
      var->nlocksup[i] = 0;
   }

   /* check, if variable should be used as NEGATED variable of the aggregation variable */
   if( SCIPvarIsBinary(var) && SCIPvarIsBinary(aggvar)
      && var->negatedvar == NULL && aggvar->negatedvar == NULL
      && SCIPrationalIsEQReal(scalar, -1.0) && SCIPrationalIsEQReal(constant, 1.0) )
   {
      /* link both variables as negation pair */
      var->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
      var->exactdata->varstatusexact = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
      var->data.negate.constant = 1.0;
      var->negatedvar = aggvar;
      aggvar->negatedvar = var;

      /* copy doNotMultiaggr status */
      aggvar->donotmultaggr |= var->donotmultaggr;

      /* mark both variables to be non-deletable */
      SCIPvarMarkNotDeletable(var);
      SCIPvarMarkNotDeletable(aggvar);
   }
   else
   {
      /* convert variable into aggregated variable */
      var->varstatus = SCIP_VARSTATUS_AGGREGATED; /*lint !e641*/
      var->exactdata->varstatusexact = SCIP_VARSTATUS_AGGREGATED; /*lint !e641*/
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->aggregate.scalar) );
      SCIP_CALL( SCIPrationalCreateBlock(blkmem, &var->exactdata->aggregate.constant) );

      var->data.aggregate.var = aggvar;
      SCIPrationalSetRational(var->exactdata->aggregate.scalar, scalar);
      SCIPrationalSetRational(var->exactdata->aggregate.constant, constant);
      var->data.aggregate.scalar = SCIPrationalGetReal(scalar);
      var->data.aggregate.constant = SCIPrationalGetReal(constant);

      /* copy doNotMultiaggr status */
      aggvar->donotmultaggr |= var->donotmultaggr;

      /* mark both variables to be non-deletable */
      SCIPvarMarkNotDeletable(var);
      SCIPvarMarkNotDeletable(aggvar);
   }

   /* make aggregated variable a parent of the aggregation variable */
   SCIP_CALL( varAddParent(aggvar, blkmem, set, var) );

   /* relock the variable, thus increasing the locks of the aggregation variable */
   for( i = 0; i < NLOCKTYPES; i++ )
   {
      SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
   }

   /* move the variable bounds to the aggregation variable:
    *  - add all variable bounds again to the variable, thus adding it to the aggregation variable
    *  - free the variable bounds data structures
    */
   assert(var->vlbs == NULL);
   assert(var->vubs == NULL);

   /* move the implications to the aggregation variable:
    *  - add all implications again to the variable, thus adding it to the aggregation variable
    *  - free the implications data structures
    */
   assert(var->implics == NULL);

   /* add the history entries to the aggregation variable and clear the history of the aggregated variable */
   SCIPhistoryUnite(aggvar->history, var->history, SCIPrationalIsNegative(scalar));
   SCIPhistoryUnite(aggvar->historycrun, var->historycrun, SCIPrationalIsNegative(scalar));
   SCIPhistoryReset(var->history);
   SCIPhistoryReset(var->historycrun);

   /* update flags of aggregation variable */
   aggvar->removable &= var->removable;

   /* update branching factors and priorities of both variables to be the maximum of both variables */
   branchfactor = MAX(aggvar->branchfactor, var->branchfactor);
   branchpriority = MAX(aggvar->branchpriority, var->branchpriority);
   SCIP_CALL( SCIPvarChgBranchFactor(aggvar, set, branchfactor) );
   SCIP_CALL( SCIPvarChgBranchPriority(aggvar, branchpriority) );
   SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
   SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );

   /* update branching direction of both variables to agree to a single direction */
   if( !SCIPrationalIsNegative(scalar) )
   {
      if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, (SCIP_BRANCHDIR)aggvar->branchdirection) );
      }
      else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(aggvar, (SCIP_BRANCHDIR)var->branchdirection) );
      }
      else if( var->branchdirection != aggvar->branchdirection )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
      }
   }
   else
   {
      if( (SCIP_BRANCHDIR)var->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIPbranchdirOpposite((SCIP_BRANCHDIR)aggvar->branchdirection)) );
      }
      else if( (SCIP_BRANCHDIR)aggvar->branchdirection == SCIP_BRANCHDIR_AUTO )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(aggvar, SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection)) );
      }
      else if( var->branchdirection != aggvar->branchdirection )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var, SCIP_BRANCHDIR_AUTO) );
      }
   }

   if( var->probindex != -1 )
   {
      /* inform problem about the variable's status change */
      SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
   }

   /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
    * variable and the problem's objective offset
    */
   SCIP_CALL( SCIPvarAddObjExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventqueue, eventfilter, obj) );

   /* issue VARFIXED event */
   SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 1) );

   SCIPrationalFreeBuffer(set->buffer, &obj);

   *aggregated = TRUE;

   return SCIP_OKAY;
}

/** Tries to aggregate an equality a*x + b*y == c consisting of two (implicit) integral active problem variables x and
 *  y. An integer aggregation (i.e. integral coefficients a' and b', such that a'*x + b'*y == c') is searched.
 *
 *  This can lead to the detection of infeasibility (e.g. if c' is fractional), or to a rejection of the aggregation
 *  (denoted by aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
 */
static
SCIP_RETCODE tryAggregateIntVars(
   SCIP_SET*             set,                /**< global SCIP settings */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             varx,               /**< integral variable x in equality a*x + b*y == c */
   SCIP_VAR*             vary,               /**< integral variable y in equality a*x + b*y == c */
   SCIP_Real             scalarx,            /**< multiplier a in equality a*x + b*y == c */
   SCIP_Real             scalary,            /**< multiplier b in equality a*x + b*y == c */
   SCIP_Real             rhs,                /**< right hand side c in equality a*x + b*y == c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_VAR* aggvar;
   char aggvarname[SCIP_MAXSTRLEN];
   SCIP_Longint scalarxn = 0;
   SCIP_Longint scalarxd = 0;
   SCIP_Longint scalaryn = 0;
   SCIP_Longint scalaryd = 0;
   SCIP_Longint a;
   SCIP_Longint b;
   SCIP_Longint c;
   SCIP_Longint scm;
   SCIP_Longint gcd;
   SCIP_Longint currentclass;
   SCIP_Longint classstep;
   SCIP_Longint xsol;
   SCIP_Longint ysol;
   SCIP_Bool success;
   SCIP_VARTYPE vartype;
   SCIP_IMPLINTTYPE impltypex;
   SCIP_IMPLINTTYPE impltypey;
   SCIP_IMPLINTTYPE impltype;

#define MAXDNOM 1000000LL

   assert(set != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(transprob != NULL);
   assert(origprob != NULL);
   assert(tree != NULL);
   assert(lp != NULL);
   assert(cliquetable != NULL);
   assert(branchcand != NULL);
   assert(eventqueue != NULL);
   assert(varx != NULL);
   assert(vary != NULL);
   assert(varx != vary);
   assert(infeasible != NULL);
   assert(aggregated != NULL);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
   assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarIsImpliedIntegral(varx));
   assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarIsImpliedIntegral(vary));
   assert(!SCIPsetIsZero(set, scalarx));
   assert(!SCIPsetIsZero(set, scalary));

   *infeasible = FALSE;
   *aggregated = FALSE;

   /* if the variable is not allowed to be aggregated */
   if( SCIPvarDoNotAggr(varx) )
   {
      SCIPsetDebugMsg(set, "variable is not allowed to be aggregated.\n");
      return SCIP_OKAY;
   }

   /* get rational representation of coefficients */
   success = SCIPrealToRational(scalarx, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalarxn, &scalarxd);
   if( success )
      success = SCIPrealToRational(scalary, -SCIPsetEpsilon(set), SCIPsetEpsilon(set), MAXDNOM, &scalaryn, &scalaryd);
   if( !success )
      return SCIP_OKAY;
   assert(scalarxd >= 1);
   assert(scalaryd >= 1);

   /* multiply equality with smallest common denominator */
   scm = SCIPcalcSmaComMul(scalarxd, scalaryd);
   a = (scm/scalarxd)*scalarxn;
   b = (scm/scalaryd)*scalaryn;
   rhs *= scm;

   /* divide equality by the greatest common divisor of a and b */
   gcd = SCIPcalcGreComDiv(ABS(a), ABS(b));
   a /= gcd;
   b /= gcd;
   rhs /= gcd;
   assert(a != 0);
   assert(b != 0);

   /* check, if right hand side is integral */
   if( !SCIPsetIsFeasIntegral(set, rhs) )
   {
      *infeasible = TRUE;
      return SCIP_OKAY;
   }
   c = (SCIP_Longint)(SCIPsetFeasFloor(set, rhs));

   /* check that the scalar and constant in the aggregation are not too large to avoid numerical problems */
   if( REALABS((SCIP_Real)(c/a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
      || REALABS((SCIP_Real)(b)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
      || REALABS((SCIP_Real)(a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
   {
      return SCIP_OKAY;
   }

   /* check, if we are in an easy case with either |a| = 1 or |b| = 1 */
   if( ( a == 1 || a == -1 ) && !SCIPvarIsImpliedIntegral(vary) )
   {
      /* aggregate x = - b/a*y + c/a */
      /*lint --e{653}*/
      SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vary, (SCIP_Real)(-b/a), (SCIP_Real)(c/a), infeasible, aggregated) );
      assert(*aggregated);
      return SCIP_OKAY;
   }
   if( ( b == 1 || b == -1 ) && !SCIPvarIsImpliedIntegral(varx) )
   {
      /* aggregate y = - a/b*x + c/b */
      /*lint --e{653}*/
      SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, varx, (SCIP_Real)(-a/b), (SCIP_Real)(c/b), infeasible, aggregated) );
      assert(*aggregated);
      return SCIP_OKAY;
   }

   /* Both variables are integers, their coefficients are not multiples of each other, and they don't have any
    * common divisor. Let (x',y') be a solution of the equality
    *   a*x + b*y == c    ->   a*x == c - b*y
    * Then x = -b*z + x', y = a*z + y' with z integral gives all solutions to the equality.
    */

   /* find initial solution (x',y'):
    *  - find y' such that c - b*y' is a multiple of a
    *    - start in equivalence class c%a
    *    - step through classes, where each step increases class number by (-b)%a, until class 0 is visited
    *    - if equivalence class 0 is visited, we are done: y' equals the number of steps taken
    *    - because a and b don't have a common divisor, each class is visited at most once, and at most a-1 steps are needed
    *  - calculate x' with x' = (c - b*y')/a (which must be integral)
    *
    * Algorithm works for a > 0 only.
    */
   if( a < 0 )
   {
      a = -a;
      b = -b;
      c = -c;
   }
   assert(a > 0);

   /* search upwards from ysol = 0 */
   ysol = 0;
   currentclass = c % a;
   if( currentclass < 0 )
      currentclass += a;
   assert(0 <= currentclass && currentclass < a);

   classstep = (-b) % a;

   if( classstep < 0 )
      classstep += a;
   assert(0 <= classstep && classstep < a);

   while( currentclass != 0 )
   {
      assert(0 <= currentclass && currentclass < a);
      currentclass += classstep;
      if( currentclass >= a )
         currentclass -= a;
      ysol++;
   }
   assert(ysol < a);
   assert(((c - b*ysol) % a) == 0);

   xsol = (c - b*ysol)/a;

   /* determine variable type for new artificial variable:
    *
    * if both variables are implicit integer the new variable can be implicit too, because the integer implication on
    * these both variables should be enforced by some other variables, otherwise the new variable needs to be of
    * integral type
    */
   vartype = (SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(vary) == SCIP_VARTYPE_CONTINUOUS
         ? SCIP_VARTYPE_CONTINUOUS : SCIP_VARTYPE_INTEGER);
   impltypex = SCIPvarGetImplType(varx);
   impltypey = SCIPvarGetImplType(vary);
   impltype = MIN(impltypex, impltypey);

   /* feasible solutions are (x,y) = (x',y') + z * (-b,a)
    * - create new integer variable z with infinite bounds
    * - aggregate variable x = -b*z + x'
    * - aggregate variable y =  a*z + y'
    * - the bounds of z are calculated automatically during aggregation
    */
   (void) SCIPsnprintf(aggvarname, SCIP_MAXSTRLEN, "agg%d", stat->nvaridx);
   SCIP_CALL( SCIPvarCreateTransformed(&aggvar, blkmem, set, stat,
         aggvarname, -SCIPsetInfinity(set), SCIPsetInfinity(set), 0.0, vartype, impltype,
         SCIPvarIsInitial(varx) || SCIPvarIsInitial(vary), SCIPvarIsRemovable(varx) && SCIPvarIsRemovable(vary),
         NULL, NULL, NULL, NULL, NULL) );

   SCIP_CALL( SCIPprobAddVar(transprob, blkmem, set, lp, branchcand, eventqueue, eventfilter, aggvar) );

   SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
         branchcand, eventqueue, eventfilter, aggvar, (SCIP_Real)(-b), (SCIP_Real)xsol, infeasible, aggregated) );
   assert(*aggregated || *infeasible);

   if( !(*infeasible) )
   {
      SCIP_CALL( SCIPvarAggregate(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, aggvar, (SCIP_Real)a, (SCIP_Real)ysol, infeasible, aggregated) );
      assert(*aggregated || *infeasible);
   }

   /* release z */
   SCIP_CALL( SCIPvarRelease(&aggvar, blkmem, set, eventqueue, lp) );

   return SCIP_OKAY;  /*lint !e438*/
}

/** Tries to aggregate an equality a*x + b*y == c consisting of two (implicit) integral active problem variables x and
 *  y. An integer aggregation (i.e. integral coefficients a' and b', such that a'*x + b'*y == c') is searched.
 *
 *  This can lead to the detection of infeasibility (e.g. if c' is fractional), or to a rejection of the aggregation
 *  (denoted by aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
 */
static
SCIP_RETCODE tryAggregateIntVarsExact(
   SCIP_SET*             set,                /**< global SCIP settings */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             varx,               /**< integral variable x in equality a*x + b*y == c */
   SCIP_VAR*             vary,               /**< integral variable y in equality a*x + b*y == c */
   SCIP_RATIONAL*        scalarx,            /**< multiplier a in equality a*x + b*y == c */
   SCIP_RATIONAL*        scalary,            /**< multiplier b in equality a*x + b*y == c */
   SCIP_RATIONAL*        rhs,                /**< right hand side c in equality a*x + b*y == c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_VAR* aggvar;
   SCIP_RATIONAL* tmprat1;
   SCIP_RATIONAL* tmprat2;
   SCIP_RATIONAL* tmprat3;
   char aggvarname[SCIP_MAXSTRLEN];
   SCIP_Longint scalarxn;
   SCIP_Longint scalarxd;
   SCIP_Longint scalaryn;
   SCIP_Longint scalaryd;
   SCIP_Longint a;
   SCIP_Longint b;
   SCIP_Longint c;
   SCIP_Longint scm;
   SCIP_Longint gcd;
   SCIP_Longint currentclass;
   SCIP_Longint classstep;
   SCIP_Longint xsol;
   SCIP_Longint ysol;
   SCIP_VARTYPE vartype;
   SCIP_IMPLINTTYPE impltypex;
   SCIP_IMPLINTTYPE impltypey;
   SCIP_IMPLINTTYPE impltype;

   assert(set != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(transprob != NULL);
   assert(origprob != NULL);
   assert(tree != NULL);
   assert(lp != NULL);
   assert(cliquetable != NULL);
   assert(branchcand != NULL);
   assert(eventqueue != NULL);
   assert(varx != NULL);
   assert(vary != NULL);
   assert(varx != vary);
   assert(infeasible != NULL);
   assert(aggregated != NULL);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
   assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetType(varx) == SCIP_VARTYPE_INTEGER || SCIPvarIsImpliedIntegral(varx));
   assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetType(vary) == SCIP_VARTYPE_INTEGER || SCIPvarIsImpliedIntegral(vary));
   assert(!SCIPrationalIsZero(scalarx));
   assert(!SCIPrationalIsZero(scalary));

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmprat1) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmprat2) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmprat3) );

   *infeasible = FALSE;
   *aggregated = FALSE;

   SCIPrationalCanonicalize(scalary);
   SCIPrationalCanonicalize(scalarx);

   scalarxd = SCIPrationalDenominator(scalarx);
   scalaryd = SCIPrationalDenominator(scalary);
   scalarxn = SCIPrationalNumerator(scalarx);
   scalaryn = SCIPrationalNumerator(scalary);

   /* multiply equality with smallest common denominator */
   scm = SCIPcalcSmaComMul(scalarxd, scalaryd);
   a = (scm/scalarxd)*scalarxn;
   b = (scm/scalaryd)*scalaryn;

   /* divide equality by the greatest common divisor of a and b */
   gcd = SCIPcalcGreComDiv(ABS(a), ABS(b));
   a /= gcd;
   b /= gcd;
   SCIPrationalSetFraction(tmprat1, scm, gcd);
   SCIPrationalMult(rhs, rhs, tmprat1);
   assert(a != 0);
   assert(b != 0);

   /* check, if right hand side is integral */
   if( !SCIPrationalIsIntegral(rhs) )
   {
      *infeasible = TRUE;
      goto FREE;
   }

   /* we know rhs is integral, so check if it is in integer range */
   if( !SCIPrationalRoundLong(&c, rhs, SCIP_R_ROUND_DOWNWARDS) )
   {
      *infeasible = TRUE;
      goto FREE;
   }

   /* check that the scalar and constant in the aggregation are not too large to avoid numerical problems */
   if( REALABS((SCIP_Real)(c/a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
      || REALABS((SCIP_Real)(b)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) /*lint !e653*/
      || REALABS((SCIP_Real)(a)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
   {
      goto FREE;
   }

   /* check, if we are in an easy case with either |a| = 1 or |b| = 1 */
   if( ( a == 1 || a == -1 ) && !SCIPvarIsImpliedIntegral(vary) )
   {
      /* aggregate x = - b/a*y + c/a */
      /*lint --e{653}*/
      SCIPrationalSetFraction(tmprat1, -b, a);
      SCIPrationalSetFraction(tmprat2, c, a);
      SCIP_CALL( SCIPvarAggregateExact(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vary, tmprat1, tmprat2, infeasible, aggregated) );
      assert(*aggregated);
      goto FREE;
   }
   if( ( b == 1 || b == -1 ) && !SCIPvarIsImpliedIntegral(varx) )
   {
      /* aggregate y = - a/b*x + c/b */
      /*lint --e{653}*/
      SCIPrationalSetFraction(tmprat1, -a, b);
      SCIPrationalSetFraction(tmprat2, c, b);
      SCIP_CALL( SCIPvarAggregateExact(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, varx, tmprat1, tmprat2, infeasible, aggregated) );
      assert(*aggregated);
      goto FREE;
   }

   /* Both variables are integers, their coefficients are not multiples of each other, and they don't have any
    * common divisor. Let (x',y') be a solution of the equality
    *   a*x + b*y == c    ->   a*x == c - b*y
    * Then x = -b*z + x', y = a*z + y' with z integral gives all solutions to the equality.
    */

   /* find initial solution (x',y'):
    *  - find y' such that c - b*y' is a multiple of a
    *    - start in equivalence class c%a
    *    - step through classes, where each step increases class number by (-b)%a, until class 0 is visited
    *    - if equivalence class 0 is visited, we are done: y' equals the number of steps taken
    *    - because a and b don't have a common divisor, each class is visited at most once, and at most a-1 steps are needed
    *  - calculate x' with x' = (c - b*y')/a (which must be integral)
    *
    * Algorithm works for a > 0 only.
    */
   if( a < 0 )
   {
      a = -a;
      b = -b;
      c = -c;
   }
   assert(a > 0);

   /* search upwards from ysol = 0 */
   ysol = 0;
   currentclass = c % a;
   if( currentclass < 0 )
      currentclass += a;
   assert(0 <= currentclass && currentclass < a);

   classstep = (-b) % a;

   if( classstep < 0 )
      classstep += a;
   assert(0 <= classstep && classstep < a);

   while( currentclass != 0 )
   {
      assert(0 <= currentclass && currentclass < a);
      currentclass += classstep;
      if( currentclass >= a )
         currentclass -= a;
      ysol++;
   }
   assert(ysol < a);
   assert(((c - b*ysol) % a) == 0);

   xsol = (c - b*ysol)/a;

   /* determine variable type for new artificial variable:
    *
    * if both variables are implicit integer the new variable can be implicit too, because the integer implication on
    * these both variables should be enforced by some other variables, otherwise the new variable needs to be of
    * integral type
    */
   vartype = (SCIPvarGetType(varx) == SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(vary) == SCIP_VARTYPE_CONTINUOUS
      ? SCIP_VARTYPE_CONTINUOUS : SCIP_VARTYPE_INTEGER);
   impltypex = SCIPvarGetImplType(varx);
   impltypey = SCIPvarGetImplType(vary);
   impltype = MIN(impltypex, impltypey);

   /* feasible solutions are (x,y) = (x',y') + z * (-b,a)
    * - create new integer variable z with infinite bounds
    * - aggregate variable x = -b*z + x'
    * - aggregate variable y =  a*z + y'
    * - the bounds of z are calculated automatically during aggregation
    */
   (void) SCIPsnprintf(aggvarname, SCIP_MAXSTRLEN, "agg%d", stat->nvaridx);
   SCIP_CALL( SCIPvarCreateTransformed(&aggvar, blkmem, set, stat,
         aggvarname, -SCIPsetInfinity(set), SCIPsetInfinity(set), 0.0, vartype, impltype,
         SCIPvarIsInitial(varx) || SCIPvarIsInitial(vary), SCIPvarIsRemovable(varx) && SCIPvarIsRemovable(vary),
         NULL, NULL, NULL, NULL, NULL) );

   SCIPrationalSetNegInfinity(tmprat1);
   SCIPrationalSetInfinity(tmprat2);
   SCIPrationalSetFraction(tmprat3, 0LL, 0LL);

   SCIP_CALL( SCIPvarAddExactData(aggvar, blkmem, tmprat1, tmprat2, tmprat3) );

   SCIP_CALL( SCIPprobAddVar(transprob, blkmem, set, lp, branchcand, eventqueue, eventfilter, aggvar) );

   SCIPrationalSetFraction(tmprat1, -b, 1LL);
   SCIPrationalSetFraction(tmprat2, xsol, 1LL);

   SCIP_CALL( SCIPvarAggregateExact(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
         branchcand, eventqueue, eventfilter, aggvar, tmprat1, tmprat2, infeasible, aggregated) );
   assert(*aggregated || *infeasible);

   if( !(*infeasible) )
   {
      SCIPrationalSetFraction(tmprat1, a, 1LL);
      SCIPrationalSetFraction(tmprat2, ysol, 1LL);

      SCIP_CALL( SCIPvarAggregateExact(vary, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, aggvar, tmprat1, tmprat2, infeasible, aggregated) );
      assert(*aggregated || *infeasible);
   }

   /* release z */
   SCIP_CALL( SCIPvarRelease(&aggvar, blkmem, set, eventqueue, lp) );

FREE:
   SCIPrationalFreeBuffer(set->buffer, &tmprat3);
   SCIPrationalFreeBuffer(set->buffer, &tmprat2);
   SCIPrationalFreeBuffer(set->buffer, &tmprat1);

   return SCIP_OKAY;  /*lint !e438*/
}

/** performs second step of SCIPaggregateVars():
 *  the variable to be aggregated is chosen among active problem variables x' and y', preferring a less strict variable
 *  type as aggregation variable (i.e. continuous variables are preferred over implicit integers, implicit integers
 *  or integers over binaries). If none of the variables is continuous, it is tried to find an integer
 *  aggregation (i.e. integral coefficients a'' and b'', such that a''*x' + b''*y' == c''). This can lead to
 *  the detection of infeasibility (e.g. if c'' is fractional), or to a rejection of the aggregation (denoted by
 *  aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
 *
 *  @todo check for fixings, infeasibility, bound changes, or domain holes:
 *     a) if there is no easy aggregation and we have one binary variable and another integer/implicit/binary variable
 *     b) for implicit integer variables with fractional aggregation scalar (we cannot (for technical reasons) and do
 *        not want to aggregate implicit integer variables, since we loose the corresponding divisibility property)
 */
SCIP_RETCODE SCIPvarTryAggregateVars(
   SCIP_SET*             set,                /**< global SCIP settings */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             varx,               /**< variable x in equality a*x + b*y == c */
   SCIP_VAR*             vary,               /**< variable y in equality a*x + b*y == c */
   SCIP_Real             scalarx,            /**< multiplier a in equality a*x + b*y == c */
   SCIP_Real             scalary,            /**< multiplier b in equality a*x + b*y == c */
   SCIP_Real             rhs,                /**< right hand side c in equality a*x + b*y == c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_Real scalar;
   SCIP_Real constant;
   SCIP_Bool easyaggr;
   SCIP_VARTYPE typex;
   SCIP_VARTYPE typey;

   assert(set != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(transprob != NULL);
   assert(origprob != NULL);
   assert(tree != NULL);
   assert(lp != NULL);
   assert(cliquetable != NULL);
   assert(branchcand != NULL);
   assert(eventqueue != NULL);
   assert(varx != NULL);
   assert(vary != NULL);
   assert(varx != vary);
   assert(infeasible != NULL);
   assert(aggregated != NULL);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
   assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
   assert(scalarx != 0.0); /*lint !e777*/
   assert(scalary != 0.0); /*lint !e777*/
   assert(!SCIPsetIsInfinity(set, REALABS(rhs)));

   *infeasible = FALSE;
   *aggregated = FALSE;

   /**@todo simplify the following code once SCIP_DEPRECATED_VARTYPE_IMPLINT is removed */
   typex = SCIPvarIsImpliedIntegral(varx) ? SCIP_DEPRECATED_VARTYPE_IMPLINT : SCIPvarGetType(varx);
   typey = SCIPvarIsImpliedIntegral(vary) ? SCIP_DEPRECATED_VARTYPE_IMPLINT : SCIPvarGetType(vary);

   /* prefer aggregating the variable of more general type (preferred aggregation variable is varx) */
   if( typex < typey ||
       ( typex == typey && SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary))  )
   {
      SCIP_VAR* var;
      SCIP_VARTYPE type;

      /* switch the variables, such that varx is the variable of more general type (cont > implint > int > bin) */
      var = vary;
      vary = varx;
      varx = var;
      scalar = scalary;
      scalary = scalarx;
      scalarx = scalar;
      type = typey;
      typey = typex;
      typex = type;
   }

   /* don't aggregate if the aggregation would lead to a binary variable aggregated to a non-binary variable */
   if( SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary) )
      return SCIP_OKAY;

   assert(typex >= typey);

   /* figure out, which variable should be aggregated */
   easyaggr = FALSE;

   /* check if it is an easy aggregation */
   if( typex == SCIP_VARTYPE_CONTINUOUS && typey != SCIP_VARTYPE_CONTINUOUS )
   {
      easyaggr = TRUE;
   }
   else if( SCIPsetIsFeasIntegral(set, scalary/scalarx) )
   {
      easyaggr = TRUE;
   }
   else if( typex == typey && SCIPsetIsFeasIntegral(set, scalarx / scalary) )
   {
      /* we have an easy aggregation if we flip the variables x and y */
      SCIP_VAR* var;

      /* switch the variables, such that varx is the aggregated variable */
      var = vary;
      vary = varx;
      varx = var;
      scalar = scalary;
      scalary = scalarx;
      scalarx = scalar;
      easyaggr = TRUE;
   }
   else if( typex == SCIP_VARTYPE_CONTINUOUS )
   {
      /* the aggregation is still easy if both variables are continuous */
      assert(typey == SCIP_VARTYPE_CONTINUOUS); /* otherwise we are in the first case */
      easyaggr = TRUE;
   }

   /* calculate aggregation scalar and constant: a*x + b*y == c  =>  x == -b/a * y + c/a */
   scalar = -scalary / scalarx;
   constant = rhs / scalarx;

   /* terminate if a bound on resolved aggregation scalar becomes too small or large so that numerical cancellation may be caused */
   if( !SCIPvarIsAggrCoefAcceptable(set, varx, scalar) )
      return SCIP_OKAY;

   /* did we find an "easy" aggregation? */
   if( easyaggr )
   {
      assert(typex >= typey);

      if( REALABS(constant) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
         return SCIP_OKAY;

      /* check aggregation for integer feasibility */
      if( typex != SCIP_VARTYPE_CONTINUOUS && typey != SCIP_VARTYPE_CONTINUOUS
         && SCIPsetIsFeasIntegral(set, scalar) && !SCIPsetIsFeasIntegral(set, constant) )
      {
         *infeasible = TRUE;
         return SCIP_OKAY;
      }

      /* if the aggregation scalar is fractional, we cannot (for technical reasons) and do not want to aggregate implicit integer variables,
       * since then we would loose the corresponding divisibility property
       */
      assert(typex != SCIP_DEPRECATED_VARTYPE_IMPLINT || SCIPsetIsFeasIntegral(set, scalar));

      /* aggregate the variable */
      SCIP_CALL( SCIPvarAggregate(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vary, scalar, constant, infeasible, aggregated) );
      assert(*aggregated || *infeasible || SCIPvarDoNotAggr(varx));
   }
   else if( ( typex == SCIP_VARTYPE_INTEGER || typex == SCIP_DEPRECATED_VARTYPE_IMPLINT )
      && ( typey == SCIP_VARTYPE_INTEGER || typey == SCIP_DEPRECATED_VARTYPE_IMPLINT ) )
   {
      /* the variables are both integral: we have to try to find an integer aggregation */
      SCIP_CALL( tryAggregateIntVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, varx, vary, scalarx, scalary, rhs, infeasible, aggregated) );
   }

   return SCIP_OKAY;
}

/** performs second step of SCIPaggregateVarsExact()
 *
 *  The variable to be aggregated is chosen among active problem variables x' and y', preferring a less strict variable
 *  type as aggregation variable (i.e. continuous variables are preferred over implicit integers, implicit integers
 *  or integers over binaries). If none of the variables is continuous, it is tried to find an integer
 *  aggregation (i.e. integral coefficients a'' and b'', such that a''*x' + b''*y' == c''). This can lead to
 *  the detection of infeasibility (e.g. if c'' is fractional), or to a rejection of the aggregation (denoted by
 *  aggregated == FALSE), if the resulting integer coefficients are too large and thus numerically instable.
 *
 *  @todo check for fixings, infeasibility, bound changes, or domain holes:
 *     a) if there is no easy aggregation and we have one binary variable and another integer/implicit/binary variable
 *     b) for implicit integer variables with fractional aggregation scalar (we cannot (for technical reasons) and do
 *        not want to aggregate implicit integer variables, since we loose the corresponding divisibility property)
 */
SCIP_RETCODE SCIPvarTryAggregateVarsExact(
   SCIP_SET*             set,                /**< global SCIP settings */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             varx,               /**< variable x in equality a*x + b*y == c */
   SCIP_VAR*             vary,               /**< variable y in equality a*x + b*y == c */
   SCIP_RATIONAL*        scalarx,            /**< multiplier a in equality a*x + b*y == c */
   SCIP_RATIONAL*        scalary,            /**< multiplier b in equality a*x + b*y == c */
   SCIP_RATIONAL*        rhs,                /**< right hand side c in equality a*x + b*y == c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_Bool easyaggr;
   SCIP_RATIONAL* quotxy;
   SCIP_RATIONAL* quotyx;
   SCIP_Real absquot;
   SCIP_Real maxscalar;
   SCIP_VARTYPE typex;
   SCIP_VARTYPE typey;

   assert(set != NULL);
   assert(blkmem != NULL);
   assert(stat != NULL);
   assert(transprob != NULL);
   assert(origprob != NULL);
   assert(tree != NULL);
   assert(lp != NULL);
   assert(cliquetable != NULL);
   assert(branchcand != NULL);
   assert(eventqueue != NULL);
   assert(varx != NULL);
   assert(vary != NULL);
   assert(varx != vary);
   assert(infeasible != NULL);
   assert(aggregated != NULL);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PRESOLVING);
   assert(SCIPvarGetStatus(varx) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetStatus(vary) == SCIP_VARSTATUS_LOOSE);
   assert(!SCIPrationalIsZero(scalarx));
   assert(!SCIPrationalIsZero(scalary));

   *infeasible = FALSE;
   *aggregated = FALSE;

   absquot = REALABS(SCIPrationalGetReal(scalarx) / SCIPrationalGetReal(scalary));
   maxscalar = SCIPsetFeastol(set) / SCIPsetEpsilon(set);
   maxscalar = MAX(maxscalar, 1.0);

   if( absquot > maxscalar || absquot < 1 / maxscalar )
      return SCIP_OKAY;

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &quotxy) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &quotyx) );

   SCIPrationalDiv(quotxy, scalarx, scalary);
   SCIPrationalInvert(quotyx, quotxy);

   /**@todo simplify the following code once SCIP_DEPRECATED_VARTYPE_IMPLINT is removed */
   typex = SCIPvarIsImpliedIntegral(varx) ? SCIP_DEPRECATED_VARTYPE_IMPLINT : SCIPvarGetType(varx);
   typey = SCIPvarIsImpliedIntegral(vary) ? SCIP_DEPRECATED_VARTYPE_IMPLINT : SCIPvarGetType(vary);

   /* prefer aggregating the variable of more general type (preferred aggregation variable is varx) */
   if( typex < typey ||
      ( typex == typey && SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary))  )
   {
      SCIP_VAR* var;
      SCIP_RATIONAL* scalar;
      SCIP_VARTYPE type;

      /* switch the variables, such that varx is the variable of more general type (cont > implint > int > bin) */
      var = vary;
      vary = varx;
      varx = var;
      scalar = scalary;
      scalary = scalarx;
      scalarx = scalar;
      type = typey;
      typey = typex;
      typex = type;
      SCIPrationalInvert(quotyx, quotyx);
      SCIPrationalInvert(quotxy, quotxy);
   }

   /* don't aggregate if the aggregation would lead to a binary variable aggregated to a non-binary variable */
   if( SCIPvarIsBinary(varx) && !SCIPvarIsBinary(vary) )
      return SCIP_OKAY;

   assert(typex >= typey);

   /* figure out, which variable should be aggregated */
   easyaggr = FALSE;

   /* check if it is an easy aggregation that means:
    *
    *   a*x + b*y == c -> x == -b/a * y + c/a iff |b/a| > feastol and |a/b| > feastol
    */
   if( !SCIPsetIsFeasZero(set, SCIPrationalGetReal(quotyx)) && !SCIPsetIsFeasZero(set, SCIPrationalGetReal(quotxy)) )
   {
      if( typex == SCIP_VARTYPE_CONTINUOUS && typey != SCIP_VARTYPE_CONTINUOUS )
      {
         easyaggr = TRUE;
      }
      else if( SCIPrationalIsIntegral(quotyx) )
      {
         easyaggr = TRUE;
      }
      else if( typex == typey && SCIPrationalIsIntegral(quotxy) )
      {
         /* we have an easy aggregation if we flip the variables x and y */
         SCIP_VAR* var;
         SCIP_RATIONAL* scalar;

         /* switch the variables, such that varx is the aggregated variable */
         var = vary;
         vary = varx;
         varx = var;
         scalar = scalary;
         scalary = scalarx;
         scalarx = scalar;
         easyaggr = TRUE;
         SCIPrationalInvert(quotyx, quotyx);
      }
      else if( typex == SCIP_VARTYPE_CONTINUOUS )
      {
         /* the aggregation is still easy if both variables are continuous */
         assert(typey == SCIP_VARTYPE_CONTINUOUS); /* otherwise we are in the first case */
         easyaggr = TRUE;
      }
   }

   /* did we find an "easy" aggregation? */
   if( easyaggr )
   {
      SCIP_RATIONAL* constant;

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &constant) );

      assert(typex >= typey);

      /* calculate aggregation scalar and constant: a*x + b*y == c  =>  x == -b/a * y + c/a */
      SCIPrationalNegate(quotyx, quotyx);
      SCIPrationalDiv(constant, rhs, scalarx);

      if( REALABS(SCIPrationalGetReal(constant)) > SCIPsetGetHugeValue(set) * SCIPsetFeastol(set) ) /*lint !e653*/
         goto FREE;

      /* check aggregation for integer feasibility */
      if( typex != SCIP_VARTYPE_CONTINUOUS && typey != SCIP_VARTYPE_CONTINUOUS
         && SCIPrationalIsIntegral(quotyx) && !SCIPrationalIsIntegral(constant) )
      {
         *infeasible = TRUE;
         goto FREE;
      }

      /* if the aggregation scalar is fractional, we cannot (for technical reasons) and do not want to aggregate implicit integer variables,
       * since then we would loose the corresponding divisibility property
       */
      assert(typex != SCIP_DEPRECATED_VARTYPE_IMPLINT || SCIPrationalIsIntegral(quotyx));

      /* aggregate the variable */
      SCIP_CALL( SCIPvarAggregateExact(varx, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vary, quotyx, constant, infeasible, aggregated) );
      assert(*aggregated || *infeasible || SCIPvarDoNotAggr(varx));
FREE:
      SCIPrationalFreeBuffer(set->buffer, &constant);
   }
   else if( (typex == SCIP_VARTYPE_INTEGER || typex == SCIP_DEPRECATED_VARTYPE_IMPLINT)
      && (typey == SCIP_VARTYPE_INTEGER || typey == SCIP_DEPRECATED_VARTYPE_IMPLINT) )
   {
      /* the variables are both integral: we have to try to find an integer aggregation */
      SCIP_CALL( tryAggregateIntVarsExact(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, varx, vary, scalarx, scalary, rhs, infeasible, aggregated) );
   }

   SCIPrationalFreeBuffer(set->buffer, &quotyx);
   SCIPrationalFreeBuffer(set->buffer, &quotxy);

   return SCIP_OKAY;
}

/** converts variable into multi-aggregated variable */
SCIP_RETCODE SCIPvarMultiaggregate(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   int                   naggvars,           /**< number n of variables in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_VAR**            aggvars,            /**< variables y_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_Real*            scalars,            /**< multipliers a_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_Real             constant,           /**< constant shift c in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_VAR** tmpvars;
   SCIP_Real* tmpscalars;
   SCIP_Real obj;
   SCIP_Real branchfactor;
   int branchpriority;
   SCIP_BRANCHDIR branchdirection;
   int nlocksdown[NLOCKTYPES];
   int nlocksup[NLOCKTYPES];
   int v;
   SCIP_Real tmpconstant;
   SCIP_Real tmpscalar;
   int ntmpvars;
   int tmpvarssize;
   int tmprequiredsize;
   int i;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(var->glbdom.lb == var->locdom.lb); /*lint !e777*/
   assert(var->glbdom.ub == var->locdom.ub); /*lint !e777*/
   assert(!SCIPsetIsInfinity(set, REALABS(constant)));
   assert(naggvars == 0 || aggvars != NULL);
   assert(naggvars == 0 || scalars != NULL);
   assert(infeasible != NULL);
   assert(aggregated != NULL);

   SCIPsetDebugMsg(set, "trying multi-aggregating variable <%s> == ...%d vars... %+g\n", var->name, naggvars, constant);

   *infeasible = FALSE;
   *aggregated = FALSE;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot multi-aggregate an untransformed original variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarMultiaggregate(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
            reopt, lp, cliquetable, branchcand, eventqueue, eventfilter, naggvars, aggvars, scalars, constant, infeasible, aggregated) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */

      /* check if we would create a self-reference */
      ntmpvars = naggvars;
      tmpvarssize = naggvars;
      tmpconstant = constant;
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpvars, aggvars, ntmpvars) );
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpscalars, scalars, ntmpvars) );

      /* get all active variables for multi-aggregation */
      SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize) );
      if( tmprequiredsize > tmpvarssize )
      {
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpvars, tmpvarssize, tmprequiredsize) );
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize, tmprequiredsize) );
         tmpvarssize = tmprequiredsize;
         SCIP_CALL( SCIPvarGetActiveRepresentatives(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, &tmpconstant, &tmprequiredsize) );
         assert( tmprequiredsize <= tmpvarssize );
      }

      tmpscalar = 0.0;

      /* iterate over all active variables of the multi-aggregation and filter all variables which are equal to the
       * possible multi-aggregated variable
       */
      for( v = ntmpvars - 1; v >= 0; --v )
      {
         assert(tmpvars[v] != NULL);
         assert(SCIPvarGetStatus(tmpvars[v]) == SCIP_VARSTATUS_LOOSE);

         if( tmpvars[v]->index == var->index )
         {
            tmpscalar += tmpscalars[v];
            tmpvars[v] = tmpvars[ntmpvars - 1];
            tmpscalars[v] = tmpscalars[ntmpvars - 1];
            --ntmpvars;
         }
      }

      /* this means that x = x + a_1*y_1 + ... + a_n*y_n + c */
      if( SCIPsetIsEQ(set, tmpscalar, 1.0) )
      {
         if( ntmpvars == 0 )
         {
            if( SCIPsetIsFeasZero(set, tmpconstant) ) /* x = x */
            {
               SCIPsetDebugMsg(set, "Possible multi-aggregation was completely resolved and detected to be redundant.\n");
               goto TERMINATE;
            }
            else /* 0 = c and c != 0 */
            {
               SCIPsetDebugMsg(set, "Multi-aggregation was completely resolved and led to infeasibility.\n");
               *infeasible = TRUE;
               goto TERMINATE;
            }
         }
         else if( ntmpvars == 1 ) /* 0 = a*y + c => y = -c/a */
         {
            assert(tmpscalars[0] != 0.0);
            assert(tmpvars[0] != NULL);

            SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(tmpvars[0]), -constant/tmpscalars[0]);
            SCIP_CALL( SCIPvarFix(tmpvars[0], blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
                  branchcand, eventqueue, eventfilter, cliquetable, -constant/tmpscalars[0], infeasible, aggregated) );
            goto TERMINATE;
         }
         else if( ntmpvars == 2 ) /* 0 = a_1*y_1 + a_2*y_2 + c => y_1 = -a_2/a_1 * y_2 - c/a_1 */
         {
            /* both variables are different active problem variables, and both scalars are non-zero: try to aggregate them */
            SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
                  SCIPvarGetName(tmpvars[0]), SCIPvarGetName(tmpvars[1]), tmpscalars[0], tmpscalars[1], -tmpconstant);

            SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
                  cliquetable, branchcand, eventqueue, eventfilter, tmpvars[0], tmpvars[1], tmpscalars[0],
                  tmpscalars[1], -tmpconstant, infeasible, aggregated) );

            goto TERMINATE;
         }
         else
            /* @todo: it is possible to multi-aggregate another variable, does it make sense?,
             *        rest looks like 0 = a_1*y_1 + ... + a_n*y_n + c and has at least three variables
             */
            goto TERMINATE;
      }
      /* this means that x = b*x + a_1*y_1 + ... + a_n*y_n + c */
      else if( tmpscalar != 0.0 ) /*lint !e777*/
      {
         tmpscalar = 1 - tmpscalar;
         tmpconstant /= tmpscalar;
         for( v = 0; v < ntmpvars; ++v )
            tmpscalars[v] /= tmpscalar;
      }

      /* check, if we are in one of the simple cases */
      if( ntmpvars == 0 )
      {
         SCIPsetDebugMsg(set, "Possible multi-aggregation led to fixing of variable <%s> to %g.\n", SCIPvarGetName(var), tmpconstant);
         SCIP_CALL( SCIPvarFix(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, tmpconstant, infeasible, aggregated) );
         goto TERMINATE;
      }

      /* if only one aggregation variable is left, we perform a normal aggregation instead of a multi-aggregation */
      if( ntmpvars == 1 )
      {
         SCIPsetDebugMsg(set, "Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %g and %g and constant %g.\n",
            SCIPvarGetName(var), SCIPvarGetName(tmpvars[0]), 1.0, -tmpscalars[0], tmpconstant);

         SCIP_CALL( SCIPvarTryAggregateVars(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, var, tmpvars[0], 1.0, -tmpscalars[0], tmpconstant,
               infeasible, aggregated) );

         goto TERMINATE;
      }

      /**@todo currently we don't perform the multi aggregation if the multi aggregation variable has a non
       *  empty hole list; this should be changed in the future  */
      if( SCIPvarGetHolelistGlobal(var) != NULL )
         goto TERMINATE;

      /* if the variable is not allowed to be multi-aggregated */
      if( SCIPvarDoNotMultaggr(var) )
      {
         SCIPsetDebugMsg(set, "variable is not allowed to be multi-aggregated.\n");
         goto TERMINATE;
      }

      /* terminate if scalars may lead to numerical trouble */
      for( v = 0; v < ntmpvars; ++v )
         if( !SCIPvarIsAggrCoefAcceptable(set, var, tmpscalars[v]) )
            goto TERMINATE;

      /* if the variable to be multi-aggregated has implications or variable bounds (i.e. is the implied variable or
       * variable bound variable of another variable), we have to remove it from the other variables implications or
       * variable bounds
       */
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
      assert(var->vlbs == NULL);
      assert(var->vubs == NULL);
      assert(var->implics == NULL);

      /* set the aggregated variable's objective value to 0.0 */
      obj = var->obj;
      SCIP_CALL( SCIPvarChgObj(var, blkmem, set, transprob, primal, lp, eventqueue, 0.0) );

      /* since we change the variable type form loose to multi aggregated, we have to adjust the number of loose
       * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
       * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
       * objective of this variable is set to zero
       */
      SCIPlpDecNLoosevars(lp);

      /* unlock all rounding locks */
      for( i = 0; i < NLOCKTYPES; i++ )
      {
         nlocksdown[i] = var->nlocksdown[i];
         nlocksup[i] = var->nlocksup[i];

         var->nlocksdown[i] = 0;
         var->nlocksup[i] = 0;
      }

      /* update aggregation bounds */
      for( v = 0; v < ntmpvars; ++v )
         varUpdateMinMaxAggrCoef(tmpvars[v], var, tmpscalars[v]);

      /* convert variable into multi-aggregated variable */
      var->varstatus = SCIP_VARSTATUS_MULTAGGR; /*lint !e641*/
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.vars, tmpvars, ntmpvars) );
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.scalars, tmpscalars, ntmpvars) );
      var->data.multaggr.constant = tmpconstant;
      var->data.multaggr.nvars = ntmpvars;
      var->data.multaggr.varssize = ntmpvars;

      /* mark variable to be non-deletable */
      SCIPvarMarkNotDeletable(var);

      /* relock the variable, thus increasing the locks of the aggregation variables */
      for( i = 0; i < NLOCKTYPES; i++ )
      {
         SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
      }

      /* update flags and branching factors and priorities of aggregation variables;
       * update preferred branching direction of all aggregation variables that don't have a preferred direction yet
       */
      branchfactor = var->branchfactor;
      branchpriority = var->branchpriority;
      branchdirection = (SCIP_BRANCHDIR)var->branchdirection;

      for( v = 0; v < ntmpvars; ++v )
      {
         assert(tmpvars[v] != NULL);
         tmpvars[v]->removable &= var->removable;
         branchfactor = MAX(tmpvars[v]->branchfactor, branchfactor);
         branchpriority = MAX(tmpvars[v]->branchpriority, branchpriority);

         /* mark variable to be non-deletable */
         SCIPvarMarkNotDeletable(tmpvars[v]);
      }
      for( v = 0; v < ntmpvars; ++v )
      {
         SCIP_CALL( SCIPvarChgBranchFactor(tmpvars[v], set, branchfactor) );
         SCIP_CALL( SCIPvarChgBranchPriority(tmpvars[v], branchpriority) );
         if( (SCIP_BRANCHDIR)tmpvars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
         {
            if( tmpscalars[v] >= 0.0 )
            {
               SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], branchdirection) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], SCIPbranchdirOpposite(branchdirection)) );
            }
         }
      }
      SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
      SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );

      if( var->probindex != -1 )
      {
         /* inform problem about the variable's status change */
         SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
      }

      /* issue VARFIXED event */
      SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 2) );

      /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
       * variables and the problem's objective offset
       */
      SCIP_CALL( SCIPvarAddObj(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp, eventqueue, eventfilter, obj) );

      *aggregated = TRUE;

   TERMINATE:
      BMSfreeBlockMemoryArray(blkmem, &tmpscalars, tmpvarssize);
      BMSfreeBlockMemoryArray(blkmem, &tmpvars, tmpvarssize);

      break;

   case SCIP_VARSTATUS_COLUMN:
      SCIPerrorMessage("cannot multi-aggregate a column variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot multi-aggregate a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      SCIPerrorMessage("cannot multi-aggregate an aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot multi-aggregate a multiple aggregated variable again\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      /* aggregate negation variable x in x' = offset - x, instead of aggregating x' directly:
       *   x' = a_1*y_1 + ... + a_n*y_n + c  ->  x = offset - x' = offset - a_1*y_1 - ... - a_n*y_n - c
       */
      assert(SCIPsetIsZero(set, var->obj));
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      /* switch the signs of the aggregation scalars */
      for( v = 0; v < naggvars; ++v )
         scalars[v] *= -1.0;

      /* perform the multi aggregation on the negation variable */
      SCIP_CALL( SCIPvarMultiaggregate(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
            cliquetable, branchcand, eventqueue, eventfilter, naggvars, aggvars, scalars,
            var->data.negate.constant - constant, infeasible, aggregated) );

      /* switch the signs of the aggregation scalars again, to reset them to their original values */
      for( v = 0; v < naggvars; ++v )
         scalars[v] *= -1.0;
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   /* check multi-aggregation on debugging solution */
   if( *infeasible || *aggregated )
      SCIP_CALL( SCIPdebugCheckAggregation(set, var, aggvars, scalars, constant, naggvars) ); /*lint !e506 !e774*/

   return SCIP_OKAY;
}

/** converts variable into multi-aggregated variable */
SCIP_RETCODE SCIPvarMultiaggregateExact(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< tranformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LPEXACT*         lpexact,            /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   int                   naggvars,           /**< number n of variables in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_VAR**            aggvars,            /**< variables y_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_RATIONAL**       scalars,            /**< multipliers a_i in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_RATIONAL*        constant,           /**< constant shift c in aggregation x = a_1*y_1 + ... + a_n*y_n + c */
   SCIP_Bool*            infeasible,         /**< pointer to store whether the aggregation is infeasible */
   SCIP_Bool*            aggregated          /**< pointer to store whether the aggregation was successful */
   )
{
   SCIP_VAR** tmpvars;
   SCIP_RATIONAL** tmpscalars;
   SCIP_RATIONAL* obj;
   SCIP_RATIONAL* tmpconstant;
   SCIP_RATIONAL* tmpscalar;
   SCIP_RATIONAL* tmpval;
   SCIP_Real branchfactor;
   int branchpriority;
   SCIP_BRANCHDIR branchdirection;
   int nlocksdown[NLOCKTYPES];
   int nlocksup[NLOCKTYPES];
   int v;
   int ntmpvars;
   int tmpvarssize;
   int tmprequiredsize;
   int i;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(set->exact_enable);
   assert(SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->locdom.lb)); /*lint !e777*/
   assert(SCIPrationalIsEQ(var->exactdata->glbdom.ub, var->exactdata->locdom.ub)); /*lint !e777*/
   assert(naggvars == 0 || aggvars != NULL);
   assert(naggvars == 0 || scalars != NULL);
   assert(infeasible != NULL);
   assert(aggregated != NULL);

   SCIPrationalDebugMessage("trying exact multi-aggregating variable <%s> == ...%d vars... %+q\n", var->name, naggvars, constant);

   *infeasible = FALSE;
   *aggregated = FALSE;

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpconstant) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpscalar) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &obj) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );

   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot multi-aggregate an untransformed original variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarMultiaggregateExact(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
            reopt, lpexact, cliquetable, branchcand, eventqueue, eventfilter, naggvars, aggvars, scalars, constant, infeasible, aggregated) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      assert(!SCIPeventqueueIsDelayed(eventqueue)); /* otherwise, the pseudo objective value update gets confused */

      /* check if we would create a self-reference */
      ntmpvars = naggvars;
      tmpvarssize = naggvars;
      SCIPrationalSetRational(tmpconstant, constant);
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &tmpvars, aggvars, ntmpvars) );
      SCIP_CALL( SCIPrationalCopyBlockArray(blkmem, &tmpscalars, scalars, ntmpvars) );

      /* get all active variables for multi-aggregation */
      SCIP_CALL( SCIPvarGetActiveRepresentativesExact(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, tmpconstant, &tmprequiredsize, FALSE) );
      if( tmprequiredsize > tmpvarssize )
      {
         SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &tmpvars, tmpvarssize, tmprequiredsize) );
         SCIP_CALL( SCIPrationalReallocBlockArray(blkmem, &tmpscalars, tmpvarssize, tmprequiredsize) );
         tmpvarssize = tmprequiredsize;
         SCIP_CALL( SCIPvarGetActiveRepresentativesExact(set, tmpvars, tmpscalars, &ntmpvars, tmpvarssize, tmpconstant, &tmprequiredsize, FALSE) );
         assert( tmprequiredsize <= tmpvarssize );
      }

      SCIPrationalSetReal(tmpscalar, 0.0);

      /* iterate over all active variables of the multi-aggregation and filter all variables which are equal to the
       * possible multi-aggregated variable
       */
      for( v = ntmpvars - 1; v >= 0; --v )
      {
         assert(tmpvars[v] != NULL);
         assert(SCIPvarGetStatusExact(tmpvars[v]) == SCIP_VARSTATUS_LOOSE);

         if( tmpvars[v]->index == var->index )
         {
            SCIPrationalAdd(tmpscalar, tmpscalar, tmpscalars[v]);
            tmpvars[v] = tmpvars[ntmpvars - 1];
            SCIPrationalSetRational(tmpscalars[v], tmpscalars[ntmpvars - 1]);
            --ntmpvars;
         }
      }

      /* this means that x = x + a_1*y_1 + ... + a_n*y_n + c */
      if( SCIPrationalIsEQReal(tmpscalar, 1.0) )
      {
         if( ntmpvars == 0 )
         {
            if( SCIPrationalIsZero(tmpconstant) ) /* x = x */
            {
               SCIPsetDebugMsg(set, "Possible multi-aggregation was completely resolved and detected to be redundant.\n");
               goto TERMINATE;
            }
            else /* 0 = c and c != 0 */
            {
               SCIPsetDebugMsg(set, "Multi-aggregation was completely resolved and led to infeasibility.\n");
               *infeasible = TRUE;
               goto TERMINATE;
            }
         }
         else if( ntmpvars == 1 ) /* 0 = a*y + c => y = -c/a */
         {
            assert(!SCIPrationalIsZero(tmpscalars[0]));
            assert(tmpvars[0] != NULL);

            SCIPrationalDiv(tmpval, constant, tmpscalars[0]);
            SCIPrationalNegate(tmpval, tmpval);

            SCIPrationalDebugMessage("Possible multi-aggregation led to fixing of variable <%s> to %q.\n", SCIPvarGetName(tmpvars[0]), tmpval);
            SCIP_CALL( SCIPvarFixExact(tmpvars[0], blkmem, set, stat, transprob, origprob, primal, tree, reopt, lpexact->fplp,
                  branchcand, eventqueue, eventfilter, cliquetable, tmpval, infeasible, aggregated) );
            goto TERMINATE;
         }
         else if( ntmpvars == 2 ) /* 0 = a_1*y_1 + a_2*y_2 + c => y_1 = -a_2/a_1 * y_2 - c/a_1 */
         {
            /* both variables are different active problem variables, and both scalars are non-zero: try to aggregate them */

            SCIPrationalNegate(tmpconstant, tmpconstant);
            SCIPrationalDebugMessage("Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %q and %q and constant %q.\n",
                  SCIPvarGetName(tmpvars[0]), SCIPvarGetName(tmpvars[1]), tmpscalars[0], tmpscalars[1], tmpconstant);

            SCIP_CALL( SCIPvarTryAggregateVarsExact(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lpexact->fplp,
                  cliquetable, branchcand, eventqueue, eventfilter, tmpvars[0], tmpvars[1], tmpscalars[0],
                  tmpscalars[1], tmpconstant, infeasible, aggregated) );

            goto TERMINATE;
         }
         else
            /** @todo: it is possible to multi-aggregate another variable, does it make sense?,
             *         rest looks like 0 = a_1*y_1 + ... + a_n*y_n + c and has at least three variables
             */
            goto TERMINATE;
      }
      /* this means that x = b*x + a_1*y_1 + ... + a_n*y_n + c */
      else if( !SCIPrationalIsZero(tmpscalar) )
      {
         SCIPrationalDiffReal(tmpscalar, tmpscalar, 1.0);
         SCIPrationalNegate(tmpscalar, tmpscalar);
         SCIPrationalDiv(tmpconstant, tmpconstant, tmpscalar);
         for( v = ntmpvars - 1; v >= 0; --v )
            SCIPrationalDiv(tmpscalars[v], tmpscalars[v], tmpscalar);
      }

      /* check, if we are in one of the simple cases */
      if( ntmpvars == 0 )
      {
         SCIPrationalDebugMessage("Possible multi-aggregation led to fixing of variable <%s> to %q.\n", SCIPvarGetName(var), tmpconstant);
         SCIP_CALL( SCIPvarFixExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lpexact->fplp, branchcand,
               eventqueue, eventfilter, cliquetable, tmpconstant, infeasible, aggregated) );
         goto TERMINATE;
      }

      /* if only one aggregation variable is left, we perform a normal aggregation instead of a multi-aggregation */
      if( ntmpvars == 1 )
      {
         SCIPrationalNegate(tmpscalars[0], tmpscalars[0]);
         SCIPrationalSetReal(tmpval, 1.0);
         SCIPrationalDebugMessage("Possible multi-aggregation led to aggregation of variables <%s> and <%s> with scalars %f and %q and constant %q.\n",
            SCIPvarGetName(var), SCIPvarGetName(tmpvars[0]), 1.0, tmpscalars[0], tmpconstant);

         SCIP_CALL( SCIPvarTryAggregateVarsExact(set, blkmem, stat, transprob, origprob, primal, tree, reopt, lpexact->fplp,
               cliquetable, branchcand, eventqueue, eventfilter, var, tmpvars[0], tmpval, tmpscalars[0], tmpconstant,
               infeasible, aggregated) );

         goto TERMINATE;
      }

      /**@todo currently we don't perform the multi aggregation if the multi aggregation variable has a non
       *  empty hole list; this should be changed in the future  */
      if( SCIPvarGetHolelistGlobal(var) != NULL )
         goto TERMINATE;

      /* if the variable is not allowed to be multi-aggregated */
      if( SCIPvarDoNotMultaggr(var) )
      {
         SCIPsetDebugMsg(set, "variable is not allowed to be multi-aggregated.\n");
         goto TERMINATE;
      }

      /* if the variable to be multi-aggregated has implications or variable bounds (i.e. is the implied variable or
       * variable bound variable of another variable), we have to remove it from the other variables implications or
       * variable bounds
       */
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
      assert(var->vlbs == NULL);
      assert(var->vubs == NULL);
      assert(var->implics == NULL);
      assert(var->cliquelist == NULL);

      /* set the aggregated variable's objective value to 0.0 */
      SCIPrationalSetRational(obj, var->exactdata->obj);
      SCIPrationalSetReal(tmpval, 0.0);
      SCIP_CALL( SCIPvarChgObjExact(var, blkmem, set, transprob, primal, lpexact, eventqueue, tmpval) );

      /* since we change the variable type form loose to multi aggregated, we have to adjust the number of loose
       * variables in the LP data structure; the loose objective value (looseobjval) in the LP data structure, however,
       * gets adjusted automatically, due to the event SCIP_EVENTTYPE_OBJCHANGED which dropped in the moment where the
       * objective of this variable is set to zero
       */
      SCIPlpDecNLoosevars(lpexact->fplp);
      SCIPlpExactDecNLoosevars(lpexact);

      /* unlock all rounding locks */
      for( i = 0; i < NLOCKTYPES; i++ )
      {
         nlocksdown[i] = var->nlocksdown[i];
         nlocksup[i] = var->nlocksup[i];

         var->nlocksdown[i] = 0;
         var->nlocksup[i] = 0;
      }

      /* convert variable into multi-aggregated variable */
      var->varstatus = SCIP_VARSTATUS_MULTAGGR; /*lint !e641*/
      var->exactdata->varstatusexact = SCIP_VARSTATUS_MULTAGGR; /*lint !e641*/
      SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &var->data.multaggr.vars, tmpvars, ntmpvars) );
      SCIP_CALL( SCIPrationalCopyBlockArray(blkmem, &(var->exactdata->multaggr.scalars), tmpscalars, ntmpvars) );
      SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &var->data.multaggr.scalars, ntmpvars) );
      for( i = 0; i < ntmpvars; ++i )
         var->data.multaggr.scalars[i] = SCIPrationalGetReal(tmpscalars[i]);
      SCIP_CALL( SCIPrationalCopyBlock(blkmem, &(var->exactdata->multaggr.constant), tmpconstant) );
      var->data.multaggr.constant = SCIPrationalGetReal(tmpconstant);
      var->data.multaggr.nvars = ntmpvars;
      var->data.multaggr.varssize = ntmpvars;

      /* mark variable to be non-deletable */
      SCIPvarMarkNotDeletable(var);

      /* relock the variable, thus increasing the locks of the aggregation variables */
      for( i = 0; i < NLOCKTYPES; i++ )
      {
         SCIP_CALL( SCIPvarAddLocks(var, blkmem, set, eventqueue, (SCIP_LOCKTYPE) i, nlocksdown[i], nlocksup[i]) );
      }

      /* update flags and branching factors and priorities of aggregation variables;
       * update preferred branching direction of all aggregation variables that don't have a preferred direction yet
       */
      branchfactor = var->branchfactor;
      branchpriority = var->branchpriority;
      branchdirection = (SCIP_BRANCHDIR)var->branchdirection;

      for( v = 0; v < ntmpvars; ++v )
      {
         assert(tmpvars[v] != NULL);
         tmpvars[v]->removable &= var->removable;
         branchfactor = MAX(tmpvars[v]->branchfactor, branchfactor);
         branchpriority = MAX(tmpvars[v]->branchpriority, branchpriority);

         /* mark variable to be non-deletable */
         SCIPvarMarkNotDeletable(tmpvars[v]);
      }
      for( v = 0; v < ntmpvars; ++v )
      {
         SCIP_CALL( SCIPvarChgBranchFactor(tmpvars[v], set, branchfactor) );
         SCIP_CALL( SCIPvarChgBranchPriority(tmpvars[v], branchpriority) );
         if( (SCIP_BRANCHDIR)tmpvars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
         {
            if( !SCIPrationalIsNegative(tmpscalars[v]) )
            {
               SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], branchdirection) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgBranchDirection(tmpvars[v], SCIPbranchdirOpposite(branchdirection)) );
            }
         }
      }
      SCIP_CALL( SCIPvarChgBranchFactor(var, set, branchfactor) );
      SCIP_CALL( SCIPvarChgBranchPriority(var, branchpriority) );

      if( var->probindex != -1 )
      {
         /* inform problem about the variable's status change */
         SCIP_CALL( SCIPprobVarChangedStatus(transprob, blkmem, set, branchcand, cliquetable, var) );
      }

      /* issue VARFIXED event */
      SCIP_CALL( varEventVarFixed(var, blkmem, set, eventqueue, 2) );

      /* reset the objective value of the aggregated variable, thus adjusting the objective value of the aggregation
       * variables and the problem's objective offset
       */
      SCIP_CALL( SCIPvarAddObjExact(var, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lpexact->fplp, eventqueue, eventfilter, obj) );

      *aggregated = TRUE;

   TERMINATE:
      SCIPrationalFreeBlockArray(blkmem, &tmpscalars, tmpvarssize);
      BMSfreeBlockMemoryArray(blkmem, &tmpvars, tmpvarssize);
      break;

   case SCIP_VARSTATUS_COLUMN:
      SCIPerrorMessage("cannot multi-aggregate a column variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot multi-aggregate a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      SCIPerrorMessage("cannot multi-aggregate an aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot multi-aggregate a multiple aggregated variable again\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      /* aggregate negation variable x in x' = offset - x, instead of aggregating x' directly:
       *   x' = a_1*y_1 + ... + a_n*y_n + c  ->  x = offset - x' = offset - a_1*y_1 - ... - a_n*y_n - c
       */
      assert(SCIPrationalIsZero(var->exactdata->obj));
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      /* switch the signs of the aggregation scalars */
      for( v = 0; v < naggvars; ++v )
         SCIPrationalNegate(scalars[v], scalars[v]);

      SCIPrationalDiffReal(tmpval, constant, var->data.negate.constant);
      SCIPrationalNegate(tmpval, tmpval);
      /* perform the multi aggregation on the negation variable */
      SCIP_CALL( SCIPvarMultiaggregateExact(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lpexact,
            cliquetable, branchcand, eventqueue, eventfilter, naggvars, aggvars, scalars,
            tmpval, infeasible, aggregated) );

      /* switch the signs of the aggregation scalars again, to reset them to their original values */
      for( v = 0; v < naggvars; ++v )
         SCIPrationalNegate(scalars[v], scalars[v]);

      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   SCIPrationalFreeBuffer(set->buffer, &tmpval);
   SCIPrationalFreeBuffer(set->buffer, &obj);
   SCIPrationalFreeBuffer(set->buffer, &tmpscalar);
   SCIPrationalFreeBuffer(set->buffer, &tmpconstant);

   return SCIP_OKAY;
}

/** transformed variables are resolved to their active, fixed, or multi-aggregated problem variable of a variable,
 * or for original variables the same variable is returned
 */
static
SCIP_VAR* varGetActiveVar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = var;

   SCIPdebugMessage("get active variable of <%s>\n", var->name);

   while( TRUE ) /*lint !e716 */
   {
      assert(retvar != NULL);

      switch( SCIPvarGetStatus(retvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_FIXED:
         return retvar;

      case SCIP_VARSTATUS_MULTAGGR:
         /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
         if ( retvar->data.multaggr.nvars == 1 )
            retvar = retvar->data.multaggr.vars[0];
         else
            return retvar;
         break;

      case SCIP_VARSTATUS_AGGREGATED:
         retvar = retvar->data.aggregate.var;
         break;

      case SCIP_VARSTATUS_NEGATED:
         retvar = retvar->negatedvar;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return NULL; /*lint !e527*/
      }
   }
}

/** returns whether variable is not allowed to be aggregated */
SCIP_Bool SCIPvarDoNotAggr(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = varGetActiveVar(var);
   assert(retvar != NULL);

   switch( SCIPvarGetStatus(retvar) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      return retvar->donotaggr;

   case SCIP_VARSTATUS_MULTAGGR:
      return FALSE;

   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_NEGATED:
   default:
      /* aggregated and negated variables should be resolved by varGetActiveVar() */
      SCIPerrorMessage("wrong variable status\n");
      SCIPABORT();
      return FALSE; /*lint !e527 */
   }
}

/** returns whether variable is not allowed to be multi-aggregated */
SCIP_Bool SCIPvarDoNotMultaggr(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = varGetActiveVar(var);
   assert(retvar != NULL);

   switch( SCIPvarGetStatus(retvar) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      return retvar->donotmultaggr;

   case SCIP_VARSTATUS_MULTAGGR:
      return FALSE;

   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_NEGATED:
   default:
      /* aggregated and negated variables should be resolved by varGetActiveVar() */
      SCIPerrorMessage("wrong variable status\n");
      SCIPABORT();
      return FALSE; /*lint !e527 */
   }
}

/** checks whether a loose variable can be used in a new aggregation with given coefficient */
SCIP_Bool SCIPvarIsAggrCoefAcceptable(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real             scalar              /**< aggregation scalar */
   )
{
   assert(set != NULL);
   assert(var != NULL);
   assert(scalar != 0.0);  /*lint !e777*/
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarGetMinAggrCoef(var) > 0.0);
   assert(SCIPvarGetMaxAggrCoef(var) >= 1.0);

   if( SCIPsetIsSumZero(set, SCIPvarGetMinAggrCoef(var) * scalar) )
      return FALSE;

   if( SCIPsetIsSumZero(set, 1.0 / (SCIPvarGetMaxAggrCoef(var) * scalar)) )
      return FALSE;

   return TRUE;
}

/** adds correct bound-data to negated variable */
static
SCIP_RETCODE varNegateExactData(
   SCIP_VAR*             negvar,             /**< the negated variable */
   SCIP_VAR*             origvar,            /**< the original variable */
   BMS_BLKMEM*           blkmem              /**< block memory of transformed problem */
   )
{
   SCIP_Real constant;

   if( origvar->exactdata == NULL )
      return SCIP_OKAY;

   assert(negvar != NULL);
   assert(origvar != NULL);
   assert(origvar->exactdata != NULL);
   assert(negvar->exactdata == NULL);

   constant = negvar->data.negate.constant;

   SCIP_CALL( SCIPvarCopyExactData(blkmem, negvar, origvar, FALSE) );

   SCIPrationalDiffReal(negvar->exactdata->glbdom.ub, origvar->exactdata->glbdom.lb, constant);
   SCIPrationalNegate(negvar->exactdata->glbdom.ub, negvar->exactdata->glbdom.ub);

   SCIPrationalDiffReal(negvar->exactdata->glbdom.lb, origvar->exactdata->glbdom.ub, constant);
   SCIPrationalNegate(negvar->exactdata->glbdom.lb, negvar->exactdata->glbdom.lb);

   SCIPrationalDiffReal(negvar->exactdata->locdom.ub, origvar->exactdata->locdom.lb, constant);
   SCIPrationalNegate(negvar->exactdata->locdom.ub, negvar->exactdata->locdom.ub);

   SCIPrationalDiffReal(negvar->exactdata->locdom.lb, origvar->exactdata->locdom.ub, constant);
   SCIPrationalNegate(negvar->exactdata->locdom.lb, negvar->exactdata->locdom.lb);

   negvar->exactdata->varstatusexact = SCIP_VARSTATUS_NEGATED;

   assert(SCIPrationalIsEQReal(negvar->exactdata->glbdom.ub, negvar->glbdom.ub));
   assert(SCIPrationalIsEQReal(negvar->exactdata->locdom.ub, negvar->locdom.ub));
   assert(SCIPrationalIsEQReal(negvar->exactdata->glbdom.lb, negvar->glbdom.lb));
   assert(SCIPrationalIsEQReal(negvar->exactdata->locdom.lb, negvar->locdom.lb));

   return SCIP_OKAY;
}

/** gets negated variable x' = offset - x of problem variable x; the negated variable is created if not yet existing;
 *  the negation offset of binary variables is always 1, the offset of other variables is fixed to lb + ub when the
 *  negated variable is created
 */
SCIP_RETCODE SCIPvarNegate(
   SCIP_VAR*             var,                /**< problem variable to negate */
   BMS_BLKMEM*           blkmem,             /**< block memory of transformed problem */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_VAR**            negvar              /**< pointer to store the negated variable */
   )
{
   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(negvar != NULL);

   /* check, if we already created the negated variable */
   if( var->negatedvar == NULL )
   {
      char negvarname[SCIP_MAXSTRLEN];

      assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED);

      SCIPsetDebugMsg(set, "creating negated variable of <%s>\n", var->name);

      /* negation is only possible for bounded variables */
      if( SCIPsetIsInfinity(set, -var->glbdom.lb) || SCIPsetIsInfinity(set, var->glbdom.ub) )
      {
         SCIPerrorMessage("cannot negate unbounded variable\n");
         return SCIP_INVALIDDATA;
      }

      (void) SCIPsnprintf(negvarname, SCIP_MAXSTRLEN, "%s_neg", var->name);

      /* create negated variable */
      SCIP_CALL( varCreate(negvar, blkmem, set, stat, negvarname, var->glbdom.lb, var->glbdom.ub, 0.0,
            SCIPvarGetType(var), SCIPvarGetImplType(var), var->initial, var->removable, NULL, NULL, NULL, NULL, NULL) );
      (*negvar)->varstatus = SCIP_VARSTATUS_NEGATED; /*lint !e641*/
      if( SCIPvarIsBinary(var) )
         (*negvar)->data.negate.constant = 1.0;
      else
         (*negvar)->data.negate.constant = var->glbdom.lb + var->glbdom.ub;

      /* create event filter for transformed variable */
      if( SCIPvarIsTransformed(var) )
      {
         SCIP_CALL( SCIPeventfilterCreate(&(*negvar)->eventfilter, blkmem) );
      }

      /* set the bounds corresponding to the negation variable */
      (*negvar)->glbdom.lb = (*negvar)->data.negate.constant - var->glbdom.ub;
      (*negvar)->glbdom.ub = (*negvar)->data.negate.constant - var->glbdom.lb;
      (*negvar)->locdom.lb = (*negvar)->data.negate.constant - var->locdom.ub;
      (*negvar)->locdom.ub = (*negvar)->data.negate.constant - var->locdom.lb;

      SCIP_CALL( varNegateExactData(*negvar, var, blkmem) );
      /**@todo create holes in the negated variable corresponding to the holes of the negation variable */

      /* link the variables together */
      var->negatedvar = *negvar;
      (*negvar)->negatedvar = var;

      /* mark both variables to be non-deletable */
      SCIPvarMarkNotDeletable(var);
      SCIPvarMarkNotDeletable(*negvar);

      /* copy the branch factor and priority, and use the negative preferred branching direction */
      (*negvar)->branchfactor = var->branchfactor;
      (*negvar)->branchpriority = var->branchpriority;
      (*negvar)->branchdirection = SCIPbranchdirOpposite((SCIP_BRANCHDIR)var->branchdirection); /*lint !e641*/

      /* copy donot(mult)aggr status */
      (*negvar)->donotaggr = var->donotaggr;
      (*negvar)->donotmultaggr = var->donotmultaggr;

      /* copy lazy bounds (they have to be flipped) */
      (*negvar)->lazylb = (*negvar)->data.negate.constant - var->lazyub;
      (*negvar)->lazyub = (*negvar)->data.negate.constant - var->lazylb;

      /* make negated variable a parent of the negation variable (negated variable is captured as a parent) */
      SCIP_CALL( varAddParent(var, blkmem, set, *negvar) );
      assert((*negvar)->nuses == 1);
   }
   assert(var->negatedvar != NULL);

   /* return the negated variable */
   *negvar = var->negatedvar;

   /* exactly one variable of the negation pair has to be marked as negated variable */
   assert((SCIPvarGetStatus(*negvar) == SCIP_VARSTATUS_NEGATED) != (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED));

   return SCIP_OKAY;
}

/** informs variable that its position in problem's vars array changed */
static
void varSetProbindex(
   SCIP_VAR*             var,                /**< problem variable */
   int                   probindex           /**< new problem index of variable (-1 for removal) */
   )
{
   assert(var != NULL);

   var->probindex = probindex;
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
   {
      assert(var->data.col != NULL);
      var->data.col->var_probindex = probindex;
   }
}

/** informs variable that its position in problem's vars array changed */
void SCIPvarSetProbindex(
   SCIP_VAR*             var,                /**< problem variable */
   int                   probindex           /**< new problem index of variable */
   )
{
   assert(var != NULL);
   assert(probindex >= 0);

   varSetProbindex(var, probindex);
}

/** gives the variable a new name
 *
 *  @note the old pointer is overwritten, which might result in a memory leakage
 */
void SCIPvarSetNamePointer(
   SCIP_VAR*             var,                /**< problem variable */
   const char*           name                /**< new name of variable */
   )
{
   assert(var != NULL);
   assert(name != NULL);

   var->name = (char*)name;
}

/** informs variable that it will be removed from the problem; adjusts probindex and removes variable from the
 *  implication graph;
 *  If 'final' is TRUE, the thorough implication graph removal is not performed. Instead, only the
 *  variable bounds and implication data structures of the variable are freed. Since in the final removal
 *  of all variables from the transformed problem, this deletes the implication graph completely and is faster
 *  than removing the variables one by one, each time updating all lists of the other variables.
 *  If 'keepimplics' is TRUE, the implications, variable bounds and cliques are kept. This should be used when the
 *  variable type is upgraded, i.e. when it gains (implied) integrality, so that existing implications are not lost.
 */
SCIP_RETCODE SCIPvarRemove(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory buffer */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             final,              /**< is this the final removal of all problem variables? */
   SCIP_Bool             keepimplics         /**< should the implications be kept? */
   )
{
   assert(SCIPvarGetProbindex(var) >= 0);
   assert(var->scip == set->scip);

   /* if the variable is active in the transformed problem, remove it from the implication graph */
   if( SCIPvarIsTransformed(var)
      && (SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN) )
   {
      if( final )
      {
         /* just destroy the data structures */
         SCIPvboundsFree(&var->vlbs, blkmem);
         SCIPvboundsFree(&var->vubs, blkmem);
         SCIPimplicsFree(&var->implics, blkmem);
      }
      else if( !keepimplics )
      {
         /* unlink the variable from all other variables' lists and free the data structures */
         SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, FALSE, TRUE) );
      }
   }

   /* mark the variable to be no longer a member of the problem */
   varSetProbindex(var, -1);

   return SCIP_OKAY;
}

/** marks the variable to be deleted from the problem */
void SCIPvarMarkDeleted(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->probindex != -1);

   var->deleted = TRUE;
}

/** marks the variable to not to be aggregated */
SCIP_RETCODE SCIPvarMarkDoNotAggr(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = varGetActiveVar(var);
   assert(retvar != NULL);

   switch( SCIPvarGetStatus(retvar) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      retvar->donotaggr = TRUE;
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot mark a multi-aggregated variable to not be aggregated.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_NEGATED:
   default:
      /* aggregated and negated variables should be resolved by varGetActiveVar() */
      SCIPerrorMessage("wrong variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** marks the variable to not to be multi-aggregated */
SCIP_RETCODE SCIPvarMarkDoNotMultaggr(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = varGetActiveVar(var);
   assert(retvar != NULL);

   switch( SCIPvarGetStatus(retvar) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      retvar->donotmultaggr = TRUE;
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot mark a multi-aggregated variable to not be multi-aggregated.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_NEGATED:
   default:
      /* aggregated and negated variables should be resolved by varGetActiveVar() */
      SCIPerrorMessage("wrong variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes type of variable; cannot be called, if var belongs to a problem */
SCIP_RETCODE SCIPvarChgType(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_VARTYPE          vartype             /**< new type of variable */
   )
{
   SCIP_EVENT* event;
   SCIP_VARTYPE oldtype;

   assert(var != NULL);

   SCIPdebugMessage("change type of <%s> from %d to %d\n", var->name, SCIPvarGetType(var), vartype);

   if( var->probindex >= 0 )
   {
      SCIPerrorMessage("cannot change type of variable already in the problem\n");
      return SCIP_INVALIDDATA;
   }

   if( vartype == SCIP_DEPRECATED_VARTYPE_IMPLINT )
   {
      if( SCIPvarGetImplType(var) != SCIP_IMPLINTTYPE_WEAK )
      {
         SCIP_CALL( SCIPvarChgImplType(var, blkmem, set, primal, lp, eventqueue, SCIP_IMPLINTTYPE_WEAK) );
      }
      return SCIP_OKAY;
   }

   oldtype = (SCIP_VARTYPE)var->vartype;
   var->vartype = vartype; /*lint !e641*/

   if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
   {
      SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var, oldtype, vartype) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
   }

   if( var->negatedvar != NULL )
   {
      assert(oldtype == (SCIP_VARTYPE)var->negatedvar->vartype
            || SCIPvarIsBinary(var) == SCIPvarIsBinary(var->negatedvar));

      var->negatedvar->vartype = vartype; /*lint !e641*/

      if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
      {
         SCIP_CALL( SCIPeventCreateTypeChanged(&event, blkmem, var->negatedvar, oldtype, vartype) );
         SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
      }
   }

   return SCIP_OKAY;
}

/** changes implied integral type of variable; cannot be called, if var belongs to a problem */
SCIP_RETCODE SCIPvarChgImplType(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_IMPLINTTYPE      impltype            /**< new implied integral type of variable */
   )
{
   SCIP_EVENT* event;
   SCIP_IMPLINTTYPE oldtype;

   assert(var != NULL);

   SCIPdebugMessage("change implied integral type of <%s> from %d to %d\n", var->name, SCIPvarGetImplType(var), impltype);

   if( var->probindex >= 0 )
   {
      SCIPerrorMessage("cannot change type of variable already in the problem\n");
      return SCIP_INVALIDDATA;
   }

   oldtype = (SCIP_IMPLINTTYPE) var->varimpltype;
   var->varimpltype = impltype; /*lint !e641*/

   if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
   {
      SCIP_CALL( SCIPeventCreateImplTypeChanged(&event, blkmem, var, oldtype, impltype) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
   }

   if( var->negatedvar != NULL )
   {
      var->negatedvar->varimpltype = impltype; /*lint !e641*/

      if( SCIPsetGetStage(set) > SCIP_STAGE_TRANSFORMING )
      {
         SCIP_CALL( SCIPeventCreateImplTypeChanged(&event, blkmem, var->negatedvar, oldtype, impltype) );
         SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );
      }
   }

   return SCIP_OKAY;
}

/** appends OBJCHANGED event to the event queue */
static
SCIP_RETCODE varEventObjChanged(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             oldobj,             /**< old objective value for variable */
   SCIP_Real             newobj              /**< new objective value for variable */
   )
{
   SCIP_EVENT* event;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(var->eventfilter != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarIsTransformed(var));

   /* In the case where the objcetive value of a variable is very close to epsilon, and it is aggregated
   * into a variable with a big objective value, round-off errors might make the assert oldobj != newobj fail.
   * Hence, we relax it by letting it pass if the variables are percieved the same and we use very large values
   * that make comparison with values close to epsilon inaccurate.
   */
   assert(!SCIPsetIsEQ(set, oldobj, newobj) ||
          (SCIPsetIsEQ(set, oldobj, newobj) && REALABS(newobj) > 1e+15 * SCIPsetEpsilon(set)) ||
          (set->exact_enable && oldobj != newobj)); /*lint !e777*/

   SCIP_CALL( SCIPeventCreateObjChanged(&event, blkmem, var, oldobj, newobj) );
   SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );

   return SCIP_OKAY;
}

/** appends OBJCHANGED event to the event queue */
static
SCIP_RETCODE varEventObjChangedExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        oldobj,             /**< old objective value for variable */
   SCIP_RATIONAL*        newobj              /**< new objective value for variable */
   )
{
   SCIP_EVENT* event;

   assert(var != NULL);
   assert(var->scip == set->scip);
   assert(var->eventfilter != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);
   assert(SCIPvarIsTransformed(var));

   /* In the case where the objcetive value of a variable is very close to epsilon, and it is aggregated
   * into a variable with a big objective value, round-off errors might make the assert oldobj != newobj fail.
   * Hence, we relax it by letting it pass if the variables are percieved the same and we use very large values
   * that make comparison with values close to epsilon inaccurate.
   */
   assert(!SCIPrationalIsEQ(oldobj, newobj));

   SCIP_CALL( SCIPeventCreateObjChanged(&event, blkmem, var, SCIPrationalGetReal(oldobj), SCIPrationalGetReal(newobj)) );
   SCIP_CALL( SCIPeventAddExactObjChg(event, blkmem, oldobj, newobj) );
   SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, primal, lp, NULL, NULL, &event) );

   return SCIP_OKAY;
}

/** changes objective value of variable */
SCIP_RETCODE SCIPvarChgObj(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             newobj              /**< new objective value for variable */
   )
{
   SCIP_Real oldobj;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   SCIPsetDebugMsg(set, "changing objective value of <%s> from %g to %g\n", var->name, var->obj, newobj);

   if( !SCIPsetIsEQ(set, var->obj, newobj) )
   {
      switch( SCIPvarGetStatus(var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
         {
            assert(SCIPprobIsTransformed(prob));

            SCIP_CALL( SCIPvarChgObj(var->data.original.transvar, blkmem, set, prob, primal, lp, eventqueue,
                  (SCIP_Real) prob->objsense * newobj/prob->objscale) );
         }
         else
            assert(set->stage == SCIP_STAGE_PROBLEM);

         var->obj = newobj;
         var->unchangedobj = newobj;

         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         oldobj = var->obj;
         var->obj = newobj;

         /* update unchanged objective value of variable */
         if( !lp->divingobjchg )
            var->unchangedobj = newobj;

         /* update the number of variables with non-zero objective coefficient;
          * we only want to do the update, if the variable is added to the problem;
          * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
          */
         if( SCIPvarIsActive(var) )
            SCIPprobUpdateNObjVars(prob, set, oldobj, var->obj);

         SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
         break;

      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_AGGREGATED:
      case SCIP_VARSTATUS_MULTAGGR:
      case SCIP_VARSTATUS_NEGATED:
         SCIPerrorMessage("cannot change objective value of a fixed, aggregated, multi-aggregated, or negated variable\n");
         return SCIP_INVALIDDATA;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** changes rational objective value of variable */
SCIP_RETCODE SCIPvarChgObjExact(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_LPEXACT*         lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        newobj              /**< new objective value for variable */
   )
{
   SCIP_Real newobjreal;
   SCIP_RATIONAL* oldobj;
   SCIP_RATIONAL* tmp;

   assert(var != NULL);
   assert(set != NULL);

   if( !set->exact_enable )
      return SCIP_OKAY;

   assert(var->exactdata != NULL);
   assert(var->scip == set->scip);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldobj) );
   newobjreal = SCIPrationalGetReal(newobj);

   SCIPrationalDebugMessage("changing exact objective value of <%s> from %q to %q\n", var->name, var->exactdata->obj, newobj);

   if( !SCIPrationalIsEQ(var->exactdata->obj, newobj) )
   {
      switch( SCIPvarGetStatusExact(var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
         {
            assert(SCIPprobIsTransformed(prob));

            SCIPrationalMultReal(tmp, newobj, (SCIP_Real) prob->objsense/prob->objscale);

            SCIP_CALL( SCIPvarChgObjExact(var->data.original.transvar, blkmem, set, prob, primal, lp, eventqueue,
                  tmp) );
         }
         else
            assert(set->stage == SCIP_STAGE_PROBLEM);

         SCIPrationalSetRational(var->exactdata->obj, newobj);
         SCIPintervalSetRational(&(var->exactdata->objinterval), newobj);
         var->obj = newobjreal;
         var->unchangedobj = newobjreal;
         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         SCIPrationalSetRational(oldobj, var->exactdata->obj);
         SCIPrationalSetRational(var->exactdata->obj, newobj);
         SCIPintervalSetRational(&(var->exactdata->objinterval), newobj);
         var->obj = newobjreal;

         /* update unchanged objective value of variable */
         if( !lp->fplp->divingobjchg )
            var->unchangedobj = newobjreal;

         /* update the number of variables with non-zero objective coefficient;
          * we only want to do the update, if the variable is added to the problem;
          * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
          */
         if( SCIPvarIsActive(var) )
            SCIPprobUpdateNObjVars(prob, set, SCIPrationalGetReal(oldobj), var->obj);

         SCIP_CALL( varEventObjChangedExact(var, blkmem, set, primal, lp->fplp, eventqueue, oldobj, var->exactdata->obj) );

         break;

      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_AGGREGATED:
      case SCIP_VARSTATUS_MULTAGGR:
      case SCIP_VARSTATUS_NEGATED:
         SCIPerrorMessage("cannot change objective value of a fixed, aggregated, multi-aggregated, or negated variable\n");
         return SCIP_INVALIDDATA;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   SCIPrationalFreeBuffer(set->buffer, &oldobj);
   SCIPrationalFreeBuffer(set->buffer, &tmp);

   return SCIP_OKAY;
}

/** adds value to objective value of variable */
SCIP_RETCODE SCIPvarAddObj(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_Real             addobj              /**< additional objective value for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage < SCIP_STAGE_INITSOLVE);

   SCIPsetDebugMsg(set, "adding %g to objective value %g of <%s>\n", addobj, var->obj, var->name);

   if( !SCIPsetIsZero(set, addobj) )
   {
      SCIP_Real oldobj;
      int i;

      switch( SCIPvarGetStatus(var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
         {
            SCIP_CALL( SCIPvarAddObj(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
                  reopt, lp, eventqueue, eventfilter, (SCIP_Real) transprob->objsense * addobj/transprob->objscale) );
         }
         else
            assert(set->stage == SCIP_STAGE_PROBLEM);

         var->obj += addobj;
         var->unchangedobj += addobj;
         assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));

         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         oldobj = var->obj;
         var->obj += addobj;

         /* update unchanged objective value of variable */
         if( !lp->divingobjchg )
         {
            var->unchangedobj += addobj;
            assert(SCIPsetIsEQ(set, var->obj, var->unchangedobj));
         }

         /* update the number of variables with non-zero objective coefficient;
          * we only want to do the update, if the variable is added to the problem;
          * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
          */
         if( SCIPvarIsActive(var) )
            SCIPprobUpdateNObjVars(transprob, set, oldobj, var->obj);

         SCIP_CALL( varEventObjChanged(var, blkmem, set, primal, lp, eventqueue, oldobj, var->obj) );
         break;

      case SCIP_VARSTATUS_FIXED:
         assert(SCIPsetIsEQ(set, var->locdom.lb, var->locdom.ub));
         SCIPprobAddObjoffset(transprob, var->locdom.lb * addobj);
         SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );
         break;

      case SCIP_VARSTATUS_AGGREGATED:
         assert(!var->donotaggr);
         /* x = a*y + c  ->  add a*addobj to obj. val. of y, and c*addobj to obj. offset of problem */
         SCIPprobAddObjoffset(transprob, var->data.aggregate.constant * addobj);
         SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );
         SCIP_CALL( SCIPvarAddObj(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
               lp, eventqueue, eventfilter, var->data.aggregate.scalar * addobj) );
         break;

      case SCIP_VARSTATUS_MULTAGGR:
         assert(!var->donotmultaggr);
         /* x = a_1*y_1 + ... + a_n*y_n  + c  ->  add a_i*addobj to obj. val. of y_i, and c*addobj to obj. offset */
         SCIPprobAddObjoffset(transprob, var->data.multaggr.constant * addobj);
         SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );
         for( i = 0; i < var->data.multaggr.nvars; ++i )
         {
            SCIP_CALL( SCIPvarAddObj(var->data.multaggr.vars[i], blkmem, set, stat, transprob, origprob, primal, tree,
                  reopt, lp, eventqueue, eventfilter, var->data.multaggr.scalars[i] * addobj) );
         }
         break;

      case SCIP_VARSTATUS_NEGATED:
         /* x' = offset - x  ->  add -addobj to obj. val. of x and offset*addobj to obj. offset of problem */
         assert(var->negatedvar != NULL);
         assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(var->negatedvar->negatedvar == var);
         SCIPprobAddObjoffset(transprob, var->data.negate.constant * addobj);
         SCIP_CALL( SCIPprimalUpdateObjoffset(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );
         SCIP_CALL( SCIPvarAddObj(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
               eventqueue, eventfilter, -addobj) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** adds exact value to objective value of variable */
SCIP_RETCODE SCIPvarAddObjExact(
   SCIP_VAR*             var,                /**< variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem data */
   SCIP_PROB*            origprob,           /**< original problem data */
   SCIP_PRIMAL*          primal,             /**< primal data */
   SCIP_TREE*            tree,               /**< branch and bound tree */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_RATIONAL*        addobj              /**< additional objective value for variable */
   )
{
   SCIP_RATIONAL* tmpobj;
   SCIP_RATIONAL* oldobj;
   SCIP_RATIONAL* multaggrobj;
   SCIP_Real oldobjreal;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage < SCIP_STAGE_INITSOLVE);

   SCIPrationalDebugMessage("adding %q to objective value %q of <%s>\n", addobj, var->exactdata->obj, var->name);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldobj) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpobj) );

   if( !SCIPrationalIsZero(addobj) )
   {
      int i;

      switch( SCIPvarGetStatusExact(var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
         {
            SCIPrationalMultReal(tmpobj, addobj, (SCIP_Real)transprob->objsense/transprob->objscale);
            SCIP_CALL( SCIPvarAddObjExact(var->data.original.transvar, blkmem, set, stat, transprob, origprob, primal, tree,
                  reopt, lp, eventqueue, eventfilter, tmpobj) );
         }
         else
            assert(set->stage == SCIP_STAGE_PROBLEM);

         SCIPrationalAdd(var->exactdata->obj, var->exactdata->obj, addobj);
         SCIPintervalSetRational(&(var->exactdata->objinterval), var->exactdata->obj);
         var->obj = SCIPrationalGetReal(var->exactdata->obj);
         var->unchangedobj = var->obj;

         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         SCIPrationalSetRational(oldobj, var->exactdata->obj);
         oldobjreal = var->obj;
         SCIPrationalAdd(var->exactdata->obj, var->exactdata->obj, addobj);
         SCIPintervalSetRational(&(var->exactdata->objinterval), var->exactdata->obj);
         var->obj = SCIPrationalGetReal(var->exactdata->obj);

         /* update unchanged objective value of variable */
         if( !lp->divingobjchg )
         {
            var->unchangedobj = var->obj;
         }

         /* update the number of variables with non-zero objective coefficient;
          * we only want to do the update, if the variable is added to the problem;
          * since the objective of inactive variables cannot be changed, this corresponds to probindex != -1
          */
         if( SCIPvarIsActive(var) )
            SCIPprobUpdateNObjVars(transprob, set, oldobjreal, var->obj);

         SCIP_CALL( varEventObjChangedExact(var, blkmem, set, primal, lp, eventqueue, oldobj, var->exactdata->obj) );
         break;

      case SCIP_VARSTATUS_FIXED:
         assert(SCIPsetIsEQ(set, var->locdom.lb, var->locdom.ub));
         SCIPrationalMult(tmpobj, var->exactdata->locdom.lb, addobj);
         SCIPprobAddObjoffsetExact(transprob, tmpobj);
         SCIP_CALL( SCIPprimalUpdateObjoffsetExact(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );
         break;

      case SCIP_VARSTATUS_AGGREGATED:
         /* x = a*y + c  ->  add a*addobj to obj. val. of y, and c*addobj to obj. offset of problem */
         SCIPrationalMult(tmpobj, var->exactdata->aggregate.constant, addobj);
         SCIPprobAddObjoffsetExact(transprob, tmpobj);
         SCIP_CALL( SCIPprimalUpdateObjoffsetExact(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );

         SCIPrationalMult(tmpobj, var->exactdata->aggregate.scalar, addobj);

         SCIP_CALL( SCIPvarAddObjExact(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, primal, tree, reopt,
               lp, eventqueue, eventfilter, tmpobj) );
         break;

      case SCIP_VARSTATUS_MULTAGGR:
         assert(!var->donotmultaggr);
         /* x = a_1*y_1 + ... + a_n*y_n  + c  ->  add a_i*addobj to obj. val. of y_i, and c*addobj to obj. offset */
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &multaggrobj) );

         SCIPrationalMult(tmpobj, var->exactdata->multaggr.constant, addobj);
         SCIPprobAddObjoffsetExact(transprob, tmpobj);
         SCIP_CALL( SCIPprimalUpdateObjoffsetExact(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );

         for( i = 0; i < var->data.multaggr.nvars; ++i )
         {
            SCIPrationalMult(multaggrobj, addobj, var->exactdata->multaggr.scalars[i]);
            SCIP_CALL( SCIPvarAddObjExact(var->data.multaggr.vars[i], blkmem, set, stat, transprob, origprob, primal, tree,
                  reopt, lp, eventqueue, eventfilter, multaggrobj) );
         }
         SCIPrationalFreeBuffer(set->buffer, &multaggrobj);
         break;

      case SCIP_VARSTATUS_NEGATED:
         /* x' = offset - x  ->  add -addobj to obj. val. of x and offset*addobj to obj. offset of problem */
         assert(var->negatedvar != NULL);
         assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(var->negatedvar->negatedvar == var);

         SCIPrationalMultReal(tmpobj, addobj, var->data.negate.constant);
         SCIPprobAddObjoffsetExact(transprob, tmpobj);
         SCIP_CALL( SCIPprimalUpdateObjoffsetExact(primal, blkmem, set, stat, eventqueue, eventfilter, transprob, origprob, tree, reopt, lp) );

         SCIPrationalNegate(tmpobj, addobj);
         SCIP_CALL( SCIPvarAddObjExact(var->negatedvar, blkmem, set, stat, transprob, origprob, primal, tree, reopt, lp,
               eventqueue, eventfilter, tmpobj) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   SCIPrationalFreeBuffer(set->buffer, &tmpobj);
   SCIPrationalFreeBuffer(set->buffer, &oldobj);

   return SCIP_OKAY;
}

/** changes objective value of variable in current dive */
SCIP_RETCODE SCIPvarChgObjDive(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_Real             newobj              /**< new objective value for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lp != NULL);

   SCIPsetDebugMsg(set, "changing objective of <%s> to %g in current dive\n", var->name, newobj);

   if( SCIPsetIsZero(set, newobj) )
      newobj = 0.0;

   /* change objective value of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarChgObjDive(var->data.original.transvar, set, lp, newobj) );
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIP_CALL( SCIPcolChgObj(var->data.col, set, lp, newobj) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      /* nothing to do here: only the constant shift in objective function would change */
      break;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      SCIP_CALL( SCIPvarChgObjDive(var->data.aggregate.var, set, lp, newobj / var->data.aggregate.scalar) );
      /* the constant can be ignored, because it would only affect the objective shift */
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change diving objective value of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgObjDive(var->negatedvar, set, lp, -newobj) );
      /* the offset can be ignored, because it would only affect the objective shift */
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** adjust lower bound to integral value, if variable is integral */
void SCIPvarAdjustLb(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            lb                  /**< pointer to lower bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lb != NULL);

   SCIPsetDebugMsg(set, "adjust lower bound %g of <%s>\n", *lb, var->name);

   *lb = adjustedLb(set, SCIPvarIsIntegral(var), *lb);
}

/** adjust lower bound to integral value, if variable is integral */
void SCIPvarAdjustLbExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        lb                  /**< pointer to lower bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lb != NULL);

   SCIPrationalDebugMessage("adjust lower bound %q of <%s>\n", lb, var->name);

   adjustedLbExact(set, SCIPvarIsIntegral(var), lb);
}

/** adjust lower bound to integral value, if variable is integral */
void SCIPvarAdjustLbExactFloat(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            lb                  /**< pointer to lower bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lb != NULL);

   SCIPsetDebugMsg(set, "adjust lower bound %g of <%s>\n", *lb, var->name);

   *lb = adjustedLbExactFloat(SCIPvarIsIntegral(var), *lb);
}

/** adjust upper bound to integral value, if variable is integral */
void SCIPvarAdjustUb(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            ub                  /**< pointer to upper bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(ub != NULL);

   SCIPsetDebugMsg(set, "adjust upper bound %g of <%s>\n", *ub, var->name);

   *ub = adjustedUb(set, SCIPvarIsIntegral(var), *ub);
}

/** adjust lower bound to integral value, if variable is integral */
void SCIPvarAdjustUbExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        ub                  /**< pointer to lower bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(ub != NULL);

   SCIPrationalDebugMessage("adjust upper bound %q of <%s>\n", ub, var->name);

   adjustedUbExact(set, SCIPvarIsIntegral(var), ub);
}

/** adjust lower bound to integral value, if variable is integral */
void SCIPvarAdjustUbExactFloat(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            ub                  /**< pointer to lower bound to adjust */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(ub != NULL);

   SCIPsetDebugMsg(set, "adjust upper bound %g of <%s>\n", *ub, var->name);

   *ub = adjustedUbExactFloat(SCIPvarIsIntegral(var), *ub);
}

/** adjust lower or upper bound to integral value, if variable is integral */
void SCIPvarAdjustBd(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_BOUNDTYPE        boundtype,          /**< type of bound to adjust */
   SCIP_Real*            bd                  /**< pointer to bound to adjust */
   )
{
   assert(boundtype == SCIP_BOUNDTYPE_LOWER || boundtype == SCIP_BOUNDTYPE_UPPER);

   if( boundtype == SCIP_BOUNDTYPE_LOWER )
      SCIPvarAdjustLb(var, set, bd);
   else
      SCIPvarAdjustUb(var, set, bd);
}

/** changes lower bound of original variable in original problem */
SCIP_RETCODE SCIPvarChgLbOriginal(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   int i;

   assert(var != NULL);
   assert(!SCIPvarIsTransformed(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage == SCIP_STAGE_PROBLEM);

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, SCIPvarGetUbOriginal(var)));
   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedLb(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetIsZero(set, newbound) )
      newbound = 0.0;

   /* original domains are only stored for ORIGINAL variables, not for NEGATED */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
   {
      SCIPsetDebugMsg(set, "changing original lower bound of <%s> from %g to %g\n",
         var->name, var->data.original.origdom.lb, newbound);

      if( SCIPsetIsEQ(set, var->data.original.origdom.lb, newbound) )
         return SCIP_OKAY;

      /* change the bound */
      var->data.original.origdom.lb = newbound;
   }
   else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
   {
      assert( var->negatedvar != NULL );
      SCIP_CALL( SCIPvarChgUbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_VAR* parentvar;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);
      assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
      assert(parentvar->negatedvar == var);
      assert(var->negatedvar == parentvar);

      SCIP_CALL( SCIPvarChgUbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
   }

   return SCIP_OKAY;
}

/** changes exact lower bound of original variable in original problem */
SCIP_RETCODE SCIPvarChgLbOriginalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_RATIONAL* tmpval;
   int i;

   assert(var != NULL);
   assert(!SCIPvarIsTransformed(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage == SCIP_STAGE_PROBLEM);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );
   SCIPrationalSetRational(tmpval, newbound);

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsLE(newbound, SCIPvarGetUbOriginalExact(var)));

   /* adjust bound to integral value if variable is of integral type */
   adjustedLbExact(set, SCIPvarIsIntegral(var), newbound);

   /* original domains are only stored for ORIGINAL variables, not for NEGATED */
   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_ORIGINAL )
   {
      SCIPrationalDebugMessage("changing original lower bound of <%s> from %q to %q\n",
         var->name, var->exactdata->origdom.lb, newbound);

      if( SCIPrationalIsEQ(var->exactdata->origdom.lb, newbound) )
      {
         SCIPrationalFreeBuffer(set->buffer, &tmpval);
         return SCIP_OKAY;
      }

      /* change the bound */
      SCIPrationalSetRational(var->exactdata->origdom.lb, newbound);
      var->data.original.origdom.lb = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_DOWNWARDS);
   }
   else if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_NEGATED )
   {
      assert( var->negatedvar != NULL );

      SCIPrationalSetReal(tmpval, var->data.negate.constant);
      SCIPrationalDiff(tmpval, tmpval, newbound);

      SCIP_CALL( SCIPvarChgUbOriginalExact(var->negatedvar, set, tmpval) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_VAR* parentvar;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);
      assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
      assert(parentvar->negatedvar == var);
      assert(var->negatedvar == parentvar);

      SCIPrationalSetReal(tmpval, parentvar->data.negate.constant);
      SCIPrationalDiff(tmpval, tmpval, newbound);

      SCIP_CALL( SCIPvarChgUbOriginalExact(parentvar, set, tmpval) );
   }

   SCIPrationalFreeBuffer(set->buffer, &tmpval);

   return SCIP_OKAY;
}

/** changes upper bound of original variable in original problem */
SCIP_RETCODE SCIPvarChgUbOriginal(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   int i;

   assert(var != NULL);
   assert(!SCIPvarIsTransformed(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage == SCIP_STAGE_PROBLEM);

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, SCIPvarGetLbOriginal(var)));
   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedUb(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetIsZero(set, newbound) )
      newbound = 0.0;

   /* original domains are only stored for ORIGINAL variables, not for NEGATED */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
   {
      SCIPsetDebugMsg(set, "changing original upper bound of <%s> from %g to %g\n",
         var->name, var->data.original.origdom.ub, newbound);

      if( SCIPsetIsEQ(set, var->data.original.origdom.ub, newbound) )
         return SCIP_OKAY;

      /* change the bound */
      var->data.original.origdom.ub = newbound;
   }
   else if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED )
   {
      assert( var->negatedvar != NULL );
      SCIP_CALL( SCIPvarChgLbOriginal(var->negatedvar, set, var->data.negate.constant - newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_VAR* parentvar;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);
      assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
      assert(parentvar->negatedvar == var);
      assert(var->negatedvar == parentvar);

      SCIP_CALL( SCIPvarChgLbOriginal(parentvar, set, parentvar->data.negate.constant - newbound) );
   }

   return SCIP_OKAY;
}

/** changes exact upper bound of original variable in original problem */
SCIP_RETCODE SCIPvarChgUbOriginalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_RATIONAL* tmpval;
   int i;

   assert(var != NULL);
   assert(!SCIPvarIsTransformed(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage == SCIP_STAGE_PROBLEM);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmpval) );

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsGE(newbound, SCIPvarGetLbOriginalExact(var)));

   /* adjust bound to integral value if variable is of integral type */
   adjustedUbExact(set, SCIPvarIsIntegral(var), newbound);

   /* original domains are only stored for ORIGINAL variables, not for NEGATED */
   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_ORIGINAL )
   {
      SCIPrationalDebugMessage("changing original upper bound of <%s> from %q to %q\n",
         var->name, var->exactdata->origdom.ub, newbound);

      if( SCIPrationalIsEQ(var->exactdata->origdom.ub, newbound) )
      {
         SCIPrationalFreeBuffer(set->buffer, &tmpval);
         return SCIP_OKAY;
      }

      /* change the bound */
      SCIPrationalSetRational(var->exactdata->origdom.ub, newbound);
      var->data.original.origdom.ub = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_UPWARDS);
   }
   else if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_NEGATED )
   {
      assert( var->negatedvar != NULL );

      SCIPrationalSetReal(tmpval, var->data.negate.constant);
      SCIPrationalDiff(tmpval, tmpval, newbound);

      SCIP_CALL( SCIPvarChgLbOriginalExact(var->negatedvar, set, tmpval) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_VAR* parentvar;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);
      assert(SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_NEGATED);
      assert(parentvar->negatedvar == var);
      assert(var->negatedvar == parentvar);

      SCIPrationalSetReal(tmpval, parentvar->data.negate.constant);
      SCIPrationalDiff(tmpval, tmpval, newbound);

      SCIP_CALL( SCIPvarChgLbOriginalExact(parentvar, set, tmpval) );
   }

   SCIPrationalFreeBuffer(set->buffer, &tmpval);

   return SCIP_OKAY;
}

/** appends GLBCHANGED event to the event queue */
static
SCIP_RETCODE varEventGlbChanged(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             oldbound,           /**< old lower bound for variable */
   SCIP_Real             newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GLBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue GLBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateGlbChanged(&event, blkmem, var, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends GLBCHANGED event to the event queue */
static
SCIP_RETCODE varEventGlbChangedExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        oldbound,           /**< old lower bound for variable */
   SCIP_RATIONAL*        newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPrationalIsEQ(oldbound, newbound));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GLBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPrationalDebugMessage("issue exact GLBCHANGED event for variable <%s>: %q -> %q\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateGlbChanged(&event, blkmem, var, SCIPrationalRoundReal(oldbound, SCIP_R_ROUND_DOWNWARDS), SCIPrationalRoundReal(newbound, SCIP_R_ROUND_DOWNWARDS)) );
      SCIP_CALL( SCIPeventAddExactBdChg(event, blkmem, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends GUBCHANGED event to the event queue */
static
SCIP_RETCODE varEventGubChanged(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             oldbound,           /**< old lower bound for variable */
   SCIP_Real             newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPsetIsEQ(set, oldbound, newbound) || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GUBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue GUBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateGubChanged(&event, blkmem, var, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends exact GUBCHANGED event to the event queue */
static
SCIP_RETCODE varEventGubChangedExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        oldbound,           /**< old lower bound for variable */
   SCIP_RATIONAL*        newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPrationalIsEQ(oldbound, newbound));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because global/root pseudo objective value has to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GUBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue GUBCHANGED event for variable <%s>: %g -> %g\n", var->name, SCIPrationalGetReal(oldbound), SCIPrationalGetReal(newbound));

      SCIP_CALL( SCIPeventCreateGubChanged(&event, blkmem, var, SCIPrationalRoundReal(oldbound, SCIP_R_ROUND_UPWARDS), SCIPrationalRoundReal(newbound, SCIP_R_ROUND_UPWARDS)) );
      SCIP_CALL( SCIPeventAddExactBdChg(event, blkmem, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends GHOLEADDED event to the event queue */
static
SCIP_RETCODE varEventGholeAdded(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right               /**< right bound of open interval in new hole */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPsetIsLT(set, left, right));

   /* check, if the variable is being tracked for bound changes */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_GHOLEADDED) != 0) )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue GHOLEADDED event for variable <%s>: (%.15g,%.15g)\n", var->name, left, right);

      SCIP_CALL( SCIPeventCreateGholeAdded(&event, blkmem, var, left, right) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** increases root bound change statistics after a global bound change */
static
void varIncRootboundchgs(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   if( SCIPvarIsActive(var) && SCIPvarIsTransformed(var) && set->stage == SCIP_STAGE_SOLVING )
   {
      stat->nrootboundchgs++;
      stat->nrootboundchgsrun++;
      if( SCIPvarIsIntegral(var) && SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) )
      {
         stat->nrootintfixings++;
         stat->nrootintfixingsrun++;
      }
   }
}

/* forward declaration, because both methods call each other recursively */

/* performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound            /**< new bound for variable */
   );

/** performs the current change in lower bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgLbGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real oldbound;
   int i;

   assert(var != NULL);
   /* local domains can violate global bounds but not more than feasibility epsilon */
   assert(SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb));
   assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedLb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the bound is feasible */
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound > var->glbdom.ub )
   {
      /* due to numerics we only want to be feasible in feasibility tolerance */
      assert(SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
      newbound = var->glbdom.ub;
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/

   SCIPsetDebugMsg(set, "process changing global lower bound of <%s> from %f to %f\n", var->name, var->glbdom.lb, newbound);

   if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* check bound on debugging solution */
   SCIP_CALL( SCIPdebugCheckLbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/

   /* change the bound */
   oldbound = var->glbdom.lb;
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->glbdom.ub));
   var->glbdom.lb = newbound;
   if( set->exact_enable && SCIPrationalIsLTReal(SCIPvarGetLbGlobalExact(var), newbound) )
      SCIPrationalSetReal(var->exactdata->glbdom.lb, newbound);
   assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
   assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* merges overlapping holes into single holes, moves bounds respectively */
      domMerge(&var->glbdom, blkmem, set, &newbound, NULL);
   }

   /* update the root bound changes counters */
   varIncRootboundchgs(var, set, stat);

   /* update the lbchginfos array by replacing worse local bounds with the new global bound and changing the
    * redundant bound changes to be branching decisions
    */
   for( i = 0; i < var->nlbchginfos; ++i )
   {
      assert(var->lbchginfos[i].var == var);

      if( var->lbchginfos[i].oldbound < var->glbdom.lb )
      {
         SCIPsetDebugMsg(set, " -> adjust lower bound change <%s>: %g -> %g due to new global lower bound %g\n",
            SCIPvarGetName(var), var->lbchginfos[i].oldbound, var->lbchginfos[i].newbound, var->glbdom.lb);
         var->lbchginfos[i].oldbound = var->glbdom.lb;
         if( SCIPsetIsLE(set, var->lbchginfos[i].newbound, var->glbdom.lb) )
         {
            /* this bound change is redundant due to the new global bound */
            var->lbchginfos[i].newbound = var->glbdom.lb;
            var->lbchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
            var->lbchginfos[i].redundant = TRUE;
         }
         else
            break; /* from now on, the remaining local bound changes are not redundant */
      }
      else
         break; /* from now on, the remaining local bound changes are not redundant */
   }

   /* remove redundant implications and variable bounds */
   if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
      && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
   {
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
   }

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventGlbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         /* this change does not affect the behavior in floating-point SCIP although it looks like it at first glance */
         {
            SCIP_Real parentnewbound;
            assert(parentvar->data.aggregate.var == var);

            if( parentvar->data.aggregate.scalar > 0 )
            {
               /* a > 0 -> change lower bound of y */
               assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, -oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
                  || (SCIPsetIsZero(set, parentvar->glbdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));

               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
                  parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
               else
                  parentnewbound = newbound;
               SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
            }
            else
            {
               /* a < 0 -> change upper bound of y */
               assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
               assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, -oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
                  || (SCIPsetIsZero(set, parentvar->glbdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));

               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
                  parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
               else
                  parentnewbound = -newbound;
               SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
            }
            break;
         }

      case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
               parentvar->data.negate.constant - newbound) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real oldbound;
   int i;

   assert(var != NULL);
   /* local domains can violate global bounds but not more than feasibility epsilon */
   assert(SCIPsetIsFeasLE(set, var->glbdom.lb , var->locdom.lb));
   assert(SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub));
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedUb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the bound is feasible */
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && newbound < var->glbdom.lb )
   {
      /* due to numerics we only want to be feasible in feasibility tolerance */
      assert(SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
      newbound = var->glbdom.lb;
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPsetIsEQ(set, newbound, 0.0) || SCIPsetIsEQ(set, newbound, 1.0)); /*lint !e641*/

   SCIPsetDebugMsg(set, "process changing global upper bound of <%s> from %f to %f\n", var->name, var->glbdom.ub, newbound);

   if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* check bound on debugging solution */
   SCIP_CALL( SCIPdebugCheckUbGlobal(set->scip, var, newbound) ); /*lint !e506 !e774*/

   /* change the bound */
   oldbound = var->glbdom.ub;
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->glbdom.lb));
   var->glbdom.ub = newbound;
   if( set->exact_enable && SCIPrationalIsGTReal(SCIPvarGetUbGlobalExact(var), newbound) )
      SCIPrationalSetReal(var->exactdata->glbdom.ub, newbound);

   assert( SCIPsetIsFeasLE(set, var->glbdom.lb, var->locdom.lb) );
   assert( SCIPsetIsFeasLE(set, var->locdom.ub, var->glbdom.ub) );

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* merges overlapping holes into single holes, moves bounds respectively */
      domMerge(&var->glbdom, blkmem, set, NULL, &newbound);
   }

   /* update the root bound changes counters */
   varIncRootboundchgs(var, set, stat);

   /* update the ubchginfos array by replacing worse local bounds with the new global bound and changing the
    * redundant bound changes to be branching decisions
    */
   for( i = 0; i < var->nubchginfos; ++i )
   {
      assert(var->ubchginfos[i].var == var);
      if( var->ubchginfos[i].oldbound > var->glbdom.ub )
      {
         SCIPsetDebugMsg(set, " -> adjust upper bound change <%s>: %g -> %g due to new global upper bound %g\n",
            SCIPvarGetName(var), var->ubchginfos[i].oldbound, var->ubchginfos[i].newbound, var->glbdom.ub);
         var->ubchginfos[i].oldbound = var->glbdom.ub;
         if( SCIPsetIsGE(set, var->ubchginfos[i].newbound, var->glbdom.ub) )
         {
            /* this bound change is redundant due to the new global bound */
            var->ubchginfos[i].newbound = var->glbdom.ub;
            var->ubchginfos[i].boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
            var->ubchginfos[i].redundant = TRUE;
         }
         else
            break; /* from now on, the remaining local bound changes are not redundant */
      }
      else
         break; /* from now on, the remaining local bound changes are not redundant */
   }

   /* remove redundant implications and variable bounds */
   if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE)
      && (!set->reopt_enable || set->stage == SCIP_STAGE_PRESOLVING) )
   {
      SCIP_CALL( SCIPvarRemoveCliquesImplicsVbs(var, blkmem, cliquetable, set, FALSE, TRUE, TRUE) );
   }

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventGubChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         /* this change does not affect the behavior in floating-point SCIP although it looks like it at first glance */
         {
            SCIP_Real parentnewbound;
            assert(parentvar->data.aggregate.var == var);

            if( parentvar->data.aggregate.scalar > 0 )
            {
               /* a > 0 -> change upper bound of y */
               assert(SCIPsetIsInfinity(set, parentvar->glbdom.ub) || SCIPsetIsInfinity(set, oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->glbdom.ub,
                     oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
                  parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
               else
                  parentnewbound = newbound;
               SCIP_CALL( varProcessChgUbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
            }
            else
            {
               /* a < 0 -> change lower bound of y */
               assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
               assert(SCIPsetIsInfinity(set, -parentvar->glbdom.lb) || SCIPsetIsInfinity(set, oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->glbdom.lb,
                     oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
                  parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
               else
                  parentnewbound = -newbound;
               SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, parentnewbound) );
            }
            break;
         }

      case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( varProcessChgLbGlobal(parentvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
               parentvar->data.negate.constant - newbound) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/* forward declaration, because both methods call each other recursively */

/* performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   );

/** performs the current change in lower bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgLbGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_RATIONAL* oldbound;
   SCIP_RATIONAL* parentnewbound;
   int i;

   assert(var != NULL);
   assert(SCIPrationalIsLE(var->exactdata->glbdom.lb, var->exactdata->locdom.lb));
   assert(SCIPrationalIsLE(var->exactdata->locdom.ub, var->exactdata->glbdom.ub));
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldbound) );

   /* adjust bound to integral value if variable is of integral type */
   adjustedLbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the bound is feasible */
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && SCIPrationalIsGT(newbound, var->exactdata->glbdom.ub) )
   {
      /* due to numerics we only want to be feasible in feasibility tolerance */
      assert(SCIPrationalIsLE(newbound, var->exactdata->glbdom.ub));
      SCIPrationalSetRational(newbound, var->exactdata->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPrationalIsEQReal(newbound, 0.0) || SCIPrationalIsEQReal(newbound, 1.0)); /*lint !e641*/

   SCIPrationalDebugMessage("process changing exact global lower bound of <%s> from %q to %q\n", var->name, var->exactdata->glbdom.lb, newbound);

   if( SCIPrationalIsEQ(newbound, var->exactdata->glbdom.lb) )
   {
      SCIPrationalFreeBuffer(set->buffer, &oldbound);
      return SCIP_OKAY;
   }

   /* change the bound */
   SCIPrationalSetRational(oldbound, var->exactdata->glbdom.lb);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsLE(newbound, var->exactdata->glbdom.ub));
   SCIPrationalSetRational(var->exactdata->glbdom.lb, newbound);
   var->glbdom.lb = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_DOWNWARDS);
   assert( SCIPrationalIsLE(var->exactdata->glbdom.lb, var->exactdata->locdom.lb) );
   assert( SCIPrationalIsLE(var->exactdata->locdom.ub, var->exactdata->glbdom.ub) );

   /* update the root bound changes counters */
   varIncRootboundchgs(var, set, stat);

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( var->eventfilter != NULL )
   {
      SCIP_CALL( varEventGlbChangedExact(var, blkmem, set, lpexact->fplp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgLbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);
         if( SCIPrationalIsPositive( parentvar->exactdata->aggregate.scalar) )
         {
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
            /* a > 0 -> change lower bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalSetRational(parentnewbound, newbound);
            SCIP_CALL( varProcessChgLbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, parentnewbound) );
            SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         }
         else
         {
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
            /* a < 0 -> change upper bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalNegate(parentnewbound, newbound);
            SCIP_CALL( varProcessChgUbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, parentnewbound) );
            SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         }
         break;

      case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         SCIPrationalDiffReal(parentnewbound, newbound, parentvar->data.negate.constant);
         SCIPrationalNegate(parentnewbound, parentnewbound);
         SCIP_CALL( varProcessChgUbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               parentnewbound) );
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }
   SCIPrationalFreeBuffer(set->buffer, &oldbound);

   return SCIP_OKAY;
}

/** performs the current change in exact upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_RATIONAL* oldbound;
   SCIP_RATIONAL* parentnewbound;
   int i;

   assert(var != NULL);
   assert(SCIPrationalIsLE(var->exactdata->glbdom.lb, var->exactdata->locdom.lb));
   assert(SCIPrationalIsLE(var->exactdata->locdom.ub, var->exactdata->glbdom.ub));
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldbound) );

   /* adjust bound to integral value if variable is of integral type */
   adjustedUbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the bound is feasible */
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && SCIPrationalIsLT(newbound, var->exactdata->glbdom.lb) )
   {
      /* due to numerics we only want to be feasible in feasibility tolerance */
      assert(SCIPrationalIsGE(newbound, var->exactdata->glbdom.lb));
      SCIPrationalSetRational(newbound, var->exactdata->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   assert(var->vartype != SCIP_VARTYPE_BINARY || SCIPrationalIsEQReal(newbound, 0.0) || SCIPrationalIsEQReal(newbound, 1.0)); /*lint !e641*/

   SCIPrationalDebugMessage("process changing exact global upper bound of <%s> from %q to %q\n", var->name, var->exactdata->glbdom.lb, newbound);

   if( SCIPrationalIsEQ(newbound, var->exactdata->glbdom.ub) )
   {
      SCIPrationalFreeBuffer(set->buffer, &oldbound);
      return SCIP_OKAY;
   }

   /* change the bound */
   SCIPrationalSetRational(oldbound, var->exactdata->glbdom.ub);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsGE(newbound, var->exactdata->glbdom.lb));
   SCIPrationalSetRational(var->exactdata->glbdom.ub, newbound);
   var->glbdom.ub = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_UPWARDS);
   assert( SCIPrationalIsLE(var->exactdata->glbdom.lb, var->exactdata->locdom.lb) );
   assert( SCIPrationalIsLE(var->exactdata->locdom.ub, var->exactdata->glbdom.ub) );

   /* update the root bound changes counters */
   varIncRootboundchgs(var, set, stat);

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventGubChangedExact(var, blkmem, set, lpexact->fplp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgUbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);
         if( SCIPrationalIsPositive( parentvar->exactdata->aggregate.scalar) )
         {
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
            /* a > 0 -> change lower bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalSetRational(parentnewbound, newbound);
            SCIP_CALL( varProcessChgUbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, parentnewbound) );
            SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         }
         else
         {
            SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
            /* a < 0 -> change upper bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalNegate(parentnewbound, newbound);
            SCIP_CALL( varProcessChgLbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, parentnewbound) );
            SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         }
         break;

      case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         SCIPrationalDiffReal(parentnewbound, newbound, parentvar->data.negate.constant);
         SCIPrationalNegate(parentnewbound, parentnewbound);
         SCIP_CALL( varProcessChgLbGlobalExact(parentvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               parentnewbound) );
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }
   SCIPrationalFreeBuffer(set->buffer, &oldbound);

   return SCIP_OKAY;
}

/** changes global lower bound of variable; if possible, adjusts bound to integral value;
 *  updates local lower bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgLbGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedLb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible
    * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
    *       here because we reset bounds to their original value!
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->glbdom.ub));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upperbound, which could have happened due to numerics */
      newbound = MIN(newbound, var->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
    * SCIPvarFix() allows fixings that are outside of the domain within feastol
    */
   assert(lp == NULL || SCIPsetIsFeasLE(set, var->glbdom.lb, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));

   SCIPsetDebugMsg(set, "changing global lower bound of <%s> from %g to %g\n", var->name, var->glbdom.lb, newbound);

   if( SCIPsetIsEQ(set, var->glbdom.lb, newbound) && !(newbound != var->glbdom.lb && newbound * var->glbdom.lb <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgLbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
               cliquetable, newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         if( newbound > SCIPvarGetLbLocal(var) )
         {
            SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
         }
         SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      if( newbound > SCIPvarGetLbLocal(var) )
      {
         /* ensure that the local bound change is not blocked */
         if( newbound > SCIPvarGetUbLocal(var) )
         {
            SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
         }
         SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      }
      SCIP_CALL( varProcessChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      {
         SCIP_Real childnewbound;
         assert(var->data.aggregate.var != NULL);

         if( var->data.aggregate.scalar > 0 )
         {
            /* a > 0 -> change lower bound of y */
            assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
               || SCIPsetIsFeasEQ(set, var->glbdom.lb,
                  var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
            if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
            else
               childnewbound = newbound;
            SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
                  childnewbound) );
         }
         else
         {
            /* a < 0 -> change upper bound of y */
            assert((SCIPsetIsInfinity(set, -var->glbdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
               || SCIPsetIsFeasEQ(set, var->glbdom.lb,
                  var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
            if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
            else
               childnewbound = -newbound;
            SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
                  childnewbound) );
         }
         break;
      }

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgUbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
            var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes global lower bound of variable; if possible, adjusts bound to integral value;
 *  updates local lower bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgLbGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_RATIONAL* childnewbound;

   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsGT(newbound, var->exactdata->glbdom.ub));

   /* adjust bound to integral value if variable is of integral type */
   adjustedLbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible
    * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
    *       here because we reset bounds to their original value!
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsGT(newbound, var->exactdata->glbdom.ub));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lowerbound, which could have happened due to numerics */
      SCIPrationalMin(newbound, newbound, var->exactdata->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
    * SCIPvarFix() allows fixings that are outside of the domain within feastol
    */
   assert(lpexact == NULL || SCIPrationalIsLE(var->exactdata->glbdom.lb, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));

   SCIPrationalDebugMessage("changing global lower bound of <%s> from %q to %q\n", var->name, var->exactdata->glbdom.lb, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgLbGlobalExact(var->data.original.transvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         if( SCIPrationalIsGT(newbound, SCIPvarGetLbLocalExact(var)) )
         {
            SCIP_CALL( SCIPvarChgLbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
         }
         SCIP_CALL( varProcessChgLbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      if( SCIPrationalIsGT(newbound, SCIPvarGetLbLocalExact(var)) )
      {
         assert(SCIPrationalIsLE(newbound, SCIPvarGetUbLocalExact(var)));
         SCIP_CALL( SCIPvarChgLbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      }
      SCIP_CALL( varProcessChgLbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPrationalIsPositive(var->exactdata->aggregate.scalar) )
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a > 0 -> change lower bound of y */
         if( !SCIPrationalIsAbsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgLbGlobalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else if( SCIPrationalIsNegative(var->exactdata->aggregate.scalar) )
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a < 0 -> change upper bound of y */
         if( !SCIPrationalIsAbsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgUbGlobalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );
      SCIPrationalDiffReal(childnewbound, newbound, var->data.negate.constant);
      SCIPrationalNegate(childnewbound, childnewbound);
      SCIP_CALL( SCIPvarChgUbGlobalExact(var->negatedvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
            childnewbound) );
      SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes global upper bound of variable; if possible, adjusts bound to integral value;
 *  updates local upper bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgUbGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedUb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible
    * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
    *       here because we reset bounds to their original value!
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->glbdom.lb));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lowerbound, which could have happened due to numerics */
      newbound = MAX(newbound, var->glbdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
    * SCIPvarFix() allows fixings that are outside of the domain within feastol
    */
   assert(lp == NULL || SCIPsetIsFeasGE(set, var->glbdom.ub, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));

   SCIPsetDebugMsg(set, "changing global upper bound of <%s> from %g to %g\n", var->name, var->glbdom.ub, newbound);

   if( SCIPsetIsEQ(set, var->glbdom.ub, newbound) && !(newbound != var->glbdom.ub && newbound * var->glbdom.ub <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgUbGlobal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         if( newbound < SCIPvarGetUbLocal(var) )
         {
            SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
         }
         SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      if( newbound < SCIPvarGetUbLocal(var) )
      {
         /* ensure that the local bound change is not blocked */
         if( newbound < SCIPvarGetLbLocal(var) )
         {
            SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
         }
         SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      }
      SCIP_CALL( varProcessChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
   {
      SCIP_Real childnewbound;
      assert(var->data.aggregate.var != NULL);

      if( var->data.aggregate.scalar > 0 )
      {
         /* a > 0 -> change lower bound of y */
         assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->glbdom.ub))
            || SCIPsetIsFeasEQ(set, var->glbdom.ub,
               var->data.aggregate.var->glbdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = newbound;
         SCIP_CALL( SCIPvarChgUbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
               childnewbound) );
      }
      else
      {
         /* a < 0 -> change upper bound of y */
         assert((SCIPsetIsInfinity(set, var->glbdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->glbdom.lb))
            || SCIPsetIsFeasEQ(set, var->glbdom.ub,
               var->data.aggregate.var->glbdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = -newbound;
         SCIP_CALL( SCIPvarChgLbGlobal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
               childnewbound) );
      }
      break;
   }

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgLbGlobal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable,
            var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes global upper bound of variable; if possible, adjusts bound to integral value;
 *  updates local upper bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgUbGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_RATIONAL* childnewbound;

   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsLT(newbound, var->exactdata->glbdom.lb));

   /* adjust bound to integral value if variable is of integral type */
   adjustedUbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible
    * @todo this does not have to be the case if the original problem was infeasible due to bounds and we are called
    *       here because we reset bounds to their original value!
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsLT(newbound, var->exactdata->glbdom.lb));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lowerbound, which could have happened due to numerics */
      SCIPrationalMax(newbound, newbound, var->exactdata->glbdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   /* the new global bound has to be tighter except we are in the original problem; this must be w.r.t. feastol because
    * SCIPvarFix() allows fixings that are outside of the domain within feastol
    */
   assert(lpexact == NULL || SCIPrationalIsGE(var->exactdata->glbdom.ub, newbound) || (set->reopt_enable && set->stage == SCIP_STAGE_PRESOLVED));

   SCIPrationalDebugMessage("changing global upper bound of <%s> from %q to %q\n", var->name, var->exactdata->glbdom.ub, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgUbGlobalExact(var->data.original.transvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         if( SCIPrationalIsLT(newbound, SCIPvarGetUbLocalExact(var)) )
         {
            SCIP_CALL( SCIPvarChgUbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
         }

         SCIP_CALL( varProcessChgUbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      if( SCIPrationalIsLT(newbound, SCIPvarGetUbLocalExact(var)) )
      {
         assert(SCIPrationalIsGE(newbound, SCIPvarGetLbLocalExact(var)));
         SCIP_CALL( SCIPvarChgUbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      }
      SCIP_CALL( varProcessChgUbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPrationalIsPositive(var->exactdata->aggregate.scalar) )
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a > 0 -> change lower bound of y */
         if( !SCIPrationalIsAbsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgUbGlobalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else if( SCIPrationalIsNegative(var->exactdata->aggregate.scalar) )
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a < 0 -> change upper bound of y */
         if( !SCIPrationalIsAbsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgLbGlobalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );
      SCIPrationalDiffReal(childnewbound, newbound, var->data.negate.constant);
      SCIPrationalNegate(childnewbound, childnewbound);
      SCIP_CALL( SCIPvarChgLbGlobalExact(var->negatedvar, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable,
            childnewbound) );
      SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes lazy lower bound of the variable, this is only possible if the variable is not in the LP yet */
SCIP_RETCODE SCIPvarChgLbLazy(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             lazylb              /**< the lazy lower bound to be set */
   )
{
   assert(var != NULL);
   assert(var->probindex != -1);
   assert(SCIPsetIsFeasGE(set, var->glbdom.ub, lazylb));
   assert(SCIPsetIsFeasGE(set, var->lazyub, lazylb));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* variable should not be in the LP */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIP_INVALIDCALL;

   var->lazylb = lazylb;

   return SCIP_OKAY;
}

/** changes lazy upper bound of the variable, this is only possible if the variable is not in the LP yet */
SCIP_RETCODE SCIPvarChgUbLazy(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             lazyub              /**< the lazy upper bound to be set */
   )
{
   assert(var != NULL);
   assert(var->probindex != -1);
   assert(SCIPsetIsFeasGE(set, lazyub, var->glbdom.lb));
   assert(SCIPsetIsFeasGE(set, lazyub, var->lazylb));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* variable should not be in the LP */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIP_INVALIDCALL;

   var->lazyub = lazyub;

   return SCIP_OKAY;
}

/** changes global bound of variable; if possible, adjusts bound to integral value;
 *  updates local bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgBdGlobal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Real             newbound,           /**< new bound for variable */
   SCIP_BOUNDTYPE        boundtype           /**< type of bound: lower or upper bound */
   )
{
   /* apply bound change to the LP data */
   switch( boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      return SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
   case SCIP_BOUNDTYPE_UPPER:
      return SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newbound);
   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }
}

/** changes exact global bound of variable; if possible, adjusts bound to integral value;
 *  updates local bound if the global bound is tighter
 */
SCIP_RETCODE SCIPvarChgBdGlobalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_RATIONAL*        newbound,           /**< new bound for variable */
   SCIP_BOUNDTYPE        boundtype           /**< type of bound: lower or upper bound */
   )
{
   /* apply bound change to the LP data */
   switch( boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      return SCIPvarChgLbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound);
   case SCIP_BOUNDTYPE_UPPER:
      return SCIPvarChgUbGlobalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, cliquetable, newbound);
   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }
}

/** appends LBTIGHTENED or LBRELAXED event to the event queue */
static
SCIP_RETCODE varEventLbChanged(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             oldbound,           /**< old lower bound for variable */
   SCIP_Real             newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.lb || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_LBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue LBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateLbChanged(&event, blkmem, var, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends LBTIGHTENED or LBRELAXED event to the event queue */
static
SCIP_RETCODE varEventLbChangedExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LPEXACT*         lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        oldbound,           /**< old lower bound for variable */
   SCIP_RATIONAL*        newbound            /**< new lower bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_LBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPrationalDebugMessage("issue exact LBCHANGED event for variable <%s>: %q -> %q\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateLbChanged(&event, blkmem, var, SCIPrationalRoundReal(oldbound, SCIP_R_ROUND_DOWNWARDS), SCIPrationalRoundReal(newbound, SCIP_R_ROUND_DOWNWARDS)) );
      SCIP_CALL( SCIPeventAddExactBdChg(event, blkmem, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp->fplp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends UBTIGHTENED or UBRELAXED event to the event queue */
static
SCIP_RETCODE varEventUbChanged(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_Real             oldbound,           /**< old upper bound for variable */
   SCIP_Real             newbound            /**< new upper bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(!SCIPsetIsEQ(set, oldbound, newbound) || newbound == var->glbdom.ub || (newbound != oldbound && newbound * oldbound <= 0.0)); /*lint !e777*/
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_UBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue UBCHANGED event for variable <%s>: %g -> %g\n", var->name, oldbound, newbound);

      SCIP_CALL( SCIPeventCreateUbChanged(&event, blkmem, var, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/** appends exact UBTIGHTENED or UBRELAXED event to the event queue */
static
SCIP_RETCODE varEventUbChangedExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LPEXACT*         lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_RATIONAL*        oldbound,           /**< old upper bound for variable */
   SCIP_RATIONAL*        newbound            /**< new upper bound for variable */
   )
{
   assert(var != NULL);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check, if the variable is being tracked for bound changes
    * COLUMN and LOOSE variables are tracked always, because row activities and LP changes have to be updated
    */
   if( (var->eventfilter->len > 0 && (var->eventfilter->eventmask & SCIP_EVENTTYPE_UBCHANGED) != 0)
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_EVENT* event;

      SCIPsetDebugMsg(set, "issue UBCHANGED event for variable <%s>: %g -> %g\n", var->name, SCIPrationalGetReal(oldbound), SCIPrationalGetReal(newbound));

      SCIP_CALL( SCIPeventCreateUbChanged(&event, blkmem, var, SCIPrationalRoundReal(oldbound, SCIP_R_ROUND_UPWARDS), SCIPrationalRoundReal(newbound, SCIP_R_ROUND_UPWARDS)) );
      SCIP_CALL( SCIPeventAddExactBdChg(event, blkmem, oldbound, newbound) );
      SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, lp->fplp, branchcand, NULL, &event) );
   }

   return SCIP_OKAY;
}

/* forward declaration, because both methods call each other recursively */

/* performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound            /**< new bound for variable */
   );

/** performs the current change in lower bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgLbLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real oldbound;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
            || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
      || (SCIPvarIsIntegral(var) && (SCIPsetIsIntegral(set, newbound)
            || SCIPsetIsEQ(set, newbound, var->locdom.ub)))
      || !SCIPvarIsIntegral(var));

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsLE(set, newbound, var->glbdom.ub));
   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedLb(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upper bound, which could have happened due to numerics */
      newbound = MIN(newbound, var->locdom.ub);

      /* we do not want to undercut the global lower bound, which could have happened due to numerics */
      newbound = MAX(newbound, var->glbdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   SCIPsetDebugMsg(set, "process changing lower bound of <%s> from %g to %g\n", var->name, var->locdom.lb, newbound);

   if( SCIPsetIsEQ(set, newbound, var->glbdom.lb) && var->glbdom.lb != var->locdom.lb ) /*lint !e777*/
      newbound = var->glbdom.lb;
   else if( SCIPsetIsEQ(set, newbound, var->locdom.lb) && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* change the bound */
   oldbound = var->locdom.lb;
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasLE(set, newbound, var->locdom.ub));
   var->locdom.lb = newbound;
   /* adjust the exact bound as well */
   if( set->exact_enable )
   {
      SCIPrationalSetReal(var->exactdata->locdom.lb, var->locdom.lb);
      SCIPrationalMax(var->exactdata->locdom.lb, var->exactdata->locdom.lb, var->exactdata->glbdom.lb);
   }

   /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
    * once update the statistic
    */
   if( stat != NULL )
      SCIPstatIncrement(stat, set, domchgcount);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* merges overlapping holes into single holes, moves bounds respectively */
      domMerge(&var->locdom, blkmem, set, &newbound, NULL);
   }

   if( SCIPisCertified(set->scip) && SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && !SCIPinProbing(set->scip) )
      SCIPvarSetLbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventLbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         /* this change does not affect the behavior in floating-point SCIP although it looks like it at first glance */
         {
            SCIP_Real parentnewbound;
            assert(parentvar->data.aggregate.var == var);

            if (!set->exact_enable)
            {
               parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
            }
            else
            {
               SCIP_INTERVAL parentboundinterval;
               SCIPintervalSet(&parentboundinterval, newbound);
               SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &parentboundinterval, parentboundinterval, parentvar->data.aggregate.scalar);
               SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &parentboundinterval, parentboundinterval, parentvar->data.aggregate.constant);
               parentnewbound = parentvar->data.aggregate.scalar > 0 ? parentboundinterval.inf : parentboundinterval.sup;
            }

            if( parentvar->data.aggregate.scalar > 0 )
            {
               /* a > 0 -> change lower bound of y */
               assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, -oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->locdom.lb, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
                  || (SCIPsetIsZero(set, parentvar->locdom.lb / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));

               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               {
                  /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
                  * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
                  * as a result, the parent's lower bound is set to it's upper bound, and not above
                  */
                  if( parentnewbound > parentvar->glbdom.ub )
                  {
                     /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
                     assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
                     parentnewbound = parentvar->glbdom.ub;
                  }
               }
               else
                  parentnewbound = newbound;
               SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
            }
            else
            {
               /* a < 0 -> change upper bound of y */
               assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
               assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, -oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->locdom.ub, oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant)
                  || (SCIPsetIsZero(set, parentvar->locdom.ub / parentvar->data.aggregate.scalar) && SCIPsetIsZero(set, oldbound)));

               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               {
                  /* if parent's new upper bound is below its lower bound, then this could be due to numerical difficulties, e.g., if numbers are large
                  * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
                  * as a result, the parent's upper bound is set to it's lower bound, and not below
                  */
                  if( parentnewbound < parentvar->glbdom.lb )
                  {
                     /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
                     assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
                     parentnewbound = parentvar->glbdom.lb;
                  }
               }
               else
                  parentnewbound = -newbound;
               SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
            }
            break;
         }
      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
               parentvar->data.negate.constant - newbound) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/** performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real oldbound;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPsetIsZero(set, newbound) || SCIPsetIsEQ(set, newbound, 1.0)
            || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
      || (SCIPvarIsIntegral(var) && (SCIPsetIsIntegral(set, newbound)
            || SCIPsetIsEQ(set, newbound, var->locdom.lb)))
      || !SCIPvarIsIntegral(var));

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsGE(set, newbound, var->glbdom.lb));
   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedUb(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lower bound, which could have happened due to numerics */
      newbound = MAX(newbound, var->locdom.lb);

      /* we do not want to exceed the global upper bound, which could have happened due to numerics */
      newbound = MIN(newbound, var->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   SCIPsetDebugMsg(set, "process changing upper bound of <%s> from %g to %g\n", var->name, var->locdom.ub, newbound);

   if( SCIPsetIsEQ(set, newbound, var->glbdom.ub) && var->glbdom.ub != var->locdom.ub  ) /*lint !e777*/
      newbound = var->glbdom.ub;
   else if( SCIPsetIsEQ(set, newbound, var->locdom.ub) && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) )  /*lint !e777*/
      return SCIP_OKAY;

   /* change the bound */
   oldbound = var->locdom.ub;
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPsetIsFeasGE(set, newbound, var->locdom.lb));
   var->locdom.ub = newbound;
   /* adjust the exact bound as well */
   if( set->exact_enable )
   {
      SCIPrationalSetReal(var->exactdata->locdom.ub, var->locdom.ub);
      SCIPrationalMin(var->exactdata->locdom.ub, var->exactdata->locdom.ub, var->exactdata->glbdom.ub);
   }

   /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
    * once update the statistic
    */
   if( stat != NULL )
      SCIPstatIncrement(stat, set, domchgcount);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* merges overlapping holes into single holes, moves bounds respectively */
      domMerge(&var->locdom, blkmem, set, NULL, &newbound);
   }

   if( SCIPisCertified(set->scip) && SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && !SCIPinProbing(set->scip) )
      SCIPvarSetUbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventUbChanged(var, blkmem, set, lp, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         /* this change does not affect the behavior in floating-point SCIP although it looks like it at first glance */
         {
            SCIP_Real parentnewbound;
            assert(parentvar->data.aggregate.var == var);

            if( !set->exact_enable )
            {
               parentnewbound = parentvar->data.aggregate.scalar * newbound + parentvar->data.aggregate.constant;
            }
            else
            {
               SCIP_INTERVAL parentboundinterval;
               SCIPintervalSet(&parentboundinterval, newbound);
               SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &parentboundinterval, parentboundinterval, parentvar->data.aggregate.scalar);
               SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &parentboundinterval, parentboundinterval, parentvar->data.aggregate.constant);
               parentnewbound = parentvar->data.aggregate.scalar > 0 ? parentboundinterval.sup : parentboundinterval.inf;
            }
            if( parentvar->data.aggregate.scalar > 0 )
            {
               /* a > 0 -> change upper bound of x */
               assert(SCIPsetIsInfinity(set, parentvar->locdom.ub) || SCIPsetIsInfinity(set, oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->locdom.ub,
                     oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               {
                  /* if parent's new upper bound is below its lower bound, then this could be due to numerical difficulties, e.g., if numbers are large
                  * thus, at least a relative comparision of the new upper bound and the current lower bound should proof consistency
                  * as a result, the parent's upper bound is set to it's lower bound, and not below
                  */
                  if( parentnewbound < parentvar->glbdom.lb )
                  {
                     /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
                     assert(SCIPsetIsFeasGE(set, parentnewbound, parentvar->glbdom.lb));
                     parentnewbound = parentvar->glbdom.lb;
                  }
               }
               else
                  parentnewbound = newbound;
               SCIP_CALL( varProcessChgUbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
            }
            else
            {
               /* a < 0 -> change lower bound of x */
               assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));
               assert(SCIPsetIsInfinity(set, -parentvar->locdom.lb) || SCIPsetIsInfinity(set, oldbound)
                  || SCIPsetIsFeasEQ(set, parentvar->locdom.lb,
                     oldbound * parentvar->data.aggregate.scalar + parentvar->data.aggregate.constant));
               if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
               {
                  /* if parent's new lower bound exceeds its upper bound, then this could be due to numerical difficulties, e.g., if numbers are large
                  * thus, at least a relative comparision of the new lower bound and the current upper bound should proof consistency
                  * as a result, the parent's lower bound is set to it's upper bound, and not above
                  */
                  if( parentnewbound > parentvar->glbdom.ub )
                  {
                     /* due to numerics we only need to be feasible w.r.t. feasibility tolerance */
                     assert(SCIPsetIsFeasLE(set, parentnewbound, parentvar->glbdom.ub));
                     parentnewbound = parentvar->glbdom.ub;
                  }
               }
               else
                  parentnewbound = -newbound;
               SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue, parentnewbound) );
            }
            break;
         }

      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( varProcessChgLbLocal(parentvar, blkmem, set, NULL, lp, branchcand, eventqueue,
               parentvar->data.negate.constant - newbound) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   return SCIP_OKAY;
}

/* forward declaration, because both methods call each other recursively */

/* performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbLocalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   );

/** performs the current change in lower bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgLbLocalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_RATIONAL* oldbound;
   SCIP_RATIONAL* parentnewbound;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPrationalIsZero(newbound) || SCIPrationalIsEQReal(newbound, 1.0)
            || SCIPrationalIsEQ(newbound, var->exactdata->locdom.ub)))
      || (SCIPvarIsIntegral(var) && (SCIPrationalIsIntegral(newbound)
            || SCIPrationalIsEQ(newbound, var->exactdata->locdom.ub)))
      || !SCIPvarIsIntegral(var));

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldbound) );

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsLE(newbound, var->exactdata->glbdom.ub));
   /* adjust bound to integral value if variable is of integral type */
   adjustedLbExact(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upper bound, which could have happened due to numerics */
      SCIPrationalMin(newbound, newbound, var->exactdata->locdom.ub);

      /* we do not want to undercut the global lower bound, which could have happened due to numerics */
      SCIPrationalMax(newbound, newbound, var->exactdata->glbdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   SCIPrationalDebugMessage("process changing lower bound of <%s> from %q to %q\n", var->name, var->exactdata->locdom.lb, newbound);

   if( SCIPrationalIsEQ(newbound, var->exactdata->glbdom.lb) && !SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->locdom.lb) ) /*lint !e777*/
      SCIPrationalSetRational(newbound, var->exactdata->glbdom.lb);

   /* change the bound */
   SCIPrationalSetRational(oldbound, var->exactdata->locdom.lb);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsLE(newbound, var->exactdata->locdom.ub));
   SCIPrationalSetRational(var->exactdata->locdom.lb, newbound);
   var->locdom.lb = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_DOWNWARDS);

   /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
    * once update the statistic
    */
   if( stat != NULL )
      SCIPstatIncrement(stat, set, domchgcount);

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventLbChangedExact(var, blkmem, set, lpexact, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgLbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         if( SCIPrationalIsPositive(parentvar->exactdata->aggregate.scalar) )
         {
            /* a > 0 -> change lower bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalSetRational(parentnewbound, newbound);

            SCIP_CALL( varProcessChgLbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, parentnewbound) );
         }
         else
         {
            /* a < 0 -> change upper bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalNegate(parentnewbound, newbound);

            SCIP_CALL( varProcessChgUbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, parentnewbound) );
         }
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         SCIPrationalDiffReal(parentnewbound, newbound, parentvar->data.negate.constant);
         SCIPrationalNegate(parentnewbound, parentnewbound);
         SCIP_CALL( varProcessChgUbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue,
               parentnewbound) );
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   SCIPrationalFreeBuffer(set->buffer, &oldbound);

   return SCIP_OKAY;
}

/** performs the current change in upper bound, changes all parents accordingly */
static
SCIP_RETCODE varProcessChgUbLocalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics, or NULL if the bound change belongs to updating the parent variables */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   SCIP_VAR* parentvar;
   SCIP_RATIONAL* oldbound;
   SCIP_RATIONAL* parentnewbound;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && (SCIPrationalIsZero(newbound) || SCIPrationalIsEQReal(newbound, 1.0)
            || SCIPrationalIsEQ(newbound, var->exactdata->locdom.ub)))
      || (SCIPvarIsIntegral(var) && (SCIPrationalIsIntegral(newbound)
            || SCIPrationalIsEQ(newbound, var->exactdata->locdom.ub)))
      || !SCIPvarIsIntegral(var));

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &oldbound) );

   /* check that the bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsGE(newbound, var->exactdata->glbdom.lb));
   /* adjust bound to integral value if variable is of integral type */
   adjustedUbExact(set, SCIPvarIsIntegral(var), newbound);

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upper bound, which could have happened due to numerics */
      SCIPrationalMax(newbound, newbound, var->exactdata->locdom.lb);

      /* we do not want to undercut the global lower bound, which could have happened due to numerics */
      SCIPrationalMin(newbound, newbound, var->exactdata->glbdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   SCIPrationalDebugMessage("process changing exact upper bound of <%s> from %q to %q\n", var->name, var->exactdata->locdom.ub, newbound);

   if( SCIPrationalIsEQ(newbound, var->exactdata->glbdom.ub) && !SCIPrationalIsEQ(var->exactdata->glbdom.ub, var->exactdata->locdom.ub) ) /*lint !e777*/
      SCIPrationalSetRational(newbound, var->exactdata->glbdom.ub);

   /* change the bound */
   SCIPrationalSetRational(oldbound, var->exactdata->locdom.ub);
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || SCIPrationalIsGE(newbound, var->exactdata->locdom.lb));
   SCIPrationalSetRational(var->exactdata->locdom.ub, newbound);
   var->locdom.ub = SCIPrationalRoundReal(newbound, SCIP_R_ROUND_UPWARDS);

   /* update statistic; during the update steps of the parent variable we pass a NULL pointer to ensure that we only
    * once update the statistic
    */
   if( stat != NULL )
      SCIPstatIncrement(stat, set, domchgcount);

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM && var->eventfilter != NULL )
   {
      SCIP_CALL( varEventUbChangedExact(var, blkmem, set, lpexact, branchcand, eventqueue, oldbound, newbound) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         SCIP_CALL( varProcessChgUbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, newbound) );
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         if( SCIPrationalIsPositive(parentvar->exactdata->aggregate.scalar) )
         {
            /* a > 0 -> change upper bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalSetRational(parentnewbound, newbound);

            SCIP_CALL( varProcessChgUbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, parentnewbound) );
         }
         else
         {
            /* a < 0 -> change lower bound of y */
            if( !SCIPrationalIsAbsInfinity(newbound) )
            {
               SCIPrationalMult(parentnewbound, parentvar->exactdata->aggregate.scalar, newbound);
               SCIPrationalAdd(parentnewbound, parentnewbound, parentvar->exactdata->aggregate.constant);
            }
            else
               SCIPrationalNegate(parentnewbound, newbound);

            SCIP_CALL( varProcessChgLbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue, parentnewbound) );
         }
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &parentnewbound) );
         SCIPrationalDiffReal(parentnewbound, newbound, parentvar->data.negate.constant);
         SCIPrationalNegate(parentnewbound, parentnewbound);
         SCIP_CALL( varProcessChgLbLocalExact(parentvar, blkmem, set, NULL, lpexact, branchcand, eventqueue,
               parentnewbound) );
         SCIPrationalFreeBuffer(set->buffer, &parentnewbound);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }

   SCIPrationalFreeBuffer(set->buffer, &oldbound);

   return SCIP_OKAY;
}

/** changes current local lower bound of variable; if possible, adjusts bound to integral value; stores inference
 *  information in variable
 */
SCIP_RETCODE SCIPvarChgLbLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedLb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasGT(set, newbound, var->locdom.ub));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upperbound, which could have happened due to numerics */
      newbound = MIN(newbound, var->locdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   SCIPsetDebugMsg(set, "changing lower bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);

   if( SCIPsetIsEQ(set, var->locdom.lb, newbound) && (!SCIPsetIsEQ(set, var->glbdom.lb, newbound) || var->locdom.lb == newbound) /*lint !e777*/
         && !(newbound != var->locdom.lb && newbound * var->locdom.lb <= 0.0) ) /*lint !e777*/
      return SCIP_OKAY;

   if( SCIPshouldCertificateTrackBounds(set->scip) )
      SCIPvarSetLbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgLbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIP_CALL( varProcessChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
   {
      SCIP_Real childnewbound;
      assert(var->data.aggregate.var != NULL);

      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> change lower bound of y */
         assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
            || SCIPsetIsFeasEQ(set, var->locdom.lb,
               var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = newbound;
         SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
               childnewbound) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         /* a < 0 -> change upper bound of y */
         assert((SCIPsetIsInfinity(set, -var->locdom.lb) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
            || SCIPsetIsFeasEQ(set, var->locdom.lb,
               var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = -newbound;
         SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
               childnewbound) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;
   }

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgUbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
            var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes current local lower bound of variable; if possible, adjusts bound to integral value; stores inference
 *  information in variable
 */
SCIP_RETCODE SCIPvarChgLbLocalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsGT(newbound, var->exactdata->locdom.ub));

   /* adjust bound to integral value if variable is of integral type */
   adjustedLbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsGT(newbound, var->exactdata->locdom.ub));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to exceed the upperbound, which could have happened due to numerics */
      SCIPrationalMin(newbound, newbound, var->exactdata->locdom.ub);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   SCIPrationalDebugMessage("changing lower bound of <%s>[%q,%q] to %q\n", var->name, var->exactdata->locdom.lb, var->exactdata->locdom.ub, newbound);

   if( SCIPshouldCertificateTrackBounds(set->scip) )
      SCIPvarSetLbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgLbLocalExact(var->data.original.transvar, blkmem, set, stat, lpexact, branchcand, eventqueue,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         SCIP_CALL( varProcessChgLbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIP_CALL( varProcessChgLbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPrationalIsPositive(var->exactdata->aggregate.scalar) )
      {
         SCIP_RATIONAL* childnewbound;

         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a > 0 -> change lower bound of y */
         if( !SCIPrationalIsNegInfinity(newbound) && !SCIPrationalIsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgLbLocalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else if( SCIPrationalIsNegative(var->exactdata->aggregate.scalar) )
      {
         SCIP_RATIONAL* childnewbound;

         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a < 0 -> change upper bound of y */
         if( !SCIPrationalIsNegInfinity(newbound) && !SCIPrationalIsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalNegate(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgUbLocalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIPrationalDiffReal(newbound, newbound, var->data.negate.constant);
      SCIPrationalNegate(newbound, newbound);
      SCIP_CALL( SCIPvarChgUbLocalExact(var->negatedvar, blkmem, set, stat, lpexact, branchcand, eventqueue,
            newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes current local upper bound of variable; if possible, adjusts bound to integral value; stores inference
 *  information in variable
 */
SCIP_RETCODE SCIPvarChgUbLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));

   /* adjust bound to integral value if variable is of integral type */
   newbound = adjustedUb(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPsetIsFeasLT(set, newbound, var->locdom.lb));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lowerbound, which could have happened due to numerics */
      newbound = MAX(newbound, var->locdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPsetIsFeasIntegral(set, newbound));

   SCIPsetDebugMsg(set, "changing upper bound of <%s>[%g,%g] to %g\n", var->name, var->locdom.lb, var->locdom.ub, newbound);

   if( SCIPsetIsEQ(set, var->locdom.ub, newbound) && (!SCIPsetIsEQ(set, var->glbdom.ub, newbound) || var->locdom.ub == newbound) /*lint !e777*/
      && !(newbound != var->locdom.ub && newbound * var->locdom.ub <= 0.0) ) /*lint !e777*/
      return SCIP_OKAY;

   if( SCIPshouldCertificateTrackBounds(set->scip) )
      SCIPvarSetUbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgUbLocal(var->data.original.transvar, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIP_CALL( varProcessChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
   {
      SCIP_Real childnewbound;
      assert(var->data.aggregate.var != NULL);

      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> change upper bound of y */
         assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, var->data.aggregate.var->locdom.ub))
            || SCIPsetIsFeasEQ(set, var->locdom.ub,
               var->data.aggregate.var->locdom.ub * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = newbound;
         SCIP_CALL( SCIPvarChgUbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
               childnewbound) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         /* a < 0 -> change lower bound of y */
         assert((SCIPsetIsInfinity(set, var->locdom.ub) && SCIPsetIsInfinity(set, -var->data.aggregate.var->locdom.lb))
            || SCIPsetIsFeasEQ(set, var->locdom.ub,
               var->data.aggregate.var->locdom.lb * var->data.aggregate.scalar + var->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = -newbound;
         SCIP_CALL( SCIPvarChgLbLocal(var->data.aggregate.var, blkmem, set, stat, lp, branchcand, eventqueue,
               childnewbound) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;
   }

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgLbLocal(var->negatedvar, blkmem, set, stat, lp, branchcand, eventqueue,
            var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes current exact local upper bound of variable; if possible, adjusts bound to integral value; stores inference
 *  information in variable
 */
SCIP_RETCODE SCIPvarChgUbLocalExact(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* check that the bound is feasible; this must be w.r.t. feastol because SCIPvarFix() allows fixings that are outside
    * of the domain within feastol
    */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsLT(newbound, var->exactdata->locdom.lb));

   /* adjust bound to integral value if variable is of integral type */
   adjustedUbExact(set, SCIPvarIsIntegral(var), newbound);

   /* check that the adjusted bound is feasible */
   assert(SCIPsetGetStage(set) == SCIP_STAGE_PROBLEM || !SCIPrationalIsLT(newbound, var->exactdata->locdom.lb));

   if( SCIPsetGetStage(set) != SCIP_STAGE_PROBLEM )
   {
      /* we do not want to undercut the lowerbound, which could have happened due to numerics */
      SCIPrationalMax(newbound, newbound, var->exactdata->locdom.lb);
   }
   assert(!SCIPvarIsIntegral(var) || SCIPrationalIsIntegral(newbound));

   SCIPrationalDebugMessage("changing upper bound of <%s>[%q,%q] to %q\n", var->name, var->exactdata->locdom.lb, var->exactdata->locdom.ub, newbound);

   if( SCIPshouldCertificateTrackBounds(set->scip) )
      SCIPvarSetUbCertificateIndexLocal(var, SCIPcertificateGetLastBoundIndex(SCIPgetCertificate(set->scip)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgUbLocalExact(var->data.original.transvar, blkmem, set, stat, lpexact, branchcand, eventqueue,
               newbound) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         SCIP_CALL( varProcessChgUbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIP_CALL( varProcessChgUbLocalExact(var, blkmem, set, stat, lpexact, branchcand, eventqueue, newbound) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPrationalIsPositive(var->exactdata->aggregate.scalar) )
      {
         SCIP_RATIONAL* childnewbound;

         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a > 0 -> change upper bound of y */
         if( !SCIPrationalIsNegInfinity(newbound) && !SCIPrationalIsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalSetRational(childnewbound, newbound);
         SCIP_CALL( SCIPvarChgUbLocalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else if( SCIPrationalIsNegative(var->exactdata->aggregate.scalar) )
      {
         SCIP_RATIONAL* childnewbound;

         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &childnewbound) );

         /* a < 0 -> change lower bound of y */
         if( !SCIPrationalIsNegInfinity(newbound) && !SCIPrationalIsInfinity(newbound) )
         {
            SCIPrationalDiff(childnewbound, newbound, var->exactdata->aggregate.constant);
            SCIPrationalDiv(childnewbound, childnewbound, var->exactdata->aggregate.scalar);
         }
         else
            SCIPrationalNegate(childnewbound, newbound);

         SCIP_CALL( SCIPvarChgLbLocalExact(var->data.aggregate.var, blkmem, set, stat, lpexact, branchcand, eventqueue,
               childnewbound) );

         SCIPrationalFreeBuffer(set->buffer, &childnewbound);
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIPrationalDiffReal(newbound, newbound, var->data.negate.constant);
      SCIPrationalNegate(newbound, newbound);
      SCIP_CALL( SCIPvarChgLbLocalExact(var->negatedvar, blkmem, set, stat, lpexact, branchcand, eventqueue,
            newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes current local bound of variable; if possible, adjusts bound to integral value; stores inference
 *  information in variable
 */
SCIP_RETCODE SCIPvarChgBdLocal(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp,                 /**< current LP data, may be NULL for original variables */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage, may be NULL for original variables */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             newbound,           /**< new bound for variable */
   SCIP_BOUNDTYPE        boundtype           /**< type of bound: lower or upper bound */
   )
{
   /* apply bound change to the LP data */
   switch( boundtype )
   {
   case SCIP_BOUNDTYPE_LOWER:
      return SCIPvarChgLbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
   case SCIP_BOUNDTYPE_UPPER:
      return SCIPvarChgUbLocal(var, blkmem, set, stat, lp, branchcand, eventqueue, newbound);
   default:
      SCIPerrorMessage("unknown bound type\n");
      return SCIP_INVALIDDATA;
   }
}

/** changes lower bound of variable in current dive; if possible, adjusts bound to integral value */
SCIP_RETCODE SCIPvarChgLbDive(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lp != NULL);
   assert(SCIPlpDiving(lp));

   /* adjust bound for integral variables */
   SCIPvarAdjustLb(var, set, &newbound);

   SCIPsetDebugMsg(set, "changing lower bound of <%s> to %g in current dive\n", var->name, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarChgLbDive(var->data.original.transvar, set, lp, newbound) );
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIP_CALL( SCIPcolChgLb(var->data.col, set, lp, newbound) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         SCIP_Real childnewbound;

         /* a > 0 -> change lower bound of y */
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = newbound;
         SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         SCIP_Real childnewbound;

         /* a < 0 -> change upper bound of y */
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = -newbound;
         SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgUbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes lower bound of variable in current exact dive */
SCIP_RETCODE SCIPvarChgLbExactDive(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lpexact != NULL);
   assert(SCIPlpExactDiving(lpexact));

   SCIPrationalDebugMessage("changing lower bound of <%s> to %q in current exact dive\n", var->name, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarChgLbExactDive(var->data.original.transvar, set, lpexact, newbound) );
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIP_CALL( SCIPcolExactChgLb(var->exactdata->colexact, set, lpexact, newbound) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      SCIPerrorMessage("cannot change the bounds of an aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      SCIPerrorMessage("cannot change the bounds of a negated variable\n");
      return SCIP_INVALIDDATA;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes upper bound of variable in current dive; if possible, adjusts bound to integral value */
SCIP_RETCODE SCIPvarChgUbDive(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_Real             newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lp != NULL);
   assert(SCIPlpDiving(lp));

   /* adjust bound for integral variables */
   SCIPvarAdjustUb(var, set, &newbound);

   SCIPsetDebugMsg(set, "changing upper bound of <%s> to %g in current dive\n", var->name, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarChgUbDive(var->data.original.transvar, set, lp, newbound) );
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIP_CALL( SCIPcolChgUb(var->data.col, set, lp, newbound) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         SCIP_Real childnewbound;

         /* a > 0 -> change upper bound of y */
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = newbound;
         SCIP_CALL( SCIPvarChgUbDive(var->data.aggregate.var, set, lp, childnewbound) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         SCIP_Real childnewbound;

         /* a < 0 -> change lower bound of y */
         if( !SCIPsetIsInfinity(set, -newbound) && !SCIPsetIsInfinity(set, newbound) )
            childnewbound = (newbound - var->data.aggregate.constant)/var->data.aggregate.scalar;
         else
            childnewbound = -newbound;
         SCIP_CALL( SCIPvarChgLbDive(var->data.aggregate.var, set, lp, childnewbound) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgLbDive(var->negatedvar, set, lp, var->data.negate.constant - newbound) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** changes upper bound of variable in current exact dive */
SCIP_RETCODE SCIPvarChgUbExactDive(
   SCIP_VAR*             var,                /**< problem variable to change */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_LPEXACT*         lpexact,            /**< current exact LP data */
   SCIP_RATIONAL*        newbound            /**< new bound for variable */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(lpexact != NULL);
   assert(SCIPlpExactDiving(lpexact));

   SCIPrationalDebugMessage("changing upper bound of <%s> to %d in current dive\n", var->name, newbound);

   /* change bounds of attached variables */
   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarChgUbExactDive(var->data.original.transvar, set, lpexact, newbound) );
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIP_CALL( SCIPcolExactChgUb(var->exactdata->colexact, set, lpexact, newbound) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      SCIPerrorMessage("cannot change variable's bounds in dive for LOOSE variables\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot change the bounds of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      SCIPerrorMessage("cannot change the bounds of an aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot change the bounds of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      SCIPerrorMessage("cannot change the bounds of a negated variable\n");
      return SCIP_INVALIDDATA;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** for a multi-aggregated variable, gives the local lower bound computed by adding the local bounds from all
 *  aggregation variables, this lower bound may be tighter than the one given by SCIPvarGetLbLocal, since the latter is
 *  not updated if bounds of aggregation variables are changing
 *
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_Real SCIPvarGetMultaggrLbLocal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   int i;
   SCIP_Real lb;
   SCIP_Real bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   posinf = FALSE;
   neginf = FALSE;
   lb = var->data.multaggr.constant;
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( var->data.multaggr.scalars[i] > 0.0 )
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);

         if( SCIPsetIsInfinity(set, bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, -bnd) )
            neginf = TRUE;
         else
            lb += var->data.multaggr.scalars[i] * bnd;
      }
      else
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);

         if( SCIPsetIsInfinity(set, -bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, bnd) )
            neginf = TRUE;
         else
            lb += var->data.multaggr.scalars[i] * bnd;
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return local lower bound of multi aggregated
       * variable
       */
      if( neginf )
         return SCIPvarGetLbLocal(var);
   }

   /* if positive infinity flag was set to true return infinity */
   if( posinf )
      return SCIPsetInfinity(set);

   return (MAX(lb, SCIPvarGetLbLocal(var))); /*lint !e666*/
}

/** for a multi-aggregated variable, gives the exact local lower bound computed by adding the local bounds from all aggregation variables
 *  this lower bound may be tighter than the one given by SCIPvarGetLbLocal, since the latter is not updated if bounds of aggregation variables are changing
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_RETCODE SCIPvarGetMultaggrLbLocalExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        result              /**< the resulting bound */
   )
{
   int i;
   SCIP_RATIONAL* lb;
   SCIP_RATIONAL* bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &lb) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &bnd) );

   posinf = FALSE;
   neginf = FALSE;
   SCIPrationalSetRational(lb, var->exactdata->multaggr.constant);
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( SCIPrationalIsPositive(var->exactdata->multaggr.scalars[i]) )
      {
         if( SCIPvarGetStatusExact(aggrvar) == SCIP_VARSTATUS_MULTAGGR )
            SCIP_CALL( SCIPvarGetMultaggrLbLocalExact(aggrvar, set, bnd) );
         else
            SCIPrationalSetRational(bnd, SCIPvarGetLbLocalExact(aggrvar));

         if( SCIPrationalIsInfinity(bnd) )
            posinf = TRUE;
         else if( SCIPrationalIsNegInfinity(bnd) )
            neginf = TRUE;
         else
            SCIPrationalAddProd(lb, var->exactdata->multaggr.scalars[i], bnd);
      }
      else
      {
         if( SCIPvarGetStatusExact(aggrvar) == SCIP_VARSTATUS_MULTAGGR )
            SCIP_CALL( SCIPvarGetMultaggrUbLocalExact(aggrvar, set, bnd) );
         else
            SCIPrationalSetRational(bnd, SCIPvarGetUbLocalExact(aggrvar));

         if( SCIPrationalIsNegInfinity(bnd) )
            posinf = TRUE;
         else if( SCIPrationalIsInfinity(bnd) )
            neginf = TRUE;
         else
            SCIPrationalAddProd(lb, var->exactdata->multaggr.scalars[i], bnd);
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return local lower bound of multi aggregated
       * variable
       */
      if( neginf )
      {
         SCIPrationalSetRational(result, SCIPvarGetLbLocalExact(var));
         break;
      }
   }

   /* if positive infinity flag was set to true return infinity */
   if( posinf && !neginf )
      SCIPrationalSetInfinity(result);
   else
      SCIPrationalMax(result, lb, SCIPvarGetLbLocalExact(var));

   SCIPrationalFreeBuffer(set->buffer, &bnd);
   SCIPrationalFreeBuffer(set->buffer, &lb);

   return SCIP_OKAY;
}

/** for a multi-aggregated variable, gives the local upper bound computed by adding the local bounds from all
 *  aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbLocal, since the latter is
 *  not updated if bounds of aggregation variables are changing
 *
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_Real SCIPvarGetMultaggrUbLocal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   int i;
   SCIP_Real ub;
   SCIP_Real bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   posinf = FALSE;
   neginf = FALSE;
   ub = var->data.multaggr.constant;
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( var->data.multaggr.scalars[i] > 0.0 )
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbLocal(aggrvar, set) : SCIPvarGetUbLocal(aggrvar);

         if( SCIPsetIsInfinity(set, bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, -bnd) )
            neginf = TRUE;
         else
            ub += var->data.multaggr.scalars[i] * bnd;
      }
      else
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbLocal(aggrvar, set) : SCIPvarGetLbLocal(aggrvar);

         if( SCIPsetIsInfinity(set, -bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, bnd) )
            neginf = TRUE;
         else
            ub += var->data.multaggr.scalars[i] * bnd;
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
       * variable
       */
      if( posinf )
         return SCIPvarGetUbLocal(var);
   }

   /* if negative infinity flag was set to true return -infinity */
   if( neginf )
      return -SCIPsetInfinity(set);

   return (MIN(ub, SCIPvarGetUbLocal(var))); /*lint !e666*/
}

/** for a multi-aggregated variable, gives the exact local upper bound computed by adding the local bounds from all aggregation variables
 *  this upper bound may be tighter than the one given by SCIPvarGetLbLocal, since the latter is not updated if bounds of aggregation variables are changing
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_RETCODE SCIPvarGetMultaggrUbLocalExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RATIONAL*        result              /**< the resulting bound */
   )
{
   int i;
   SCIP_RATIONAL* ub;
   SCIP_RATIONAL* bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &ub) );
   SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &bnd) );

   posinf = FALSE;
   neginf = FALSE;
   SCIPrationalSetRational(ub, var->exactdata->multaggr.constant);
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( SCIPrationalIsPositive(var->exactdata->multaggr.scalars[i]) )
      {
         if( SCIPvarGetStatusExact(aggrvar) == SCIP_VARSTATUS_MULTAGGR )
            SCIP_CALL( SCIPvarGetMultaggrUbLocalExact(aggrvar, set, bnd) );
         else
            SCIPrationalSetRational(bnd, SCIPvarGetUbLocalExact(aggrvar));

         if( SCIPrationalIsInfinity(bnd) )
            posinf = TRUE;
         else if( SCIPrationalIsNegInfinity(bnd) )
            neginf = TRUE;
         else
            SCIPrationalAddProd(ub, var->exactdata->multaggr.scalars[i], bnd);
      }
      else
      {
         if( SCIPvarGetStatusExact(aggrvar) == SCIP_VARSTATUS_MULTAGGR )
            SCIP_CALL( SCIPvarGetMultaggrLbLocalExact(aggrvar, set, bnd) );
         else
            SCIPrationalSetRational(bnd, SCIPvarGetLbLocalExact(aggrvar));

         if( SCIPrationalIsNegInfinity(bnd) )
            posinf = TRUE;
         else if( SCIPrationalIsInfinity(bnd) )
            neginf = TRUE;
         else
            SCIPrationalAddProd(ub, var->exactdata->multaggr.scalars[i], bnd);
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return local lower bound of multi aggregated
       * variable
       */
      if( posinf )
      {
         SCIPrationalSetRational(result, SCIPvarGetUbLocalExact(var));
         break;
      }
   }

   /* if positive infinity flag was set to true return infinity */
   if( !posinf && neginf )
      SCIPrationalSetNegInfinity(result);
   else
      SCIPrationalMin(result, ub, SCIPvarGetUbLocalExact(var));

   SCIPrationalFreeBuffer(set->buffer, &bnd);
   SCIPrationalFreeBuffer(set->buffer, &ub);

   return SCIP_OKAY;
}

/** for a multi-aggregated variable, gives the global lower bound computed by adding the global bounds from all
 *  aggregation variables, this global bound may be tighter than the one given by SCIPvarGetLbGlobal, since the latter is
 *  not updated if bounds of aggregation variables are changing
 *
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_Real SCIPvarGetMultaggrLbGlobal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   int i;
   SCIP_Real lb;
   SCIP_Real bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   posinf = FALSE;
   neginf = FALSE;
   lb = var->data.multaggr.constant;
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( var->data.multaggr.scalars[i] > 0.0 )
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);

         if( SCIPsetIsInfinity(set, bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, -bnd) )
            neginf = TRUE;
         else
            lb += var->data.multaggr.scalars[i] * bnd;
      }
      else
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);

         if( SCIPsetIsInfinity(set, -bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, bnd) )
            neginf = TRUE;
         else
            lb += var->data.multaggr.scalars[i] * bnd;
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return global lower bound of multi aggregated
       * variable
       */
      if( neginf )
         return SCIPvarGetLbGlobal(var);
   }

   /* if positive infinity flag was set to true return infinity */
   if( posinf )
      return SCIPsetInfinity(set);

   return (MAX(lb, SCIPvarGetLbGlobal(var))); /*lint !e666*/
}

/** for a multi-aggregated variable, gives the global upper bound computed by adding the global bounds from all
 *  aggregation variables, this upper bound may be tighter than the one given by SCIPvarGetUbGlobal, since the latter is
 *  not updated if bounds of aggregation variables are changing
 *
 *  calling this function for a non-multi-aggregated variable is not allowed
 */
SCIP_Real SCIPvarGetMultaggrUbGlobal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   int i;
   SCIP_Real ub;
   SCIP_Real bnd;
   SCIP_VAR* aggrvar;
   SCIP_Bool posinf;
   SCIP_Bool neginf;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert((SCIP_VARSTATUS) var->varstatus == SCIP_VARSTATUS_MULTAGGR);

   posinf = FALSE;
   neginf = FALSE;
   ub = var->data.multaggr.constant;
   for( i = var->data.multaggr.nvars-1 ; i >= 0 ; --i )
   {
      aggrvar = var->data.multaggr.vars[i];
      if( var->data.multaggr.scalars[i] > 0.0 )
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrUbGlobal(aggrvar, set) : SCIPvarGetUbGlobal(aggrvar);

         if( SCIPsetIsInfinity(set, bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, -bnd) )
            neginf = TRUE;
         else
            ub += var->data.multaggr.scalars[i] * bnd;
      }
      else
      {
         bnd = SCIPvarGetStatus(aggrvar) == SCIP_VARSTATUS_MULTAGGR ? SCIPvarGetMultaggrLbGlobal(aggrvar, set) : SCIPvarGetLbGlobal(aggrvar);

         if( SCIPsetIsInfinity(set, -bnd) )
            posinf = TRUE;
         else if( SCIPsetIsInfinity(set, bnd) )
            neginf = TRUE;
         else
            ub += var->data.multaggr.scalars[i] * bnd;
      }

      /* stop if two diffrent infinities (or a -infinity) were found and return local upper bound of multi aggregated
       * variable
       */
      if( posinf )
         return SCIPvarGetUbGlobal(var);
   }

   /* if negative infinity flag was set to true return -infinity */
   if( neginf )
      return -SCIPsetInfinity(set);

   return (MIN(ub, SCIPvarGetUbGlobal(var))); /*lint !e666*/
}

/** adds a hole to the original domain of the variable */
SCIP_RETCODE SCIPvarAddHoleOriginal(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right               /**< right bound of open interval in new hole */
   )
{
   SCIP_Bool added;

   assert(var != NULL);
   assert(!SCIPvarIsTransformed(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL || SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
   assert(SCIPvarIsIntegral(var));
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(set->stage == SCIP_STAGE_PROBLEM);

   SCIPsetDebugMsg(set, "adding original hole (%g,%g) to <%s>\n", left, right, var->name);

   if( SCIPsetIsEQ(set, left, right) )
      return SCIP_OKAY;

   /* the interval should not be empty */
   assert(SCIPsetIsLT(set, left, right));

   /* the the interval bound should already be adjusted */
   assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarIsIntegral(var), left)));
   assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarIsIntegral(var), right)));

   /* the the interval should lay between the lower and upper bound */
   assert(SCIPsetIsGE(set, left, SCIPvarGetLbOriginal(var)));
   assert(SCIPsetIsLE(set, right, SCIPvarGetUbOriginal(var)));

   /* add domain hole */
   SCIP_CALL( domAddHole(&var->data.original.origdom, blkmem, set, left, right, &added) );

   /* merges overlapping holes into single holes, moves bounds respectively if hole was added */
   if( added )
   {
      domMerge(&var->data.original.origdom, blkmem, set, NULL, NULL);
   }

   /**@todo add hole in parent and child variables (just like with bound changes);
    *       warning! original vars' holes are in original blkmem, transformed vars' holes in transformed blkmem
    */

   return SCIP_OKAY;
}

/** performs the current add of domain, changes all parents accordingly */
static
SCIP_RETCODE varProcessAddHoleGlobal(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right,              /**< right bound of open interval in new hole */
   SCIP_Bool*            added               /**< pointer to store whether the hole was added */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real newlb;
   SCIP_Real newub;
   int i;

   assert(var != NULL);
   assert(added != NULL);
   assert(blkmem != NULL);

   /* the interval should not be empty */
   assert(SCIPsetIsLT(set, left, right));

   /* the interval bound should already be adjusted */
   assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarIsIntegral(var), left)));
   assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarIsIntegral(var), right)));

   /* the interval should lay between the lower and upper bound */
   assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
   assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));

   /* @todo add debugging mechanism for holes when using a debugging solution */

   /* add hole to hole list */
   SCIP_CALL( domAddHole(&var->glbdom, blkmem, set, left, right, added) );

   /* check if the hole is redundant */
   if( !(*added) )
      return SCIP_OKAY;

   /* current bounds */
   newlb = var->glbdom.lb;
   newub = var->glbdom.ub;

   /* merge domain holes */
   domMerge(&var->glbdom, blkmem, set, &newlb, &newub);

   /* the bound should not be changed */
   assert(SCIPsetIsEQ(set, newlb, var->glbdom.lb));
   assert(SCIPsetIsEQ(set, newub, var->glbdom.ub));

   /* issue bound change event */
   assert(SCIPvarIsTransformed(var) == (var->eventfilter != NULL));
   if( var->eventfilter != NULL )
   {
      SCIP_CALL( varEventGholeAdded(var, blkmem, set, eventqueue, left, right) );
   }

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_Real parentnewleft;
      SCIP_Real parentnewright;
      SCIP_Bool localadded;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         parentnewleft = left;
         parentnewright = right;
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);

         if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
         {
            /* a > 0 -> change upper bound of x */
            parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
            parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
         }
         else
         {
            /* a < 0 -> change lower bound of x */
            assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));

            parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
            parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
         }
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);

         parentnewright = -left + parentvar->data.negate.constant;
         parentnewleft = -right + parentvar->data.negate.constant;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }

      SCIPsetDebugMsg(set, "add global hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));

      /* perform hole added for parent variable */
      assert(blkmem != NULL);
      assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
      SCIP_CALL( varProcessAddHoleGlobal(parentvar, blkmem, set, stat, eventqueue,
            parentnewleft, parentnewright, &localadded) );
      assert(localadded);
   }

   return SCIP_OKAY;
}

/** adds a hole to the variable's global and local domain */
SCIP_RETCODE SCIPvarAddHoleGlobal(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right,              /**< right bound of open interval in new hole */
   SCIP_Bool*            added               /**< pointer to store whether the hole was added */
   )
{
   SCIP_Real childnewleft;
   SCIP_Real childnewright;

   assert(var != NULL);
   assert(SCIPvarIsIntegral(var));
   assert(blkmem != NULL);
   assert(added != NULL);

   SCIPsetDebugMsg(set, "adding global hole (%g,%g) to <%s>\n", left, right, var->name);

   /* the interval should not be empty */
   assert(SCIPsetIsLT(set, left, right));

   /* the the interval bound should already be adjusted */
   assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarIsIntegral(var), left)));
   assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarIsIntegral(var), right)));

   /* the the interval should lay between the lower and upper bound */
   assert(SCIPsetIsGE(set, left, SCIPvarGetLbGlobal(var)));
   assert(SCIPsetIsLE(set, right, SCIPvarGetUbGlobal(var)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarAddHoleGlobal(var->data.original.transvar, blkmem, set, stat, eventqueue,
               left, right, added) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);

         SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
         if( *added )
         {
            SCIP_Bool localadded;

            SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
         }
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIP_CALL( varProcessAddHoleGlobal(var, blkmem, set, stat, eventqueue, left, right, added) );
      if( *added )
      {
         SCIP_Bool localadded;

         SCIP_CALL( SCIPvarAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, &localadded) );
      }
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot add hole of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);

      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> change lower bound of y */
         childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
         childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
         childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarAddHoleGlobal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
            childnewleft, childnewright, added) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot add a hole of a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      childnewright = -left + var->data.negate.constant;
      childnewleft = -right + var->data.negate.constant;

      SCIP_CALL( SCIPvarAddHoleGlobal(var->negatedvar, blkmem, set, stat, eventqueue,
            childnewleft, childnewright, added) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** performs the current add of domain, changes all parents accordingly */
static
SCIP_RETCODE varProcessAddHoleLocal(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right,              /**< right bound of open interval in new hole */
   SCIP_Bool*            added               /**< pointer to store whether the hole was added, or NULL */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real newlb;
   SCIP_Real newub;
   int i;

   assert(var != NULL);
   assert(added != NULL);
   assert(blkmem != NULL);

   /* the interval should not be empty */
   assert(SCIPsetIsLT(set, left, right));

   /* the the interval bound should already be adjusted */
   assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarIsIntegral(var), left)));
   assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarIsIntegral(var), right)));

   /* the the interval should lay between the lower and upper bound */
   assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
   assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));

   /* add hole to hole list */
   SCIP_CALL( domAddHole(&var->locdom, blkmem, set, left, right, added) );

   /* check if the hole is redundant */
   if( !(*added) )
      return SCIP_OKAY;

   /* current bounds */
   newlb = var->locdom.lb;
   newub = var->locdom.ub;

   /* merge domain holes */
   domMerge(&var->locdom, blkmem, set, &newlb, &newub);

   /* the bound should not be changed */
   assert(SCIPsetIsEQ(set, newlb, var->locdom.lb));
   assert(SCIPsetIsEQ(set, newub, var->locdom.ub));

#ifdef SCIP_DISABLED_CODE
   /* issue LHOLEADDED event */
   SCIP_EVENT event;
   assert(var->eventfilter != NULL);
   SCIP_CALL( SCIPeventChgType(&event, SCIP_EVENTTYPE_LHOLEADDED) );
   SCIP_CALL( SCIPeventProcess(&event, set, NULL, NULL, NULL, var->eventfilter) );
#endif

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      SCIP_Real parentnewleft;
      SCIP_Real parentnewright;
      SCIP_Bool localadded;

      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         parentnewleft = left;
         parentnewright = right;
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
         assert(parentvar->data.aggregate.var == var);

         if( SCIPsetIsPositive(set, parentvar->data.aggregate.scalar) )
         {
            /* a > 0 -> change upper bound of x */
            parentnewleft = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
            parentnewright = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
         }
         else
         {
            /* a < 0 -> change lower bound of x */
            assert(SCIPsetIsNegative(set, parentvar->data.aggregate.scalar));

            parentnewright = parentvar->data.aggregate.scalar * left + parentvar->data.aggregate.constant;
            parentnewleft = parentvar->data.aggregate.scalar * right + parentvar->data.aggregate.constant;
         }
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = offset - x'  ->  x' = offset - x */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);

         parentnewright = -left + parentvar->data.negate.constant;
         parentnewleft = -right + parentvar->data.negate.constant;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }

      SCIPsetDebugMsg(set, "add local hole (%g,%g) to parent variable <%s>\n", parentnewleft, parentnewright, SCIPvarGetName(parentvar));

      /* perform hole added for parent variable */
      assert(blkmem != NULL);
      assert(SCIPsetIsLT(set, parentnewleft, parentnewright));
      SCIP_CALL( varProcessAddHoleLocal(parentvar, blkmem, set, stat, eventqueue,
            parentnewleft, parentnewright, &localadded) );
      assert(localadded);
   }

   return SCIP_OKAY;
}

/** adds a hole to the variable's current local domain */
SCIP_RETCODE SCIPvarAddHoleLocal(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue, may be NULL for original variables */
   SCIP_Real             left,               /**< left bound of open interval in new hole */
   SCIP_Real             right,              /**< right bound of open interval in new hole */
   SCIP_Bool*            added               /**< pointer to store whether the hole was added */
   )
{
   SCIP_Real childnewleft;
   SCIP_Real childnewright;

   assert(var != NULL);

   SCIPsetDebugMsg(set, "adding local hole (%g,%g) to <%s>\n", left, right, var->name);

   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarIsIntegral(var));
   assert(blkmem != NULL);
   assert(added != NULL);

   /* the interval should not be empty */
   assert(SCIPsetIsLT(set, left, right));

   /* the the interval bound should already be adjusted */
   assert(SCIPsetIsEQ(set, left, adjustedUb(set, SCIPvarIsIntegral(var), left)));
   assert(SCIPsetIsEQ(set, right, adjustedLb(set, SCIPvarIsIntegral(var), right)));

   /* the the interval should lay between the lower and upper bound */
   assert(SCIPsetIsGE(set, left, SCIPvarGetLbLocal(var)));
   assert(SCIPsetIsLE(set, right, SCIPvarGetUbLocal(var)));

   /* change bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarAddHoleLocal(var->data.original.transvar, blkmem, set, stat, eventqueue,
               left, right, added) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         SCIPstatIncrement(stat, set, domchgcount);
         SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      SCIPstatIncrement(stat, set, domchgcount);
      SCIP_CALL( varProcessAddHoleLocal(var, blkmem, set, stat, eventqueue, left, right, added) );
      break;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot add domain hole to a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);

      if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> change lower bound of y */
         childnewleft = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
         childnewright = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         childnewright = (left - var->data.aggregate.constant)/var->data.aggregate.scalar;
         childnewleft = (right - var->data.aggregate.constant)/var->data.aggregate.scalar;
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarAddHoleLocal(var->data.aggregate.var, blkmem, set, stat, eventqueue,
            childnewleft, childnewright, added) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot add domain hole to a multi-aggregated variable.\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      childnewright = -left + var->data.negate.constant;
      childnewleft = -right + var->data.negate.constant;

      SCIP_CALL( SCIPvarAddHoleLocal(var->negatedvar, blkmem, set, stat, eventqueue, childnewleft, childnewright, added) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** resets the global and local bounds of original variable to their original values */
SCIP_RETCODE SCIPvarResetBounds(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarIsOriginal(var));
   /* resetting of bounds on original variables which have a transformed counterpart easily fails if, e.g.,
    * the transformed variable has been fixed */
   assert(SCIPvarGetTransVar(var) == NULL);

   /* copy the original bounds back to the global and local bounds */
   SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.lb) );
   SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->data.original.origdom.ub) );
   SCIP_CALL( SCIPvarChgLbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.lb) );
   SCIP_CALL( SCIPvarChgUbLocal(var, blkmem, set, stat, NULL, NULL, NULL, var->data.original.origdom.ub) );

   if( var->exactdata != NULL )
   {
      SCIP_CALL( SCIPvarChgLbGlobalExact(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->exactdata->origdom.lb) );
      SCIP_CALL( SCIPvarChgUbGlobalExact(var, blkmem, set, stat, NULL, NULL, NULL, NULL, var->exactdata->origdom.ub) );
      SCIP_CALL( SCIPvarChgLbLocalExact(var, blkmem, set, stat, NULL, NULL, NULL, var->exactdata->origdom.lb) );
      SCIP_CALL( SCIPvarChgUbLocalExact(var, blkmem, set, stat, NULL, NULL, NULL, var->exactdata->origdom.ub) );
   }

   /* free the global and local holelists and duplicate the original ones */
   /**@todo this has also to be called recursively with methods similar to SCIPvarChgLbGlobal() */
   holelistFree(&var->glbdom.holelist, blkmem);
   holelistFree(&var->locdom.holelist, blkmem);
   SCIP_CALL( holelistDuplicate(&var->glbdom.holelist, blkmem, set, var->data.original.origdom.holelist) );
   SCIP_CALL( holelistDuplicate(&var->locdom.holelist, blkmem, set, var->data.original.origdom.holelist) );

   return SCIP_OKAY;
}

/** issues a IMPLADDED event on the given variable */
static
SCIP_RETCODE varEventImplAdded(
   SCIP_VAR*             var,                /**< problem variable to change */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue          /**< event queue */
   )
{
   SCIP_EVENT* event;

   assert(var != NULL);

   /* issue IMPLADDED event on variable */
   SCIP_CALL( SCIPeventCreateImplAdded(&event, blkmem, var) );
   SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, NULL, &event) );

   return SCIP_OKAY;
}

/** actually performs the addition of a variable bound to the variable's vbound arrays */
static
SCIP_RETCODE varAddVbound(
   SCIP_VAR*             var,                /**< problem variable x in x <= b*z + d  or  x >= b*z + d */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_BOUNDTYPE        vbtype,             /**< type of variable bound (LOWER or UPPER) */
   SCIP_VAR*             vbvar,              /**< variable z    in x <= b*z + d  or  x >= b*z + d */
   SCIP_Real             vbcoef,             /**< coefficient b in x <= b*z + d  or  x >= b*z + d */
   SCIP_Real             vbconstant          /**< constant d    in x <= b*z + d  or  x >= b*z + d */
   )
{
   SCIP_Bool added;

   /* It can happen that the variable "var" and the variable "vbvar" are the same variable. For example if a variable
    * gets aggregated, the variable bounds (vbound) of that variable are copied to the other variable. A variable bound
    * variable of the aggregated variable might be the same as the one its gets aggregated too.
    *
    * If the variable "var" and the variable "vbvar" are the same, the variable bound which should be added here has to
    * be redundant. This is the case since an infeasibility should have be detected in the previous methods. As well as
    * the bounds of the variable which should be also already be tightened in the previous methods. Therefore, the
    * variable bound can be ignored.
    *
    * From the way the the variable bound system is implemented (detecting infeasibility, tighten bounds), the
    * equivalence of the variables should be checked here.
    */
   if( var == vbvar )
   {
      /* in this case the variable bound has to be redundant, this means for possible assignments to this variable; this
       * can be checked via the global bounds of the variable */
#ifndef NDEBUG
      SCIP_Real lb;
      SCIP_Real ub;

      lb = SCIPvarGetLbGlobal(var);
      ub = SCIPvarGetUbGlobal(var);

      if(vbtype == SCIP_BOUNDTYPE_LOWER)
      {
         if( vbcoef > 0.0 )
         {
            assert(SCIPsetIsGE(set, lb,  lb * vbcoef + vbconstant) );
            assert(SCIPsetIsGE(set, ub,  ub * vbcoef + vbconstant) );
         }
         else
         {
            assert(SCIPsetIsGE(set, lb,  ub * vbcoef + vbconstant) );
            assert(SCIPsetIsGE(set, ub,  lb * vbcoef + vbconstant) );
         }
      }
      else
      {
         assert(vbtype == SCIP_BOUNDTYPE_UPPER);
         if( vbcoef > 0.0 )
         {
            assert(SCIPsetIsLE(set, lb,  lb * vbcoef + vbconstant) );
            assert(SCIPsetIsLE(set, ub,  ub * vbcoef + vbconstant) );
         }
         else
         {
            assert(SCIPsetIsLE(set, lb,  ub * vbcoef + vbconstant) );
            assert(SCIPsetIsLE(set, ub,  lb * vbcoef + vbconstant) );
         }
      }
#endif
      SCIPsetDebugMsg(set, "redundant variable bound: <%s> %s %g<%s> %+g\n",
         SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);

      return SCIP_OKAY;
   }

   SCIPsetDebugMsg(set, "adding variable bound: <%s> %s %g<%s> %+g\n",
      SCIPvarGetName(var), vbtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", vbcoef, SCIPvarGetName(vbvar), vbconstant);

   /* check variable bound on debugging solution */
   SCIP_CALL( SCIPdebugCheckVbound(set, var, vbtype, vbvar, vbcoef, vbconstant) ); /*lint !e506 !e774*/

   /* perform the addition */
   if( vbtype == SCIP_BOUNDTYPE_LOWER )
   {
      SCIP_CALL( SCIPvboundsAdd(&var->vlbs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
   }
   else
   {
      SCIP_CALL( SCIPvboundsAdd(&var->vubs, blkmem, set, vbtype, vbvar, vbcoef, vbconstant, &added) );
   }
   var->closestvblpcount = -1;

   if( added )
   {
      /* issue IMPLADDED event */
      SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
   }

   return SCIP_OKAY;
}

/** checks whether the given implication is redundant or infeasible w.r.t. the implied variables global bounds */
static
void checkImplic(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_BOUNDTYPE        impltype,           /**< type       of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
   SCIP_Real             implbound,          /**< bound b    in implication y <= b or y >= b */
   SCIP_Bool*            redundant,          /**< pointer to store whether the implication is redundant */
   SCIP_Bool*            infeasible          /**< pointer to store whether the implication is infeasible */
   )
{
   SCIP_Real impllb;
   SCIP_Real implub;

   assert(redundant != NULL);
   assert(infeasible != NULL);

   impllb = SCIPvarGetLbGlobal(implvar);
   implub = SCIPvarGetUbGlobal(implvar);
   if( impltype == SCIP_BOUNDTYPE_LOWER )
   {
      *infeasible = SCIPsetIsFeasGT(set, implbound, implub);
      *redundant = SCIPsetIsFeasLE(set, implbound, impllb);
   }
   else
   {
      *infeasible = SCIPsetIsFeasLT(set, implbound, impllb);
      *redundant = SCIPsetIsFeasGE(set, implbound, implub);
   }
}

/** applies the given implication, if it is not redundant */
static
SCIP_RETCODE applyImplic(
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_BOUNDTYPE        impltype,           /**< type       of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
   SCIP_Real             implbound,          /**< bound b    in implication y <= b or y >= b */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to count the number of performed bound changes, or NULL */
   )
{
   SCIP_Real implub;
   SCIP_Real impllb;

   assert(infeasible != NULL);

   *infeasible = FALSE;

   implub = SCIPvarGetUbGlobal(implvar);
   impllb = SCIPvarGetLbGlobal(implvar);
   if( impltype == SCIP_BOUNDTYPE_LOWER )
   {
      if( SCIPsetIsFeasGT(set, implbound, implub) )
      {
         /* the implication produces a conflict: the problem is infeasible */
         *infeasible = TRUE;
      }
      else if( SCIPsetIsFeasGT(set, implbound, impllb) )
      {
         /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
          * with the local bound, in this case we need to store the bound change as pending bound change
          */
         if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
         {
            assert(tree != NULL);
            assert(transprob != NULL);
            assert(SCIPprobIsTransformed(transprob));

            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_LOWER, FALSE) );
         }
         else
         {
            SCIP_CALL( SCIPvarChgLbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
         }

         if( nbdchgs != NULL )
            (*nbdchgs)++;
      }
   }
   else
   {
      if( SCIPsetIsFeasLT(set, implbound, impllb) )
      {
         /* the implication produces a conflict: the problem is infeasible */
         *infeasible = TRUE;
      }
      else if( SCIPsetIsFeasLT(set, implbound, implub) )
      {
         /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
          * with the local bound, in this case we need to store the bound change as pending bound change
          */
         if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
         {
            assert(tree != NULL);
            assert(transprob != NULL);
            assert(SCIPprobIsTransformed(transprob));

            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, implvar, implbound, SCIP_BOUNDTYPE_UPPER, FALSE) );
         }
         else
         {
            SCIP_CALL( SCIPvarChgUbGlobal(implvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, implbound) );
         }

         if( nbdchgs != NULL )
            (*nbdchgs)++;
      }
   }

   return SCIP_OKAY;
}

/** actually performs the addition of an implication to the variable's implication arrays,
 *  and adds the corresponding implication or variable bound to the implied variable;
 *  if the implication is conflicting, the variable is fixed to the opposite value;
 *  if the variable is already fixed to the given value, the implication is performed immediately;
 *  if the implication is redundant with respect to the variables' global bounds, it is ignored
 */
static
SCIP_RETCODE varAddImplic(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_Bool             varfixing,          /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_BOUNDTYPE        impltype,           /**< type       of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
   SCIP_Real             implbound,          /**< bound b    in implication y <= b or y >= b */
   SCIP_Bool             isshortcut,         /**< is the implication a shortcut, i.e., added as part of the transitive closure of another implication? */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs,            /**< pointer to count the number of performed bound changes, or NULL */
   SCIP_Bool*            added               /**< pointer to store whether an implication was added */
   )
{
   SCIP_Bool redundant;
   SCIP_Bool conflict;

   assert(var != NULL);
   assert(SCIPvarIsActive(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
   assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
   assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
   assert(infeasible != NULL);
   assert(added != NULL);

   /* check implication on debugging solution */
   SCIP_CALL( SCIPdebugCheckImplic(set, var, varfixing, implvar, impltype, implbound) ); /*lint !e506 !e774*/

   *infeasible = FALSE;
   *added = FALSE;

   /* check, if the implication is redundant or infeasible */
   checkImplic(set, implvar, impltype, implbound, &redundant, &conflict);
   assert(!redundant || !conflict);
   if( redundant )
      return SCIP_OKAY;

   if( var == implvar )
   {
      /* special cases appear were a bound to a variable implies itself to be outside the bounds:
       * x == varfixing  =>  x < 0 or x > 1
       */
      if( SCIPsetIsLT(set, implbound, 0.0) || SCIPsetIsGT(set, implbound, 1.0) )
         conflict = TRUE;
      else
      {
         /* variable implies itself: x == varfixing  =>  x == (impltype == SCIP_BOUNDTYPE_LOWER) */
         assert(SCIPsetIsZero(set, implbound) || SCIPsetIsEQ(set, implbound, 1.0));
         assert(SCIPsetIsZero(set, implbound) == (impltype == SCIP_BOUNDTYPE_UPPER));
         assert(SCIPsetIsEQ(set, implbound, 1.0) == (impltype == SCIP_BOUNDTYPE_LOWER));
         conflict = conflict || ((varfixing == TRUE) == (impltype == SCIP_BOUNDTYPE_UPPER));
         if( !conflict )
            return SCIP_OKAY;
      }
   }

   /* check, if the variable is already fixed */
   if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
   {
      /* if the variable is fixed to the given value, perform the implication; otherwise, ignore the implication */
      if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
      {
         SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
               eventfilter, cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
      }
      return SCIP_OKAY;
   }

   assert((impltype == SCIP_BOUNDTYPE_LOWER && SCIPsetIsGT(set, implbound, SCIPvarGetLbGlobal(implvar)))
      || (impltype == SCIP_BOUNDTYPE_UPPER && SCIPsetIsLT(set, implbound, SCIPvarGetUbGlobal(implvar))));

   if( !conflict )
   {
      assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */

      if( SCIPvarIsBinary(implvar) )
      {
         SCIP_VAR* vars[2];
         SCIP_Bool vals[2];

         assert(SCIPsetIsFeasEQ(set, implbound, 1.0) || SCIPsetIsFeasZero(set, implbound));
         assert((impltype == SCIP_BOUNDTYPE_UPPER) == SCIPsetIsFeasZero(set, implbound));

         vars[0] = var;
         vars[1] = implvar;
         vals[0] = varfixing;
         vals[1] = (impltype == SCIP_BOUNDTYPE_UPPER);

         /* add the clique to the clique table */
         SCIP_CALL( SCIPcliquetableAdd(cliquetable, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, vars, vals, 2, FALSE, &conflict, nbdchgs) );

         if( !conflict )
            return SCIP_OKAY;
      }
      else
      {
         /* add implication x == 0/1 -> y <= b / y >= b to the implications list of x */
         SCIPsetDebugMsg(set, "adding implication: <%s> == %u  ==>  <%s> %s %g\n",
            SCIPvarGetName(var), varfixing,
            SCIPvarGetName(implvar), impltype == SCIP_BOUNDTYPE_UPPER ? "<=" : ">=", implbound);
         SCIP_CALL( SCIPimplicsAdd(&var->implics, blkmem, set, stat, varfixing, implvar, impltype, implbound,
               isshortcut, &conflict, added) );
      }
   }
   assert(!conflict || !(*added));

   /* on conflict, fix the variable to the opposite value */
   if( conflict )
   {
      SCIPsetDebugMsg(set, " -> implication yields a conflict: fix <%s> == %d\n", SCIPvarGetName(var), !varfixing);

      /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
       * with the local bound, in this case we need to store the bound change as pending bound change
       */
      if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
      {
         assert(tree != NULL);
         assert(transprob != NULL);
         assert(SCIPprobIsTransformed(transprob));

         if( varfixing )
         {
            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
         }
         else
         {
            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
         }
      }
      else
      {
         if( varfixing )
         {
            SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
         }
         else
         {
            SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
         }
      }
      if( nbdchgs != NULL )
         (*nbdchgs)++;

      return SCIP_OKAY;
   }
   else if( *added )
   {
      /* issue IMPLADDED event */
      SCIP_CALL( varEventImplAdded(var, blkmem, set, eventqueue) );
   }
   else
   {
      /* the implication was redundant: the inverse is also redundant */
      return SCIP_OKAY;
   }

   assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */

   /* check, whether implied variable is binary */
   if( !SCIPvarIsBinary(implvar) )
   {
      SCIP_Real lb;
      SCIP_Real ub;

      /* add inverse variable bound to the variable bounds of y with global bounds y \in [lb,ub]:
       *   x == 0 -> y <= b  <->  y <= (ub - b)*x + b
       *   x == 1 -> y <= b  <->  y <= (b - ub)*x + ub
       *   x == 0 -> y >= b  <->  y >= (lb - b)*x + b
       *   x == 1 -> y >= b  <->  y >= (b - lb)*x + lb
       * for numerical reasons, ignore variable bounds with large absolute coefficient
       */
      lb = SCIPvarGetLbGlobal(implvar);
      ub = SCIPvarGetUbGlobal(implvar);
      if( impltype == SCIP_BOUNDTYPE_UPPER )
      {
         if( REALABS(implbound - ub) <= MAXABSVBCOEF )
         {
            SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, var,
                  varfixing ? implbound - ub : ub - implbound, varfixing ? ub : implbound) );
         }
      }
      else
      {
         if( REALABS(implbound - lb) <= MAXABSVBCOEF )
         {
            SCIP_CALL( varAddVbound(implvar, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, var,
                  varfixing ? implbound - lb : lb - implbound, varfixing ? lb : implbound) );
         }
      }
   }

   return SCIP_OKAY;
}

/** adds transitive closure for binary implication x = a -> y = b */
static
SCIP_RETCODE varAddTransitiveBinaryClosureImplic(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_Bool             varfixing,          /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_Bool             implvarfixing,      /**< fixing b in implication */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to count the number of performed bound changes, or NULL */
   )
{
   SCIP_VAR** implvars;
   SCIP_BOUNDTYPE* impltypes;
   SCIP_Real* implbounds;
   int nimpls;
   int i;

   *infeasible = FALSE;

   /* binary variable: implications of implvar */
   nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
   implvars = SCIPimplicsGetVars(implvar->implics, implvarfixing);
   impltypes = SCIPimplicsGetTypes(implvar->implics, implvarfixing);
   implbounds = SCIPimplicsGetBounds(implvar->implics, implvarfixing);

   /* if variable has too many implications, the implication graph may become too dense */
   i = MIN(nimpls, MAXIMPLSCLOSURE) - 1;

   /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
    * implvars[i] is fixed, s.t. the implication y == varfixing -> z <= b / z >= b is deleted; this affects the
    * array over which we currently iterate; the only thing that can happen, is that elements of the array are
    * deleted; in this case, the subsequent elements are moved to the front; if we iterate from back to front, the
    * only thing that can happen is that we add the same implication twice - this does no harm
    */
   while ( i >= 0 && !(*infeasible) )
   {
      SCIP_Bool added;

      assert(implvars[i] != implvar);

      /* we have x == varfixing -> y == implvarfixing -> z <= b / z >= b:
       * add implication x == varfixing -> z <= b / z >= b to the implications list of x
       */
      if( SCIPvarIsActive(implvars[i]) )
      {
         SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
               eventqueue, eventfilter, varfixing, implvars[i], impltypes[i], implbounds[i], TRUE, infeasible, nbdchgs, &added) );
         assert(SCIPimplicsGetNImpls(implvar->implics, implvarfixing) <= nimpls);
         nimpls = SCIPimplicsGetNImpls(implvar->implics, implvarfixing);
         i = MIN(i, nimpls); /* some elements from the array could have been removed */
      }
      --i;
   }

   return SCIP_OKAY;
}

/** adds given implication to the variable's implication list, and adds all implications directly implied by this
 *  implication to the variable's implication list;
 *  if the implication is conflicting, the variable is fixed to the opposite value;
 *  if the variable is already fixed to the given value, the implication is performed immediately;
 *  if the implication is redundant with respect to the variables' global bounds, it is ignored
 */
static
SCIP_RETCODE varAddTransitiveImplic(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_Bool             varfixing,          /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_BOUNDTYPE        impltype,           /**< type       of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
   SCIP_Real             implbound,          /**< bound b    in implication y <= b or y >= b */
   SCIP_Bool             transitive,         /**< should transitive closure of implication also be added? */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to count the number of performed bound changes, or NULL */
   )
{
   SCIP_Bool added;

   assert(var != NULL);
   assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
   assert(SCIPvarIsActive(var));
   assert(implvar != NULL);
   assert(SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED);
   assert(infeasible != NULL);

   /* add implication x == varfixing -> y <= b / y >= b to the implications list of x */
   SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable, branchcand,
         eventqueue, eventfilter, varfixing, implvar, impltype, implbound, FALSE, infeasible, nbdchgs, &added) );

   if( *infeasible || var == implvar || !transitive || !added )
      return SCIP_OKAY;

   assert(SCIPvarIsActive(implvar)); /* a fixed implvar would either cause a redundancy or infeasibility */

   /* add transitive closure */
   if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(implvar) )
   {
      SCIP_Bool implvarfixing;

      implvarfixing = (impltype == SCIP_BOUNDTYPE_LOWER);

      /* binary variable: implications of implvar */
      SCIP_CALL( varAddTransitiveBinaryClosureImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
            cliquetable, branchcand, eventqueue, eventfilter, varfixing, implvar, implvarfixing, infeasible, nbdchgs) );

      /* inverse implication */
      if( !(*infeasible) )
      {
         SCIP_CALL( varAddTransitiveBinaryClosureImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, !implvarfixing, var, !varfixing, infeasible, nbdchgs) );
      }
   }
   else
   {
      /* non-binary variable: variable lower bounds of implvar */
      if( impltype == SCIP_BOUNDTYPE_UPPER && implvar->vlbs != NULL )
      {
         SCIP_VAR** vlbvars;
         SCIP_Real* vlbcoefs;
         SCIP_Real* vlbconstants;
         int nvlbvars;
         int i;

         nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
         vlbvars = SCIPvboundsGetVars(implvar->vlbs);
         vlbcoefs = SCIPvboundsGetCoefs(implvar->vlbs);
         vlbconstants = SCIPvboundsGetConstants(implvar->vlbs);

         /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
          * vlbvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
          * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
          * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
          * is that we add the same implication twice - this does no harm
          */
         i = nvlbvars-1;
         while ( i >= 0 && !(*infeasible) )
         {
            assert(vlbvars[i] != implvar);
            assert(!SCIPsetIsZero(set, vlbcoefs[i]));

            /* we have x == varfixing -> y <= b and y >= c*z + d:
             *   c > 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
             *   c < 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
             *
             * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
             *       SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
             *       aggregation variable (the one which will stay active);
             *
             *       W.l.o.g. we consider the variable upper bounds for now. Let "vubvar" be a variable upper bound of
             *       the aggregated variable "aggvar"; During that copying of that variable upper bound variable
             *       "vubvar" the variable lower and upper bounds of this variable "vubvar" are also considered; note
             *       that the "aggvar" can be a variable lower bound variable of the variable "vubvar"; Due to that
             *       situation it can happen that we reach that code place where "vlbvars[i] == aggvar". In particular
             *       the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
             *       but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
             *       have to explicitly check that the active variable has not a variable status
             *       SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
             */
            if( SCIPvarIsActive(vlbvars[i]) && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vlbvars[i]) != SCIP_VARSTATUS_NEGATED )
            {
               SCIP_Real vbimplbound;

               vbimplbound = (implbound - vlbconstants[i])/vlbcoefs[i];
               if( vlbcoefs[i] >= 0.0 )
               {
                  vbimplbound = adjustedUb(set, SCIPvarIsIntegral(vlbvars[i]), vbimplbound);
                  SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                        branchcand, eventqueue, eventfilter, varfixing, vlbvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
                        infeasible, nbdchgs, &added) );
               }
               else
               {
                  vbimplbound = adjustedLb(set, SCIPvarIsIntegral(vlbvars[i]), vbimplbound);
                  SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                        branchcand, eventqueue, eventfilter, varfixing, vlbvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
                        infeasible, nbdchgs, &added) );
               }
               nvlbvars = SCIPvboundsGetNVbds(implvar->vlbs);
               i = MIN(i, nvlbvars); /* some elements from the array could have been removed */
            }
            --i;
         }
      }

      /* non-binary variable: variable upper bounds of implvar */
      if( impltype == SCIP_BOUNDTYPE_LOWER && implvar->vubs != NULL )
      {
         SCIP_VAR** vubvars;
         SCIP_Real* vubcoefs;
         SCIP_Real* vubconstants;
         int nvubvars;
         int i;

         nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
         vubvars = SCIPvboundsGetVars(implvar->vubs);
         vubcoefs = SCIPvboundsGetCoefs(implvar->vubs);
         vubconstants = SCIPvboundsGetConstants(implvar->vubs);

         /* we have to iterate from back to front, because in varAddImplic() it may happen that a conflict is detected and
          * vubvars[i] is fixed, s.t. the variable bound is deleted; this affects the array over which we currently
          * iterate; the only thing that can happen, is that elements of the array are deleted; in this case, the
          * subsequent elements are moved to the front; if we iterate from back to front, the only thing that can happen
          * is that we add the same implication twice - this does no harm
          */
         i = nvubvars-1;
         while ( i >= 0 && !(*infeasible) )
         {
            assert(vubvars[i] != implvar);
            assert(!SCIPsetIsZero(set, vubcoefs[i]));

            /* we have x == varfixing -> y >= b and y <= c*z + d:
             *   c > 0: add implication x == varfixing -> z >= (b-d)/c to the implications list of x
             *   c < 0: add implication x == varfixing -> z <= (b-d)/c to the implications list of x
             *
             * @note during an aggregation the aggregated variable "aggrvar" (the one which will have the status
             *       SCIP_VARSTATUS_AGGREGATED afterwards) copies its variable lower and uppers bounds to the
             *       aggregation variable (the one which will stay active);
             *
             *       W.l.o.g. we consider the variable lower bounds for now. Let "vlbvar" be a variable lower bound of
             *       the aggregated variable "aggvar"; During that copying of that variable lower bound variable
             *       "vlbvar" the variable lower and upper bounds of this variable "vlbvar" are also considered; note
             *       that the "aggvar" can be a variable upper bound variable of the variable "vlbvar"; Due to that
             *       situation it can happen that we reach that code place where "vubvars[i] == aggvar". In particular
             *       the "aggvar" has already the variable status SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED
             *       but is still active since the aggregation is not finished yet (in SCIPvarAggregate()); therefore we
             *       have to explicitly check that the active variable has not a variable status
             *       SCIP_VARSTATUS_AGGREGATED or SCIP_VARSTATUS_NEGATED;
             */
            if( SCIPvarIsActive(vubvars[i]) && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_AGGREGATED && SCIPvarGetStatus(vubvars[i]) != SCIP_VARSTATUS_NEGATED )
            {
               SCIP_Real vbimplbound;

               vbimplbound = (implbound - vubconstants[i])/vubcoefs[i];
               if( vubcoefs[i] >= 0.0 )
               {
                  vbimplbound = adjustedLb(set, SCIPvarIsIntegral(vubvars[i]), vbimplbound);
                  SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                        branchcand, eventqueue, eventfilter, varfixing, vubvars[i], SCIP_BOUNDTYPE_LOWER, vbimplbound, TRUE,
                        infeasible, nbdchgs, &added) );
               }
               else
               {
                  vbimplbound = adjustedUb(set, SCIPvarIsIntegral(vubvars[i]), vbimplbound);
                  SCIP_CALL( varAddImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                        branchcand, eventqueue, eventfilter, varfixing, vubvars[i], SCIP_BOUNDTYPE_UPPER, vbimplbound, TRUE,
                        infeasible, nbdchgs, &added) );
               }
               nvubvars = SCIPvboundsGetNVbds(implvar->vubs);
               i = MIN(i, nvubvars); /* some elements from the array could have been removed */
            }
            --i;
         }
      }
   }

   return SCIP_OKAY;
}

/** informs variable x about a globally valid variable lower bound x >= b*z + d with integer variable z;
 *  if z is binary, the corresponding valid implication for z is also added;
 *  improves the global bounds of the variable and the vlb variable if possible
 */
SCIP_RETCODE SCIPvarAddVlb(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             vlbvar,             /**< variable z    in x >= b*z + d */
   SCIP_Real             vlbcoef,            /**< coefficient b in x >= b*z + d */
   SCIP_Real             vlbconstant,        /**< constant d    in x >= b*z + d */
   SCIP_Bool             transitive,         /**< should transitive closure of implication also be added? */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to store the number of performed bound changes, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarIsIntegral(vlbvar));
   assert(infeasible != NULL);

   SCIPsetDebugMsg(set, "adding variable lower bound <%s> >= %g<%s> + %g\n", SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);

   *infeasible = FALSE;
   if( nbdchgs != NULL )
      *nbdchgs = 0;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarAddVlb(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
            cliquetable, branchcand, eventqueue, eventfilter, vlbvar, vlbcoef, vlbconstant, transitive, infeasible, nbdchgs) );
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
      SCIP_CALL( SCIPvarGetProbvarSum(&vlbvar, set, &vlbcoef, &vlbconstant) );
      SCIPsetDebugMsg(set, " -> transformed to variable lower bound <%s> >= %g<%s> + %g\n",
            SCIPvarGetName(var), vlbcoef, SCIPvarGetName(vlbvar), vlbconstant);

      /* if the variables are the same, just update the corresponding bound */
      if( var == vlbvar )
      {
         /* if the variables cancel out, the variable bound constraint is redundant or proves global infeasibility */
         if( SCIPsetIsEQ(set, vlbcoef, 1.0) )
         {
            if( SCIPsetIsFeasPositive(set, vlbconstant) )
               *infeasible = TRUE;
         }
         else
         {
            SCIP_Real lb = SCIPvarGetLbGlobal(var);
            SCIP_Real ub = SCIPvarGetUbGlobal(var);

            /* the variable bound constraint defines a new upper bound */
            if( SCIPsetIsGT(set, vlbcoef, 1.0) )
            {
               /* bound might be adjusted due to integrality condition */
               SCIP_Real newub = adjustedUb(set, SCIPvarIsIntegral(var), vlbconstant / (1.0 - vlbcoef));

               /* check bounds for feasibility */
               if( SCIPsetIsFeasLT(set, newub, lb) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global upper bound of variable */
               if( SCIPsetIsFeasLT(set, newub, ub) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
                  }

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
            }
            /* the variable bound constraint defines a new lower bound */
            else
            {
               assert(SCIPsetIsLT(set, vlbcoef, 1.0));

               /* bound might be adjusted due to integrality condition */
               SCIP_Real newlb = adjustedLb(set, SCIPvarIsIntegral(var), vlbconstant / (1.0 - vlbcoef));

               /* check bounds for feasibility */
               if( SCIPsetIsFeasGT(set, newlb, ub) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global lower bound of variable */
               if( SCIPsetIsFeasGT(set, newlb, lb) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
                  }

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
            }
         }
      }
      /* if the vlb coefficient is zero, just update the lower bound of the variable */
      else if( SCIPsetIsZero(set, vlbcoef) )
      {
         /* bound might be adjusted due to integrality condition */
         vlbconstant = adjustedLb(set, SCIPvarIsIntegral(var), vlbconstant);

         /* check bounds for feasibility */
         if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetUbGlobal(var)) )
            *infeasible = TRUE;
         /* improve global lower bound of variable */
         else if( SCIPsetIsFeasGT(set, vlbconstant, SCIPvarGetLbGlobal(var)) )
         {
            /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
             * with the local bound, in this case we need to store the bound change as pending bound change
             */
            if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
            {
               assert(tree != NULL);
               assert(transprob != NULL);
               assert(SCIPprobIsTransformed(transprob));

               SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                     tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, vlbconstant, SCIP_BOUNDTYPE_LOWER, FALSE) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vlbconstant) );
            }

            if( nbdchgs != NULL )
               (*nbdchgs)++;
         }
      }
      else if( SCIPvarIsActive(vlbvar) )
      {
         SCIP_Real xlb;
         SCIP_Real xub;
         SCIP_Real zlb;
         SCIP_Real zub;
         SCIP_Real minvlb;
         SCIP_Real maxvlb;

         assert(SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vlbvar) == SCIP_VARSTATUS_COLUMN);
         assert(vlbcoef != 0.0);

         minvlb = -SCIPsetInfinity(set);
         maxvlb = SCIPsetInfinity(set);

         xlb = SCIPvarGetLbGlobal(var);
         xub = SCIPvarGetUbGlobal(var);
         zlb = SCIPvarGetLbGlobal(vlbvar);
         zub = SCIPvarGetUbGlobal(vlbvar);

         /* improve global bounds of vlb variable, and calculate minimal and maximal value of variable bound */
         if( vlbcoef >= 0.0 )
         {
            if( !SCIPsetIsInfinity(set, xub) )
            {
               /* x >= b*z + d  ->  z <= (x-d)/b */
               SCIP_Real newzub = adjustedUb(set, SCIPvarIsIntegral(vlbvar), (xub - vlbconstant) / vlbcoef);

               /* check bounds for feasibility */
               if( SCIPsetIsFeasLT(set, newzub, zlb) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global upper bound of variable */
               if( SCIPsetIsFeasLT(set, newzub, zub) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, vlbvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgUbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
                  }
                  zub = newzub;

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
               maxvlb = vlbcoef * zub + vlbconstant;
               if( !SCIPsetIsInfinity(set, -zlb) )
                  minvlb = vlbcoef * zlb + vlbconstant;
            }
            else
            {
               if( !SCIPsetIsInfinity(set, zub) )
                  maxvlb = vlbcoef * zub + vlbconstant;
               if( !SCIPsetIsInfinity(set, -zlb) )
                  minvlb = vlbcoef * zlb + vlbconstant;
            }
         }
         else
         {
            if( !SCIPsetIsInfinity(set, xub) )
            {
               /* x >= b*z + d  ->  z >= (x-d)/b */
               SCIP_Real newzlb = adjustedLb(set, SCIPvarIsIntegral(vlbvar), (xub - vlbconstant) / vlbcoef);

               /* check bounds for feasibility */
               if( SCIPsetIsFeasGT(set, newzlb, zub) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global lower bound of variable */
               if( SCIPsetIsFeasGT(set, newzlb, zlb) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, vlbvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgLbGlobal(vlbvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
                  }
                  zlb = newzlb;

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
               maxvlb = vlbcoef * zlb + vlbconstant;
               if( !SCIPsetIsInfinity(set, zub) )
                  minvlb = vlbcoef * zub + vlbconstant;
            }
            else
            {
               if( !SCIPsetIsInfinity(set, -zlb) )
                  maxvlb = vlbcoef * zlb + vlbconstant;
               if( !SCIPsetIsInfinity(set, zub) )
                  minvlb = vlbcoef * zub + vlbconstant;
            }
         }
         if( maxvlb < minvlb )
            maxvlb = minvlb;

         /* adjust bounds due to integrality of variable */
         minvlb = adjustedLb(set, SCIPvarIsIntegral(var), minvlb);
         maxvlb = adjustedLb(set, SCIPvarIsIntegral(var), maxvlb);

         /* check bounds for feasibility */
         if( SCIPsetIsFeasGT(set, minvlb, xub) )
         {
            *infeasible = TRUE;
            return SCIP_OKAY;
         }

         /* improve global lower bound of variable */
         if( SCIPsetIsFeasGT(set, minvlb, xlb) )
         {
            /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
             * with the local bound, in this case we need to store the bound change as pending bound change
             */
            if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
            {
               assert(tree != NULL);
               assert(transprob != NULL);
               assert(SCIPprobIsTransformed(transprob));

               SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                     tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, minvlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, minvlb) );
            }
            xlb = minvlb;

            if( nbdchgs != NULL )
               (*nbdchgs)++;
         }
         minvlb = xlb;

         /* improve variable bound for binary z by moving the variable's global bound to the vlb constant */
         if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(vlbvar) )
         {
            /* b > 0: x >= (maxvlb - minvlb) * z + minvlb
             * b < 0: x >= (minvlb - maxvlb) * z + maxvlb
             */

            assert(!SCIPsetIsInfinity(set, maxvlb) && !SCIPsetIsInfinity(set, -minvlb));

            if( vlbcoef >= 0.0 )
            {
               vlbcoef = maxvlb - minvlb;
               vlbconstant = minvlb;
            }
            else
            {
               vlbcoef = minvlb - maxvlb;
               vlbconstant = maxvlb;
            }
         }

         /* add variable bound to the variable bounds list */
         if( SCIPsetIsFeasGT(set, maxvlb, xlb) )
         {
            assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
            assert(!SCIPsetIsZero(set, vlbcoef));

            /* if one of the variables is binary, add the corresponding implication to the variable's implication
             * list, thereby also adding the variable bound (or implication) to the other variable
             */
            if( SCIPvarGetType(vlbvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(vlbvar) )
            {
               /* add corresponding implication:
                *   b > 0, x >= b*z + d  <->  z == 1 -> x >= b+d
                *   b < 0, x >= b*z + d  <->  z == 0 -> x >= d
                */
               SCIP_CALL( varAddTransitiveImplic(vlbvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, (vlbcoef >= 0.0), var, SCIP_BOUNDTYPE_LOWER, maxvlb, transitive,
                     infeasible, nbdchgs) );
            }
            else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(var) )
            {
               /* add corresponding implication:
                *   b > 0, x >= b*z + d  <->  x == 0 -> z <= -d/b
                *   b < 0, x >= b*z + d  <->  x == 0 -> z >= -d/b
                */
               SCIP_Real implbound;
               implbound = -vlbconstant/vlbcoef;

               /* tighten the implication bound if the variable is integer */
               if( SCIPvarIsIntegral(vlbvar) )
               {
                  if( vlbcoef >= 0 )
                     implbound = SCIPsetFloor(set, implbound);
                  else
                     implbound = SCIPsetCeil(set, implbound);
               }
               SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, FALSE, vlbvar, (vlbcoef >= 0.0 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER),
                     implbound, transitive, infeasible, nbdchgs) );
            }
            else
            {
               SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_LOWER, vlbvar, vlbcoef, vlbconstant) );
            }
         }
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      /* x = a*y + c:  x >= b*z + d  <=>  a*y + c >= b*z + d  <=>  y >= b/a * z + (d-c)/a, if a > 0
       *                                                           y <= b/a * z + (d-c)/a, if a < 0
       */

      /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
      SCIP_CALL( SCIPvarGetProbvarSum(&vlbvar, set, &vlbcoef, &vlbconstant) );

      /* if the variables cancel out, the variable bound constraint is redundant or proves global infeasibility */
      assert(var->data.aggregate.var != NULL);
      if( var->data.aggregate.var == vlbvar && SCIPsetIsEQ(set, var->data.aggregate.scalar, vlbcoef) )
      {
         if( SCIPsetIsFeasLT(set, var->data.aggregate.constant, vlbconstant) )
            *infeasible = TRUE;
      }
      else if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> add variable lower bound */
         SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, vlbvar, vlbcoef/var->data.aggregate.scalar,
               (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         /* a < 0 -> add variable upper bound */
         SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, vlbvar, vlbcoef/var->data.aggregate.scalar,
               (vlbconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      /* nothing to do here */
      break;

   case SCIP_VARSTATUS_NEGATED:
      /* x = offset - x':  x >= b*z + d  <=>  offset - x' >= b*z + d  <=>  x' <= -b*z + (offset-d) */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vlbvar, -vlbcoef, var->data.negate.constant - vlbconstant, transitive, infeasible,
            nbdchgs) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** informs variable x about a globally valid variable upper bound x <= b*z + d with integer variable z;
 *  if z is binary, the corresponding valid implication for z is also added;
 *  updates the global bounds of the variable and the vub variable correspondingly
 */
SCIP_RETCODE SCIPvarAddVub(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_VAR*             vubvar,             /**< variable z    in x <= b*z + d */
   SCIP_Real             vubcoef,            /**< coefficient b in x <= b*z + d */
   SCIP_Real             vubconstant,        /**< constant d    in x <= b*z + d */
   SCIP_Bool             transitive,         /**< should transitive closure of implication also be added? */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to store the number of performed bound changes, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarIsIntegral(vubvar));
   assert(infeasible != NULL);

   SCIPsetDebugMsg(set, "adding variable upper bound <%s> <= %g<%s> + %g\n", SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);

   *infeasible = FALSE;
   if( nbdchgs != NULL )
      *nbdchgs = 0;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarAddVub(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
            cliquetable, branchcand, eventqueue, eventfilter, vubvar, vubcoef, vubconstant, transitive, infeasible, nbdchgs) );
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
      SCIP_CALL( SCIPvarGetProbvarSum(&vubvar, set, &vubcoef, &vubconstant) );
      SCIPsetDebugMsg(set, " -> transformed to variable upper bound <%s> <= %g<%s> + %g\n",
            SCIPvarGetName(var), vubcoef, SCIPvarGetName(vubvar), vubconstant);

      /* if the variables are the same, just update the corresponding bound */
      if( var == vubvar )
      {
         /* if the variables cancel out, the variable bound constraint is redundant or proves global infeasibility */
         if( SCIPsetIsEQ(set, vubcoef, 1.0) )
         {
            if( SCIPsetIsFeasNegative(set, vubconstant) )
               *infeasible = TRUE;
         }
         else
         {
            SCIP_Real lb = SCIPvarGetLbGlobal(var);
            SCIP_Real ub = SCIPvarGetUbGlobal(var);

            /* the variable bound constraint defines a new lower bound */
            if( SCIPsetIsGT(set, vubcoef, 1.0) )
            {
               /* bound might be adjusted due to integrality condition */
               SCIP_Real newlb = adjustedLb(set, SCIPvarIsIntegral(var), vubconstant / (1.0 - vubcoef));

               /* check bounds for feasibility */
               if( SCIPsetIsFeasGT(set, newlb, ub) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global lower bound of variable */
               if( SCIPsetIsFeasGT(set, newlb, lb) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, newlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newlb) );
                  }

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
            }
            /* the variable bound constraint defines a new upper bound */
            else
            {
               assert(SCIPsetIsLT(set, vubcoef, 1.0));

               /* bound might be adjusted due to integrality condition */
               SCIP_Real newub = adjustedUb(set, SCIPvarIsIntegral(var), vubconstant / (1.0 - vubcoef));

               /* check bounds for feasibility */
               if( SCIPsetIsFeasLT(set, newub, lb) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global upper bound of variable */
               if( SCIPsetIsFeasLT(set, newub, ub) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, newub, SCIP_BOUNDTYPE_UPPER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newub) );
                  }

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
            }
         }
      }
      /* if the vub coefficient is zero, just update the upper bound of the variable */
      else if( SCIPsetIsZero(set, vubcoef) )
      {
         /* bound might be adjusted due to integrality condition */
         vubconstant = adjustedUb(set, SCIPvarIsIntegral(var), vubconstant);

         /* check bounds for feasibility */
         if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetLbGlobal(var)) )
            *infeasible = TRUE;
         /* improve global upper bound of variable */
         else if( SCIPsetIsFeasLT(set, vubconstant, SCIPvarGetUbGlobal(var)) )
         {
            /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
             * with the local bound, in this case we need to store the bound change as pending bound change
             */
            if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
            {
               assert(tree != NULL);
               assert(transprob != NULL);
               assert(SCIPprobIsTransformed(transprob));

               SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                     tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, vubconstant, SCIP_BOUNDTYPE_UPPER, FALSE) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, vubconstant) );
            }

            if( nbdchgs != NULL )
               (*nbdchgs)++;
         }
      }
      else if( SCIPvarIsActive(vubvar) )
      {
         SCIP_Real xlb;
         SCIP_Real xub;
         SCIP_Real zlb;
         SCIP_Real zub;
         SCIP_Real minvub;
         SCIP_Real maxvub;

         assert(SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(vubvar) == SCIP_VARSTATUS_COLUMN);
         assert(vubcoef != 0.0);

         minvub = -SCIPsetInfinity(set);
         maxvub = SCIPsetInfinity(set);

         xlb = SCIPvarGetLbGlobal(var);
         xub = SCIPvarGetUbGlobal(var);
         zlb = SCIPvarGetLbGlobal(vubvar);
         zub = SCIPvarGetUbGlobal(vubvar);

         /* improve global bounds of vub variable, and calculate minimal and maximal value of variable bound */
         if( vubcoef >= 0.0 )
         {
            if( !SCIPsetIsInfinity(set, -xlb) )
            {
               /* x <= b*z + d  ->  z >= (x-d)/b */
               SCIP_Real newzlb = adjustedLb(set, SCIPvarIsIntegral(vubvar), (xlb - vubconstant) / vubcoef);

               /* check bounds for feasibility */
               if( SCIPsetIsFeasGT(set, newzlb, zub) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global lower bound of variable */
               if( SCIPsetIsFeasGT(set, newzlb, zlb) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, vubvar, newzlb, SCIP_BOUNDTYPE_LOWER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgLbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzlb) );
                  }
                  zlb = newzlb;

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
               minvub = vubcoef * zlb + vubconstant;
               if( !SCIPsetIsInfinity(set, zub) )
                  maxvub = vubcoef * zub + vubconstant;
            }
            else
            {
               if( !SCIPsetIsInfinity(set, zub) )
                  maxvub = vubcoef * zub + vubconstant;
               if( !SCIPsetIsInfinity(set, -zlb) )
                  minvub = vubcoef * zlb + vubconstant;
            }
         }
         else
         {
            if( !SCIPsetIsInfinity(set, -xlb) )
            {
               /* x <= b*z + d  ->  z <= (x-d)/b */
               SCIP_Real newzub = adjustedUb(set, SCIPvarIsIntegral(vubvar), (xlb - vubconstant) / vubcoef);

               /* check bounds for feasibility */
               if( SCIPsetIsFeasLT(set, newzub, zlb) )
               {
                  *infeasible = TRUE;
                  return SCIP_OKAY;
               }

               /* improve global upper bound of variable */
               if( SCIPsetIsFeasLT(set, newzub, zub) )
               {
                  /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
                   * with the local bound, in this case we need to store the bound change as pending bound change
                   */
                  if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
                  {
                     assert(tree != NULL);
                     assert(transprob != NULL);
                     assert(SCIPprobIsTransformed(transprob));

                     SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                           tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, vubvar, newzub, SCIP_BOUNDTYPE_UPPER, FALSE) );
                  }
                  else
                  {
                     SCIP_CALL( SCIPvarChgUbGlobal(vubvar, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, newzub) );
                  }
                  zub = newzub;

                  if( nbdchgs != NULL )
                     (*nbdchgs)++;
               }
               minvub = vubcoef * zub + vubconstant;
               if( !SCIPsetIsInfinity(set, -zlb) )
                  maxvub = vubcoef * zlb + vubconstant;
            }
            else
            {
               if( !SCIPsetIsInfinity(set, zub) )
                  minvub = vubcoef * zub + vubconstant;
               if( !SCIPsetIsInfinity(set, -zlb) )
                  maxvub = vubcoef * zlb + vubconstant;
            }
         }
         if( minvub > maxvub )
            minvub = maxvub;

         /* adjust bounds due to integrality of vub variable */
         minvub = adjustedUb(set, SCIPvarIsIntegral(var), minvub);
         maxvub = adjustedUb(set, SCIPvarIsIntegral(var), maxvub);

         /* check bounds for feasibility */
         if( SCIPsetIsFeasLT(set, maxvub, xlb) )
         {
            *infeasible = TRUE;
            return SCIP_OKAY;
         }

         /* improve global upper bound of variable */
         if( SCIPsetIsFeasLT(set, maxvub, xub) )
         {
            /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
             * with the local bound, in this case we need to store the bound change as pending bound change
             */
            if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
            {
               assert(tree != NULL);
               assert(transprob != NULL);
               assert(SCIPprobIsTransformed(transprob));

               SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                     tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, maxvub, SCIP_BOUNDTYPE_UPPER, FALSE) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, maxvub) );
            }
            xub = maxvub;

            if( nbdchgs != NULL )
               (*nbdchgs)++;
         }
         maxvub = xub;

         /* improve variable bound for binary z by moving the variable's global bound to the vub constant */
         if( SCIPvarIsBinary(vubvar) )
         {
            /* b > 0: x <= (maxvub - minvub) * z + minvub
             * b < 0: x <= (minvub - maxvub) * z + maxvub
             */

            assert(!SCIPsetIsInfinity(set, maxvub) && !SCIPsetIsInfinity(set, -minvub));

            if( vubcoef >= 0.0 )
            {
               vubcoef = maxvub - minvub;
               vubconstant = minvub;
            }
            else
            {
               vubcoef = minvub - maxvub;
               vubconstant = maxvub;
            }
         }

         /* add variable bound to the variable bounds list */
         if( SCIPsetIsFeasLT(set, minvub, xub) )
         {
            assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED);
            assert(!SCIPsetIsZero(set, vubcoef));

            /* if one of the variables is binary, add the corresponding implication to the variable's implication
             * list, thereby also adding the variable bound (or implication) to the other variable
             */
            if( SCIPvarGetType(vubvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(vubvar) )
            {
               /* add corresponding implication:
                *   b > 0, x <= b*z + d  <->  z == 0 -> x <= d
                *   b < 0, x <= b*z + d  <->  z == 1 -> x <= b+d
                */
               SCIP_CALL( varAddTransitiveImplic(vubvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, (vubcoef < 0.0), var, SCIP_BOUNDTYPE_UPPER, minvub, transitive,
                     infeasible, nbdchgs) );
            }
            else if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(var) )
            {
               /* add corresponding implication:
                *   b > 0, x <= b*z + d  <->  x == 1 -> z >= (1-d)/b
                *   b < 0, x <= b*z + d  <->  x == 1 -> z <= (1-d)/b
                */
               SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, TRUE, vubvar, (vubcoef >= 0.0 ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER),
                     (1.0-vubconstant)/vubcoef, transitive, infeasible, nbdchgs) );
            }
            else
            {
               SCIP_CALL( varAddVbound(var, blkmem, set, eventqueue, SCIP_BOUNDTYPE_UPPER, vubvar, vubcoef, vubconstant) );
            }
         }
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      /* x = a*y + c:  x <= b*z + d  <=>  a*y + c <= b*z + d  <=>  y <= b/a * z + (d-c)/a, if a > 0
       *                                                           y >= b/a * z + (d-c)/a, if a < 0
       */

      /* transform b*z + d into the corresponding sum after transforming z to an active problem variable */
      SCIP_CALL( SCIPvarGetProbvarSum(&vubvar, set, &vubcoef, &vubconstant) );

      /* if the variables cancel out, the variable bound constraint is redundant or proves global infeasibility */
      assert(var->data.aggregate.var != NULL);
      if( var->data.aggregate.var == vubvar && SCIPsetIsEQ(set, var->data.aggregate.scalar, vubcoef) )
      {
         if( SCIPsetIsFeasGT(set, var->data.aggregate.constant, vubconstant) )
            *infeasible = TRUE;
      }
      else if( SCIPsetIsPositive(set, var->data.aggregate.scalar) )
      {
         /* a > 0 -> add variable upper bound */
         SCIP_CALL( SCIPvarAddVub(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, vubvar, vubcoef/var->data.aggregate.scalar,
               (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
      }
      else if( SCIPsetIsNegative(set, var->data.aggregate.scalar) )
      {
         /* a < 0 -> add variable lower bound */
         SCIP_CALL( SCIPvarAddVlb(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, vubvar, vubcoef/var->data.aggregate.scalar,
               (vubconstant - var->data.aggregate.constant)/var->data.aggregate.scalar, transitive, infeasible, nbdchgs) );
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      /* nothing to do here */
      break;

   case SCIP_VARSTATUS_NEGATED:
      /* x = offset - x':  x <= b*z + d  <=>  offset - x' <= b*z + d  <=>  x' >= -b*z + (offset-d) */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
            branchcand, eventqueue, eventfilter, vubvar, -vubcoef, var->data.negate.constant - vubconstant, transitive, infeasible,
            nbdchgs) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** informs binary variable x about a globally valid implication:  x == 0 or x == 1  ==>  y <= b  or  y >= b;
 *  also adds the corresponding implication or variable bound to the implied variable;
 *  if the implication is conflicting, the variable is fixed to the opposite value;
 *  if the variable is already fixed to the given value, the implication is performed immediately;
 *  if the implication is redundant with respect to the variables' global bounds, it is ignored
 */
SCIP_RETCODE SCIPvarAddImplic(
   SCIP_VAR*             var,                /**< problem variable  */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_Bool             varfixing,          /**< FALSE if y should be added in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y in implication y <= b or y >= b */
   SCIP_BOUNDTYPE        impltype,           /**< type       of implication y <= b (SCIP_BOUNDTYPE_UPPER) or y >= b (SCIP_BOUNDTYPE_LOWER) */
   SCIP_Real             implbound,          /**< bound b    in implication y <= b or y >= b */
   SCIP_Bool             transitive,         /**< should transitive closure of implication also be added? */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to store the number of performed bound changes, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY);
   assert(infeasible != NULL);

   *infeasible = FALSE;
   if( nbdchgs != NULL )
      *nbdchgs = 0;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      SCIP_CALL( SCIPvarAddImplic(var->data.original.transvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
            cliquetable, branchcand, eventqueue, eventfilter, varfixing, implvar, impltype, implbound, transitive, infeasible,
            nbdchgs) );
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
      /* if the variable is fixed (although it has no FIXED status), and varfixing corresponds to the fixed value of
       * the variable, the implication can be applied directly;
       * otherwise, add implication to the implications list (and add inverse of implication to the implied variable)
       */
      if( SCIPvarGetLbGlobal(var) > 0.5 || SCIPvarGetUbGlobal(var) < 0.5 )
      {
         if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
         {
            SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
                  eventfilter, cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
         }
      }
      else
      {
         SCIP_CALL( SCIPvarGetProbvarBound(&implvar, &implbound, &impltype) );
         SCIPvarAdjustBd(implvar, set, impltype, &implbound);
         if( SCIPvarIsActive(implvar) || SCIPvarGetStatus(implvar) == SCIP_VARSTATUS_FIXED )
         {
            SCIP_CALL( varAddTransitiveImplic(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                  branchcand, eventqueue, eventfilter, varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
         }
      }
      break;

   case SCIP_VARSTATUS_FIXED:
      /* if varfixing corresponds to the fixed value of the variable, the implication can be applied directly */
      if( varfixing == (SCIPvarGetLbGlobal(var) > 0.5) )
      {
         SCIP_CALL( applyImplic(blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand, eventqueue,
               eventfilter, cliquetable, implvar, impltype, implbound, infeasible, nbdchgs) );
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      /* implication added for x == 1:
       *   x == 1 && x =  1*z + 0  ==>  y <= b or y >= b    <==>    z >= 1  ==>  y <= b or y >= b
       *   x == 1 && x = -1*z + 1  ==>  y <= b or y >= b    <==>    z <= 0  ==>  y <= b or y >= b
       * implication added for x == 0:
       *   x == 0 && x =  1*z + 0  ==>  y <= b or y >= b    <==>    z <= 0  ==>  y <= b or y >= b
       *   x == 0 && x = -1*z + 1  ==>  y <= b or y >= b    <==>    z >= 1  ==>  y <= b or y >= b
       *
       * use only binary variables z
       */
      assert(var->data.aggregate.var != NULL);
      if( SCIPvarIsBinary(var->data.aggregate.var) )
      {
         assert( (SCIPsetIsEQ(set, var->data.aggregate.scalar, 1.0) && SCIPsetIsZero(set, var->data.aggregate.constant))
            || (SCIPsetIsEQ(set, var->data.aggregate.scalar, -1.0) && SCIPsetIsEQ(set, var->data.aggregate.constant, 1.0)) );

         if( var->data.aggregate.scalar > 0 )
         {
            SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                  cliquetable, branchcand, eventqueue, eventfilter, varfixing, implvar, impltype, implbound, transitive, infeasible,
                  nbdchgs) );
         }
         else
         {
            SCIP_CALL( SCIPvarAddImplic(var->data.aggregate.var, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                  cliquetable, branchcand, eventqueue, eventfilter, !varfixing, implvar, impltype, implbound, transitive, infeasible,
                  nbdchgs) );
         }
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      /* nothing to do here */
      break;

   case SCIP_VARSTATUS_NEGATED:
      /* implication added for x == 1:
       *   x == 1 && x = -1*z + 1  ==>  y <= b or y >= b    <==>    z <= 0  ==>  y <= b or y >= b
       * implication added for x == 0:
       *   x == 0 && x = -1*z + 1  ==>  y <= b or y >= b    <==>    z >= 1  ==>  y <= b or y >= b
       */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      assert(SCIPvarIsBinary(var->negatedvar));

      if( SCIPvarGetType(var->negatedvar) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(var->negatedvar) )
      {
         SCIP_CALL( SCIPvarAddImplic(var->negatedvar, blkmem, set, stat,  transprob, origprob, tree, reopt, lp,
               cliquetable, branchcand, eventqueue, eventfilter, !varfixing, implvar, impltype, implbound, transitive, infeasible, nbdchgs) );
      }
      /* in case one both variables are not of binary type we have to add the implication as variable bounds */
      else
      {
         /* if the implied variable is of binary type exchange the variables */
         if( SCIPvarGetType(implvar) == SCIP_VARTYPE_BINARY )
         {
            SCIP_CALL( SCIPvarAddImplic(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                  branchcand, eventqueue, eventfilter, (impltype == SCIP_BOUNDTYPE_UPPER) ? TRUE : FALSE, var->negatedvar,
                  varfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER, varfixing ? 1.0 : 0.0, transitive,
                  infeasible, nbdchgs) );
         }
         else
         {
            /* both variables are not of binary type but are implicit binary; in that case we can only add this
             * implication as variable bounds
             */

            /* add variable lower bound on the negation of var */
            if( varfixing )
            {
               /* (x = 1 => i) z = 0 ii) z = 1) <=> ( i) z = 1 ii) z = 0 => ~x = 1), this is done by adding ~x >= b*z + d
                * as variable lower bound
                */
               SCIP_CALL( SCIPvarAddVlb(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : -1.0,
                     (impltype == SCIP_BOUNDTYPE_UPPER) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
            }
            else
            {
               /* (x = 0 => i) z = 0 ii) z = 1) <=> ( i) z = 1 ii) z = 0 => ~x = 0), this is done by adding ~x <= b*z + d
                * as variable upper bound
                */
               SCIP_CALL( SCIPvarAddVub(var->negatedvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp,
                     cliquetable, branchcand, eventqueue, eventfilter, implvar, (impltype == SCIP_BOUNDTYPE_UPPER) ? -1.0 : 1.0,
                     (impltype == SCIP_BOUNDTYPE_UPPER) ? 1.0 : 0.0, transitive, infeasible, nbdchgs) );
            }

            /* add variable bound on implvar */
            if( impltype == SCIP_BOUNDTYPE_UPPER )
            {
               /* (z = 1 => i) x = 0 ii) x = 1) <=> ( i) ~x = 0 ii) ~x = 1 => z = 0), this is done by adding z <= b*~x + d
                * as variable upper bound
                */
               SCIP_CALL( SCIPvarAddVub(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                     branchcand, eventqueue, eventfilter, var->negatedvar, (varfixing) ? 1.0 : -1.0,
                     (varfixing) ? 0.0 : 1.0, transitive, infeasible, nbdchgs) );
            }
            else
            {
               /* (z = 0 => i) x = 0 ii) x = 1) <=> ( i) ~x = 0 ii) ~x = 1 => z = 1), this is done by adding z >= b*~x + d
                * as variable upper bound
                */
               SCIP_CALL( SCIPvarAddVlb(implvar, blkmem, set, stat, transprob, origprob, tree, reopt, lp, cliquetable,
                     branchcand, eventqueue, eventfilter, var->negatedvar, (varfixing) ? -1.0 : 1.0, (varfixing) ? 1.0 : 0.0,
                     transitive, infeasible, nbdchgs) );
            }
         }
      }
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** returns whether there is an implication x == varfixing -> y <= b or y >= b in the implication graph;
 *  implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
 *  both variables must be active, variable x must be binary
 */
SCIP_Bool SCIPvarHasImplic(
   SCIP_VAR*             var,                /**< problem variable x */
   SCIP_Bool             varfixing,          /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y to search for */
   SCIP_BOUNDTYPE        impltype            /**< type of implication y <=/>= b to search for */
   )
{
   assert(var != NULL);
   assert(implvar != NULL);
   assert(SCIPvarIsActive(var));
   assert(SCIPvarIsActive(implvar));
   assert(SCIPvarIsBinary(var));

   return var->implics != NULL && SCIPimplicsContainsImpl(var->implics, varfixing, implvar, impltype);
}

/** returns whether there is an implication x == varfixing -> y == implvarfixing in the implication graph;
 *  implications that are represented as cliques in the clique table are not regarded (use SCIPvarsHaveCommonClique());
 *  both variables must be active binary variables
 */
SCIP_Bool SCIPvarHasBinaryImplic(
   SCIP_VAR*             var,                /**< problem variable x */
   SCIP_Bool             varfixing,          /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y to search for */
   SCIP_Bool             implvarfixing       /**< value of the implied variable to search for */
   )
{
   assert(SCIPvarIsBinary(implvar));

   return SCIPvarHasImplic(var, varfixing, implvar, implvarfixing ? SCIP_BOUNDTYPE_LOWER : SCIP_BOUNDTYPE_UPPER);
}

/** gets the values of b in implications x == varfixing -> y <= b or y >= b in the implication graph;
 *  the values are set to SCIP_INVALID if there is no implied bound
 */
void SCIPvarGetImplicVarBounds(
   SCIP_VAR*             var,                /**< problem variable x */
   SCIP_Bool             varfixing,          /**< FALSE if y should be searched in implications for x == 0, TRUE for x == 1 */
   SCIP_VAR*             implvar,            /**< variable y to search for */
   SCIP_Real*            lb,                 /**< buffer to store the value of the implied lower bound */
   SCIP_Real*            ub                  /**< buffer to store the value of the implied upper bound */
   )
{
   int lowerpos;
   int upperpos;
   SCIP_Real* bounds;

   assert(lb != NULL);
   assert(ub != NULL);

   *lb = SCIP_INVALID;
   *ub = SCIP_INVALID;

   if( var->implics == NULL )
      return;

   SCIPimplicsGetVarImplicPoss(var->implics, varfixing, implvar, &lowerpos, &upperpos);
   bounds = SCIPvarGetImplBounds(var, varfixing);

   if( bounds == NULL )
      return;

   if( lowerpos >= 0 )
      *lb = bounds[lowerpos];

   if( upperpos >= 0 )
      *ub = bounds[upperpos];
}


/** fixes the bounds of a binary variable to the given value, counting bound changes and detecting infeasibility */
SCIP_RETCODE SCIPvarFixBinary(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Bool             value,              /**< value to fix variable to */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to count the number of performed bound changes, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(infeasible != NULL);

   *infeasible = FALSE;

   if( value == FALSE )
   {
      if( var->glbdom.lb > 0.5 )
         *infeasible = TRUE;
      else if( var->glbdom.ub > 0.5 )
      {
         /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
          * with the local bound, in this case we need to store the bound change as pending bound change
          */
         if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
         {
            assert(tree != NULL);
            assert(transprob != NULL);
            assert(SCIPprobIsTransformed(transprob));

            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, 0.0, SCIP_BOUNDTYPE_UPPER, FALSE) );
         }
         else
         {
            SCIP_CALL( SCIPvarChgUbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 0.0) );
         }

         if( nbdchgs != NULL )
            (*nbdchgs)++;
      }
   }
   else
   {
      if( var->glbdom.ub < 0.5 )
         *infeasible = TRUE;
      else if( var->glbdom.lb < 0.5 )
      {
         /* during solving stage it can happen that the global bound change cannot be applied directly because it conflicts
          * with the local bound, in this case we need to store the bound change as pending bound change
          */
         if( SCIPsetGetStage(set) >= SCIP_STAGE_SOLVING )
         {
            assert(tree != NULL);
            assert(transprob != NULL);
            assert(SCIPprobIsTransformed(transprob));

            SCIP_CALL( SCIPnodeAddBoundchg(SCIPtreeGetRootNode(tree), blkmem, set, stat, transprob, origprob,
                  tree, reopt, lp, branchcand, eventqueue, eventfilter, cliquetable, var, 1.0, SCIP_BOUNDTYPE_LOWER, FALSE) );
         }
         else
         {
            SCIP_CALL( SCIPvarChgLbGlobal(var, blkmem, set, stat, lp, branchcand, eventqueue, cliquetable, 1.0) );
         }

         if( nbdchgs != NULL )
            (*nbdchgs)++;
      }
   }

   return SCIP_OKAY;
}

/** adds the variable to the given clique and updates the list of cliques the binary variable is member of;
 *  if the variable now appears twice in the clique with the same value, it is fixed to the opposite value;
 *  if the variable now appears twice in the clique with opposite values, all other variables are fixed to
 *  the opposite of the value they take in the clique
 */
SCIP_RETCODE SCIPvarAddClique(
   SCIP_VAR*             var,                /**< problem variable  */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            transprob,          /**< transformed problem */
   SCIP_PROB*            origprob,           /**< original problem */
   SCIP_TREE*            tree,               /**< branch and bound tree if in solving stage */
   SCIP_REOPT*           reopt,              /**< reoptimization data structure */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_BRANCHCAND*      branchcand,         /**< branching candidate storage */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_EVENTFILTER*     eventfilter,        /**< global event filter */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Bool             value,              /**< value of the variable in the clique */
   SCIP_CLIQUE*          clique,             /**< clique the variable should be added to */
   SCIP_Bool*            infeasible,         /**< pointer to store whether an infeasibility was detected */
   int*                  nbdchgs             /**< pointer to count the number of performed bound changes, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(SCIPvarIsBinary(var));
   assert(infeasible != NULL);

   *infeasible = FALSE;

   /* get corresponding active problem variable */
   SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(SCIPvarIsBinary(var));

   /* only column and loose variables may be member of a clique */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      SCIP_Bool doubleentry;
      SCIP_Bool oppositeentry;

      /* add variable to clique */
      SCIP_CALL( SCIPcliqueAddVar(clique, blkmem, set, var, value, &doubleentry, &oppositeentry) );

      /* add clique to variable's clique list */
      SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );

      /* check consistency of cliquelist */
      SCIPcliquelistCheck(var->cliquelist, var);

      /* if the variable now appears twice with the same value in the clique, it can be fixed to the opposite value */
      if( doubleentry )
      {
         SCIP_CALL( SCIPvarFixBinary(var, blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
               eventqueue, eventfilter, cliquetable, !value, infeasible, nbdchgs) );
      }

      /* if the variable appears with both values in the clique, all other variables of the clique can be fixed
       * to the opposite of the value they take in the clique
       */
      if( oppositeentry )
      {
         SCIP_VAR** vars;
         SCIP_Bool* values;
         int nvars;
         int i;

         nvars = SCIPcliqueGetNVars(clique);
         vars = SCIPcliqueGetVars(clique);
         values = SCIPcliqueGetValues(clique);
         for( i = 0; i < nvars && !(*infeasible); ++i )
         {
            if( vars[i] == var )
               continue;

            SCIP_CALL( SCIPvarFixBinary(vars[i], blkmem, set, stat, transprob, origprob, tree, reopt, lp, branchcand,
                  eventqueue, eventfilter, cliquetable, !values[i], infeasible, nbdchgs) );
         }
      }
   }

   return SCIP_OKAY;
}

/** adds a filled clique to the cliquelists of all corresponding variables */
SCIP_RETCODE SCIPvarsAddClique(
   SCIP_VAR**            vars,               /**< problem variables */
   SCIP_Bool*            values,             /**< values of the variables in the clique */
   int                   nvars,              /**< number of problem variables */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_CLIQUE*          clique              /**< clique that contains all given variables and values */
   )
{
   SCIP_VAR* var;
   int v;

   assert(vars != NULL);
   assert(values != NULL);
   assert(nvars > 0);
   assert(set != NULL);
   assert(blkmem != NULL);
   assert(clique != NULL);

   for( v = nvars - 1; v >= 0; --v )
   {
      var = vars[v];
      assert(SCIPvarIsBinary(var));
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);

      /* add clique to variable's clique list */
      SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, values[v], clique) );

      /* check consistency of cliquelist */
      SCIPcliquelistCheck(var->cliquelist, var);
   }

   return SCIP_OKAY;
}

/** adds a clique to the list of cliques of the given binary variable, but does not change the clique
 *  itself
 */
SCIP_RETCODE SCIPvarAddCliqueToList(
   SCIP_VAR*             var,                /**< problem variable  */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             value,              /**< value of the variable in the clique */
   SCIP_CLIQUE*          clique              /**< clique that should be removed from the variable's clique list */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsBinary(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);

   /* add clique to variable's clique list */
   SCIP_CALL( SCIPcliquelistAdd(&var->cliquelist, blkmem, set, value, clique) );

   return SCIP_OKAY;
}


/** deletes a clique from the list of cliques the binary variable is member of, but does not change the clique
 *  itself
 */
SCIP_RETCODE SCIPvarDelCliqueFromList(
   SCIP_VAR*             var,                /**< problem variable  */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_Bool             value,              /**< value of the variable in the clique */
   SCIP_CLIQUE*          clique              /**< clique that should be removed from the variable's clique list */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsBinary(var));

   /* delete clique from variable's clique list */
   SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );

   return SCIP_OKAY;
}

/** deletes the variable from the given clique and updates the list of cliques the binary variable is member of */
SCIP_RETCODE SCIPvarDelClique(
   SCIP_VAR*             var,                /**< problem variable  */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_CLIQUETABLE*     cliquetable,        /**< clique table data structure */
   SCIP_Bool             value,              /**< value of the variable in the clique */
   SCIP_CLIQUE*          clique              /**< clique the variable should be removed from */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsBinary(var));

   /* get corresponding active problem variable */
   SCIP_CALL( SCIPvarGetProbvarBinary(&var, &value) );
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED
      || SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(SCIPvarIsBinary(var));

   /* only column and loose variables may be member of a clique */
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE )
   {
      /* delete clique from variable's clique list */
      SCIP_CALL( SCIPcliquelistDel(&var->cliquelist, blkmem, value, clique) );

      /* delete variable from clique */
      SCIPcliqueDelVar(clique, cliquetable, var, value);

      /* check consistency of cliquelist */
      SCIPcliquelistCheck(var->cliquelist, var);
   }

   return SCIP_OKAY;
}

/** returns whether there is a clique that contains both given variable/value pairs;
 *  the variables must be active binary variables;
 *  if regardimplics is FALSE, only the cliques in the clique table are looked at;
 *  if regardimplics is TRUE, both the cliques and the implications of the implication graph are regarded
 *
 *  @note a variable with it's negated variable are NOT! in a clique
 *  @note a variable with itself are in a clique
 */
SCIP_Bool SCIPvarsHaveCommonClique(
   SCIP_VAR*             var1,               /**< first variable */
   SCIP_Bool             value1,             /**< value of first variable */
   SCIP_VAR*             var2,               /**< second variable */
   SCIP_Bool             value2,             /**< value of second variable */
   SCIP_Bool             regardimplics       /**< should the implication graph also be searched for a clique? */
   )
{
   assert(var1 != NULL);
   assert(var2 != NULL);
   assert(SCIPvarIsActive(var1));
   assert(SCIPvarIsActive(var2));
   assert(SCIPvarIsBinary(var1));
   assert(SCIPvarIsBinary(var2));

   return (SCIPcliquelistsHaveCommonClique(var1->cliquelist, value1, var2->cliquelist, value2)
      || (regardimplics && SCIPvarHasImplic(var1, value1, var2, value2 ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER)));
}

/** actually changes the branch factor of the variable and of all parent variables */
static
SCIP_RETCODE varProcessChgBranchFactor(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             branchfactor        /**< factor to weigh variable's branching score with */
   )
{
   SCIP_VAR* parentvar;
   SCIP_Real eps;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* only use positive values */
   eps = SCIPsetEpsilon(set);
   branchfactor = MAX(branchfactor, eps);

   SCIPsetDebugMsg(set, "process changing branch factor of <%s> from %f to %f\n", var->name, var->branchfactor, branchfactor);

   if( SCIPsetIsEQ(set, branchfactor, var->branchfactor) )
      return SCIP_OKAY;

   /* change the branch factor */
   var->branchfactor = branchfactor;

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         /* do not change priorities across the border between transformed and original problem */
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/

      case SCIP_VARSTATUS_AGGREGATED:
      case SCIP_VARSTATUS_NEGATED:
         SCIP_CALL( varProcessChgBranchFactor(parentvar, set, branchfactor) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_ERROR; /*lint !e527*/
      }
   }

   return SCIP_OKAY;
}

/** sets the branch factor of the variable; this value can be used in the branching methods to scale the score
 *  values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
 */
SCIP_RETCODE SCIPvarChgBranchFactor(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             branchfactor        /**< factor to weigh variable's branching score with */
   )
{
   int v;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(branchfactor >= 0.0);

   SCIPdebugMessage("changing branch factor of <%s> from %g to %g\n", var->name, var->branchfactor, branchfactor);

   if( SCIPsetIsEQ(set, var->branchfactor, branchfactor) )
      return SCIP_OKAY;

   /* change priorities of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgBranchFactor(var->data.original.transvar, set, branchfactor) );
      }
      else
      {
         assert(set->stage == SCIP_STAGE_PROBLEM);
         var->branchfactor = branchfactor;
      }
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      SCIP_CALL( varProcessChgBranchFactor(var, set, branchfactor) );
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      SCIP_CALL( SCIPvarChgBranchFactor(var->data.aggregate.var, set, branchfactor) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      for( v = 0; v < var->data.multaggr.nvars; ++v )
      {
         SCIP_CALL( SCIPvarChgBranchFactor(var->data.multaggr.vars[v], set, branchfactor) );
      }
      break;

   case SCIP_VARSTATUS_NEGATED:
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgBranchFactor(var->negatedvar, set, branchfactor) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_ERROR; /*lint !e527*/
   }

   return SCIP_OKAY;
}

/** actually changes the branch priority of the variable and of all parent variables */
static
SCIP_RETCODE varProcessChgBranchPriority(
   SCIP_VAR*             var,                /**< problem variable */
   int                   branchpriority      /**< branching priority of the variable */
   )
{
   SCIP_VAR* parentvar;
   int i;

   assert(var != NULL);

   SCIPdebugMessage("process changing branch priority of <%s> from %d to %d\n",
      var->name, var->branchpriority, branchpriority);

   if( branchpriority == var->branchpriority )
      return SCIP_OKAY;

   /* change the branch priority */
   var->branchpriority = branchpriority;

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         /* do not change priorities across the border between transformed and original problem */
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/

      case SCIP_VARSTATUS_AGGREGATED:
      case SCIP_VARSTATUS_NEGATED:
         SCIP_CALL( varProcessChgBranchPriority(parentvar, branchpriority) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_ERROR;
      }
   }

   return SCIP_OKAY;
}

/** sets the branch priority of the variable; variables with higher branch priority are always preferred to variables
 *  with lower priority in selection of branching variable
 */
SCIP_RETCODE SCIPvarChgBranchPriority(
   SCIP_VAR*             var,                /**< problem variable */
   int                   branchpriority      /**< branching priority of the variable */
   )
{
   int v;

   assert(var != NULL);

   SCIPdebugMessage("changing branch priority of <%s> from %d to %d\n", var->name, var->branchpriority, branchpriority);

   if( var->branchpriority == branchpriority )
      return SCIP_OKAY;

   /* change priorities of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgBranchPriority(var->data.original.transvar, branchpriority) );
      }
      else
         var->branchpriority = branchpriority;
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      SCIP_CALL( varProcessChgBranchPriority(var, branchpriority) );
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      SCIP_CALL( SCIPvarChgBranchPriority(var->data.aggregate.var, branchpriority) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      for( v = 0; v < var->data.multaggr.nvars; ++v )
      {
         SCIP_CALL( SCIPvarChgBranchPriority(var->data.multaggr.vars[v], branchpriority) );
      }
      break;

   case SCIP_VARSTATUS_NEGATED:
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgBranchPriority(var->negatedvar, branchpriority) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_ERROR; /*lint !e527*/
   }

   return SCIP_OKAY;
}

/** actually changes the branch direction of the variable and of all parent variables */
static
SCIP_RETCODE varProcessChgBranchDirection(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        branchdirection     /**< preferred branch direction of the variable (downwards, upwards, auto) */
   )
{
   SCIP_VAR* parentvar;
   int i;

   assert(var != NULL);

   SCIPdebugMessage("process changing branch direction of <%s> from %u to %d\n",
      var->name, var->branchdirection, branchdirection);

   if( branchdirection == (SCIP_BRANCHDIR)var->branchdirection )
      return SCIP_OKAY;

   /* change the branch direction */
   var->branchdirection = branchdirection; /*lint !e641*/

   /* process parent variables */
   for( i = 0; i < var->nparentvars; ++i )
   {
      parentvar = var->parentvars[i];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         /* do not change directions across the border between transformed and original problem */
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/

      case SCIP_VARSTATUS_AGGREGATED:
         if( parentvar->data.aggregate.scalar > 0.0 )
         {
            SCIP_CALL( varProcessChgBranchDirection(parentvar, branchdirection) );
         }
         else
         {
            SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
         }
         break;

      case SCIP_VARSTATUS_NEGATED:
         SCIP_CALL( varProcessChgBranchDirection(parentvar, SCIPbranchdirOpposite(branchdirection)) );
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_ERROR; /*lint !e527*/
      }
   }

   return SCIP_OKAY;
}

/** sets the branch direction of the variable; variables with higher branch direction are always preferred to variables
 *  with lower direction in selection of branching variable
 */
SCIP_RETCODE SCIPvarChgBranchDirection(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        branchdirection     /**< preferred branch direction of the variable (downwards, upwards, auto) */
   )
{
   int v;

   assert(var != NULL);

   SCIPdebugMessage("changing branch direction of <%s> from %u to %d\n", var->name, var->branchdirection, branchdirection);

   if( (SCIP_BRANCHDIR)var->branchdirection == branchdirection )
      return SCIP_OKAY;

   /* change directions of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar != NULL )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var->data.original.transvar, branchdirection) );
      }
      else
         var->branchdirection = branchdirection; /*lint !e641*/
      break;

   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      SCIP_CALL( varProcessChgBranchDirection(var, branchdirection) );
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, branchdirection) );
      }
      else
      {
         SCIP_CALL( SCIPvarChgBranchDirection(var->data.aggregate.var, SCIPbranchdirOpposite(branchdirection)) );
      }
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      for( v = 0; v < var->data.multaggr.nvars; ++v )
      {
         /* only update branching direction of aggregation variables, if they don't have a preferred direction yet */
         assert(var->data.multaggr.vars[v] != NULL);
         if( (SCIP_BRANCHDIR)var->data.multaggr.vars[v]->branchdirection == SCIP_BRANCHDIR_AUTO )
         {
            if( var->data.multaggr.scalars[v] > 0.0 )
            {
               SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], branchdirection) );
            }
            else
            {
               SCIP_CALL( SCIPvarChgBranchDirection(var->data.multaggr.vars[v], SCIPbranchdirOpposite(branchdirection)) );
            }
         }
      }
      break;

   case SCIP_VARSTATUS_NEGATED:
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarChgBranchDirection(var->negatedvar, SCIPbranchdirOpposite(branchdirection)) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_ERROR; /*lint !e527*/
   }

   return SCIP_OKAY;
}

/** compares the index of two variables, only active, fixed or negated variables are allowed, if a variable
 *  is negated then the index of the corresponding active variable is taken, returns -1 if first is
 *  smaller than, and +1 if first is greater than second variable index; returns 0 if both indices
 *  are equal, which means both variables are equal
 */
int SCIPvarCompareActiveAndNegated(
   SCIP_VAR*             var1,               /**< first problem variable */
   SCIP_VAR*             var2                /**< second problem variable */
   )
{
   assert(var1 != NULL);
   assert(var2 != NULL);
   assert(SCIPvarIsActive(var1) || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_FIXED);
   assert(SCIPvarIsActive(var2) || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_FIXED);

   if( SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED )
      var1 = SCIPvarGetNegatedVar(var1);
   if( SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED )
      var2 = SCIPvarGetNegatedVar(var2);

   assert(var1 != NULL);
   assert(var2 != NULL);

   if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) )
      return -1;
   else if( SCIPvarGetIndex(var1) > SCIPvarGetIndex(var2) )
      return +1;

   assert(var1 == var2);
   return 0;
}

/** comparison method for sorting active and negated variables by non-decreasing index, active and negated
 *  variables are handled as the same variables
 */
SCIP_DECL_SORTPTRCOMP(SCIPvarCompActiveAndNegated)
{
   return SCIPvarCompareActiveAndNegated((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
}

/** compares the index of two variables, returns -1 if first is smaller than, and +1 if first is greater than second
 *  variable index; returns 0 if both indices are equal, which means both variables are equal
 */
int SCIPvarCompare(
   SCIP_VAR*             var1,               /**< first problem variable */
   SCIP_VAR*             var2                /**< second problem variable */
   )
{
   assert(var1 != NULL);
   assert(var2 != NULL);

   if( var1->index < var2->index )
      return -1;
   else if( var1->index > var2->index )
      return +1;
   else
   {
      assert(var1 == var2);
      return 0;
   }
}

/** comparison method for sorting variables by non-decreasing index */
SCIP_DECL_SORTPTRCOMP(SCIPvarComp)
{
   return SCIPvarCompare((SCIP_VAR*)elem1, (SCIP_VAR*)elem2);
}

/** comparison method for sorting variables by non-decreasing objective coefficient */
SCIP_DECL_SORTPTRCOMP(SCIPvarCompObj)
{
   SCIP_Real obj1;
   SCIP_Real obj2;

   obj1 = SCIPvarGetObj((SCIP_VAR*)elem1);
   obj2 = SCIPvarGetObj((SCIP_VAR*)elem2);

   if( obj1 < obj2 )
      return -1;
   else if( obj1 > obj2 )
      return +1;
   else
      return 0;
}

/** hash key retrieval function for variables */
SCIP_DECL_HASHGETKEY(SCIPvarGetHashkey)
{  /*lint --e{715}*/
   return elem;
}

/** returns TRUE iff the indices of both variables are equal */
SCIP_DECL_HASHKEYEQ(SCIPvarIsHashkeyEq)
{  /*lint --e{715}*/
   if( key1 == key2 )
      return TRUE;
   return FALSE;
}

/** returns the hash value of the key */
SCIP_DECL_HASHKEYVAL(SCIPvarGetHashkeyVal)
{  /*lint --e{715}*/
   assert( SCIPvarGetIndex((SCIP_VAR*) key) >= 0 );
   return (unsigned int) SCIPvarGetIndex((SCIP_VAR*) key);
}

/** return for given variables all their active counterparts; all active variables will be pairwise different */
SCIP_RETCODE SCIPvarsGetActiveVars(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_VAR**            vars,               /**< variable array with given variables and as output all active
                                              *   variables, if enough slots exist
                                              */
   int*                  nvars,              /**< number of given variables, and as output number of active variables,
                                              *   if enough slots exist
                                              */
   int                   varssize,           /**< available slots in vars array */
   int*                  requiredsize        /**< pointer to store the required array size for the active variables */
   )
{
   SCIP_VAR** activevars;
   int nactivevars;
   int activevarssize;

   SCIP_VAR* var;
   int v;

   SCIP_VAR** tmpvars;
   SCIP_VAR** multvars;
   int tmpvarssize;
   int ntmpvars;
   int noldtmpvars;
   int nmultvars;

   assert(set != NULL);
   assert(nvars != NULL);
   assert(vars != NULL || *nvars == 0);
   assert(varssize >= *nvars);
   assert(requiredsize != NULL);

   *requiredsize = 0;

   if( *nvars == 0 )
      return SCIP_OKAY;

   nactivevars = 0;
   activevarssize = *nvars;
   ntmpvars = *nvars;
   tmpvarssize = *nvars;

   /* temporary memory */
   SCIP_CALL( SCIPsetAllocBufferArray(set, &activevars, activevarssize) );
   /* coverity[copy_paste_error] */
   SCIP_CALL( SCIPsetDuplicateBufferArray(set, &tmpvars, vars, ntmpvars) );

   noldtmpvars = ntmpvars;

   /* sort all variables to combine equal variables easily */
   SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
   for( v = ntmpvars - 1; v > 0; --v )
   {
      /* combine same variables */
      if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
      {
         --ntmpvars;
         tmpvars[v] = tmpvars[ntmpvars];
      }
   }
   /* sort all variables again to combine equal variables later on */
   if( noldtmpvars > ntmpvars )
      SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);

   /* collect for each variable the representation in active variables */
   while( ntmpvars >= 1 )
   {
      --ntmpvars;
      var = tmpvars[ntmpvars];
      assert( var != NULL );

      switch( SCIPvarGetStatus(var) )
      {
         case SCIP_VARSTATUS_ORIGINAL:
            if( var->data.original.transvar == NULL )
            {
               SCIPerrorMessage("original variable has no transformed variable attached\n");
               SCIPABORT();
               return SCIP_INVALIDDATA; /*lint !e527*/
            }
            tmpvars[ntmpvars] = var->data.original.transvar;
            ++ntmpvars;
            break;

         case SCIP_VARSTATUS_AGGREGATED:
            tmpvars[ntmpvars] = var->data.aggregate.var;
            ++ntmpvars;
            break;

         case SCIP_VARSTATUS_NEGATED:
            tmpvars[ntmpvars] = var->negatedvar;
            ++ntmpvars;
            break;

         case SCIP_VARSTATUS_LOOSE:
         case SCIP_VARSTATUS_COLUMN:
            /* check for space in temporary memory */
            if( nactivevars >= activevarssize )
            {
               activevarssize *= 2;
               SCIP_CALL( SCIPsetReallocBufferArray(set, &activevars, activevarssize) );
               assert(nactivevars < activevarssize);
            }
            activevars[nactivevars] = var;
            nactivevars++;
            break;

         case SCIP_VARSTATUS_MULTAGGR:
            /* x = a_1*y_1 + ... + a_n*y_n + c */
            nmultvars = var->data.multaggr.nvars;
            multvars = var->data.multaggr.vars;

            /* check for space in temporary memory */
            if( nmultvars + ntmpvars > tmpvarssize )
            {
               while( nmultvars + ntmpvars > tmpvarssize )
                  tmpvarssize *= 2;
               SCIP_CALL( SCIPsetReallocBufferArray(set, &tmpvars, tmpvarssize) );
               assert(nmultvars + ntmpvars <= tmpvarssize);
            }

            /* copy all multi-aggregation variables into our working array */
            BMScopyMemoryArray(&tmpvars[ntmpvars], multvars, nmultvars); /*lint !e866*/

            /* get active, fixed or multi-aggregated corresponding variables for all new ones */
            SCIPvarsGetProbvar(&tmpvars[ntmpvars], nmultvars);

            ntmpvars += nmultvars;
            noldtmpvars = ntmpvars;

            /* sort all variables to combine equal variables easily */
            SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);
            for( v = ntmpvars - 1; v > 0; --v )
            {
               /* combine same variables */
               if( SCIPvarCompare(tmpvars[v], tmpvars[v - 1]) == 0 )
               {
                  --ntmpvars;
                  tmpvars[v] = tmpvars[ntmpvars];
               }
            }
            /* sort all variables again to combine equal variables later on */
            if( noldtmpvars > ntmpvars )
               SCIPsortPtr((void**)tmpvars, SCIPvarComp, ntmpvars);

            break;

         case SCIP_VARSTATUS_FIXED:
            /* no need for memorizing fixed variables */
            break;

         default:
            SCIPerrorMessage("unknown variable status\n");
            SCIPABORT();
            return SCIP_INVALIDDATA; /*lint !e527*/
      }
   }

   /* sort variable array by variable index */
   SCIPsortPtr((void**)activevars, SCIPvarComp, nactivevars);

   /* eliminate duplicates and count required size */
   v = nactivevars - 1;
   while( v > 0 )
   {
      /* combine both variable since they are the same */
      if( SCIPvarCompare(activevars[v - 1], activevars[v]) == 0 )
      {
         --nactivevars;
         activevars[v] = activevars[nactivevars];
      }
      --v;
   }
   *requiredsize = nactivevars;

   if( varssize >= *requiredsize )
   {
      assert(vars != NULL);

      *nvars = *requiredsize;
      BMScopyMemoryArray(vars, activevars, nactivevars);
   }

   SCIPsetFreeBufferArray(set, &tmpvars);
   SCIPsetFreeBufferArray(set, &activevars);

   return SCIP_OKAY;
}

/** gets corresponding active, fixed, or multi-aggregated problem variables of given variables,
 *  @note the content of the given array will/might change
 */
void SCIPvarsGetProbvar(
   SCIP_VAR**            vars,               /**< array of problem variables */
   int                   nvars               /**< number of variables */
   )
{
   int v;

   assert(vars != NULL || nvars == 0);

   for( v = nvars - 1; v >= 0; --v )
   {
      assert(vars != NULL);
      assert(vars[v] != NULL);

      vars[v] = SCIPvarGetProbvar(vars[v]);
      assert(vars[v] != NULL);
   }
}

/** gets corresponding active, fixed, or multi-aggregated problem variable of a variable */
SCIP_VAR* SCIPvarGetProbvar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* retvar;

   assert(var != NULL);

   retvar = var;

   SCIPdebugMessage("get problem variable of <%s>\n", var->name);

   while( TRUE ) /*lint !e716 */
   {
      assert(retvar != NULL);

      switch( SCIPvarGetStatus(retvar) )
      {
         case SCIP_VARSTATUS_ORIGINAL:
            if( retvar->data.original.transvar == NULL )
            {
               SCIPerrorMessage("original variable has no transformed variable attached\n");
               SCIPABORT();
               return NULL; /*lint !e527 */
            }
            retvar = retvar->data.original.transvar;
            break;

         case SCIP_VARSTATUS_LOOSE:
         case SCIP_VARSTATUS_COLUMN:
         case SCIP_VARSTATUS_FIXED:
            return retvar;

         case SCIP_VARSTATUS_MULTAGGR:
            /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
            if ( retvar->data.multaggr.nvars == 1 )
               retvar = retvar->data.multaggr.vars[0];
            else
               return retvar;
            break;

         case SCIP_VARSTATUS_AGGREGATED:
            retvar = retvar->data.aggregate.var;
            break;

         case SCIP_VARSTATUS_NEGATED:
            retvar = retvar->negatedvar;
            break;

         default:
            SCIPerrorMessage("unknown variable status\n");
            SCIPABORT();
            return NULL; /*lint !e527*/
      }
   }
}

/** gets corresponding active, fixed, or multi-aggregated problem variables of binary variables and updates the given
 *  negation status of each variable
 */
SCIP_RETCODE SCIPvarsGetProbvarBinary(
   SCIP_VAR***           vars,               /**< pointer to binary problem variables */
   SCIP_Bool**           negatedarr,         /**< pointer to corresponding array to update the negation status */
   int                   nvars               /**< number of variables and values in vars and negated array */
   )
{
   SCIP_VAR** var;
   SCIP_Bool* negated;
   int v;

   assert(vars != NULL);
   assert(*vars != NULL || nvars == 0);
   assert(negatedarr != NULL);
   assert(*negatedarr != NULL || nvars == 0);

   for( v = nvars - 1; v >= 0; --v )
   {
      var = &((*vars)[v]);
      negated = &((*negatedarr)[v]);

      /* get problem variable */
      SCIP_CALL( SCIPvarGetProbvarBinary(var, negated) );
   }

   return SCIP_OKAY;
}


/** gets corresponding active, fixed, or multi-aggregated problem variable of a binary variable and updates the given
 *  negation status (this means you have to assign a value to SCIP_Bool negated before calling this method, usually
 *  FALSE is used)
 */
SCIP_RETCODE SCIPvarGetProbvarBinary(
   SCIP_VAR**            var,                /**< pointer to binary problem variable */
   SCIP_Bool*            negated             /**< pointer to update the negation status */
   )
{
   SCIP_Bool active = FALSE;
#ifndef NDEBUG
   SCIP_Real constant = 0.0;
   SCIP_Bool orignegated;
#endif

   assert(var != NULL);
   assert(*var != NULL);
   assert(negated != NULL);
   assert(SCIPvarIsBinary(*var));

#ifndef NDEBUG
   orignegated = *negated;
#endif

   while( !active && *var != NULL )
   {
      switch( SCIPvarGetStatus(*var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( (*var)->data.original.transvar == NULL )
            return SCIP_OKAY;
         *var = (*var)->data.original.transvar;
         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_FIXED:
         active = TRUE;
         break;

      case SCIP_VARSTATUS_MULTAGGR:
         /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
         if ( (*var)->data.multaggr.nvars == 1 )
         {
            assert( (*var)->data.multaggr.vars != NULL );
            assert( (*var)->data.multaggr.scalars != NULL );
            assert( SCIPvarIsBinary((*var)->data.multaggr.vars[0]) );
            assert(!EPSZ((*var)->data.multaggr.scalars[0], 1e-06));

            /* if not all variables were fully propagated, it might happen that a variable is multi-aggregated to
             * another variable which needs to be fixed
             *
             * e.g. x = y - 1 => (x = 0 && y = 1)
             * e.g. x = y + 1 => (x = 1 && y = 0)
             *
             * is this special case we need to return the muti-aggregation
             */
            if( EPSEQ((*var)->data.multaggr.constant, -1.0, 1e-06) || (EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06) && EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06)) )
            {
               assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06));
            }
            else
            {
               /* @note due to fixations, a multi-aggregation can have a constant of zero and a negative scalar or even
                *       a scalar in absolute value unequal to one, in this case this aggregation variable needs to be
                *       fixed to zero, but this should be done by another enforcement; so not depending on the scalar,
                *       we will return the aggregated variable;
                */
               if( !EPSEQ(REALABS((*var)->data.multaggr.scalars[0]), 1.0, 1e-06) )
               {
                  active = TRUE;
                  break;
               }

               /* @note it may also happen that the constant is larger than 1 or smaller than 0, in that case the
                *       aggregation variable needs to be fixed to one, but this should be done by another enforcement;
                *       so if this is the case, we will return the aggregated variable
                */
               assert(EPSZ((*var)->data.multaggr.constant, 1e-06) || EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06)
                  || EPSZ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1e-06)
                  || EPSEQ((*var)->data.multaggr.constant + (*var)->data.multaggr.scalars[0], 1.0, 1e-06));

               if( !EPSZ((*var)->data.multaggr.constant, 1e-06) && !EPSEQ((*var)->data.multaggr.constant, 1.0, 1e-06) )
               {
                  active = TRUE;
                  break;
               }

               assert(EPSEQ((*var)->data.multaggr.scalars[0], 1.0, 1e-06) || EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));

               if( EPSZ((*var)->data.multaggr.constant, 1e-06) )
               {
                  /* if the scalar is negative, either the aggregation variable is already fixed to zero or has at
                   * least one uplock (that hopefully will enforce this fixation to zero); can it happen that this
                   * variable itself is multi-aggregated again?
                   */
                  assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06) ?
                     ((SCIPvarGetUbGlobal((*var)->data.multaggr.vars[0]) < 0.5) ||
                        SCIPvarGetNLocksUpType((*var)->data.multaggr.vars[0], SCIP_LOCKTYPE_MODEL) > 0) : TRUE);
               }
               else
               {
                  assert(EPSEQ((*var)->data.multaggr.scalars[0], -1.0, 1e-06));
#ifndef NDEBUG
                  constant += (*negated) != orignegated ? -1.0 : 1.0;
#endif

                  *negated = !(*negated);
               }
               *var = (*var)->data.multaggr.vars[0];
               break;
            }
         }
         active = TRUE;  /*lint !e838*/
         break;

      case SCIP_VARSTATUS_AGGREGATED:  /* x = a'*x' + c'  =>  a*x + c == (a*a')*x' + (a*c' + c) */
         assert((*var)->data.aggregate.var != NULL);
         assert(EPSEQ((*var)->data.aggregate.scalar, 1.0, 1e-06) || EPSEQ((*var)->data.aggregate.scalar, -1.0, 1e-06));
         assert(EPSLE((*var)->data.aggregate.var->glbdom.ub - (*var)->data.aggregate.var->glbdom.lb, 1.0, 1e-06));
#ifndef NDEBUG
         constant += (*negated) != orignegated ? -(*var)->data.aggregate.constant : (*var)->data.aggregate.constant;
#endif

         *negated = ((*var)->data.aggregate.scalar > 0.0) ? *negated : !(*negated);
         *var = (*var)->data.aggregate.var;
         break;

      case SCIP_VARSTATUS_NEGATED:     /* x =  - x' + c'  =>  a*x + c ==   (-a)*x' + (a*c' + c) */
         assert((*var)->negatedvar != NULL);
#ifndef NDEBUG
         constant += (*negated) != orignegated ? -1.0 : 1.0;
#endif

         *negated = !(*negated);
         *var = (*var)->negatedvar;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }
   }
   assert(active == (*var != NULL));

   if( active )
   {
      assert(SCIPvarIsBinary(*var));
      assert(EPSZ(constant, 1e-06) || EPSEQ(constant, 1.0, 1e-06));
      assert(EPSZ(constant, 1e-06) == ((*negated) == orignegated));

      return SCIP_OKAY;
   }
   else
   {
      SCIPerrorMessage("active variable path leads to NULL pointer\n");
      return SCIP_INVALIDDATA;
   }
}

/** transforms given variable, boundtype and bound to the corresponding active, fixed, or multi-aggregated variable
 *  values
 */
SCIP_RETCODE SCIPvarGetProbvarBound(
   SCIP_VAR**            var,                /**< pointer to problem variable */
   SCIP_Real*            bound,              /**< pointer to bound value to transform */
   SCIP_BOUNDTYPE*       boundtype           /**< pointer to type of bound: lower or upper bound */
   )
{
   assert(var != NULL);
   assert(*var != NULL);
   assert(bound != NULL);
   assert(boundtype != NULL);

   SCIPdebugMessage("get probvar bound %g of type %d of variable <%s>\n", *bound, *boundtype, (*var)->name);

   switch( SCIPvarGetStatus(*var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( (*var)->data.original.transvar == NULL )
      {
         SCIPerrorMessage("original variable has no transformed variable attached\n");
         return SCIP_INVALIDDATA;
      }
      *var = (*var)->data.original.transvar;
      SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
      if ( (*var)->data.multaggr.nvars == 1 )
      {
         assert( (*var)->data.multaggr.vars != NULL );
         assert( (*var)->data.multaggr.scalars != NULL );
         assert( (*var)->data.multaggr.scalars[0] != 0.0 );

         (*bound) /= (*var)->data.multaggr.scalars[0];
         (*bound) -= (*var)->data.multaggr.constant/(*var)->data.multaggr.scalars[0];
         if ( (*var)->data.multaggr.scalars[0] < 0.0 )
         {
            if ( *boundtype == SCIP_BOUNDTYPE_LOWER )
               *boundtype = SCIP_BOUNDTYPE_UPPER;
            else
               *boundtype = SCIP_BOUNDTYPE_LOWER;
         }
         *var = (*var)->data.multaggr.vars[0];
         SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED:  /* x = a*y + c  ->  y = x/a - c/a */
      assert((*var)->data.aggregate.var != NULL);
      assert((*var)->data.aggregate.scalar != 0.0);

      (*bound) /= (*var)->data.aggregate.scalar;
      (*bound) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
      if( (*var)->data.aggregate.scalar < 0.0 )
      {
         if( *boundtype == SCIP_BOUNDTYPE_LOWER )
            *boundtype = SCIP_BOUNDTYPE_UPPER;
         else
            *boundtype = SCIP_BOUNDTYPE_LOWER;
      }
      *var = (*var)->data.aggregate.var;
      SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
      break;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert((*var)->negatedvar != NULL);
      assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert((*var)->negatedvar->negatedvar == *var);
      (*bound) = (*var)->data.negate.constant - *bound;
      if( *boundtype == SCIP_BOUNDTYPE_LOWER )
         *boundtype = SCIP_BOUNDTYPE_UPPER;
      else
         *boundtype = SCIP_BOUNDTYPE_LOWER;
      *var = (*var)->negatedvar;
      SCIP_CALL( SCIPvarGetProbvarBound(var, bound, boundtype) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** transforms given variable, boundtype and exact bound to the corresponding active, fixed, or multi-aggregated variable
 *  values
 */
SCIP_RETCODE SCIPvarGetProbvarBoundExact(
   SCIP_VAR**            var,                /**< pointer to problem variable */
   SCIP_RATIONAL*        bound,              /**< pointer to bound value to transform */
   SCIP_BOUNDTYPE*       boundtype           /**< pointer to type of bound: lower or upper bound */
   )
{
   assert(var != NULL);
   assert(*var != NULL);
   assert(bound != NULL);
   assert(boundtype != NULL);

   SCIPrationalDebugMessage("get probvar bound %q of type %d of variable <%s>\n", bound, *boundtype, (*var)->name);

   switch( SCIPvarGetStatusExact(*var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( (*var)->data.original.transvar == NULL )
      {
         SCIPerrorMessage("original variable has no transformed variable attached\n");
         return SCIP_INVALIDDATA;
      }
      *var = (*var)->data.original.transvar;
      SCIP_CALL( SCIPvarGetProbvarBoundExact(var, bound, boundtype) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
      if( (*var)->data.multaggr.nvars == 1 )
      {
         assert( (*var)->data.multaggr.vars != NULL );
         assert( (*var)->data.multaggr.scalars != NULL );
         assert( (*var)->data.multaggr.scalars[0] != 0.0 );

         SCIPrationalDiff(bound, bound, (*var)->exactdata->multaggr.constant);
         SCIPrationalDiv(bound, bound, (*var)->exactdata->multaggr.scalars[0]);

         if( SCIPrationalIsNegative((*var)->exactdata->multaggr.scalars[0]) )
         {
            if ( *boundtype == SCIP_BOUNDTYPE_LOWER )
               *boundtype = SCIP_BOUNDTYPE_UPPER;
            else
               *boundtype = SCIP_BOUNDTYPE_LOWER;
         }
         *var = (*var)->data.multaggr.vars[0];
         SCIP_CALL( SCIPvarGetProbvarBoundExact(var, bound, boundtype) );
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED:  /* x = a*y + c  ->  y = x/a - c/a */
      assert((*var)->data.aggregate.var != NULL);
      assert((*var)->data.aggregate.scalar != 0.0);

      SCIPrationalDiff(bound, bound, (*var)->exactdata->aggregate.constant);
      SCIPrationalDiv(bound, bound, (*var)->exactdata->aggregate.scalar);

      if( SCIPrationalIsNegative((*var)->exactdata->aggregate.scalar) )
      {
         if( *boundtype == SCIP_BOUNDTYPE_LOWER )
            *boundtype = SCIP_BOUNDTYPE_UPPER;
         else
            *boundtype = SCIP_BOUNDTYPE_LOWER;
      }
      *var = (*var)->data.aggregate.var;
      SCIP_CALL( SCIPvarGetProbvarBoundExact(var, bound, boundtype) );
      break;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert((*var)->negatedvar != NULL);
      assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert((*var)->negatedvar->negatedvar == *var);
      SCIPrationalDiffReal(bound, bound, (*var)->data.negate.constant);
      SCIPrationalNegate(bound, bound);
      if( *boundtype == SCIP_BOUNDTYPE_LOWER )
         *boundtype = SCIP_BOUNDTYPE_UPPER;
      else
         *boundtype = SCIP_BOUNDTYPE_LOWER;
      *var = (*var)->negatedvar;
      SCIP_CALL( SCIPvarGetProbvarBoundExact(var, bound, boundtype) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** transforms given variable and domain hole to the corresponding active, fixed, or multi-aggregated variable
 *  values
 */
SCIP_RETCODE SCIPvarGetProbvarHole(
   SCIP_VAR**            var,                /**< pointer to problem variable */
   SCIP_Real*            left,               /**< pointer to left bound of open interval in hole to transform */
   SCIP_Real*            right               /**< pointer to right bound of open interval in hole to transform */
   )
{
   assert(var != NULL);
   assert(*var != NULL);
   assert(left != NULL);
   assert(right != NULL);

   SCIPdebugMessage("get probvar hole (%g,%g) of variable <%s>\n", *left, *right, (*var)->name);

   switch( SCIPvarGetStatus(*var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( (*var)->data.original.transvar == NULL )
      {
         SCIPerrorMessage("original variable has no transformed variable attached\n");
         return SCIP_INVALIDDATA;
      }
      *var = (*var)->data.original.transvar;
      SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   case SCIP_VARSTATUS_FIXED:
   case SCIP_VARSTATUS_MULTAGGR:
      break;

   case SCIP_VARSTATUS_AGGREGATED:  /* x = a*y + c  ->  y = x/a - c/a */
      assert((*var)->data.aggregate.var != NULL);
      assert((*var)->data.aggregate.scalar != 0.0);

      /* scale back */
      (*left) /= (*var)->data.aggregate.scalar;
      (*right) /= (*var)->data.aggregate.scalar;

      /* shift back */
      (*left) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;
      (*right) -= (*var)->data.aggregate.constant/(*var)->data.aggregate.scalar;

      *var = (*var)->data.aggregate.var;

      /* check if the  interval bounds have to swapped */
      if( (*var)->data.aggregate.scalar < 0.0 )
      {
         SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
      }
      else
      {
         SCIP_CALL( SCIPvarGetProbvarHole(var, left, right) );
      }
      break;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert((*var)->negatedvar != NULL);
      assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert((*var)->negatedvar->negatedvar == *var);

      /* shift and scale back */
      (*left) = (*var)->data.negate.constant - (*left);
      (*right) = (*var)->data.negate.constant - (*right);

      *var = (*var)->negatedvar;

      /* through the negated variable the left and right interval bound have to swapped */
      SCIP_CALL( SCIPvarGetProbvarHole(var, right, left) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** transforms given variable, scalar and constant to the corresponding active, fixed, or
 *  multi-aggregated variable, scalar and constant; if the variable resolves to a fixed variable,
 *  "scalar" will be 0.0 and the value of the sum will be stored in "constant"; a multi-aggregation
 *  with only one active variable (this can happen due to fixings after the multi-aggregation),
 *  is treated like an aggregation; if the multi-aggregation constant is infinite, "scalar" will be 0.0
 */
SCIP_RETCODE SCIPvarGetProbvarSum(
   SCIP_VAR**            var,                /**< pointer to problem variable x in sum a*x + c */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real*            scalar,             /**< pointer to scalar a in sum a*x + c */
   SCIP_Real*            constant            /**< pointer to constant c in sum a*x + c */
   )
{
   assert(var != NULL);
   assert(scalar != NULL);
   assert(constant != NULL);

   while( *var != NULL )
   {
      switch( SCIPvarGetStatus(*var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( (*var)->data.original.transvar == NULL )
         {
            SCIPerrorMessage("original variable has no transformed variable attached\n");
            return SCIP_INVALIDDATA;
         }
         *var = (*var)->data.original.transvar;
         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         return SCIP_OKAY;

      case SCIP_VARSTATUS_FIXED:       /* x = c'          =>  a*x + c ==             (a*c' + c) */
         if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
         {
            if( SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)) )
            {
               assert(*scalar != 0.0);
               if( (*scalar) * (*var)->glbdom.lb > 0.0 )
                  (*constant) = SCIPsetInfinity(set);
               else
                  (*constant) = -SCIPsetInfinity(set);
            }
            else
               (*constant) += *scalar * (*var)->glbdom.lb;
         }
#ifndef NDEBUG
         else
         {
            assert(!SCIPsetIsInfinity(set, (*constant)) || !((*scalar) * (*var)->glbdom.lb < 0.0 &&
                  (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
            assert(!SCIPsetIsInfinity(set, -(*constant)) || !((*scalar) * (*var)->glbdom.lb > 0.0 &&
                  (SCIPsetIsInfinity(set, (*var)->glbdom.lb) || SCIPsetIsInfinity(set, -((*var)->glbdom.lb)))));
         }
#endif
         *scalar = 0.0;
         return SCIP_OKAY;

      case SCIP_VARSTATUS_MULTAGGR:
         /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
         if ( (*var)->data.multaggr.nvars == 1 )
         {
            assert((*var)->data.multaggr.vars != NULL);
            assert((*var)->data.multaggr.scalars != NULL);
            assert((*var)->data.multaggr.vars[0] != NULL);
            if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
            {
               /* the multi-aggregation constant can be infinite, if one of the multi-aggregation variables
                * was fixed to +/-infinity; ensure that the constant is set to +/-infinity, too, and the scalar
                * is set to 0.0, because the multi-aggregated variable can be seen as fixed, too
                */
               if( SCIPsetIsInfinity(set, (*var)->data.multaggr.constant)
                  || SCIPsetIsInfinity(set, -((*var)->data.multaggr.constant)) )
               {
                  if( (*scalar) * (*var)->data.multaggr.constant > 0 )
                  {
                     assert(!SCIPsetIsInfinity(set, -(*constant)));
                     (*constant) = SCIPsetInfinity(set);
                  }
                  else
                  {
                     assert(!SCIPsetIsInfinity(set, *constant));
                     (*constant) = -SCIPsetInfinity(set);
                  }
                  (*scalar) = 0.0;
               }
               else
                  (*constant) += *scalar * (*var)->data.multaggr.constant;
            }
            (*scalar) *= (*var)->data.multaggr.scalars[0];
            *var = (*var)->data.multaggr.vars[0];
            break;
         }
         return SCIP_OKAY;

      case SCIP_VARSTATUS_AGGREGATED:  /* x = a'*x' + c'  =>  a*x + c == (a*a')*x' + (a*c' + c) */
         assert((*var)->data.aggregate.var != NULL);
         assert(!SCIPsetIsInfinity(set, (*var)->data.aggregate.constant)
            && !SCIPsetIsInfinity(set, (*var)->data.aggregate.constant));
         if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
            (*constant) += *scalar * (*var)->data.aggregate.constant;
         (*scalar) *= (*var)->data.aggregate.scalar;
         *var = (*var)->data.aggregate.var;
         break;

      case SCIP_VARSTATUS_NEGATED:     /* x =  - x' + c'  =>  a*x + c ==   (-a)*x' + (a*c' + c) */
         assert((*var)->negatedvar != NULL);
         assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert((*var)->negatedvar->negatedvar == *var);
         assert(!SCIPsetIsInfinity(set, (*var)->data.negate.constant)
            && !SCIPsetIsInfinity(set, (*var)->data.negate.constant));
         if( !SCIPsetIsInfinity(set, (*constant)) && !SCIPsetIsInfinity(set, -(*constant)) )
            (*constant) += *scalar * (*var)->data.negate.constant;
         (*scalar) *= -1.0;
         *var = (*var)->negatedvar;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/
      }
   }
   *scalar = 0.0;

   return SCIP_OKAY;
}

/** transforms given variable, scalar and constant to the corresponding active, fixed, or
 *  multi-aggregated variable, scalar and constant; if the variable resolves to a fixed variable,
 *  "scalar" will be 0.0 and the value of the sum will be stored in "constant"; a multi-aggregation
 *  with only one active variable (this can happen due to fixings after the multi-aggregation),
 *  is treated like an aggregation; if the multi-aggregation constant is infinite, "scalar" will be 0.0
 */
SCIP_RETCODE SCIPvarGetProbvarSumExact(
   SCIP_VAR**            var,                /**< pointer to problem variable x in sum a*x + c */
   SCIP_RATIONAL*        scalar,             /**< pointer to scalar a in sum a*x + c */
   SCIP_RATIONAL*        constant            /**< pointer to constant c in sum a*x + c */
   )
{
   assert(var != NULL);
   assert(scalar != NULL);
   assert(constant != NULL);
   assert(SCIPvarGetStatusExact(*var) == SCIPvarGetStatus(*var));

   while( *var != NULL )
   {
      switch( SCIPvarGetStatusExact(*var) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         if( (*var)->data.original.transvar == NULL )
         {
            SCIPerrorMessage("original variable has no transformed variable attached\n");
            return SCIP_INVALIDDATA;
         }
         *var = (*var)->data.original.transvar;
         break;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         return SCIP_OKAY;

      case SCIP_VARSTATUS_FIXED:       /* x = c'          =>  a*x + c ==             (a*c' + c) */
         if( !SCIPrationalIsInfinity(constant) && !SCIPrationalIsNegInfinity(constant) )
         {
            if( SCIPrationalIsInfinity((*var)->exactdata->glbdom.lb) || SCIPrationalIsNegInfinity(((*var)->exactdata->glbdom.lb)) )
            {
               assert(!SCIPrationalIsZero(scalar));
               if( SCIPrationalIsPositive(scalar) == SCIPrationalIsPositive((*var)->exactdata->glbdom.lb) )
                  SCIPrationalSetInfinity(constant);
               else
                  SCIPrationalSetNegInfinity(constant);
            }
            else
               SCIPrationalAddProd(constant, scalar, (*var)->exactdata->glbdom.lb);
         }
         SCIPrationalSetReal(scalar, 0.0);
         return SCIP_OKAY;

      case SCIP_VARSTATUS_MULTAGGR:
         /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
         if ( (*var)->data.multaggr.nvars == 1 )
         {
            assert((*var)->data.multaggr.vars != NULL);
            assert((*var)->data.multaggr.scalars != NULL);
            assert((*var)->data.multaggr.vars[0] != NULL);
            if( !SCIPrationalIsAbsInfinity(constant) )
            {
               /* the multi-aggregation constant can be infinite, if one of the multi-aggregation variables
                * was fixed to +/-infinity; ensure that the constant is set to +/-infinity, too, and the scalar
                * is set to 0.0, because the multi-aggregated variable can be seen as fixed, too
                */
               if( SCIPrationalIsAbsInfinity((*var)->exactdata->multaggr.constant) )
               {
                  if( SCIPrationalGetSign(scalar) == SCIPrationalGetSign((*var)->exactdata->multaggr.constant) && !SCIPrationalIsZero(scalar) )
                  {
                     assert(!SCIPrationalIsNegInfinity(constant));
                     SCIPrationalSetInfinity(constant);
                  }
                  else
                  {
                     assert(!SCIPrationalIsInfinity(constant));
                     SCIPrationalSetNegInfinity(constant);
                  }
                  SCIPrationalSetReal(scalar, 0.0);
               }
               else
                  SCIPrationalAddProd(constant, scalar, (*var)->exactdata->multaggr.constant);
            }
            SCIPrationalMult(scalar, scalar, (*var)->exactdata->multaggr.scalars[0]);
            *var = (*var)->data.multaggr.vars[0];
            break;
         }
         return SCIP_OKAY;

      case SCIP_VARSTATUS_AGGREGATED:  /* x = a'*x' + c'  =>  a*x + c == (a*a')*x' + (a*c' + c) */
         assert((*var)->data.aggregate.var != NULL);
         assert(!SCIPrationalIsAbsInfinity((*var)->exactdata->aggregate.constant));
         if( !SCIPrationalIsAbsInfinity(constant) )
            SCIPrationalAddProd(constant, scalar, (*var)->exactdata->aggregate.constant);
         SCIPrationalMult(scalar, scalar, (*var)->exactdata->aggregate.scalar);
         *var = (*var)->data.aggregate.var;
         break;
      case SCIP_VARSTATUS_NEGATED:     /* x =  - x' + c'  =>  a*x + c ==   (-a)*x' + (a*c' + c) */
         assert((*var)->negatedvar != NULL);
         assert(SCIPvarGetStatus((*var)->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert((*var)->negatedvar->negatedvar == *var);
         if( !SCIPrationalIsInfinity(constant) && !SCIPrationalIsNegInfinity(constant) )
            SCIPrationalAddProdReal(constant, scalar, (*var)->data.negate.constant);

         SCIPrationalNegate(scalar, scalar);
         *var = (*var)->negatedvar;
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/
      }
   }
   SCIPrationalSetReal(scalar, 0.0);

   return SCIP_OKAY;
}


/** retransforms given variable, scalar and constant to the corresponding original variable, scalar
 *  and constant, if possible; if the retransformation is impossible, NULL is returned as variable
 */
SCIP_RETCODE SCIPvarGetOrigvarSum(
   SCIP_VAR**            var,                /**< pointer to problem variable x in sum a*x + c */
   SCIP_Real*            scalar,             /**< pointer to scalar a in sum a*x + c */
   SCIP_Real*            constant            /**< pointer to constant c in sum a*x + c */
   )
{
   SCIP_VAR* parentvar;

   assert(var != NULL);
   assert(*var != NULL);
   assert(scalar != NULL);
   assert(constant != NULL);

   while( !SCIPvarIsOriginal(*var) )
   {
      /* if the variable has no parent variables, it was generated during solving and has no corresponding original
       * var
       */
      if( (*var)->nparentvars == 0 )
      {
         /* negated variables do not need to have a parent variables, and negated variables can exist in original
          * space
          */
         if( SCIPvarGetStatus(*var) == SCIP_VARSTATUS_NEGATED &&
            ((*var)->negatedvar->nparentvars == 0 || (*var)->negatedvar->parentvars[0] != *var) )
         {
            *scalar *= -1.0;
            *constant -= (*var)->data.negate.constant * (*scalar);
            *var = (*var)->negatedvar;

            continue;
         }
         /* if the variables does not have any parent the variables was created during solving and has no original
          * counterpart
          */
         else
         {
            *var = NULL;

            return SCIP_OKAY;
         }
      }

      /* follow the link to the first parent variable */
      parentvar = (*var)->parentvars[0];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatus(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + b  ->  y = (x-b)/a,  s*y + c = (s/a)*x + c-b*s/a */
         assert(parentvar->data.aggregate.var == *var);
         assert(parentvar->data.aggregate.scalar != 0.0);
         *scalar /= parentvar->data.aggregate.scalar;
         *constant -= parentvar->data.aggregate.constant * (*scalar);
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = b - y  ->  y = b - x,  s*y + c = -s*x + c+b*s */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         *scalar *= -1.0;
         *constant -= parentvar->data.negate.constant * (*scalar);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }

      assert( parentvar != NULL );
      *var = parentvar;
   }

   return SCIP_OKAY;
}

/** retransforms given variable, scalar anqd constant to the corresponding original variable, scalar
 *  and constant, if possible; if the retransformation is impossible, NULL is returned as variable
 */
SCIP_RETCODE SCIPvarGetOrigvarSumExact(
   SCIP_VAR**            var,                /**< pointer to problem variable x in sum a*x + c */
   SCIP_RATIONAL*        scalar,             /**< pointer to scalar a in sum a*x + c */
   SCIP_RATIONAL*        constant            /**< pointer to constant c in sum a*x + c */
   )
{
   SCIP_VAR* parentvar;

   assert(var != NULL);
   assert(*var != NULL);
   assert(scalar != NULL);
   assert(constant != NULL);

   while( !SCIPvarIsOriginal(*var) )
   {
      /* if the variable has no parent variables, it was generated during solving and has no corresponding original
       * var
       */
      if( (*var)->nparentvars == 0 )
      {
         /* negated variables do not need to have a parent variables, and negated variables can exist in original
          * space
          */
         if( SCIPvarGetStatusExact(*var) == SCIP_VARSTATUS_NEGATED &&
            ((*var)->negatedvar->nparentvars == 0 || (*var)->negatedvar->parentvars[0] != *var) )
         {
            SCIPrationalNegate(scalar, scalar);
            SCIPrationalDiffProdReal(constant, scalar, (*var)->data.negate.constant);
            *var = (*var)->negatedvar;

            continue;
         }
         /* if the variables does not have any parent the variables was created during solving and has no original
          * counterpart
          */
         else
         {
            *var = NULL;

            return SCIP_OKAY;
         }
      }

      /* follow the link to the first parent variable */
      parentvar = (*var)->parentvars[0];
      assert(parentvar != NULL);

      switch( SCIPvarGetStatusExact(parentvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
         break;

      case SCIP_VARSTATUS_COLUMN:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_FIXED:
      case SCIP_VARSTATUS_MULTAGGR:
         SCIPerrorMessage("column, loose, fixed or multi-aggregated variable cannot be the parent of a variable\n");
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + b  ->  y = (x-b)/a,  s*y + c = (s/a)*x + c-b*s/a */
         assert(parentvar->data.aggregate.var == *var);
         assert(parentvar->data.aggregate.scalar != 0.0);
         SCIPrationalDiv(scalar, scalar, parentvar->exactdata->aggregate.scalar);
         SCIPrationalDiffProd(constant, scalar, parentvar->exactdata->aggregate.constant);
         break;

      case SCIP_VARSTATUS_NEGATED: /* x = b - y  ->  y = b - x,  s*y + c = -s*x + c+b*s */
         assert(parentvar->negatedvar != NULL);
         assert(SCIPvarGetStatusExact(parentvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(parentvar->negatedvar->negatedvar == parentvar);
         SCIPrationalNegate(scalar, scalar);
         SCIPrationalDiffProdReal(constant, scalar, parentvar->data.negate.constant);
         break;

      default:
         SCIPerrorMessage("unknown variable status\n");
         return SCIP_INVALIDDATA;
      }

      assert( parentvar != NULL );
      *var = parentvar;
   }

   return SCIP_OKAY;
}


/** returns whether the given variable is the direct counterpart of an original problem variable */
SCIP_Bool SCIPvarIsTransformedOrigvar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_VAR* parentvar;
   assert(var != NULL);

   if( !SCIPvarIsTransformed(var) || var->nparentvars < 1 )
      return FALSE;

   assert(var->parentvars != NULL);
   parentvar = var->parentvars[0];
   assert(parentvar != NULL);

   /* we follow the aggregation tree to the root unless an original variable has been found - the first entries in the parentlist are candidates */
   while( parentvar->nparentvars >= 1 && SCIPvarGetStatus(parentvar) != SCIP_VARSTATUS_ORIGINAL )
      parentvar = parentvar->parentvars[0];
   assert( parentvar != NULL );

   return ( SCIPvarGetStatus(parentvar) == SCIP_VARSTATUS_ORIGINAL );
}

/** gets objective value of variable in current SCIP_LP; the value can be different from the objective value stored in
 *  the variable's own data due to diving, that operate only on the LP without updating the variables
 */
SCIP_Real SCIPvarGetObjLP(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   /* get bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      return SCIPvarGetObjLP(var->data.original.transvar);

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      return SCIPcolGetObj(var->data.col);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      return var->obj;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      return var->data.aggregate.scalar * SCIPvarGetObjLP(var->data.aggregate.var);

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot get the objective value of a multiple aggregated variable\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return -SCIPvarGetObjLP(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets lower bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
 *  data due to diving or conflict analysis, that operate only on the LP without updating the variables
 */
SCIP_Real SCIPvarGetLbLP(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* get bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      return SCIPvarGetLbLP(var->data.original.transvar, set);

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      return SCIPcolGetLb(var->data.col);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set)))
         || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set))) )
      {
         return -SCIPsetInfinity(set);
      }
      else if( var->data.aggregate.scalar > 0.0 )
      {
         /* a > 0 -> get lower bound of y */
         return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
      }
      else if( var->data.aggregate.scalar < 0.0 )
      {
         /* a < 0 -> get upper bound of y */
         return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         SCIPABORT();
         return SCIP_INVALID; /*lint !e527*/
      }

   case SCIP_VARSTATUS_MULTAGGR:
      /**@todo get the sides of the corresponding linear constraint */
      SCIPerrorMessage("getting the bounds of a multiple aggregated variable is not implemented yet\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetUbLP(var->negatedvar, set);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** gets upper bound of variable in current SCIP_LP; the bound can be different from the bound stored in the variable's own
 *  data due to diving or conflict analysis, that operate only on the LP without updating the variables
 */
SCIP_Real SCIPvarGetUbLP(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* get bounds of attached variables */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      assert(var->data.original.transvar != NULL);
      return SCIPvarGetUbLP(var->data.original.transvar, set);

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      return SCIPcolGetUb(var->data.col);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_FIXED:
      return var->locdom.ub;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  ->  y = (x-c)/a */
      assert(var->data.aggregate.var != NULL);
      if( (var->data.aggregate.scalar > 0.0 && SCIPsetIsInfinity(set, SCIPvarGetUbLP(var->data.aggregate.var, set)))
         || (var->data.aggregate.scalar < 0.0 && SCIPsetIsInfinity(set, -SCIPvarGetLbLP(var->data.aggregate.var, set))) )
      {
         return SCIPsetInfinity(set);
      }
      if( var->data.aggregate.scalar > 0.0 )
      {
         /* a > 0 -> get upper bound of y */
         return var->data.aggregate.scalar * SCIPvarGetUbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
      }
      else if( var->data.aggregate.scalar < 0.0 )
      {
         /* a < 0 -> get lower bound of y */
         return var->data.aggregate.scalar * SCIPvarGetLbLP(var->data.aggregate.var, set) + var->data.aggregate.constant;
      }
      else
      {
         SCIPerrorMessage("scalar is zero in aggregation\n");
         SCIPABORT();
         return SCIP_INVALID; /*lint !e527*/
      }

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot get the bounds of a multi-aggregated variable.\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetLbLP(var->negatedvar, set);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** gets primal LP solution value of variable */
SCIP_Real SCIPvarGetLPSol_rec(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIP_INVALID;
      return SCIPvarGetLPSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
      return SCIPvarGetBestBoundLocal(var);

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      return SCIPcolGetPrimsol(var->data.col);

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub || (var->exactdata != NULL && SCIPrationalIsEQ(var->exactdata->locdom.lb, var->exactdata->locdom.ub))); /*lint !e777*/
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
   {
      SCIP_Real lpsolval;

      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      lpsolval = SCIPvarGetLPSol(var->data.aggregate.var);

      /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
       * corresponding infinity value instead of performing an arithmetical transformation (compare method
       * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
       * (or is called by) a public interface method; instead, we only assert that values are finite
       * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
       * positives and negatives if the parameter <numerics/infinity> is modified by the user
       */
//       assert(lpsolval > -SCIP_DEFAULT_INFINITY);
//       assert(lpsolval < +SCIP_DEFAULT_INFINITY);

      if( lpsolval >= SCIP_DEFAULT_INFINITY )
         return (var->data.aggregate.scalar > 0) ? SCIP_DEFAULT_INFINITY : -SCIP_DEFAULT_INFINITY;
      else if( lpsolval <= -SCIP_DEFAULT_INFINITY )
         return (var->data.aggregate.scalar > 0) ? -SCIP_DEFAULT_INFINITY : SCIP_DEFAULT_INFINITY;

      return var->data.aggregate.scalar * lpsolval + var->data.aggregate.constant;
   }
   case SCIP_VARSTATUS_MULTAGGR:
   {
      SCIP_Real primsol;
      int i;

      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      primsol = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         primsol += var->data.multaggr.scalars[i] * SCIPvarGetLPSol(var->data.multaggr.vars[i]);
      return primsol;
   }
   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetLPSol(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** gets exact primal LP solution value of variable or value of safe dual solution */
void SCIPvarGetLPSolExact_rec(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_RATIONAL*        res                 /**< store the resulting value */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         SCIPrationalSetInfinity(res);
      SCIPvarGetLPSolExact(var->data.original.transvar, res);
      break;

   case SCIP_VARSTATUS_LOOSE:
      SCIPrationalSetRational(res, SCIPvarGetBestBoundLocalExact(var));
      break;

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      SCIPrationalSetRational(res, SCIPcolExactGetPrimsol(var->exactdata->colexact));
      break;

   case SCIP_VARSTATUS_FIXED:
      assert(SCIPrationalIsEQ(var->exactdata->locdom.lb, var->exactdata->locdom.ub)); /*lint !e777*/
      SCIPrationalSetRational(res, var->exactdata->locdom.lb);
      break;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(var->data.aggregate.var != NULL);
      SCIPvarGetLPSolExact(var->data.aggregate.var, res);
      SCIPrationalMult(res, res, var->exactdata->aggregate.scalar);
      SCIPrationalAdd(res, res, var->exactdata->aggregate.constant);
      break;

   case SCIP_VARSTATUS_MULTAGGR:
   {
      SCIP_RATIONAL* tmp;
      int i;

      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);

      (void) SCIPrationalCreate(&tmp);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      SCIPrationalSetRational(res, var->exactdata->multaggr.constant);
      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         SCIPvarGetLPSolExact(var->data.multaggr.vars[i], tmp);
         SCIPrationalAddProd(res, var->exactdata->multaggr.scalars[i], tmp);
      }
      SCIPrationalFree(&tmp);
      break;
   }

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIPvarGetLPSolExact(var->negatedvar, res);
      SCIPrationalDiffReal(res, res, var->data.negate.constant);
      SCIPrationalNegate(res, res);
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
   }
}

/** gets primal NLP solution value of variable */
SCIP_Real SCIPvarGetNLPSol_rec(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_Real solval;
   int i;

   assert(var != NULL);

   /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      return SCIPvarGetNLPSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
         return var->nlpsol;

   case SCIP_VARSTATUS_FIXED:
      assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var));  /*lint !e777*/
      assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var));    /*lint !e777*/
      assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var));   /*lint !e777*/
      return SCIPvarGetLbGlobal(var);

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  =>  y = (x-c)/a */
      solval = SCIPvarGetNLPSol(var->data.aggregate.var);
      return var->data.aggregate.scalar * solval + var->data.aggregate.constant;

   case SCIP_VARSTATUS_MULTAGGR:
      solval = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         solval += var->data.multaggr.scalars[i] * SCIPvarGetNLPSol(var->data.multaggr.vars[i]);
      return solval;

   case SCIP_VARSTATUS_NEGATED:
      solval = SCIPvarGetNLPSol(var->negatedvar);
      return var->data.negate.constant - solval;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** gets pseudo solution value of variable at current node */
static
SCIP_Real SCIPvarGetPseudoSol_rec(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_Real pseudosol;
   int i;

   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIP_INVALID;
      return SCIPvarGetPseudoSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPvarGetBestBoundLocal(var);

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
   {
      SCIP_Real pseudosolval;
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
       * corresponding infinity value instead of performing an arithmetical transformation (compare method
       * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
       * (or is called by) a public interface method; instead, we only assert that values are finite
       * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
       * positives and negatives if the parameter <numerics/infinity> is modified by the user
       */
      pseudosolval = SCIPvarGetPseudoSol(var->data.aggregate.var);
      assert(pseudosolval > -SCIP_DEFAULT_INFINITY);
      assert(pseudosolval < +SCIP_DEFAULT_INFINITY);
      return var->data.aggregate.scalar * pseudosolval + var->data.aggregate.constant;
   }
   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      pseudosol = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         pseudosol += var->data.multaggr.scalars[i] * SCIPvarGetPseudoSol(var->data.multaggr.vars[i]);
      return pseudosol;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetPseudoSol(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** gets exact pseudo solution value of variable at current node */
static
SCIP_RATIONAL* SCIPvarGetPseudoSolExact_rec(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return NULL;
      return SCIPvarGetPseudoSolExact(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPvarGetBestBoundLocalExact(var);

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
      return var->exactdata->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_MULTAGGR:
   case SCIP_VARSTATUS_NEGATED:
   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return NULL; /*lint !e527*/
   }
}

/** gets current LP or pseudo solution value of variable */
SCIP_Real SCIPvarGetSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Bool             getlpval            /**< should the LP solution value be returned? */
   )
{
   if( getlpval )
      return SCIPvarGetLPSol(var);
   else
      return SCIPvarGetPseudoSol(var);
}

/** gets current exact LP or pseudo solution value of variable */
void SCIPvarGetSolExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_RATIONAL*        res,                /**< the resulting value */
   SCIP_Bool             getlpval            /**< should the LP solution value be returned? */
   )
{
   assert(var != NULL);

   if( getlpval )
      SCIPvarGetLPSolExact(var, res);
   else
      SCIPrationalSetRational(res, SCIPvarGetPseudoSolExact(var));
}

/** remembers the current solution as root solution in the problem variables */
void SCIPvarStoreRootSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Bool             roothaslp           /**< is the root solution from LP? */
   )
{
   assert(var != NULL);

   var->rootsol = SCIPvarGetSol(var, roothaslp);
}

/** updates the current solution as best root solution of the given variable if it is better */
void SCIPvarUpdateBestRootSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             rootsol,            /**< root solution value */
   SCIP_Real             rootredcost,        /**< root reduced cost */
   SCIP_Real             rootlpobjval        /**< objective value of the root LP */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* if reduced cost are zero nothing to update */
   if( SCIPsetIsDualfeasZero(set, rootredcost) )
      return;

   /* check if we have already a best combination stored */
   if( !SCIPsetIsDualfeasZero(set, var->bestrootredcost) )
   {
      SCIP_Real currcutoffbound;
      SCIP_Real cutoffbound;
      SCIP_Real bound;

      /* compute the cutoff bound which would improve the corresponding bound with the current stored root solution,
       * root reduced cost, and root LP objective value combination
       */
      if( var->bestrootredcost > 0.0 )
         bound = SCIPvarGetUbGlobal(var);
      else
         bound = SCIPvarGetLbGlobal(var);

      currcutoffbound = (bound - var->bestrootsol) * var->bestrootredcost + var->bestrootlpobjval;

      /* compute the cutoff bound which would improve the corresponding bound with new root solution, root reduced
       * cost, and root LP objective value combination
       */
      if( rootredcost > 0.0 )
         bound = SCIPvarGetUbGlobal(var);
      else
         bound = SCIPvarGetLbGlobal(var);

      cutoffbound = (bound - rootsol) * rootredcost + rootlpobjval;

      /* check if an improving root solution, root reduced cost, and root LP objective value is at hand */
      if( cutoffbound > currcutoffbound )
      {
         SCIPsetDebugMsg(set, "-> <%s> update potential cutoff bound <%g> -> <%g>\n",
            SCIPvarGetName(var), currcutoffbound, cutoffbound);

         var->bestrootsol = rootsol;
         var->bestrootredcost = rootredcost;
         var->bestrootlpobjval = rootlpobjval;
      }
   }
   else
   {
      SCIPsetDebugMsg(set, "-> <%s> initialize best root reduced cost information\n", SCIPvarGetName(var));
      SCIPsetDebugMsg(set, "   -> rootsol <%g>\n", rootsol);
      SCIPsetDebugMsg(set, "   -> rootredcost <%g>\n", rootredcost);
      SCIPsetDebugMsg(set, "   -> rootlpobjval <%g>\n", rootlpobjval);

      var->bestrootsol = rootsol;
      var->bestrootredcost = rootredcost;
      var->bestrootlpobjval = rootlpobjval;
   }
}

/** returns the solution of the variable in the last root node's relaxation, if the root relaxation is not yet
 *  completely solved, zero is returned
 */
SCIP_Real SCIPvarGetRootSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_Real rootsol;
   int i;

   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      return SCIPvarGetRootSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return var->rootsol;

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
       * corresponding infinity value instead of performing an arithmetical transformation (compare method
       * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
       * (or is called by) a public interface method; instead, we only assert that values are finite
       * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
       * positives and negatives if the parameter <numerics/infinity> is modified by the user
       */
      assert(SCIPvarGetRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
      assert(SCIPvarGetRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
      return var->data.aggregate.scalar * SCIPvarGetRootSol(var->data.aggregate.var) + var->data.aggregate.constant;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      rootsol = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         rootsol += var->data.multaggr.scalars[i] * SCIPvarGetRootSol(var->data.multaggr.vars[i]);
      return rootsol;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetRootSol(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** returns for given variable the reduced cost */
static
SCIP_Real getImplVarRedcost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             varfixing,          /**< FALSE if for x == 0, TRUE for x == 1 */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
   {
      SCIP_COL* col;
      SCIP_Real primsol;
      SCIP_BASESTAT basestat;
      SCIP_Bool lpissolbasic;

      col = SCIPvarGetCol(var);
      assert(col != NULL);

      basestat = SCIPcolGetBasisStatus(col);
      lpissolbasic = SCIPlpIsSolBasic(lp);
      primsol = SCIPcolGetPrimsol(col);

      if( (lpissolbasic && (basestat == SCIP_BASESTAT_LOWER || basestat == SCIP_BASESTAT_UPPER)) ||
         (!lpissolbasic && (SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol) || SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol))) )
      {
         SCIP_Real redcost = SCIPcolGetRedcost(col, stat, lp);

         assert(set->exact_enable || (((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)) ||
               (lpissolbasic && basestat == SCIP_BASESTAT_LOWER)) ? (!SCIPsetIsDualfeasNegative(set, redcost) ||
               SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE));
         assert(set->exact_enable || (((!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)) ||
               (lpissolbasic && basestat == SCIP_BASESTAT_UPPER)) ? (!SCIPsetIsDualfeasPositive(set, redcost) ||
               SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var))) : TRUE));

         if( (varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_LOWER) ||
                  (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetLbLocal(var), primsol)))) ||
            (!varfixing && ((lpissolbasic && basestat == SCIP_BASESTAT_UPPER) ||
                  (!lpissolbasic && SCIPsetIsFeasEQ(set, SCIPvarGetUbLocal(var), primsol)))) )
            return redcost;
         else
            return 0.0;
      }

      return 0.0;
   }

   return 0.0;
}

#define MAX_CLIQUELENGTH 50
/** returns for the given binary variable the reduced cost which are given by the variable itself and its implication if
 *  the binary variable is fixed to the given value
 */
SCIP_Real SCIPvarGetImplRedcost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Bool             varfixing,          /**< FALSE if for x == 0, TRUE for x == 1 */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_PROB*            prob,               /**< transformed problem, or NULL */
   SCIP_LP*              lp                  /**< current LP data */
   )
{
   SCIP_Real implredcost;
   int ncliques;
   int nvars;

   assert(SCIPvarIsBinary(var));
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);

   /* get reduced cost of given variable */
   implredcost = getImplVarRedcost(var, set, varfixing, stat, lp);

#ifdef SCIP_MORE_DEBUG
   SCIPsetDebugMsg(set, "variable <%s> itself has reduced cost of %g\n", SCIPvarGetName(var), implredcost);
#endif

   /* the following algorithm is expensive */
   ncliques = SCIPvarGetNCliques(var, varfixing);

   if( ncliques > 0 )
   {
      SCIP_CLIQUE** cliques;
      SCIP_CLIQUE* clique;
      SCIP_VAR** clqvars;
      SCIP_VAR** probvars;
      SCIP_VAR* clqvar;
      SCIP_Bool* clqvalues;
      int* entries;
      int* ids;
      SCIP_Real redcost;
      SCIP_Bool cleanedup;
      int nclqvars;
      int nentries;
      int nids;
      int id;
      int c;
      int v;

      assert(prob != NULL);
      assert(SCIPprobIsTransformed(prob));

      nentries = SCIPprobGetNVars(prob) - SCIPprobGetNContVars(prob) + 1;

      SCIP_CALL_ABORT( SCIPsetAllocBufferArray(set, &ids, nentries) );
      nids = 0;
      SCIP_CALL_ABORT( SCIPsetAllocCleanBufferArray(set, &entries, nentries) );

      cliques = SCIPvarGetCliques(var, varfixing);
      assert(cliques != NULL);

      for( c = ncliques - 1; c >= 0; --c )
      {
         clique = cliques[c];
         assert(clique != NULL);
         nclqvars = SCIPcliqueGetNVars(clique);
         assert(nclqvars > 0);

         if( nclqvars > MAX_CLIQUELENGTH )
            continue;

         clqvars = SCIPcliqueGetVars(clique);
         clqvalues = SCIPcliqueGetValues(clique);
         assert(clqvars != NULL);
         assert(clqvalues != NULL);

         cleanedup = SCIPcliqueIsCleanedUp(clique);

         for( v = nclqvars - 1; v >= 0; --v )
         {
            clqvar = clqvars[v];
            assert(clqvar != NULL);

            /* ignore binary variable which are fixed */
            if( clqvar != var && (cleanedup || SCIPvarIsActive(clqvar)) &&
               (SCIPvarGetLbLocal(clqvar) < 0.5 && SCIPvarGetUbLocal(clqvar) > 0.5) )
            {
               int probindex = SCIPvarGetProbindex(clqvar) + 1;
               assert(0 < probindex && probindex < nentries);

#ifdef SCIP_DISABLED_CODE
               /* check that the variable was not yet visited or does not appear with two contradicting implications, ->
                * can appear since there is no guarantee that all these infeasible bounds were found
                */
               assert(!entries[probindex] || entries[probindex] == (clqvalues[v] ? probindex : -probindex));
#endif
               if( entries[probindex] == 0 )
               {
                  ids[nids] = probindex;
                  ++nids;

                  /* mark variable as visited */
                  entries[probindex] = (clqvalues[v] ? probindex : -probindex);
               }
            }
         }
      }

      probvars = SCIPprobGetVars(prob);
      assert(probvars != NULL);

      /* add all implied reduced cost */
      for( v = nids - 1; v >= 0; --v )
      {
         id = ids[v];
         assert(0 < id && id < nentries);
         assert(entries[id] != 0);
         assert(probvars[id - 1] != NULL);
         assert(SCIPvarIsActive(probvars[id - 1]));
         assert(SCIPvarIsBinary(probvars[id - 1]));
         assert(SCIPvarGetLbLocal(probvars[id - 1]) < 0.5 && SCIPvarGetUbLocal(probvars[id - 1]) > 0.5);

         if( (entries[id] > 0) != varfixing )
            redcost = getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);
         else
            redcost = -getImplVarRedcost(probvars[id - 1], set, (entries[id] < 0), stat, lp);

         if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
            implredcost += redcost;

         /* reset entries clear buffer array */
         entries[id] = 0;
      }

      SCIPsetFreeCleanBufferArray(set, &entries);
      SCIPsetFreeBufferArray(set, &ids);
   }

#ifdef SCIP_MORE_DEBUG
   SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) has implied reduced cost of %g\n", SCIPvarGetName(var), ncliques,
      implredcost);
#endif

   /* collect non-binary implication information */
   nvars = SCIPimplicsGetNImpls(var->implics, varfixing);

   if( nvars > 0 )
   {
      SCIP_VAR** vars;
      SCIP_VAR* implvar;
      SCIP_COL* col;
      SCIP_Real* bounds;
      SCIP_BOUNDTYPE* boundtypes;
      SCIP_Real redcost;
      SCIP_Real lb;
      SCIP_Real ub;
      SCIP_Bool lpissolbasic;
      int v;

      vars =  SCIPimplicsGetVars(var->implics, varfixing);
      boundtypes = SCIPimplicsGetTypes(var->implics, varfixing);
      bounds = SCIPimplicsGetBounds(var->implics, varfixing);
      lpissolbasic = SCIPlpIsSolBasic(lp);

      for( v = nvars - 1; v >= 0; --v )
      {
         implvar = vars[v];
         assert(implvar != NULL);

         lb = SCIPvarGetLbLocal(implvar);
         ub = SCIPvarGetUbLocal(implvar);

         /* ignore binary variable which are fixed or not of column status */
         if( SCIPvarGetStatus(implvar) != SCIP_VARSTATUS_COLUMN || SCIPsetIsFeasEQ(set, lb, ub) )
            continue;

         col = SCIPvarGetCol(implvar);
         assert(col != NULL);
         redcost = 0.0;

         /* solved lp with basis information or not? */
         if( lpissolbasic )
         {
            SCIP_BASESTAT basestat = SCIPcolGetBasisStatus(col);

            /* check if the implication is not not yet applied */
            if( basestat == SCIP_BASESTAT_LOWER && boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasGT(set, bounds[v], lb) )
            {
               redcost = SCIPcolGetRedcost(col, stat, lp);
               assert(!SCIPsetIsDualfeasNegative(set, redcost));

               if( !varfixing )
                  redcost *= (lb - bounds[v]);
               else
                  redcost *= (bounds[v] - lb);
            }
            else if( basestat == SCIP_BASESTAT_UPPER && boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasLT(set, bounds[v], ub) )
            {
               redcost = SCIPcolGetRedcost(col, stat, lp);
               assert(!SCIPsetIsDualfeasPositive(set, redcost));

               if( varfixing )
                  redcost *= (bounds[v] - ub);
               else
                  redcost *= (ub - bounds[v]);
            }
         }
         else
         {
            SCIP_Real primsol = SCIPcolGetPrimsol(col);

            /* check if the implication is not not yet applied */
            if( boundtypes[v] == SCIP_BOUNDTYPE_LOWER && SCIPsetIsFeasEQ(set, lb, primsol) && SCIPsetIsFeasGT(set, bounds[v], lb) )
            {
               redcost = SCIPcolGetRedcost(col, stat, lp);
               assert(!SCIPsetIsDualfeasNegative(set, redcost));

               if( varfixing )
                  redcost *= (lb - bounds[v]);
               else
                  redcost *= (bounds[v] - lb);
            }
            else if( boundtypes[v] == SCIP_BOUNDTYPE_UPPER && SCIPsetIsFeasEQ(set, ub, primsol) && SCIPsetIsFeasLT(set, bounds[v], ub) )
            {
               redcost = SCIPcolGetRedcost(col, stat, lp);
               assert(!SCIPsetIsDualfeasPositive(set, redcost));

               if( varfixing )
                  redcost *= (bounds[v] - ub);
               else
                  redcost *= (ub - bounds[v]);
            }
         }

         /* improve implied reduced cost */
         if( (varfixing && SCIPsetIsDualfeasPositive(set, redcost)) || (!varfixing && SCIPsetIsDualfeasNegative(set, redcost)) )
            implredcost += redcost;
      }
   }

#ifdef SCIP_MORE_DEBUG
   SCIPsetDebugMsg(set, "variable <%s> incl. cliques (%d) and implications (%d) has implied reduced cost of %g\n",
      SCIPvarGetName(var), ncliques, nvars, implredcost);
#endif

   return implredcost;
}

/** returns the best solution (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation, if
 *  the root relaxation is not yet completely solved, zero is returned
 */
SCIP_Real SCIPvarGetBestRootSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_Real rootsol;
   int i;

   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      return SCIPvarGetBestRootSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return var->bestrootsol;

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      /* a correct implementation would need to check the value of var->data.aggregate.var for infinity and return the
       * corresponding infinity value instead of performing an arithmetical transformation (compare method
       * SCIPvarGetLbLP()); however, we do not want to introduce a SCIP or SCIP_SET pointer to this method, since it is
       * (or is called by) a public interface method; instead, we only assert that values are finite
       * w.r.t. SCIP_DEFAULT_INFINITY, which seems to be true in our regression tests; note that this may yield false
       * positives and negatives if the parameter <numerics/infinity> is modified by the user
       */
      assert(SCIPvarGetBestRootSol(var->data.aggregate.var) > -SCIP_DEFAULT_INFINITY);
      assert(SCIPvarGetBestRootSol(var->data.aggregate.var) < +SCIP_DEFAULT_INFINITY);
      return var->data.aggregate.scalar * SCIPvarGetBestRootSol(var->data.aggregate.var) + var->data.aggregate.constant;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      rootsol = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         rootsol += var->data.multaggr.scalars[i] * SCIPvarGetBestRootSol(var->data.multaggr.vars[i]);
      return rootsol;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetBestRootSol(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the best reduced costs (w.r.t. root reduced cost propagation) of the variable in the root node's relaxation,
 *  if the root relaxation is not yet completely solved, or the variable was no column of the root LP, SCIP_INVALID is
 *  returned
 */
SCIP_Real SCIPvarGetBestRootRedcost(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIP_INVALID;
      return SCIPvarGetBestRootRedcost(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return var->bestrootredcost;

   case SCIP_VARSTATUS_FIXED:
   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_MULTAGGR:
   case SCIP_VARSTATUS_NEGATED:
      return 0.0;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the best objective value (w.r.t. root reduced cost propagation) of the root LP which belongs the root
 *  reduced cost which is accessible via SCIPvarGetRootRedcost() or the variable was no column of the root LP,
 *  SCIP_INVALID is returned
 */
SCIP_Real SCIPvarGetBestRootLPObjval(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIP_INVALID;
      return SCIPvarGetBestRootLPObjval(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return var->bestrootlpobjval;

   case SCIP_VARSTATUS_FIXED:
   case SCIP_VARSTATUS_AGGREGATED:
   case SCIP_VARSTATUS_MULTAGGR:
   case SCIP_VARSTATUS_NEGATED:
      return SCIP_INVALID;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** set the given solution as the best root solution w.r.t. root reduced cost propagation in the variables */
void SCIPvarSetBestRootSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real             rootsol,            /**< root solution value */
   SCIP_Real             rootredcost,        /**< root reduced cost */
   SCIP_Real             rootlpobjval        /**< objective value of the root LP */
   )
{
   assert(var != NULL);

   var->bestrootsol = rootsol;
   var->bestrootredcost = rootredcost;
   var->bestrootlpobjval = rootlpobjval;
}

/** stores the solution value as relaxation solution in the problem variable */
SCIP_RETCODE SCIPvarSetRelaxSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_RELAXATION*      relaxation,         /**< global relaxation data */
   SCIP_Real             solval,             /**< solution value in the current relaxation solution */
   SCIP_Bool             updateobj           /**< should the objective value be updated? */
   )
{
   assert(var != NULL);
   assert(relaxation != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      SCIP_CALL( SCIPvarSetRelaxSol(var->data.original.transvar, set, relaxation, solval, updateobj) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      if( updateobj )
         SCIPrelaxationSolObjAdd(relaxation, var->obj * (solval - var->relaxsol));
      var->relaxsol = solval;
      break;

   case SCIP_VARSTATUS_FIXED:
      if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
      {
         SCIPerrorMessage("cannot set relaxation solution value for variable <%s> fixed to %.15g to different value %.15g\n",
            SCIPvarGetName(var), var->glbdom.lb, solval);
         return SCIP_INVALIDDATA;
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  =>  y = (x-c)/a */
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      SCIP_CALL( SCIPvarSetRelaxSol(var->data.aggregate.var, set, relaxation,
            (solval - var->data.aggregate.constant)/var->data.aggregate.scalar, updateobj) );
      break;
   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      SCIP_CALL( SCIPvarSetRelaxSol(var->negatedvar, set, relaxation, var->data.negate.constant - solval, updateobj) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/** returns the solution value of the problem variable in the relaxation solution
 *
 *  @todo Inline this function - similar to SCIPvarGetLPSol_rec.
 */
SCIP_Real SCIPvarGetRelaxSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set                 /**< global SCIP settings */
   )
{
   SCIP_Real solvalsum;
   SCIP_Real solval;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* only values for non fixed variables (LOOSE or COLUMN) are stored; others have to be transformed */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      return SCIPvarGetRelaxSol(var->data.original.transvar, set);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return var->relaxsol;

   case SCIP_VARSTATUS_FIXED:
      assert(SCIPvarGetLbGlobal(var) == SCIPvarGetUbGlobal(var));  /*lint !e777*/
      assert(SCIPvarGetLbLocal(var) == SCIPvarGetUbLocal(var));    /*lint !e777*/
      assert(SCIPvarGetLbGlobal(var) == SCIPvarGetLbLocal(var));   /*lint !e777*/
      return SCIPvarGetLbGlobal(var);

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  =>  y = (x-c)/a */
      solval = SCIPvarGetRelaxSol(var->data.aggregate.var, set);
      if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
      {
         if( var->data.aggregate.scalar * solval > 0.0 )
            return SCIPsetInfinity(set);
         if( var->data.aggregate.scalar * solval < 0.0 )
            return -SCIPsetInfinity(set);
      }
      return var->data.aggregate.scalar * solval + var->data.aggregate.constant;

   case SCIP_VARSTATUS_MULTAGGR:
      solvalsum = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         solval = SCIPvarGetRelaxSol(var->data.multaggr.vars[i], set);
         if( SCIPsetIsInfinity(set, solval) || SCIPsetIsInfinity(set, -solval) )
         {
            if( var->data.multaggr.scalars[i] * solval > 0.0 )
               return SCIPsetInfinity(set);
            if( var->data.multaggr.scalars[i] * solval < 0.0 )
               return -SCIPsetInfinity(set);
         }
         solvalsum += var->data.multaggr.scalars[i] * solval;
      }
      return solvalsum;

   case SCIP_VARSTATUS_NEGATED:
      solval = SCIPvarGetRelaxSol(var->negatedvar, set);
      if( SCIPsetIsInfinity(set, solval) )
         return -SCIPsetInfinity(set);
      if( SCIPsetIsInfinity(set, -solval) )
         return SCIPsetInfinity(set);
      return var->data.negate.constant - solval;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_INVALID; /*lint !e527*/
   }
}

/** returns the solution value of the transformed problem variable in the relaxation solution */
SCIP_Real SCIPvarGetRelaxSolTransVar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);

   return var->relaxsol;
}

/** stores the solution value as NLP solution in the problem variable */
SCIP_RETCODE SCIPvarSetNLPSol(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             solval              /**< solution value in the current NLP solution */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   /* we want to store only values for non fixed variables (LOOSE or COLUMN); others have to be transformed */
   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      SCIP_CALL( SCIPvarSetNLPSol(var->data.original.transvar, set, solval) );
      break;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      var->nlpsol = solval;
      break;

   case SCIP_VARSTATUS_FIXED:
      if( !SCIPsetIsEQ(set, solval, var->glbdom.lb) )
      {
         SCIPerrorMessage("cannot set NLP solution value for variable <%s> fixed to %.15g to different value %.15g\n",
            SCIPvarGetName(var), var->glbdom.lb, solval);
         SCIPABORT();
         return SCIP_INVALIDCALL; /*lint !e527*/
      }
      break;

   case SCIP_VARSTATUS_AGGREGATED: /* x = a*y + c  =>  y = (x-c)/a */
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      SCIP_CALL( SCIPvarSetNLPSol(var->data.aggregate.var, set, (solval - var->data.aggregate.constant)/var->data.aggregate.scalar) );
      break;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot set solution value for multiple aggregated variable\n");
      SCIPABORT();
      return SCIP_INVALIDCALL; /*lint !e527*/

   case SCIP_VARSTATUS_NEGATED:
      SCIP_CALL( SCIPvarSetNLPSol(var->negatedvar, set, var->data.negate.constant - solval) );
      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return SCIP_ERROR; /*lint !e527*/
   }

   return SCIP_OKAY;
}

/** returns a weighted average solution value of the variable in all feasible primal solutions found so far */
SCIP_Real SCIPvarGetAvgSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_Real avgsol;
   int i;

   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      return SCIPvarGetAvgSol(var->data.original.transvar);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      avgsol = var->primsolavg;
      avgsol = MAX(avgsol, var->glbdom.lb);
      avgsol = MIN(avgsol, var->glbdom.ub);
      return avgsol;

   case SCIP_VARSTATUS_FIXED:
      assert(var->locdom.lb == var->locdom.ub); /*lint !e777*/
      return var->locdom.lb;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      return var->data.aggregate.scalar * SCIPvarGetAvgSol(var->data.aggregate.var)
         + var->data.aggregate.constant;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      avgsol = var->data.multaggr.constant;
      for( i = 0; i < var->data.multaggr.nvars; ++i )
         avgsol += var->data.multaggr.scalars[i] * SCIPvarGetAvgSol(var->data.multaggr.vars[i]);
      return avgsol;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      return var->data.negate.constant - SCIPvarGetAvgSol(var->negatedvar);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns solution value and index of variable lower bound that is closest to the variable's value in the given primal solution
 *  or current LP solution if no primal solution is given; returns an index of -1 if no variable lower bound is available
 */
void SCIPvarGetClosestVlb(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_SOL*             sol,                /**< primal solution, or NULL for LP solution */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real*            closestvlb,         /**< pointer to store the value of the closest variable lower bound */
   int*                  closestvlbidx       /**< pointer to store the index of the closest variable lower bound */
   )
{
   int nvlbs;

   assert(var != NULL);
   assert(stat != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(closestvlb != NULL);
   assert(closestvlbidx != NULL);

   *closestvlbidx = -1;
   *closestvlb = SCIP_REAL_MIN;

   nvlbs = SCIPvarGetNVlbs(var);
   if( nvlbs > 0 )
   {
      SCIP_VAR** vlbvars;
      SCIP_Real* vlbcoefs;
      SCIP_Real* vlbconsts;
      int i;

      vlbvars = SCIPvarGetVlbVars(var);
      vlbcoefs = SCIPvarGetVlbCoefs(var);
      vlbconsts = SCIPvarGetVlbConstants(var);

      /* check for cached values */
      if( var->closestvblpcount == stat->lpcount && var->closestvlbidx != -1 && sol == NULL)
      {
         i = var->closestvlbidx;
         assert(0 <= i && i < nvlbs);
         assert(SCIPvarIsActive(vlbvars[i]));
         *closestvlbidx = i;
         *closestvlb = vlbcoefs[i] * SCIPvarGetLPSol(vlbvars[i]) + vlbconsts[i];
      }
      else
      {
         /* search best VUB */
         for( i = 0; i < nvlbs; i++ )
         {
            if( SCIPvarIsActive(vlbvars[i]) )
            {
               SCIP_Real vlbsol;

               vlbsol = vlbcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vlbvars[i]) : SCIPsolGetVal(sol, set, stat, vlbvars[i])) + vlbconsts[i];
               if( vlbsol > *closestvlb )
               {
                  *closestvlb = vlbsol;
                  *closestvlbidx = i;
               }
            }
         }

         if( sol == NULL )
         {
            /* update cached value */
            if( var->closestvblpcount != stat->lpcount )
               var->closestvubidx = -1;
            var->closestvlbidx = *closestvlbidx;
            var->closestvblpcount = stat->lpcount;
         }
      }
   }
}

/** returns solution value and index of variable upper bound that is closest to the variable's value in the given primal solution;
 *  or current LP solution if no primal solution is given; returns an index of -1 if no variable upper bound is available
 */
void SCIPvarGetClosestVub(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_SOL*             sol,                /**< primal solution, or NULL for LP solution */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real*            closestvub,         /**< pointer to store the value of the closest variable upper bound */
   int*                  closestvubidx       /**< pointer to store the index of the closest variable upper bound */
   )
{
   int nvubs;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(closestvub != NULL);
   assert(closestvubidx != NULL);

   *closestvubidx = -1;
   *closestvub = SCIP_REAL_MAX;

   nvubs = SCIPvarGetNVubs(var);
   if( nvubs > 0 )
   {
      SCIP_VAR** vubvars;
      SCIP_Real* vubcoefs;
      SCIP_Real* vubconsts;
      int i;

      vubvars = SCIPvarGetVubVars(var);
      vubcoefs = SCIPvarGetVubCoefs(var);
      vubconsts = SCIPvarGetVubConstants(var);

      /* check for cached values */
      if( var->closestvblpcount == stat->lpcount && var->closestvubidx != -1 && sol == NULL)
      {
         i = var->closestvubidx;
         assert(0 <= i && i < nvubs);
         assert(SCIPvarIsActive(vubvars[i]));
         *closestvubidx = i;
         *closestvub = vubcoefs[i] * SCIPvarGetLPSol(vubvars[i]) + vubconsts[i];
      }
      else
      {
         /* search best VUB */
         for( i = 0; i < nvubs; i++ )
         {
            if( SCIPvarIsActive(vubvars[i]) )
            {
               SCIP_Real vubsol;

               vubsol = vubcoefs[i] * (sol == NULL ? SCIPvarGetLPSol(vubvars[i]) : SCIPsolGetVal(sol, set, stat, vubvars[i])) + vubconsts[i];
               if( vubsol < *closestvub )
               {
                  *closestvub = vubsol;
                  *closestvubidx = i;
               }
            }
         }

         if( sol == NULL )
         {
            /* update cached value */
            if( var->closestvblpcount != stat->lpcount )
               var->closestvlbidx = -1;
            var->closestvubidx = *closestvubidx;
            var->closestvblpcount = stat->lpcount;
         }
      }
   }
}

/** resolves variable to columns and adds them with the coefficient to the row */
SCIP_RETCODE SCIPvarAddToRow(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_LP*              lp,                 /**< current LP data */
   SCIP_ROW*             row,                /**< LP row */
   SCIP_Real             val                 /**< value of coefficient */
   )
{
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(row != NULL);
   assert(!SCIPsetIsInfinity(set, REALABS(val)));

   SCIPsetDebugMsg(set, "adding coefficient %g<%s> to row <%s>\n", val, var->name, row->name);

   if ( SCIPsetIsZero(set, val) )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot add untransformed original variable <%s> to LP row <%s>\n", var->name, row->name);
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarAddToRow(var->data.original.transvar, blkmem, set, stat, eventqueue, prob, lp, row, val) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
      /* add globally fixed variables as constant */
      if( SCIPsetIsEQ(set, var->glbdom.lb, var->glbdom.ub) )
      {
         SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->glbdom.lb) );
         return SCIP_OKAY;
      }
      /* convert loose variable into column */
      SCIP_CALL( SCIPvarColumn(var, blkmem, set, stat, prob, lp) );
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);
      /*lint -fallthrough*/

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      assert(var->data.col->var == var);
      SCIP_CALL( SCIProwIncCoef(row, blkmem, set, eventqueue, lp, var->data.col, val) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_FIXED:
      assert(var->glbdom.lb == var->glbdom.ub || (set->exact_enable && SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->glbdom.ub))); /*lint !e777*/
      assert(var->locdom.lb == var->locdom.ub || (set->exact_enable && SCIPrationalIsEQ(var->exactdata->locdom.lb, var->exactdata->locdom.ub))); /*lint !e777*/
      assert(var->locdom.lb == var->glbdom.lb || (set->exact_enable && SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->locdom.lb))); /*lint !e777*/
      assert(!SCIPsetIsInfinity(set, REALABS(var->locdom.lb)));
      SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, val * var->locdom.lb) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!var->donotaggr);
      assert(var->data.aggregate.var != NULL);
      SCIP_CALL( SCIPvarAddToRow(var->data.aggregate.var, blkmem, set, stat, eventqueue, prob, lp,
            row, var->data.aggregate.scalar * val) );
      SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.aggregate.constant * val) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);
      /* Due to method SCIPvarFlattenAggregationGraph(), this assert is no longer correct
       * assert(var->data.multaggr.nvars >= 2);
       */
      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         SCIP_CALL( SCIPvarAddToRow(var->data.multaggr.vars[i], blkmem, set, stat, eventqueue, prob, lp,
               row, var->data.multaggr.scalars[i] * val) );
      }
      SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.multaggr.constant * val) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);
      SCIP_CALL( SCIPvarAddToRow(var->negatedvar, blkmem, set, stat, eventqueue, prob, lp, row, -val) );
      SCIP_CALL( SCIProwAddConstant(row, blkmem, set, stat, eventqueue, lp, var->data.negate.constant * val) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** resolves variable to exact columns and adds them with the coefficient to the exact Row */
SCIP_RETCODE SCIPvarAddToRowExact(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
   SCIP_PROB*            prob,               /**< problem data */
   SCIP_LPEXACT*         lpexact,            /**< current LP data */
   SCIP_ROWEXACT*        rowexact,           /**< LP row */
   SCIP_RATIONAL*        val                 /**< value of coefficient */
   )
{
   SCIP_RATIONAL* tmp;
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(rowexact != NULL);
   assert(!SCIPrationalIsAbsInfinity(val));

   SCIPrationalDebugMessage("adding coefficient %q<%s> to exact row <%s>\n", val, var->name, rowexact->fprow->name);

   if ( SCIPrationalIsZero(val) )
      return SCIP_OKAY;

   switch( SCIPvarGetStatusExact(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot add untransformed original variable <%s> to excact LP row <%s>\n", var->name, rowexact->fprow->name);
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarAddToRowExact(var->data.original.transvar, blkmem, set, stat, eventqueue, prob, lpexact, rowexact, val) );
      break;

   case SCIP_VARSTATUS_LOOSE:
      /* add globally fixed variables as constant */
      if( SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->glbdom.ub) )
      {
         SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );
         SCIPrationalMult(tmp, val, var->exactdata->glbdom.lb);
         SCIP_CALL( SCIProwExactAddConstant(rowexact, set, stat, lpexact, tmp) );
         SCIPrationalFreeBuffer(set->buffer, &tmp);
         break;
      }
      /* convert loose variable into column */
      SCIP_CALL( SCIPvarColumnExact(var, blkmem, set, stat, lpexact) );
      assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_COLUMN);
      /*lint -fallthrough*/

   case SCIP_VARSTATUS_COLUMN:
      assert(var->data.col != NULL);
      assert(var->data.col->var == var);
      SCIP_CALL( SCIProwExactIncCoef(rowexact, blkmem, set, eventqueue, lpexact, var->exactdata->colexact, val) );
      break;

   case SCIP_VARSTATUS_FIXED:
      assert(SCIPrationalIsEQ(var->exactdata->glbdom.lb, var->exactdata->glbdom.ub)); /*lint !e777*/
      assert(SCIPrationalIsEQ(var->exactdata->locdom.lb, var->exactdata->locdom.ub)); /*lint !e777*/
      assert(SCIPrationalIsEQ(var->exactdata->locdom.lb, var->exactdata->glbdom.lb)); /*lint !e777*/
      assert(!SCIPrationalIsAbsInfinity(var->exactdata->locdom.lb));

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );

      SCIPrationalMult(tmp, val, var->exactdata->locdom.lb);
      SCIP_CALL( SCIProwExactAddConstant(rowexact, set, stat, lpexact, tmp) );

      SCIPrationalFreeBuffer(set->buffer, &tmp);

      break;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(var->data.aggregate.var != NULL);

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );
      SCIPrationalMult(tmp, var->exactdata->aggregate.scalar, val);
      SCIP_CALL( SCIPvarAddToRowExact(var->data.aggregate.var, blkmem, set, stat, eventqueue, prob, lpexact,
            rowexact, tmp) );
      SCIPrationalMult(tmp, var->exactdata->aggregate.constant, val);
      SCIP_CALL( SCIProwExactAddConstant(rowexact, set, stat, lpexact, tmp) );
      SCIPrationalFreeBuffer(set->buffer, &tmp);
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      assert(!var->donotmultaggr);
      assert(var->data.multaggr.vars != NULL);
      assert(var->data.multaggr.scalars != NULL);

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );

      for( i = 0; i < var->data.multaggr.nvars; ++i )
      {
         SCIPrationalMult(tmp, var->exactdata->multaggr.scalars[i], val);
         SCIP_CALL( SCIPvarAddToRowExact(var->data.multaggr.vars[i], blkmem, set, stat, eventqueue, prob, lpexact,
               rowexact, tmp) );
      }
      SCIPrationalMult(tmp, var->exactdata->multaggr.constant, val);
      SCIP_CALL( SCIProwExactAddConstant(rowexact, set, stat, lpexact, tmp) );

      SCIPrationalFreeBuffer(set->buffer, &tmp);
      return SCIP_OKAY;

   case SCIP_VARSTATUS_NEGATED: /* x' = offset - x  ->  x = offset - x' */
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) != SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar->negatedvar == var);

      SCIP_CALL( SCIPrationalCreateBuffer(set->buffer, &tmp) );

      SCIPrationalNegate(tmp, val);
      SCIP_CALL( SCIPvarAddToRowExact(var->negatedvar, blkmem, set, stat, eventqueue, prob, lpexact, rowexact, tmp) );

      SCIPrationalMultReal(tmp, val, var->data.negate.constant);
      SCIP_CALL( SCIProwExactAddConstant(rowexact, set, stat, lpexact, tmp) );

      SCIPrationalFreeBuffer(set->buffer, &tmp);

      break;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }

   return SCIP_OKAY;
}

/* optionally, define this compiler flag to write complete variable histories to a file */
#ifdef SCIP_HISTORYTOFILE
SCIP_Longint counter = 0l;
const char* historypath="."; /* allows for user-defined path; use '.' for calling directory of SCIP */
#include "scip/scip.h"
#endif

/** updates the pseudo costs of the given variable and the global pseudo costs after a change of
 *  "solvaldelta" in the variable's solution value and resulting change of "objdelta" in the LP's objective value
 */
SCIP_RETCODE SCIPvarUpdatePseudocost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             solvaldelta,        /**< difference of variable's new LP value - old LP value */
   SCIP_Real             objdelta,           /**< difference of new LP's objective value - old LP's objective value */
   SCIP_Real             weight              /**< weight in (0,1] of this update in pseudo cost sum */
   )
{
   SCIP_Real oldrootpseudocosts;
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update pseudo costs of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarUpdatePseudocost(var->data.original.transvar, set, stat, solvaldelta, objdelta, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      /* store old pseudo-costs for root LP best-estimate update */
      oldrootpseudocosts = SCIPvarGetMinPseudocostScore(var, stat, set, SCIPvarGetRootSol(var));

      /* update history */
      SCIPhistoryUpdatePseudocost(var->history, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdatePseudocost(var->historycrun, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdatePseudocost(stat->glbhistory, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdatePseudocost(stat->glbhistorycrun, set, solvaldelta, objdelta, weight);

      /* update root LP best-estimate */
      SCIP_CALL( SCIPstatUpdateVarRootLPBestEstimate(stat, set, var, oldrootpseudocosts) );

      /* append history to file */
#ifdef SCIP_HISTORYTOFILE
   {
      FILE* f;
      char filename[256];
      SCIP_NODE* currentnode;
      SCIP_NODE* parentnode;
      currentnode = SCIPgetFocusNode(set->scip);
      parentnode = SCIPnodeGetParent(currentnode);

      sprintf(filename, "%s/%s.pse", historypath, SCIPgetProbName(set->scip));
      f = fopen(filename, "a");
      if( NULL != f )
      {
         fprintf(f, "%lld %s \t %lld \t %lld \t %lld \t %d \t %15.9f \t %.3f\n",
            ++counter,
            SCIPvarGetName(var),
            SCIPnodeGetNumber(currentnode),
            parentnode != NULL ? SCIPnodeGetNumber(parentnode) : -1,
            SCIPgetNLPIterations(set->scip),
            SCIPgetDepth(set->scip),
            objdelta,
            solvaldelta);
         fclose(f);
      }
   }
#endif
      return SCIP_OKAY;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update pseudo cost values of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      SCIP_CALL( SCIPvarUpdatePseudocost(var->data.aggregate.var, set, stat,
            solvaldelta/var->data.aggregate.scalar, objdelta, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update pseudo cost values of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      SCIP_CALL( SCIPvarUpdatePseudocost(var->negatedvar, set, stat, -solvaldelta, objdelta, weight) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** updates the ancestral pseudo costs of the given variable and the global ancestral pseudo costs after a change of
 *  "solvaldelta" in the variable's solution value and resulting change of "objdelta" in the LP's objective value
 */
SCIP_RETCODE SCIPvarUpdateAncPseudocost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             solvaldelta,        /**< difference of variable's new LP value - old LP value */
   SCIP_Real             objdelta,           /**< difference of new LP's objective value - old LP's objective value */
   SCIP_Real             weight              /**< weight in (0,1] of this update in discounted pseudo cost sum */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(stat != NULL);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update ancestral pseudo costs of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarUpdateAncPseudocost(var->data.original.transvar, set, stat, solvaldelta, objdelta, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      /* update history */
      SCIPhistoryUpdateAncPseudocost(var->history, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdateAncPseudocost(var->historycrun, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdateAncPseudocost(stat->glbhistory, set, solvaldelta, objdelta, weight);
      SCIPhistoryUpdateAncPseudocost(stat->glbhistorycrun, set, solvaldelta, objdelta, weight);
      return SCIP_OKAY;

   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update ancestral pseudo cost values of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      assert(!SCIPsetIsZero(set, var->data.aggregate.scalar));
      SCIP_CALL( SCIPvarUpdateAncPseudocost(var->data.aggregate.var, set, stat,
            solvaldelta/var->data.aggregate.scalar, objdelta, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update ancestral pseudo cost values of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      SCIP_CALL( SCIPvarUpdateAncPseudocost(var->negatedvar, set, stat, -solvaldelta, objdelta, weight) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value */
SCIP_Real SCIPvarGetPseudocost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             solvaldelta         /**< difference of variable's new LP value - old LP value */
   )
{
   SCIP_BRANCHDIR dir;

   assert(var != NULL);
   assert(stat != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);
      else
         return SCIPvarGetPseudocost(var->data.original.transvar, stat, solvaldelta);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);

      return SCIPhistoryGetPseudocostCount(var->history, dir) > 0.0
         ? SCIPhistoryGetPseudocost(var->history, solvaldelta)
         : SCIPhistoryGetPseudocost(stat->glbhistory, solvaldelta);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      return SCIPvarGetPseudocost(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetPseudocost(var->negatedvar, stat, -solvaldelta);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets the variable's ancestral pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value */
SCIP_Real SCIPvarGetAncPseudocost(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             solvaldelta         /**< difference of variable's new LP value - old LP value */
   )
{
   SCIP_BRANCHDIR dir;

   assert(var != NULL);
   assert(stat != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetAncPseudocost(stat->glbhistory, solvaldelta);
      else
         return SCIPvarGetAncPseudocost(var->data.original.transvar, stat, solvaldelta);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);

      return SCIPhistoryGetAncPseudocostCount(var->history, dir) > 0.0
         ? SCIPhistoryGetAncPseudocost(var->history, solvaldelta)
         : SCIPhistoryGetAncPseudocost(stat->glbhistory, solvaldelta);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      return SCIPvarGetAncPseudocost(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAncPseudocost(var->negatedvar, stat, -solvaldelta);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets the variable's pseudo cost value for the given step size "solvaldelta" in the variable's LP solution value,
 *  only using the pseudo cost information of the current run
 */
SCIP_Real SCIPvarGetPseudocostCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             solvaldelta         /**< difference of variable's new LP value - old LP value */
   )
{
   SCIP_BRANCHDIR dir;

   assert(var != NULL);
   assert(stat != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);
      else
         return SCIPvarGetPseudocostCurrentRun(var->data.original.transvar, stat, solvaldelta);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      dir = (solvaldelta >= 0.0 ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);

      return SCIPhistoryGetPseudocostCount(var->historycrun, dir) > 0.0
         ? SCIPhistoryGetPseudocost(var->historycrun, solvaldelta)
         : SCIPhistoryGetPseudocost(stat->glbhistorycrun, solvaldelta);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      return SCIPvarGetPseudocostCurrentRun(var->data.aggregate.var, stat, var->data.aggregate.scalar * solvaldelta);

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetPseudocostCurrentRun(var->negatedvar, stat, -solvaldelta);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets the variable's (possible fractional) number of pseudo cost updates for the given direction */
SCIP_Real SCIPvarGetPseudocostCount(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetPseudocostCount(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetPseudocostCount(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetPseudocostCount(var->data.aggregate.var, dir);
      else
         return SCIPvarGetPseudocostCount(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetPseudocostCount(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets the variable's (possible fractional) number of pseudo cost updates for the given direction,
 *  only using the pseudo cost information of the current run
 */
SCIP_Real SCIPvarGetPseudocostCountCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetPseudocostCountCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetPseudocostCount(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetPseudocostCountCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetPseudocostCountCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** gets the variable's (possible fractional) number of ancestor pseudo cost updates for the given direction,
 *  only using the pseudo cost information of the current run
 */
SCIP_Real SCIPvarGetAncPseudocostCountCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetAncPseudocostCountCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetAncPseudocostCount(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAncPseudocostCountCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetAncPseudocostCountCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAncPseudocostCountCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** compares both possible directions for rounding the given solution value and returns the minimum pseudo-costs of the variable */
SCIP_Real SCIPvarGetMinPseudocostScore(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_Real             solval              /**< solution value, e.g., LP solution value */
   )
{
   SCIP_Real upscore;
   SCIP_Real downscore;
   SCIP_Real solvaldeltaup;
   SCIP_Real solvaldeltadown;

   /* LP root estimate only works for variables with fractional LP root solution */
   if( SCIPsetIsFeasIntegral(set, solval) )
      return 0.0;

   /* no min pseudo-cost score is calculated as long as the variable was not initialized in a direction */
   if( SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_DOWNWARDS) < 1.0 || SCIPvarGetPseudocostCount(var, SCIP_BRANCHDIR_UPWARDS) < 1.0 )
      return 0.0;

   /* compute delta's to ceil and floor of root LP solution value */
   solvaldeltaup = SCIPsetCeil(set, solval) - solval;
   solvaldeltadown = SCIPsetFloor(set, solval) - solval;

   upscore = SCIPvarGetPseudocost(var, stat, solvaldeltaup);
   downscore = SCIPvarGetPseudocost(var, stat, solvaldeltadown);

   return MIN(upscore, downscore);
}

/** gets the an estimate of the variable's pseudo cost variance in direction \p dir */
SCIP_Real SCIPvarGetPseudocostVariance(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir,                /**< branching direction (downwards, or upwards) */
   SCIP_Bool             onlycurrentrun      /**< return pseudo cost variance only for current branch and bound run */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetPseudocostVariance(var->data.original.transvar, dir, onlycurrentrun);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      if( onlycurrentrun )
         return SCIPhistoryGetPseudocostVariance(var->historycrun, dir);
      else
         return SCIPhistoryGetPseudocostVariance(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetPseudocostVariance(var->data.aggregate.var, dir, onlycurrentrun);
      else
         return SCIPvarGetPseudocostVariance(var->data.aggregate.var, SCIPbranchdirOpposite(dir), onlycurrentrun);

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetPseudocostVariance(var->negatedvar, SCIPbranchdirOpposite(dir), onlycurrentrun);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** calculates a confidence bound for this variable under the assumption of normally distributed pseudo costs
 *
 *  The confidence bound \f$ \theta \geq 0\f$ denotes the interval borders \f$ [X - \theta, \ X + \theta]\f$, which contains
 *  the true pseudo costs of the variable, i.e., the expected value of the normal distribution, with a probability
 *  of 2 * clevel - 1.
 *
 *  @return value of confidence bound for this variable
 */
SCIP_Real SCIPvarCalcPscostConfidenceBound(
   SCIP_VAR*             var,                /**< variable in question */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_BRANCHDIR        dir,                /**< the branching direction for the confidence bound */
   SCIP_Bool             onlycurrentrun,     /**< should only the current run be taken into account */
   SCIP_CONFIDENCELEVEL  clevel              /**< confidence level for the interval */
   )
{
   SCIP_Real confidencebound;

   confidencebound = SCIPvarGetPseudocostVariance(var, dir, onlycurrentrun);
   if( SCIPsetIsFeasPositive(set, confidencebound) )
   {
      SCIP_Real count;

      if( onlycurrentrun )
         count = SCIPvarGetPseudocostCountCurrentRun(var, dir);
      else
         count = SCIPvarGetPseudocostCount(var, dir);
      /* assertion is valid because variance is positive */
      assert(count >= 1.9);

      confidencebound /= count; /*lint !e414 division by zero can obviously not occur */
      confidencebound = sqrt(confidencebound);

      /* the actual, underlying distribution of the mean is a student-t-distribution with degrees of freedom equal to
       * the number of pseudo cost evaluations of this variable in the respective direction. */
      confidencebound *= SCIPstudentTGetCriticalValue(clevel, (int)SCIPsetFloor(set, count) - 1);
   }
   else
      confidencebound = 0.0;

   return confidencebound;
}

/** check if the current pseudo cost relative error in a direction violates the given threshold. The Relative
 *  Error is calculated at a specific confidence level
 */
SCIP_Bool SCIPvarIsPscostRelerrorReliable(
   SCIP_VAR*             var,                /**< variable in question */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             threshold,          /**< threshold for relative errors to be considered reliable (enough) */
   SCIP_CONFIDENCELEVEL  clevel              /**< a given confidence level */
   )
{
   SCIP_Real downsize;
   SCIP_Real upsize;
   SCIP_Real size;
   SCIP_Real relerrorup;
   SCIP_Real relerrordown;
   SCIP_Real relerror;

   /* check, if the pseudo cost score of the variable is reliable */
   downsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_DOWNWARDS);
   upsize = SCIPvarGetPseudocostCountCurrentRun(var, SCIP_BRANCHDIR_UPWARDS);
   size = MIN(downsize, upsize);

   /* Pseudo costs relative error can only be reliable if both directions have been tried at least twice */
   if( size <= 1.9 )
      return FALSE;

   /* use the relative error between the current mean pseudo cost value of the candidate and its upper
    * confidence interval bound at confidence level of 95% for individual variable reliability.
    * this is only possible if we have at least 2 measurements and therefore a valid variance estimate.
    */
   if( downsize >= 1.9 )
   {
      SCIP_Real normval;

      relerrordown = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_DOWNWARDS, TRUE, clevel);
      normval = SCIPvarGetPseudocostCurrentRun(var, stat, -1.0);
      normval = MAX(1.0, normval);

      relerrordown /= normval;
   }
   else
      relerrordown = 0.0;

   if( upsize >= 1.9 )
   {
      SCIP_Real normval;

      relerrorup = SCIPvarCalcPscostConfidenceBound(var, set, SCIP_BRANCHDIR_UPWARDS, TRUE, clevel);
      normval = SCIPvarGetPseudocostCurrentRun(var, stat, +1.0);
      normval = MAX(1.0, normval);
      relerrorup /= normval;
   }
   else
      relerrorup = 0.0;

   /* consider the relative error threshold violated, if it is violated in at least one branching direction */
   relerror = MAX(relerrorup, relerrordown);

   return (relerror <= threshold);
}

/** check if variable pseudo-costs have a significant difference in location. The significance depends on
 *  the choice of \p clevel and on the kind of tested hypothesis. The one-sided hypothesis, which
 *  should be rejected, is that fracy * mu_y >= fracx * mu_x, where mu_y and mu_x denote the
 *  unknown location means of the underlying pseudo-cost distributions of x and y.
 *
 *  This method is applied best if variable x has a better pseudo-cost score than y. The method hypothesizes that y were actually
 *  better than x (despite the current information), meaning that y can be expected to yield branching
 *  decisions as least as good as x in the long run. If the method returns TRUE, the current history information is
 *  sufficient to safely rely on the alternative hypothesis that x yields indeed a better branching score (on average)
 *  than y.
 *
 *  @note The order of x and y matters for the one-sided hypothesis
 *
 *  @note set \p onesided to FALSE if you are not sure which variable is better. The hypothesis tested then reads
 *        fracy * mu_y == fracx * mu_x vs the alternative hypothesis fracy * mu_y != fracx * mu_x.
 *
 *  @return TRUE if the hypothesis can be safely rejected at the given confidence level
 */
SCIP_Bool SCIPvarSignificantPscostDifference(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_VAR*             varx,               /**< variable x */
   SCIP_Real             fracx,              /**< the fractionality of variable x */
   SCIP_VAR*             vary,               /**< variable y */
   SCIP_Real             fracy,              /**< the fractionality of variable y */
   SCIP_BRANCHDIR        dir,                /**< branching direction */
   SCIP_CONFIDENCELEVEL  clevel,             /**< confidence level for rejecting hypothesis */
   SCIP_Bool             onesided            /**< should a one-sided hypothesis y >= x be tested? */
   )
{
   SCIP_Real meanx;
   SCIP_Real meany;
   SCIP_Real variancex;
   SCIP_Real variancey;
   SCIP_Real countx;
   SCIP_Real county;
   SCIP_Real tresult;
   SCIP_Real realdirection;

   if( varx == vary )
      return FALSE;

   countx = SCIPvarGetPseudocostCount(varx, dir);
   county = SCIPvarGetPseudocostCount(vary, dir);

   /* if not at least 2 measurements were taken, return FALSE */
   if( countx <= 1.9 || county <= 1.9 )
      return FALSE;

   realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);

   meanx = fracx * SCIPvarGetPseudocost(varx, stat, realdirection);
   meany = fracy * SCIPvarGetPseudocost(vary, stat, realdirection);

   variancex = SQR(fracx) * SCIPvarGetPseudocostVariance(varx, dir, FALSE);
   variancey = SQR(fracy) * SCIPvarGetPseudocostVariance(vary, dir, FALSE);

   /* if there is no variance, the means are taken from a constant distribution */
   if( SCIPsetIsFeasEQ(set, variancex + variancey, 0.0) )
      return (onesided ? SCIPsetIsFeasGT(set, meanx, meany) : !SCIPsetIsFeasEQ(set, meanx, meany));

   tresult = SCIPcomputeTwoSampleTTestValue(meanx, meany, variancex, variancey, countx, county);

   /* for the two-sided hypothesis, just take the absolute of t */
   if( !onesided )
      tresult = REALABS(tresult);

   return (tresult >= SCIPstudentTGetCriticalValue(clevel, (int)(countx + county - 2)));
}

/** tests at a given confidence level whether the variable pseudo-costs only have a small probability to
 *  exceed a \p threshold. This is useful to determine if past observations provide enough evidence
 *  to skip an expensive strong-branching step if there is already a candidate that has been proven to yield an improvement
 *  of at least \p threshold.
 *
 *  @note use \p clevel to adjust the level of confidence. For SCIP_CONFIDENCELEVEL_MIN, the method returns TRUE if
 *        the estimated probability to exceed \p threshold is less than 25 %.
 *
 *  @see  SCIP_Confidencelevel for a list of available levels. The used probability limits refer to the one-sided levels
 *        of confidence.
 *
 *  @return TRUE if the variable pseudo-cost probabilistic model is likely to be smaller than \p threshold
 *          at the given confidence level \p clevel.
 */
SCIP_Bool SCIPvarPscostThresholdProbabilityTest(
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_VAR*             var,                /**< variable x */
   SCIP_Real             frac,               /**< the fractionality of variable x */
   SCIP_Real             threshold,          /**< the threshold to test against */
   SCIP_BRANCHDIR        dir,                /**< branching direction */
   SCIP_CONFIDENCELEVEL  clevel              /**< confidence level for rejecting hypothesis */
   )
{
   SCIP_Real mean;
   SCIP_Real variance;
   SCIP_Real count;
   SCIP_Real realdirection;
   SCIP_Real probability;
   SCIP_Real problimit;

   count = SCIPvarGetPseudocostCount(var, dir);

   /* if not at least 2 measurements were taken, return FALSE */
   if( count <= 1.9 )
      return FALSE;

   realdirection = (dir == SCIP_BRANCHDIR_DOWNWARDS ? -1.0 : 1.0);

   mean = frac * SCIPvarGetPseudocost(var, stat, realdirection);
   variance = SQR(frac) * SCIPvarGetPseudocostVariance(var, dir, FALSE);

   /* if mean is at least threshold, it has at least a 50% probability to exceed threshold, we therefore return FALSE */
   if( SCIPsetIsFeasGE(set, mean, threshold) )
      return FALSE;

   /* if there is no variance, the means are taken from a constant distribution */
   if( SCIPsetIsFeasEQ(set, variance, 0.0) )
      return SCIPsetIsFeasLT(set, mean, threshold);

   /* obtain probability of a normally distributed random variable at given mean and variance to yield at most threshold */
   probability = SCIPnormalCDF(mean, variance, threshold);

   /* determine a probability limit corresponding to the given confidence level */
   switch( clevel )
   {
      case SCIP_CONFIDENCELEVEL_MIN:
         problimit = 0.75;
         break;
      case SCIP_CONFIDENCELEVEL_LOW:
         problimit = 0.875;
         break;
      case SCIP_CONFIDENCELEVEL_MEDIUM:
         problimit = 0.9;
         break;
      case SCIP_CONFIDENCELEVEL_HIGH:
         problimit = 0.95;
         break;
      case SCIP_CONFIDENCELEVEL_MAX:
         problimit = 0.975;
         break;
      default:
         problimit = -1;
         SCIPerrorMessage("Confidence level set to unknown value <%d>", (int)clevel);
         SCIPABORT();
         break;
   }

   return (probability >= problimit);
}

/** find the corresponding history entry if already existing, otherwise create new entry */
static
SCIP_RETCODE findValuehistoryEntry(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_HISTORY**        history             /**< pointer to store the value based history, or NULL */
   )
{
   assert(var != NULL);
   assert(blkmem != NULL);
   assert(set != NULL);
   assert(history != NULL);

   (*history) = NULL;

   if( var->valuehistory == NULL )
   {
      SCIP_CALL( SCIPvaluehistoryCreate(&var->valuehistory, blkmem) );
   }

   SCIP_CALL( SCIPvaluehistoryFind(var->valuehistory, blkmem, set, value, history) );

   return SCIP_OKAY;
}

/** check if value based history should be used */
static
SCIP_Bool useValuehistory(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   SCIP_SET*             set                 /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   )
{
   /* check if the domain value is unknown (not specific) */
   if( value == SCIP_UNKNOWN ) /*lint !e777*/
      return FALSE;

   assert(set != NULL);

   /* check if value based history should be collected */
   if( !set->history_valuebased )
      return FALSE;

   /* value based history is not collected for binary variable since the standard history already contains all information */
   if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
      return FALSE;

   /* value based history is not collected for continuous variables */
   if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
      return FALSE;

   return TRUE;
}

/** increases VSIDS of the variable by the given weight */
SCIP_RETCODE SCIPvarIncVSIDS(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir,                /**< branching direction */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   SCIP_Real             weight              /**< weight of this update in VSIDS */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   if( SCIPsetIsZero(set, weight) )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarIncVSIDS(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryIncVSIDS(var->history, dir, weight);
      SCIPhistoryIncVSIDS(var->historycrun, dir, weight);

      if( useValuehistory(var, value, set) )
      {
         SCIP_HISTORY* history;

         SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
         assert(history != NULL);

         SCIPhistoryIncVSIDS(history, dir, weight);
         SCIPsetDebugMsg(set, "variable (<%s> %s %g) + <%g> = <%g>\n", SCIPvarGetName(var), dir == SCIP_BRANCHDIR_UPWARDS ? ">=" : "<=",
            value, weight, SCIPhistoryGetVSIDS(history, dir));
      }

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;

      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
      }
      else
      {
         assert(var->data.aggregate.scalar < 0.0);
         SCIP_CALL( SCIPvarIncVSIDS(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      }
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      value = 1.0 - value;

      SCIP_CALL( SCIPvarIncVSIDS(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** scales the VSIDS of the variable by the given scalar */
SCIP_RETCODE SCIPvarScaleVSIDS(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real             scalar              /**< scalar to multiply the VSIDSs with */
   )
{
   assert(var != NULL);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update VSIDS of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarScaleVSIDS(var->data.original.transvar, scalar) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryScaleVSIDS(var->history, scalar);
      SCIPhistoryScaleVSIDS(var->historycrun, scalar);
      SCIPvaluehistoryScaleVSIDS(var->valuehistory, scalar);

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update VSIDS of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      SCIP_CALL( SCIPvarScaleVSIDS(var->data.aggregate.var, scalar) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update VSIDS of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      SCIP_CALL( SCIPvarScaleVSIDS(var->negatedvar, scalar) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** increases the number of active conflicts by one and the overall length of the variable by the given length */
SCIP_RETCODE SCIPvarIncNActiveConflicts(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir,                /**< branching direction */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   SCIP_Real             length              /**< length of the conflict */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update conflict score of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.original.transvar, blkmem, set, stat, dir, value, length) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryIncNActiveConflicts(var->history, dir, length);
      SCIPhistoryIncNActiveConflicts(var->historycrun, dir, length);

      if( useValuehistory(var, value, set) )
      {
         SCIP_HISTORY* history;

         SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
         assert(history != NULL);

         SCIPhistoryIncNActiveConflicts(history, dir, length);
      }

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update conflict score of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;

      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, dir, value, length) );
      }
      else
      {
         assert(var->data.aggregate.scalar < 0.0);
         SCIP_CALL( SCIPvarIncNActiveConflicts(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
      }
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update conflict score of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      value = 1.0 - value;

      SCIP_CALL( SCIPvarIncNActiveConflicts(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, length) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** gets the number of active conflicts containing this variable in given direction */
SCIP_Longint SCIPvarGetNActiveConflicts(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetNActiveConflicts(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNActiveConflicts(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetNActiveConflicts(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetNActiveConflicts(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** gets the number of active conflicts containing this variable in given direction
 *  in the current run
 */
SCIP_Longint SCIPvarGetNActiveConflictsCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetNActiveConflictsCurrentRun(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNActiveConflicts(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetNActiveConflictsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetNActiveConflictsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** gets the average conflict length in given direction due to branching on the variable */
SCIP_Real SCIPvarGetAvgConflictlength(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetAvgConflictlength(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
         return SCIPhistoryGetAvgConflictlength(var->history, dir);
   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgConflictlength(var->data.aggregate.var, dir);
      else
         return SCIPvarGetAvgConflictlength(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgConflictlength(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/**  gets the average conflict length in given direction due to branching on the variable
 *   in the current run
 */
SCIP_Real SCIPvarGetAvgConflictlengthCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetAvgConflictlengthCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return  SCIPhistoryGetAvgConflictlength(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetAvgConflictlengthCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgConflictlengthCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** increases the number of branchings counter of the variable */
SCIP_RETCODE SCIPvarIncNBranchings(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir,                /**< branching direction (downwards, or upwards) */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   int                   depth               /**< depth at which the bound change took place */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update branching counter of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarIncNBranchings(var->data.original.transvar, blkmem, set, stat, dir, value, depth) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryIncNBranchings(var->history, dir, depth);
      SCIPhistoryIncNBranchings(var->historycrun, dir, depth);
      SCIPhistoryIncNBranchings(stat->glbhistory, dir, depth);
      SCIPhistoryIncNBranchings(stat->glbhistorycrun, dir, depth);

      if( useValuehistory(var, value, set) )
      {
         SCIP_HISTORY* history;

         SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
         assert(history != NULL);

         SCIPhistoryIncNBranchings(history, dir, depth);
      }

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update branching counter of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;

      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, dir, value, depth) );
      }
      else
      {
         assert(var->data.aggregate.scalar < 0.0);
         SCIP_CALL( SCIPvarIncNBranchings(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
      }
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update branching counter of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      value = 1.0 - value;

      SCIP_CALL( SCIPvarIncNBranchings(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, depth) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** increases the inference sum of the variable by the given weight */
SCIP_RETCODE SCIPvarIncInferenceSum(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir,                /**< branching direction (downwards, or upwards) */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   SCIP_Real             weight              /**< weight of this update in inference score */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update inference counter of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarIncInferenceSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryIncInferenceSum(var->history, dir, weight);
      SCIPhistoryIncInferenceSum(var->historycrun, dir, weight);
      SCIPhistoryIncInferenceSum(stat->glbhistory, dir, weight);
      SCIPhistoryIncInferenceSum(stat->glbhistorycrun, dir, weight);

      if( useValuehistory(var, value, set) )
      {
         SCIP_HISTORY* history;

         SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
         assert(history != NULL);

         SCIPhistoryIncInferenceSum(history, dir, weight);
      }

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update inference counter of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;

      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
      }
      else
      {
         assert(var->data.aggregate.scalar < 0.0);
         SCIP_CALL( SCIPvarIncInferenceSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      }
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update inference counter of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      value = 1.0 - value;

      SCIP_CALL( SCIPvarIncInferenceSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** increases the cutoff sum of the variable by the given weight */
SCIP_RETCODE SCIPvarIncCutoffSum(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_SET*             set,                /**< global SCIP settings, or NULL if the domain value is SCIP_UNKNOWN */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir,                /**< branching direction (downwards, or upwards) */
   SCIP_Real             value,              /**< domain value, or SCIP_UNKNOWN */
   SCIP_Real             weight              /**< weight of this update in cutoff score */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   /* check if history statistics should be collected for a variable */
   if( !stat->collectvarhistory )
      return SCIP_OKAY;

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
      {
         SCIPerrorMessage("cannot update cutoff sum of original untransformed variable\n");
         return SCIP_INVALIDDATA;
      }
      SCIP_CALL( SCIPvarIncCutoffSum(var->data.original.transvar, blkmem, set, stat, dir, value, weight) );
      return SCIP_OKAY;

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
   {
      SCIPhistoryIncCutoffSum(var->history, dir, weight);
      SCIPhistoryIncCutoffSum(var->historycrun, dir, weight);
      SCIPhistoryIncCutoffSum(stat->glbhistory, dir, weight);
      SCIPhistoryIncCutoffSum(stat->glbhistorycrun, dir, weight);

      if( useValuehistory(var, value, set) )
      {
         SCIP_HISTORY* history;

         SCIP_CALL( findValuehistoryEntry(var, value, blkmem, set, &history) );
         assert(history != NULL);

         SCIPhistoryIncCutoffSum(history, dir, weight);
      }

      return SCIP_OKAY;
   }
   case SCIP_VARSTATUS_FIXED:
      SCIPerrorMessage("cannot update cutoff sum of a fixed variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_AGGREGATED:
      value = (value - var->data.aggregate.constant)/var->data.aggregate.scalar;

      if( var->data.aggregate.scalar > 0.0 )
      {
         SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, dir, value, weight) );
      }
      else
      {
         assert(var->data.aggregate.scalar < 0.0);
         SCIP_CALL( SCIPvarIncCutoffSum(var->data.aggregate.var, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      }
      return SCIP_OKAY;

   case SCIP_VARSTATUS_MULTAGGR:
      SCIPerrorMessage("cannot update cutoff sum of a multi-aggregated variable\n");
      return SCIP_INVALIDDATA;

   case SCIP_VARSTATUS_NEGATED:
      value = 1.0 - value;

      SCIP_CALL( SCIPvarIncCutoffSum(var->negatedvar, blkmem, set, stat, SCIPbranchdirOpposite(dir), value, weight) );
      return SCIP_OKAY;

   default:
      SCIPerrorMessage("unknown variable status\n");
      return SCIP_INVALIDDATA;
   }
}

/** returns the number of times, a bound of the variable was changed in given direction due to branching */
SCIP_Longint SCIPvarGetNBranchings(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetNBranchings(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNBranchings(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNBranchings(var->data.aggregate.var, dir);
      else
         return SCIPvarGetNBranchings(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetNBranchings(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** returns the number of times, a bound of the variable was changed in given direction due to branching
 *  in the current run
 */
SCIP_Longint SCIPvarGetNBranchingsCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetNBranchingsCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNBranchings(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetNBranchingsCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetNBranchingsCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** returns the average depth of bound changes in given direction due to branching on the variable */
SCIP_Real SCIPvarGetAvgBranchdepth(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetAvgBranchdepth(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return  SCIPhistoryGetAvgBranchdepth(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, dir);
      else
         return SCIPvarGetAvgBranchdepth(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgBranchdepth(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the average depth of bound changes in given direction due to branching on the variable
 *  in the current run
 */
SCIP_Real SCIPvarGetAvgBranchdepthCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetAvgBranchdepthCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return  SCIPhistoryGetAvgBranchdepth(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetAvgBranchdepthCurrentRun(var->data.aggregate.var,
            dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgBranchdepthCurrentRun(var->negatedvar,
         dir == SCIP_BRANCHDIR_DOWNWARDS ? SCIP_BRANCHDIR_UPWARDS : SCIP_BRANCHDIR_DOWNWARDS);

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the variable's VSIDS score */
SCIP_Real SCIPvarGetVSIDS_rec(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetVSIDS(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE); /* column case already handled in if condition above */
      return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetVSIDS(var->data.aggregate.var, stat, dir);
      else
         /* coverity[overrun-local] */
         return SCIPvarGetVSIDS(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      /* coverity[overrun-local] */
      return SCIPvarGetVSIDS(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the variable's VSIDS score only using conflicts of the current run */
SCIP_Real SCIPvarGetVSIDSCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   if( dir != SCIP_BRANCHDIR_DOWNWARDS && dir != SCIP_BRANCHDIR_UPWARDS )
   {
      SCIPerrorMessage("invalid branching direction %d when asking for VSIDS value\n", dir);
      return SCIP_INVALID;
   }

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetVSIDSCurrentRun(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetVSIDS(var->historycrun, dir)/stat->vsidsweight;

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetVSIDSCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetVSIDSCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the number of inferences branching on this variable in given direction triggered */
SCIP_Real SCIPvarGetInferenceSum(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetInferenceSum(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetInferenceSum(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetInferenceSum(var->data.aggregate.var, dir);
      else
         return SCIPvarGetInferenceSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetInferenceSum(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the number of inferences branching on this variable in given direction triggered
 *  in the current run
 */
SCIP_Real SCIPvarGetInferenceSumCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0.0;
      else
         return SCIPvarGetInferenceSumCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetInferenceSum(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetInferenceSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetInferenceSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the average number of inferences found after branching on the variable in given direction */
SCIP_Real SCIPvarGetAvgInferences(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetAvgInferences(stat->glbhistory, dir);
      else
         return SCIPvarGetAvgInferences(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      if( SCIPhistoryGetNBranchings(var->history, dir) > 0 )
         return SCIPhistoryGetAvgInferences(var->history, dir);
      else
      {
         int nimpls;
         int ncliques;

         nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
         ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
         return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistory, dir);  /*lint !e790*/
      }

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetAvgInferences(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgInferences(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the average number of inferences found after branching on the variable in given direction
 *  in the current run
 */
SCIP_Real SCIPvarGetAvgInferencesCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir);
      else
         return SCIPvarGetAvgInferencesCurrentRun(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      if( SCIPhistoryGetNBranchings(var->historycrun, dir) > 0 )
         return SCIPhistoryGetAvgInferences(var->historycrun, dir);
      else
      {
         int nimpls;
         int ncliques;

         nimpls = SCIPvarGetNImpls(var, dir == SCIP_BRANCHDIR_UPWARDS);
         ncliques = SCIPvarGetNCliques(var, dir == SCIP_BRANCHDIR_UPWARDS);
         return nimpls + ncliques > 0 ? (SCIP_Real)(nimpls + 2*ncliques) : SCIPhistoryGetAvgInferences(stat->glbhistorycrun, dir);  /*lint !e790*/
      }

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetAvgInferencesCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgInferencesCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the number of cutoffs branching on this variable in given direction produced */
SCIP_Real SCIPvarGetCutoffSum(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetCutoffSum(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetCutoffSum(var->history, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetCutoffSum(var->data.aggregate.var, dir);
      else
         return SCIPvarGetCutoffSum(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetCutoffSum(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** returns the number of cutoffs branching on this variable in given direction produced in the current run */
SCIP_Real SCIPvarGetCutoffSumCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return 0;
      else
         return SCIPvarGetCutoffSumCurrentRun(var->data.original.transvar, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetCutoffSum(var->historycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, dir);
      else
         return SCIPvarGetCutoffSumCurrentRun(var->data.aggregate.var, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetCutoffSumCurrentRun(var->negatedvar, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0; /*lint !e527*/
   }
}

/** returns the average number of cutoffs found after branching on the variable in given direction */
SCIP_Real SCIPvarGetAvgCutoffs(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);
      else
         return SCIPvarGetAvgCutoffs(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNBranchings(var->history, dir) > 0
         ? SCIPhistoryGetAvgCutoffs(var->history, dir)
         : SCIPhistoryGetAvgCutoffs(stat->glbhistory, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetAvgCutoffs(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgCutoffs(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the average number of cutoffs found after branching on the variable in given direction in the current run */
SCIP_Real SCIPvarGetAvgCutoffsCurrentRun(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(dir == SCIP_BRANCHDIR_DOWNWARDS || dir == SCIP_BRANCHDIR_UPWARDS);

   switch( SCIPvarGetStatus(var) )
   {
   case SCIP_VARSTATUS_ORIGINAL:
      if( var->data.original.transvar == NULL )
         return SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);
      else
         return SCIPvarGetAvgCutoffsCurrentRun(var->data.original.transvar, stat, dir);

   case SCIP_VARSTATUS_LOOSE:
   case SCIP_VARSTATUS_COLUMN:
      return SCIPhistoryGetNBranchings(var->historycrun, dir) > 0
         ? SCIPhistoryGetAvgCutoffs(var->historycrun, dir)
         : SCIPhistoryGetAvgCutoffs(stat->glbhistorycrun, dir);

   case SCIP_VARSTATUS_FIXED:
      return 0.0;

   case SCIP_VARSTATUS_AGGREGATED:
      if( var->data.aggregate.scalar > 0.0 )
         return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, dir);
      else
         return SCIPvarGetAvgCutoffsCurrentRun(var->data.aggregate.var, stat, SCIPbranchdirOpposite(dir));

   case SCIP_VARSTATUS_MULTAGGR:
      return 0.0;

   case SCIP_VARSTATUS_NEGATED:
      return SCIPvarGetAvgCutoffsCurrentRun(var->negatedvar, stat, SCIPbranchdirOpposite(dir));

   default:
      SCIPerrorMessage("unknown variable status\n");
      SCIPABORT();
      return 0.0; /*lint !e527*/
   }
}

/** returns the variable's average GMI efficacy score value generated from simplex tableau rows of this variable */
SCIP_Real SCIPvarGetAvgGMIScore(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   assert(var != NULL);
   assert(stat != NULL);

   switch( SCIPvarGetStatus(var) )
   {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar == NULL )
            return 0.0;
         else
            return SCIPvarGetAvgGMIScore(var->data.original.transvar, stat);

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         return SCIPhistoryGetAvgGMIeff(var->history);

      case SCIP_VARSTATUS_FIXED:
         return 0.0;

      case SCIP_VARSTATUS_AGGREGATED:
         return SCIPvarGetAvgGMIScore(var->data.aggregate.var, stat);

      case SCIP_VARSTATUS_MULTAGGR:
         return 0.0;

      case SCIP_VARSTATUS_NEGATED:
         return SCIPvarGetAvgGMIScore(var->negatedvar, stat);

      default:
      SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return 0.0; /*lint !e527*/
   }
}

/** increase the variable's GMI efficacy scores generated from simplex tableau rows of this variable */
SCIP_RETCODE SCIPvarIncGMIeffSum(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             gmieff              /**< efficacy of last GMI cut produced when variable was frac and basic */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(gmieff >= 0);

   switch( SCIPvarGetStatus(var) )
   {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
            SCIP_CALL( SCIPvarIncGMIeffSum(var->data.original.transvar, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         SCIPhistoryIncGMIeffSum(var->history, gmieff);
         return SCIP_OKAY;

      case SCIP_VARSTATUS_FIXED:
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED:
         SCIP_CALL( SCIPvarIncGMIeffSum(var->data.aggregate.var, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_NEGATED:
         SCIP_CALL( SCIPvarIncGMIeffSum(var->negatedvar, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_MULTAGGR:
         return SCIP_INVALIDDATA;

      default:
      SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/
   }
}

/** returns the variable's last GMI efficacy score value generated from a simplex tableau row of this variable */
SCIP_Real SCIPvarGetLastGMIScore(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat                /**< problem statistics */
   )
{
   assert(var != NULL);
   assert(stat != NULL);

   switch( SCIPvarGetStatus(var) )
   {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
            return SCIPvarGetLastGMIScore(var->data.original.transvar, stat);
         return 0.0;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         return SCIPhistoryGetLastGMIeff(var->history);

      case SCIP_VARSTATUS_FIXED:
         return 0.0;

      case SCIP_VARSTATUS_AGGREGATED:
         return SCIPvarGetLastGMIScore(var->data.aggregate.var, stat);

      case SCIP_VARSTATUS_MULTAGGR:
         return 0.0;

      case SCIP_VARSTATUS_NEGATED:
         return SCIPvarGetLastGMIScore(var->negatedvar, stat);

      default:
      SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return 0.0; /*lint !e527*/
   }
}


/** sets the variable's last GMI efficacy score value generated from a simplex tableau row of this variable */
SCIP_RETCODE SCIPvarSetLastGMIScore(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_Real             gmieff              /**< efficacy of last GMI cut produced when variable was frac and basic */
   )
{
   assert(var != NULL);
   assert(stat != NULL);
   assert(gmieff >= 0);

   switch( SCIPvarGetStatus(var) )
   {
      case SCIP_VARSTATUS_ORIGINAL:
         if( var->data.original.transvar != NULL )
            SCIP_CALL( SCIPvarSetLastGMIScore(var->data.original.transvar, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         SCIPhistorySetLastGMIeff(var->history, gmieff);
         return SCIP_OKAY;

      case SCIP_VARSTATUS_FIXED:
         return SCIP_INVALIDDATA;

      case SCIP_VARSTATUS_AGGREGATED:
         SCIP_CALL( SCIPvarSetLastGMIScore(var->data.aggregate.var, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_NEGATED:
         SCIP_CALL( SCIPvarSetLastGMIScore(var->negatedvar, stat, gmieff) );
         return SCIP_OKAY;

      case SCIP_VARSTATUS_MULTAGGR:
         return SCIP_INVALIDDATA;

      default:
      SCIPerrorMessage("unknown variable status\n");
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/
   }
}



/*
 * information methods for bound changes
 */

/** creates an artificial bound change information object with depth = INT_MAX and pos = -1 */
SCIP_RETCODE SCIPbdchginfoCreate(
   SCIP_BDCHGINFO**      bdchginfo,          /**< pointer to store bound change information */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_VAR*             var,                /**< active variable that changed the bounds */
   SCIP_BOUNDTYPE        boundtype,          /**< type of bound for var: lower or upper bound */
   SCIP_Real             oldbound,           /**< old value for bound */
   SCIP_Real             newbound            /**< new value for bound */
   )
{
   assert(bdchginfo != NULL);

   SCIP_ALLOC( BMSallocBlockMemory(blkmem, bdchginfo) );
   (*bdchginfo)->oldbound = oldbound;
   (*bdchginfo)->newbound = newbound;
   (*bdchginfo)->var = var;
   (*bdchginfo)->inferencedata.var = var;
   (*bdchginfo)->inferencedata.reason.prop = NULL;
   (*bdchginfo)->inferencedata.info = 0;
   (*bdchginfo)->bdchgidx.depth = INT_MAX;
   (*bdchginfo)->bdchgidx.pos = -1;
   (*bdchginfo)->pos = 0;
   (*bdchginfo)->boundchgtype = SCIP_BOUNDCHGTYPE_BRANCHING; /*lint !e641*/
   (*bdchginfo)->boundtype = boundtype; /*lint !e641*/
   (*bdchginfo)->inferboundtype = boundtype; /*lint !e641*/
   (*bdchginfo)->redundant = FALSE;

   return SCIP_OKAY;
}

/** frees a bound change information object */
void SCIPbdchginfoFree(
   SCIP_BDCHGINFO**      bdchginfo,          /**< pointer to store bound change information */
   BMS_BLKMEM*           blkmem              /**< block memory */
   )
{
   assert(bdchginfo != NULL);

   BMSfreeBlockMemory(blkmem, bdchginfo);
}

/** returns the bound change information for the last lower bound change on given active problem variable before or
 *  after the bound change with the given index was applied;
 *  returns NULL, if no change to the lower bound was applied up to this point of time
 */
SCIP_BDCHGINFO* SCIPvarGetLbchgInfo(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_BDCHGIDX*        bdchgidx,           /**< bound change index representing time on path to current node */
   SCIP_Bool             after               /**< should the bound change with given index be included? */
   )
{
   int i;

   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   /* search the correct bound change information for the given bound change index */
   if( after )
   {
      for( i = var->nlbchginfos-1; i >= 0; --i )
      {
         assert(var->lbchginfos[i].var == var);
         assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
         assert(var->lbchginfos[i].pos == i);

         /* if we reached the (due to global bounds) redundant bound changes, return NULL */
         if( var->lbchginfos[i].redundant )
            return NULL;
         assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);

         /* if we reached the bound change index, return the current bound change info */
         if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->lbchginfos[i].bdchgidx) )
            return &var->lbchginfos[i];
      }
   }
   else
   {
      for( i = var->nlbchginfos-1; i >= 0; --i )
      {
         assert(var->lbchginfos[i].var == var);
         assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
         assert(var->lbchginfos[i].pos == i);

         /* if we reached the (due to global bounds) redundant bound changes, return NULL */
         if( var->lbchginfos[i].redundant )
            return NULL;
         assert(var->lbchginfos[i].oldbound < var->lbchginfos[i].newbound);

         /* if we reached the bound change index, return the current bound change info */
         if( SCIPbdchgidxIsEarlier(&var->lbchginfos[i].bdchgidx, bdchgidx) )
            return &var->lbchginfos[i];
      }
   }

   return NULL;
}

/** returns the bound change information for the last upper bound change on given active problem variable before or
 *  after the bound change with the given index was applied;
 *  returns NULL, if no change to the upper bound was applied up to this point of time
 */
SCIP_BDCHGINFO* SCIPvarGetUbchgInfo(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_BDCHGIDX*        bdchgidx,           /**< bound change index representing time on path to current node */
   SCIP_Bool             after               /**< should the bound change with given index be included? */
   )
{
   int i;

   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   /* search the correct bound change information for the given bound change index */
   if( after )
   {
      for( i = var->nubchginfos-1; i >= 0; --i )
      {
         assert(var->ubchginfos[i].var == var);
         assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
         assert(var->ubchginfos[i].pos == i);

         /* if we reached the (due to global bounds) redundant bound changes, return NULL */
         if( var->ubchginfos[i].redundant )
            return NULL;
         assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);

         /* if we reached the bound change index, return the current bound change info */
         if( !SCIPbdchgidxIsEarlier(bdchgidx, &var->ubchginfos[i].bdchgidx) )
            return &var->ubchginfos[i];
      }
   }
   else
   {
      for( i = var->nubchginfos-1; i >= 0; --i )
      {
         assert(var->ubchginfos[i].var == var);
         assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
         assert(var->ubchginfos[i].pos == i);

         /* if we reached the (due to global bounds) redundant bound changes, return NULL */
         if( var->ubchginfos[i].redundant )
            return NULL;
         assert(var->ubchginfos[i].oldbound > var->ubchginfos[i].newbound);

         /* if we reached the bound change index, return the current bound change info */
         if( SCIPbdchgidxIsEarlier(&var->ubchginfos[i].bdchgidx, bdchgidx) )
            return &var->ubchginfos[i];
      }
   }

   return NULL;
}

/** returns the bound change information for the last lower or upper bound change on given active problem variable
 *  before or after the bound change with the given index was applied;
 *  returns NULL, if no change to the lower/upper bound was applied up to this point of time
 */
SCIP_BDCHGINFO* SCIPvarGetBdchgInfo(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_BOUNDTYPE        boundtype,          /**< type of bound: lower or upper bound */
   SCIP_BDCHGIDX*        bdchgidx,           /**< bound change index representing time on path to current node */
   SCIP_Bool             after               /**< should the bound change with given index be included? */
   )
{
   if( boundtype == SCIP_BOUNDTYPE_LOWER )
      return SCIPvarGetLbchgInfo(var, bdchgidx, after);
   else
   {
      assert(boundtype == SCIP_BOUNDTYPE_UPPER);
      return SCIPvarGetUbchgInfo(var, bdchgidx, after);
   }
}

/** bound change index representing the initial time before any bound changes took place */
static SCIP_BDCHGIDX initbdchgidx = {-2, 0};

/** bound change index representing the presolving stage */
static SCIP_BDCHGIDX presolvebdchgidx = {-1, 0};

/** returns the last bound change index, at which the bounds of the given variable were tightened */
SCIP_BDCHGIDX* SCIPvarGetLastBdchgIndex(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_BDCHGIDX* lbchgidx;
   SCIP_BDCHGIDX* ubchgidx;

   assert(var != NULL);

   var = SCIPvarGetProbvar(var);

   /* check, if variable is original without transformed variable */
   if( var == NULL )
      return &initbdchgidx;

   /* check, if variable was fixed in presolving */
   if( !SCIPvarIsActive(var) )
      return &presolvebdchgidx;

   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);

   /* get depths of last bound change information for the lower and upper bound */
   lbchgidx = (var->nlbchginfos > 0 && !var->lbchginfos[var->nlbchginfos-1].redundant
      ? &var->lbchginfos[var->nlbchginfos-1].bdchgidx : &initbdchgidx);
   ubchgidx = (var->nubchginfos > 0 && !var->ubchginfos[var->nubchginfos-1].redundant
      ? &var->ubchginfos[var->nubchginfos-1].bdchgidx : &initbdchgidx);

   if( SCIPbdchgidxIsEarlierNonNull(lbchgidx, ubchgidx) )
      return ubchgidx;
   else
      return lbchgidx;
}

/** returns the last depth level, at which the bounds of the given variable were tightened;
 *  returns -2, if the variable's bounds are still the global bounds
 *  returns -1, if the variable was fixed in presolving
 */
int SCIPvarGetLastBdchgDepth(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   SCIP_BDCHGIDX* bdchgidx;

   bdchgidx = SCIPvarGetLastBdchgIndex(var);
   assert(bdchgidx != NULL);

   return bdchgidx->depth;
}

/** returns at which depth in the tree a bound change was applied to the variable that conflicts with the
 *  given bound; returns -1 if the bound does not conflict with the current local bounds of the variable
 */
int SCIPvarGetConflictingBdchgDepth(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_BOUNDTYPE        boundtype,          /**< bound type of the conflicting bound */
   SCIP_Real             bound               /**< conflicting bound */
   )
{
   int i;

   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);

   if( boundtype == SCIP_BOUNDTYPE_LOWER )
   {
      /* check if the bound is in conflict with the current local bounds */
      if( SCIPsetIsLE(set, bound, var->locdom.ub) )
         return -1;

      /* check if the bound is in conflict with the global bound */
      if( SCIPsetIsGT(set, bound, var->glbdom.ub) )
         return 0;

      /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
      assert(var->nubchginfos > 0);
      assert(SCIPsetIsGT(set, bound, var->ubchginfos[var->nubchginfos-1].newbound));

      /* search for the first conflicting bound change */
      for( i = var->nubchginfos-1; i > 0 && SCIPsetIsGT(set, bound, var->ubchginfos[i-1].newbound); --i )
      {
         assert(var->ubchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
         assert((SCIP_BOUNDTYPE)var->ubchginfos[i].boundtype == SCIP_BOUNDTYPE_UPPER);
      }
      assert(SCIPsetIsGT(set, bound, var->ubchginfos[i].newbound));             /* bound change i is conflicting */
      assert(i == 0 || SCIPsetIsLE(set, bound, var->ubchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */

      /* return the depth at which the first conflicting bound change took place */
      return var->ubchginfos[i].bdchgidx.depth;
   }
   else
   {
      assert(boundtype == SCIP_BOUNDTYPE_UPPER);

      /* check if the bound is in conflict with the current local bounds */
      if( SCIPsetIsGE(set, bound, var->locdom.lb) )
         return -1;

      /* check if the bound is in conflict with the global bound */
      if( SCIPsetIsLT(set, bound, var->glbdom.lb) )
         return 0;

      /* local bounds are in conflict with the given bound -> there must be at least one conflicting change! */
      assert(var->nlbchginfos > 0);
      assert(SCIPsetIsLT(set, bound, var->lbchginfos[var->nlbchginfos-1].newbound));

      /* search for the first conflicting bound change */
      for( i = var->nlbchginfos-1; i > 0 && SCIPsetIsLT(set, bound, var->lbchginfos[i-1].newbound); --i )
      {
         assert(var->lbchginfos[i].var == var); /* perform sanity check on the search for the first conflicting bound */
         assert((SCIP_BOUNDTYPE)var->lbchginfos[i].boundtype == SCIP_BOUNDTYPE_LOWER);
      }
      assert(SCIPsetIsLT(set, bound, var->lbchginfos[i].newbound));             /* bound change i is conflicting */
      assert(i == 0 || SCIPsetIsGE(set, bound, var->lbchginfos[i-1].newbound)); /* bound change i-1 is not conflicting */

      /* return the depth at which the first conflicting bound change took place */
      return var->lbchginfos[i].bdchgidx.depth;
   }
}

/** returns whether the first binary variable was fixed earlier than the second one;
 *  returns FALSE, if the first variable is not fixed, and returns TRUE, if the first variable is fixed, but the
 *  second one is not fixed
 */
SCIP_Bool SCIPvarWasFixedEarlier(
   SCIP_VAR*             var1,               /**< first binary variable */
   SCIP_VAR*             var2                /**< second binary variable */
   )
{
   SCIP_BDCHGIDX* bdchgidx1;
   SCIP_BDCHGIDX* bdchgidx2;

   assert(var1 != NULL);
   assert(var2 != NULL);
   assert(SCIPvarIsBinary(var1));
   assert(SCIPvarIsBinary(var2));

   var1 = SCIPvarGetProbvar(var1);
   var2 = SCIPvarGetProbvar(var2);
   assert(var1 != NULL);
   assert(var2 != NULL);

   /* check, if variables are globally fixed */
   if( !SCIPvarIsActive(var2) || var2->glbdom.lb > 0.5 || var2->glbdom.ub < 0.5 )
      return FALSE;
   if( !SCIPvarIsActive(var1) || var1->glbdom.lb > 0.5 || var1->glbdom.ub < 0.5 )
      return TRUE;

   assert(SCIPvarGetStatus(var1) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var1) == SCIP_VARSTATUS_COLUMN);
   assert(SCIPvarGetStatus(var2) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var2) == SCIP_VARSTATUS_COLUMN);
   assert(SCIPvarIsBinary(var1));
   assert(SCIPvarIsBinary(var2));
   assert(var1->nlbchginfos + var1->nubchginfos <= 1);
   assert(var2->nlbchginfos + var2->nubchginfos <= 1);
   assert(var1->nlbchginfos == 0 || !var1->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
   assert(var1->nubchginfos == 0 || !var1->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */
   assert(var2->nlbchginfos == 0 || !var2->lbchginfos[0].redundant); /* otherwise, var would be globally fixed */
   assert(var2->nubchginfos == 0 || !var2->ubchginfos[0].redundant); /* otherwise, var would be globally fixed */

   if( var1->nlbchginfos == 1 )
      bdchgidx1 = &var1->lbchginfos[0].bdchgidx;
   else if( var1->nubchginfos == 1 )
      bdchgidx1 = &var1->ubchginfos[0].bdchgidx;
   else
      bdchgidx1 = NULL;

   if( var2->nlbchginfos == 1 )
      bdchgidx2 = &var2->lbchginfos[0].bdchgidx;
   else if( var2->nubchginfos == 1 )
      bdchgidx2 = &var2->ubchginfos[0].bdchgidx;
   else
      bdchgidx2 = NULL;

   return SCIPbdchgidxIsEarlier(bdchgidx1, bdchgidx2);
}

/** for a given array of variables, this function counts the numbers of variables for each variable and implied type combination */
void SCIPvarsCountTypes(
   SCIP_VAR**            vars,               /**< array of variables to count the types for */
   int                   nvars,              /**< number of variables in the array */
   int*                  nbinvars,           /**< pointer to store number of binary variables or NULL if not needed */
   int*                  nintvars,           /**< pointer to store number of integer variables or NULL if not needed */
   int*                  nbinimplvars,       /**< pointer to store number of binary implied integral vars or NULL if not needed */
   int*                  nintimplvars,       /**< pointer to store number of integer implied integral vars or NULL if not needed */
   int*                  ncontimplvars,      /**< pointer to store number of continuous implied integral vars or NULL if not needed */
   int*                  ncontvars           /**< pointer to store number of continuous variables or NULL if not needed */
   )
{
   assert(vars != NULL || nvars == 0);
   int binvars = 0;
   int binimplvars = 0;
   int intvars = 0;
   int intimplvars = 0;
   int contvars = 0;
   int contimplvars = 0;
   int v;

   for( v = 0; v < nvars; ++v )
   {
      SCIP_Bool implied = SCIPvarIsImpliedIntegral(vars[v]);
      switch( SCIPvarGetType(vars[v]) )
      {
         case SCIP_VARTYPE_BINARY:
            if( implied )
               ++binimplvars;
            else
               ++binvars;
            break;
         case SCIP_VARTYPE_INTEGER:
            if( implied )
               ++intimplvars;
            else
               ++intvars;
            break;
         case SCIP_VARTYPE_CONTINUOUS:
            if( implied )
               ++contimplvars;
            else
               ++contvars;
            break;
         default:
            SCIPerrorMessage("unknown variable type\n");
            SCIPABORT();
      } /*lint !e788*/
   }
   if( nbinvars != NULL )
      *nbinvars = binvars;
   if( nintvars != NULL )
      *nintvars = intvars;
   if( nbinimplvars != NULL )
      *nbinimplvars = binimplvars;
   if( nintimplvars != NULL )
      *nintimplvars = intimplvars;
   if( ncontimplvars != NULL )
      *ncontimplvars = contimplvars;
   if( ncontvars != NULL )
      *ncontvars = contvars;
}

/*
 * Hash functions
 */

/** gets the key (i.e. the name) of the given variable */
SCIP_DECL_HASHGETKEY(SCIPhashGetKeyVar)
{  /*lint --e{715}*/
   SCIP_VAR* var = (SCIP_VAR*)elem;

   assert(var != NULL);
   return var->name;
}




/*
 * simple functions implemented as defines
 */

/* In debug mode, the following methods are implemented as function calls to ensure
 * type validity.
 * In optimized mode, the methods are implemented as defines to improve performance.
 * However, we want to have them in the library anyways, so we have to undef the defines.
 */

#undef SCIPboundchgGetNewbound
#undef SCIPboundchgGetLPSolVal
#undef SCIPboundchgGetVar
#undef SCIPboundchgGetBoundchgtype
#undef SCIPboundchgGetBoundtype
#undef SCIPboundchgIsRedundant
#undef SCIPdomchgGetNBoundchgs
#undef SCIPdomchgGetBoundchg
#undef SCIPholelistGetLeft
#undef SCIPholelistGetRight
#undef SCIPholelistGetNext
#undef SCIPvarGetName
#undef SCIPvarGetNUses
#undef SCIPvarGetData
#undef SCIPvarSetData
#undef SCIPvarSetDelorigData
#undef SCIPvarSetTransData
#undef SCIPvarSetDeltransData
#undef SCIPvarGetStatus
#undef SCIPvarIsOriginal
#undef SCIPvarIsTransformed
#undef SCIPvarIsNegated
#undef SCIPvarGetType
#undef SCIPvarIsBinary
#undef SCIPvarIsIntegral
#undef SCIPvarIsImpliedIntegral
#undef SCIPvarIsNonimpliedIntegral
#undef SCIPvarIsInitial
#undef SCIPvarIsRemovable
#undef SCIPvarIsDeleted
#undef SCIPvarIsDeletable
#undef SCIPvarMarkDeletable
#undef SCIPvarMarkNotDeletable
#undef SCIPvarIsActive
#undef SCIPvarGetIndex
#undef SCIPvarGetProbindex
#undef SCIPvarGetTransVar
#undef SCIPvarGetCol
#undef SCIPvarIsInLP
#undef SCIPvarGetMinAggrCoef
#undef SCIPvarGetMaxAggrCoef
#undef SCIPvarGetAggrVar
#undef SCIPvarGetAggrScalar
#undef SCIPvarGetAggrConstant
#undef SCIPvarGetMultaggrNVars
#undef SCIPvarGetMultaggrVars
#undef SCIPvarGetMultaggrScalars
#undef SCIPvarGetMultaggrConstant
#undef SCIPvarGetNegatedVar
#undef SCIPvarGetNegationVar
#undef SCIPvarGetNegationConstant
#undef SCIPvarGetObj
#undef SCIPvarGetLbOriginal
#undef SCIPvarGetUbOriginal
#undef SCIPvarGetHolelistOriginal
#undef SCIPvarGetLbGlobal
#undef SCIPvarGetUbGlobal
#undef SCIPvarGetHolelistGlobal
#undef SCIPvarGetBestBoundGlobal
#undef SCIPvarGetWorstBoundGlobal
#undef SCIPvarGetLbLocal
#undef SCIPvarGetUbLocal
#undef SCIPvarGetHolelistLocal
#undef SCIPvarGetBestBoundLocal
#undef SCIPvarGetWorstBoundLocal
#undef SCIPvarGetBestBoundType
#undef SCIPvarGetWorstBoundType
#undef SCIPvarGetLbLazy
#undef SCIPvarGetUbLazy
#undef SCIPvarGetBranchFactor
#undef SCIPvarGetBranchPriority
#undef SCIPvarGetBranchDirection
#undef SCIPvarGetNVlbs
#undef SCIPvarGetVlbVars
#undef SCIPvarGetVlbCoefs
#undef SCIPvarGetVlbConstants
#undef SCIPvarGetNVubs
#undef SCIPvarGetVubVars
#undef SCIPvarGetVubCoefs
#undef SCIPvarGetVubConstants
#undef SCIPvarGetNImpls
#undef SCIPvarGetImplVars
#undef SCIPvarGetImplTypes
#undef SCIPvarGetImplBounds
#undef SCIPvarGetImplIds
#undef SCIPvarGetNCliques
#undef SCIPvarGetCliques
#undef SCIPvarGetLPSol
#undef SCIPvarGetNLPSol
#undef SCIPvarGetBdchgInfoLb
#undef SCIPvarGetNBdchgInfosLb
#undef SCIPvarGetBdchgInfoUb
#undef SCIPvarGetNBdchgInfosUb
#undef SCIPvarGetValuehistory
#undef SCIPvarGetPseudoSol
#undef SCIPvarCatchEvent
#undef SCIPvarDropEvent
#undef SCIPvarGetVSIDS
#undef SCIPvarGetCliqueComponentIdx
#undef SCIPvarIsRelaxationOnly
#undef SCIPvarMarkRelaxationOnly
#undef SCIPbdchgidxGetPos
#undef SCIPbdchgidxGetDepth
#undef SCIPbdchgidxIsEarlierNonNull
#undef SCIPbdchgidxIsEarlier
#undef SCIPbdchginfoGetOldbound
#undef SCIPbdchginfoGetNewbound
#undef SCIPbdchginfoGetVar
#undef SCIPbdchginfoGetChgtype
#undef SCIPbdchginfoGetBoundtype
#undef SCIPbdchginfoGetDepth
#undef SCIPbdchginfoGetPos
#undef SCIPbdchginfoGetIdx
#undef SCIPbdchginfoGetInferVar
#undef SCIPbdchginfoGetInferCons
#undef SCIPbdchginfoGetInferProp
#undef SCIPbdchginfoGetInferInfo
#undef SCIPbdchginfoGetInferBoundtype
#undef SCIPbdchginfoIsRedundant
#undef SCIPbdchginfoHasInferenceReason
#undef SCIPbdchginfoIsTighter


/** returns the new value of the bound in the bound change data */
SCIP_Real SCIPboundchgGetNewbound(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return boundchg->newbound;
}

/** returns the lp solution value in the branching data of the bound change data */
SCIP_Real SCIPboundchgGetLPSolVal(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return boundchg->data.branchingdata.lpsolval;
}

/** returns the variable of the bound change in the bound change data */
SCIP_VAR* SCIPboundchgGetVar(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return boundchg->var;
}

/** returns the bound change type of the bound change in the bound change data */
SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return (SCIP_BOUNDCHGTYPE)(boundchg->boundchgtype);
}

/** returns the bound type of the bound change in the bound change data */
SCIP_BOUNDTYPE SCIPboundchgGetBoundtype(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return (SCIP_BOUNDTYPE)(boundchg->boundtype);
}

/** returns whether the bound change is redundant due to a more global bound that is at least as strong */
SCIP_Bool SCIPboundchgIsRedundant(
   SCIP_BOUNDCHG*        boundchg            /**< bound change data */
   )
{
   assert(boundchg != NULL);

   return boundchg->redundant;
}

/** returns the number of bound changes in the domain change data */
int SCIPdomchgGetNBoundchgs(
   SCIP_DOMCHG*          domchg              /**< domain change data */
   )
{
   return domchg != NULL ? domchg->domchgbound.nboundchgs : 0;
}

/** returns a particular bound change in the domain change data */
SCIP_BOUNDCHG* SCIPdomchgGetBoundchg(
   SCIP_DOMCHG*          domchg,             /**< domain change data */
   int                   pos                 /**< position of the bound change in the domain change data */
   )
{
   assert(domchg != NULL);
   assert(0 <= pos && pos < (int)domchg->domchgbound.nboundchgs);

   return &domchg->domchgbound.boundchgs[pos];
}

/** returns left bound of open interval in hole */
SCIP_Real SCIPholelistGetLeft(
   SCIP_HOLELIST*        holelist            /**< hole list pointer to hole of interest */
   )
{
   assert(holelist != NULL);

   return holelist->hole.left;
}

/** returns right bound of open interval in hole */
SCIP_Real SCIPholelistGetRight(
   SCIP_HOLELIST*        holelist            /**< hole list pointer to hole of interest */
   )
{
   assert(holelist != NULL);

   return holelist->hole.right;
}

/** returns next hole in list */
SCIP_HOLELIST* SCIPholelistGetNext(
   SCIP_HOLELIST*        holelist            /**< hole list pointer to hole of interest */
   )
{
   assert(holelist != NULL);

   return holelist->next;
}

/** returns the name of the variable
 *
 *  @note to change the name of a variable, use SCIPchgVarName() from scip.h
 */
const char* SCIPvarGetName(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->name;
}

/** gets number of times, the variable is currently captured */
int SCIPvarGetNUses(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->nuses;
}

/** returns the user data of the variable */
SCIP_VARDATA* SCIPvarGetData(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->vardata;
}

/** sets the user data for the variable */
void SCIPvarSetData(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_VARDATA*         vardata             /**< user variable data */
   )
{
   assert(var != NULL);

   var->vardata = vardata;
}

/** sets method to free user data for the original variable */
void SCIPvarSetDelorigData(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_DECL_VARDELORIG  ((*vardelorig))     /**< frees user data of original variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);

   var->vardelorig = vardelorig;
}

/** sets method to transform user data of the variable */
void SCIPvarSetTransData(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_DECL_VARTRANS    ((*vartrans))       /**< creates transformed user data by transforming original user data */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);

   var->vartrans = vartrans;
}

/** sets method to free transformed user data for the variable */
void SCIPvarSetDeltransData(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_DECL_VARDELTRANS ((*vardeltrans))    /**< frees user data of transformed variable */
   )
{
   assert(var != NULL);

   var->vardeltrans = vardeltrans;
}

/** sets method to copy this variable into sub-SCIPs */
void SCIPvarSetCopyData(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_DECL_VARCOPY     ((*varcopy))        /**< copy method of the variable */
   )
{
   assert(var != NULL);

   var->varcopy = varcopy;
}

/** sets the initial flag of a variable; only possible for original or loose variables */
SCIP_RETCODE SCIPvarSetInitial(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Bool             initial             /**< initial flag */
   )
{
   assert(var != NULL);

   if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
      return SCIP_INVALIDCALL;

   var->initial = initial;

   return SCIP_OKAY;
}

/** sets the removable flag of a variable; only possible for original or loose variables */
SCIP_RETCODE SCIPvarSetRemovable(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Bool             removable           /**< removable flag */
   )
{
   assert(var != NULL);

   if( (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_ORIGINAL && (SCIP_VARSTATUS)var->varstatus != SCIP_VARSTATUS_LOOSE )
      return SCIP_INVALIDCALL;

   var->removable = removable;

   return SCIP_OKAY;
}

/** gets status of variable */
SCIP_VARSTATUS SCIPvarGetStatus(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIP_VARSTATUS)(var->varstatus);
}

/** returns the status of the exact variable data */
SCIP_VARSTATUS SCIPvarGetStatusExact(
   SCIP_VAR*             var                 /**< scip variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);

   return var->exactdata->varstatusexact;
}

/** returns whether the variable has exact variable data */
SCIP_Bool SCIPvarIsExact(
   SCIP_VAR*             var                 /**< scip variable */
   )
{
   assert(var != NULL);

   return (var->exactdata != NULL);
}

/** returns whether the variable belongs to the original problem */
SCIP_Bool SCIPvarIsOriginal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);

   return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL
      || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED
         && SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL));
}

/** returns whether the variable belongs to the transformed problem */
SCIP_Bool SCIPvarIsTransformed(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED || var->negatedvar != NULL);

   return (SCIPvarGetStatus(var) != SCIP_VARSTATUS_ORIGINAL
      && (SCIPvarGetStatus(var) != SCIP_VARSTATUS_NEGATED
         || SCIPvarGetStatus(var->negatedvar) != SCIP_VARSTATUS_ORIGINAL));
}

/** returns whether the variable was created by negation of a different variable */
SCIP_Bool SCIPvarIsNegated(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
}

/** gets type of variable */
SCIP_VARTYPE SCIPvarGetType(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIP_VARTYPE)(var->vartype);
}

/** gets the implied integral type of the variable */
SCIP_IMPLINTTYPE SCIPvarGetImplType(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIP_IMPLINTTYPE)(var->varimpltype);
}

/** returns TRUE if the variable is of binary type; this is the case if:
 *  (1) variable type is binary
 *  (2) variable type is integer or implicit integer and
 *      (i)  the lazy lower bound or the global lower bound is greater than or equal to zero
 *      (ii) the lazy upper bound or the global upper bound is less than or equal to one
 */
SCIP_Bool SCIPvarIsBinary(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY ||
      ((SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS || SCIPvarGetImplType(var) != SCIP_IMPLINTTYPE_NONE)
         && MAX(var->glbdom.lb, var->lazylb) >= 0.0 && MIN(var->glbdom.ub, var->lazyub) <= 1.0));
}

/** returns whether variable is of integral type (binary, integer, or implied integral of any type) */
SCIP_Bool SCIPvarIsIntegral(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return (SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS || SCIPvarGetImplType(var) != SCIP_IMPLINTTYPE_NONE);
}

/** returns whether variable is implied integral (weakly or strongly) */
SCIP_Bool SCIPvarIsImpliedIntegral(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return (SCIPvarGetImplType(var) != SCIP_IMPLINTTYPE_NONE);
}

/** returns TRUE if the variable is integral, but not implied integral. */
SCIP_Bool SCIPvarIsNonimpliedIntegral(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS && SCIPvarGetImplType(var) == SCIP_IMPLINTTYPE_NONE;
}

/** returns whether variable's column should be present in the initial root LP */
SCIP_Bool SCIPvarIsInitial(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->initial;
}

/** returns whether variable's column is removable from the LP (due to aging or cleanup) */
SCIP_Bool SCIPvarIsRemovable(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->removable;
}

/** returns whether the variable was deleted from the problem */
SCIP_Bool SCIPvarIsDeleted(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->deleted;
}

/** marks the variable to be deletable, i.e., it may be deleted completely from the problem;
 *  method can only be called before the variable is added to the problem by SCIPaddVar() or SCIPaddPricedVar()
 */
void SCIPvarMarkDeletable(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->probindex == -1);

   var->deletable = TRUE;
}

/** marks the variable to be not deletable from the problem */
void SCIPvarMarkNotDeletable(
   SCIP_VAR*             var
   )
{
   assert(var != NULL);

   var->deletable = FALSE;
}

/** marks variable to be deleted from global structures (cliques etc.) when cleaning up
 *
 *  @note: this is not equivalent to marking the variable itself for deletion, this is done by using SCIPvarMarkDeletable()
 */
void SCIPvarMarkDeleteGlobalStructures(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   var->delglobalstructs = TRUE;
}

/** returns whether the variable was flagged for deletion from global structures (cliques etc.) */
SCIP_Bool SCIPvarIsMarkedDeleteGlobalStructures(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->delglobalstructs;
}

/** returns whether a variable has been introduced to define a relaxation
 *
 * These variables are only valid for the current SCIP solve round,
 * they are not contained in any (checked) constraints, but may be used
 * in cutting planes, for example.
 * Relaxation-only variables are not copied by SCIPcopyVars and cuts
 * that contain these variables are not added as linear constraints when
 * restarting or transferring information from a copied SCIP to a SCIP.
 * Also conflicts with relaxation-only variables are not generated at
 * the moment.
 */
SCIP_Bool SCIPvarIsRelaxationOnly(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->relaxationonly;
}

/** marks that this variable has only been introduced to define a relaxation
 *
 * The variable must not have a coefficient in the objective and must be deletable.
 * If it is not marked deletable, it will be marked as deletable, which is only possible
 * before the variable is added to a problem.
 *
 * @see SCIPvarIsRelaxationOnly
 * @see SCIPvarMarkDeletable
 */
void SCIPvarMarkRelaxationOnly(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetObj(var) == 0.0);

   if( !SCIPvarIsDeletable(var) )
      SCIPvarMarkDeletable(var);

   var->relaxationonly = TRUE;
}

/** returns whether variable is allowed to be deleted completely from the problem */
SCIP_Bool SCIPvarIsDeletable(
   SCIP_VAR*             var
   )
{
   assert(var != NULL);

   return var->deletable;
}

/** returns whether variable is an active (neither fixed nor aggregated) variable */
SCIP_Bool SCIPvarIsActive(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (var->probindex >= 0);
}

/** gets unique index of variable */
int SCIPvarGetIndex(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->index;
}

/** gets position of variable in problem, or -1 if variable is not active */
int SCIPvarGetProbindex(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->probindex;
}

/** gets transformed variable of ORIGINAL variable */
SCIP_VAR* SCIPvarGetTransVar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL);

   return var->data.original.transvar;
}

/** gets column of COLUMN variable */
SCIP_COL* SCIPvarGetCol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN);

   return var->data.col;
}

/** gets exact column of COLUMN variable */
SCIP_COLEXACT* SCIPvarGetColExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_COLUMN);

   return var->exactdata->colexact;
}

/** returns whether the variable is a COLUMN variable that is member of the current LP */
SCIP_Bool SCIPvarIsInLP(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN && SCIPcolIsInLP(var->data.col));
}

/** gets minimal absolute coefficient of a loose variable in (multi)aggregations of other variables */
SCIP_Real SCIPvarGetMinAggrCoef(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);

   return var->data.loose.minaggrcoef;
}

/** gets lower bound on absolute coefficient of a loose variable in (multi)aggregations of other variables */
SCIP_Real SCIPvarGetMaxAggrCoef(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE);

   return var->data.loose.maxaggrcoef;
}

/** gets upper bound on absolute coefficient of a loose variable in (multi)aggregations of other variables */
SCIP_VAR* SCIPvarGetAggrVar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
   assert(!var->donotaggr);

   return var->data.aggregate.var;
}

/** gets aggregation scalar a of an aggregated variable x = a*y + c */
SCIP_Real SCIPvarGetAggrScalar(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
   assert(!var->donotaggr);

   return var->data.aggregate.scalar;
}

/** gets aggregation scalar a of an aggregated variable x = a*y + c */
SCIP_RATIONAL* SCIPvarGetAggrScalarExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_AGGREGATED);

   return var->exactdata->aggregate.scalar;
}

/** gets aggregation constant c of an aggregated variable x = a*y + c */
SCIP_Real SCIPvarGetAggrConstant(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED);
   assert(!var->donotaggr);

   return var->data.aggregate.constant;
}

/** gets aggregation constant c of an aggregated variable x = a*y + c */
SCIP_RATIONAL* SCIPvarGetAggrConstantExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_AGGREGATED);

   return var->exactdata->aggregate.constant;
}

/** gets number n of aggregation variables of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
int SCIPvarGetMultaggrNVars(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);

   return var->data.multaggr.nvars;
}

/** gets vector of aggregation variables y of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIP_VAR** SCIPvarGetMultaggrVars(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);

   return var->data.multaggr.vars;
}

/** gets vector of aggregation scalars a of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIP_Real* SCIPvarGetMultaggrScalars(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);

   return var->data.multaggr.scalars;
}

/** gets vector of exact aggregation scalars a of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIP_RATIONAL** SCIPvarGetMultaggrScalarsExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);
   assert(var->exactdata != NULL);

   return var->exactdata->multaggr.scalars;
}

/** gets aggregation constant c of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIP_Real SCIPvarGetMultaggrConstant(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);

   return var->data.multaggr.constant;
}

/** gets exact aggregation constant c of a multi aggregated variable x = a0*y0 + ... + a(n-1)*y(n-1) + c */
SCIP_RATIONAL* SCIPvarGetMultaggrConstantExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_MULTAGGR);
   assert(!var->donotmultaggr);
   assert(var->exactdata != NULL);

   return var->exactdata->multaggr.constant;
}

/** gets the negation of the given variable; may return NULL, if no negation is existing yet */
SCIP_VAR* SCIPvarGetNegatedVar(
   SCIP_VAR*             var                 /**< negated problem variable */
   )
{
   assert(var != NULL);

   return var->negatedvar;
}

/** gets the negation variable x of a negated variable x' = offset - x */
SCIP_VAR* SCIPvarGetNegationVar(
   SCIP_VAR*             var                 /**< negated problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);

   return var->negatedvar;
}

/** gets the negation offset of a negated variable x' = offset - x */
SCIP_Real SCIPvarGetNegationConstant(
   SCIP_VAR*             var                 /**< negated problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);

   return var->data.negate.constant;
}

/** gets objective function value of variable */
SCIP_Real SCIPvarGetObj(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->obj;
}

/** gets exact objective function value of variable */
SCIP_RATIONAL* SCIPvarGetObjExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);

   return var->exactdata->obj;
}

/** gets exact objective function value of variable */
SCIP_INTERVAL SCIPvarGetObjInterval(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);

   return var->exactdata->objinterval;
}

/** gets the unchanged objective function value of a variable (ignoring temproray changes performed in probing mode) */
SCIP_Real SCIPvarGetUnchangedObj(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->unchangedobj;
}

/** gets corresponding objective value of active, fixed, or multi-aggregated problem variable of given variable
 *  e.g. obj(x) = 1 this method returns for ~x the value -1
 */
SCIP_RETCODE SCIPvarGetAggregatedObj(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_Real*            aggrobj             /**< pointer to store the aggregated objective value */
   )
{
   SCIP_VAR* probvar = var;
   SCIP_Real mult = 1.0;

   assert(probvar != NULL);
   assert(aggrobj != NULL);

   while( probvar != NULL )
   {
      switch( SCIPvarGetStatus(probvar) )
      {
      case SCIP_VARSTATUS_ORIGINAL:
      case SCIP_VARSTATUS_LOOSE:
      case SCIP_VARSTATUS_COLUMN:
         (*aggrobj) = mult * SCIPvarGetObj(probvar);
         return SCIP_OKAY;

      case SCIP_VARSTATUS_FIXED:
         assert(SCIPvarGetObj(probvar) == 0.0);
         (*aggrobj) = 0.0;
         return SCIP_OKAY;

      case SCIP_VARSTATUS_MULTAGGR:
         /* handle multi-aggregated variables depending on one variable only (possibly caused by SCIPvarFlattenAggregationGraph()) */
         if ( probvar->data.multaggr.nvars == 1 )
         {
            assert( probvar->data.multaggr.vars != NULL );
            assert( probvar->data.multaggr.scalars != NULL );
            assert( probvar->data.multaggr.vars[0] != NULL );
            mult *= probvar->data.multaggr.scalars[0];
            probvar = probvar->data.multaggr.vars[0];
            break;
         }
         else
         {
            SCIP_Real tmpobj;
            int v;

            (*aggrobj) = 0.0;

            for( v = probvar->data.multaggr.nvars - 1; v >= 0; --v )
            {
               SCIP_CALL( SCIPvarGetAggregatedObj(probvar->data.multaggr.vars[v], &tmpobj) );
               (*aggrobj) += probvar->data.multaggr.scalars[v] * tmpobj;
            }
            return SCIP_OKAY;
         }

      case SCIP_VARSTATUS_AGGREGATED:  /* x = a'*x' + c'  =>  a*x + c == (a*a')*x' + (a*c' + c) */
         assert(probvar->data.aggregate.var != NULL);
         mult *= probvar->data.aggregate.scalar;
         probvar = probvar->data.aggregate.var;
         break;

      case SCIP_VARSTATUS_NEGATED:     /* x =  - x' + c'  =>  a*x + c ==   (-a)*x' + (a*c' + c) */
         assert(probvar->negatedvar != NULL);
         assert(SCIPvarGetStatus(probvar->negatedvar) != SCIP_VARSTATUS_NEGATED);
         assert(probvar->negatedvar->negatedvar == probvar);
         mult *= -1.0;
         probvar = probvar->negatedvar;
         break;

      default:
         SCIPABORT();
         return SCIP_INVALIDDATA; /*lint !e527*/
      }
   }

   return SCIP_INVALIDDATA;
}

/** gets original lower bound of original problem variable (i.e. the bound set in problem creation) */
SCIP_Real SCIPvarGetLbOriginal(
   SCIP_VAR*             var                 /**< original problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsOriginal(var));

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
      return var->data.original.origdom.lb;
   else
   {
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);

      return var->data.negate.constant - var->negatedvar->data.original.origdom.ub;
   }
}

/** gets exact original lower bound of original problem variable (i.e. the bound set in problem creation) */
SCIP_RATIONAL* SCIPvarGetLbOriginalExact(
   SCIP_VAR*             var                 /**< original problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsOriginal(var));
   assert(var->exactdata != NULL);

   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_ORIGINAL )
      return var->exactdata->origdom.lb;
   else
   {
      assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);

      SCIPerrorMessage("negated var not implemented yet for rational data \n");
      SCIPABORT();
      return NULL;
   }
}

/** gets original upper bound of original problem variable (i.e. the bound set in problem creation) */
SCIP_Real SCIPvarGetUbOriginal(
   SCIP_VAR*             var                 /**< original problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsOriginal(var));

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
      return var->data.original.origdom.ub;
   else
   {
      assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatus(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);

      return var->data.negate.constant - var->negatedvar->data.original.origdom.lb;
   }
}

/** gets exact original upper bound of original problem variable (i.e. the bound set in problem creation) */
SCIP_RATIONAL* SCIPvarGetUbOriginalExact(
   SCIP_VAR*             var                 /**< original problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsOriginal(var));
   assert(var->exactdata != NULL);

   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_ORIGINAL )
      return var->exactdata->origdom.ub;
   else
   {
      assert(SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_NEGATED);
      assert(var->negatedvar != NULL);
      assert(SCIPvarGetStatusExact(var->negatedvar) == SCIP_VARSTATUS_ORIGINAL);

      SCIPerrorMessage("negated var not implemented yet for rational data \n");
      SCIPABORT();
      return NULL;
   }
}

/** gets the original hole list of an original variable */
SCIP_HOLELIST* SCIPvarGetHolelistOriginal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsOriginal(var));

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_ORIGINAL )
      return var->data.original.origdom.holelist;

   return NULL;
}

/** gets global lower bound of variable */
SCIP_Real SCIPvarGetLbGlobal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->glbdom.lb;
}

/** gets exact global lower bound of variable */
SCIP_RATIONAL* SCIPvarGetLbGlobalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.lb != NULL);

   return var->exactdata->glbdom.lb;
}

/** gets global upper bound of variable */
SCIP_Real SCIPvarGetUbGlobal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->glbdom.ub;
}

/** gets exact global upper bound of variable */
SCIP_RATIONAL* SCIPvarGetUbGlobalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.ub != NULL);

   return var->exactdata->glbdom.ub;
}

/** gets the global hole list of an active variable */
SCIP_HOLELIST* SCIPvarGetHolelistGlobal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->glbdom.holelist;
}

/** gets best global bound of variable with respect to the objective function */
SCIP_Real SCIPvarGetBestBoundGlobal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return var->glbdom.lb;
   else
      return var->glbdom.ub;
}

/** gets best exact global bound of variable with respect to the objective function */
SCIP_RATIONAL* SCIPvarGetBestBoundGlobalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.lb != NULL);
   assert(var->exactdata->glbdom.ub != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return var->exactdata->glbdom.lb;
   else
      return var->exactdata->glbdom.ub;
}

/** gets worst global bound of variable with respect to the objective function */
SCIP_Real SCIPvarGetWorstBoundGlobal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return var->glbdom.ub;
   else
      return var->glbdom.lb;
}

/** gets worst exact global bound of variable with respect to the objective function */
SCIP_RATIONAL* SCIPvarGetWorstBoundGlobalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.lb != NULL);
   assert(var->exactdata->glbdom.ub != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return var->exactdata->glbdom.ub;
   else
      return var->exactdata->glbdom.lb;
}

/** gets current lower bound of variable */
SCIP_Real SCIPvarGetLbLocal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->locdom.lb;
}

/** gets current exact lower bound of variable */
SCIP_RATIONAL* SCIPvarGetLbLocalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.lb != NULL);

   return var->exactdata->locdom.lb;
}

void SCIPvarGetLbLocalExactMaximal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_RATIONAL*        output              /**< output rational */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.lb != NULL);
   SCIPrationalSetFraction(output, (SCIP_Longint) (floor(var->locdom.lb)), 1LL);
   SCIPrationalMax(output, output, var->exactdata->locdom.lb);
}

/** gets current upper bound of variable */
SCIP_Real SCIPvarGetUbLocal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->locdom.ub;
}

/** gets current exact upper bound of variable */
SCIP_RATIONAL* SCIPvarGetUbLocalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.ub != NULL);

   return var->exactdata->locdom.ub;
}

void SCIPvarGetUbLocalExactMinimal(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_RATIONAL*        output              /**< output rational */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.ub != NULL);
   SCIPrationalSetFraction(output, (SCIP_Longint) (ceil(var->locdom.ub)), 1LL);
   SCIPrationalMin(output, output, var->exactdata->locdom.ub);
}

/** gets the current hole list of an active variable */
SCIP_HOLELIST* SCIPvarGetHolelistLocal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->locdom.holelist;
}

/** gets best local bound of variable with respect to the objective function */
SCIP_Real SCIPvarGetBestBoundLocal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return var->locdom.lb;
   else
      return var->locdom.ub;
}

/** gets best exact local bound of variable with respect to the objective function */
SCIP_RATIONAL* SCIPvarGetBestBoundLocalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.lb != NULL);
   assert(var->exactdata->locdom.ub != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return var->exactdata->locdom.lb;
   else
      return var->exactdata->locdom.ub;
}

/** gets worst local bound of variable with respect to the objective function */
SCIP_Real SCIPvarGetWorstBoundLocal(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return var->locdom.ub;
   else
      return var->locdom.lb;
}

/** gets worst exact local bound of variable with respect to the objective function */
SCIP_RATIONAL* SCIPvarGetWorstBoundLocalExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.lb != NULL);
   assert(var->exactdata->locdom.ub != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return var->exactdata->locdom.ub;
   else
      return var->exactdata->locdom.lb;
}

/** gets type (lower or upper) of best bound of variable with respect to the objective function */
SCIP_BOUNDTYPE SCIPvarGetBestBoundType(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return SCIP_BOUNDTYPE_LOWER;
   else
      return SCIP_BOUNDTYPE_UPPER;
}

/** gets type (lower or upper) of best bound of variable with respect to the objective function */
SCIP_BOUNDTYPE SCIPvarGetBestBoundTypeExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return SCIP_BOUNDTYPE_LOWER;
   else
      return SCIP_BOUNDTYPE_UPPER;
}

/** gets type (lower or upper) of worst bound of variable with respect to the objective function */
SCIP_BOUNDTYPE SCIPvarGetWorstBoundType(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( var->obj >= 0.0 )
      return SCIP_BOUNDTYPE_UPPER;
   else
      return SCIP_BOUNDTYPE_LOWER;
}

/** gets type (lower or upper) of worst bound of variable with respect to the objective function */
SCIP_BOUNDTYPE SCIPvarGetWorstBoundTypeExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(var->exactdata->obj != NULL);

   if( !SCIPrationalIsNegative(var->exactdata->obj) )
      return SCIP_BOUNDTYPE_UPPER;
   else
      return SCIP_BOUNDTYPE_LOWER;
}

/** gets lazy lower bound of variable, returns -infinity if the variable has no lazy lower bound */
SCIP_Real SCIPvarGetLbLazy(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->lazylb;
}

/** gets lazy upper bound of variable, returns infinity if the variable has no lazy upper bound */
SCIP_Real SCIPvarGetUbLazy(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->lazyub;
}

/** gets the branch factor of the variable; this value can be used in the branching methods to scale the score
 *  values of the variables; higher factor leads to a higher probability that this variable is chosen for branching
 */
SCIP_Real SCIPvarGetBranchFactor(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->branchfactor;
}

/** gets the branch priority of the variable; variables with higher priority should always be preferred to variables
 *  with lower priority
 */
int SCIPvarGetBranchPriority(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->branchpriority;
}

/** gets the preferred branch direction of the variable (downwards, upwards, or auto) */
SCIP_BRANCHDIR SCIPvarGetBranchDirection(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return (SCIP_BRANCHDIR)var->branchdirection;
}

/** gets number of variable lower bounds x >= b_i*z_i + d_i of given variable x */
int SCIPvarGetNVlbs(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetNVbds(var->vlbs);
}

/** gets array with bounding variables z_i in variable lower bounds x >= b_i*z_i + d_i of given variable x;
 *  the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
 */
SCIP_VAR** SCIPvarGetVlbVars(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetVars(var->vlbs);
}

/** gets array with bounding coefficients b_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
SCIP_Real* SCIPvarGetVlbCoefs(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetCoefs(var->vlbs);
}

/** gets array with bounding constants d_i in variable lower bounds x >= b_i*z_i + d_i of given variable x */
SCIP_Real* SCIPvarGetVlbConstants(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetConstants(var->vlbs);
}

/** gets number of variable upper bounds x <= b_i*z_i + d_i of given variable x */
int SCIPvarGetNVubs(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetNVbds(var->vubs);
}

/** gets array with bounding variables z_i in variable upper bounds x <= b_i*z_i + d_i of given variable x;
 *  the variable bounds are sorted by increasing variable index of the bounding variable z_i (see SCIPvarGetIndex())
 */
SCIP_VAR** SCIPvarGetVubVars(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetVars(var->vubs);
}

/** gets array with bounding coefficients b_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
SCIP_Real* SCIPvarGetVubCoefs(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetCoefs(var->vubs);
}

/** gets array with bounding constants d_i in variable upper bounds x <= b_i*z_i + d_i of given variable x */
SCIP_Real* SCIPvarGetVubConstants(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return SCIPvboundsGetConstants(var->vubs);
}

/** gets number of implications  y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
 *  there are no implications for nonbinary variable x
 */
int SCIPvarGetNImpls(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for implications for x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   return SCIPimplicsGetNImpls(var->implics, varfixing);
}

/** gets array with implication variables y of implications  y <= b or y >= b for x == 0 or x == 1 of given active
 *  problem variable x, there are no implications for nonbinary variable x;
 *  the implications are sorted such that implications with binary implied variables precede the ones with non-binary
 *  implied variables, and as a second criteria, the implied variables are sorted by increasing variable index
 *  (see SCIPvarGetIndex())
 */
SCIP_VAR** SCIPvarGetImplVars(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for implications for x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   return SCIPimplicsGetVars(var->implics, varfixing);
}

/** gets array with implication types of implications  y <= b or y >= b for x == 0 or x == 1 of given active problem
 *  variable x (SCIP_BOUNDTYPE_UPPER if y <= b, SCIP_BOUNDTYPE_LOWER if y >= b),
 *  there are no implications for nonbinary variable x
 */
SCIP_BOUNDTYPE* SCIPvarGetImplTypes(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for implications for x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   return SCIPimplicsGetTypes(var->implics, varfixing);
}

/** gets array with implication bounds b of implications  y <= b or y >= b for x == 0 or x == 1 of given active problem
 *  variable x, there are no implications for nonbinary variable x
 */
SCIP_Real* SCIPvarGetImplBounds(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for implications for x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   return SCIPimplicsGetBounds(var->implics, varfixing);
}

/** Gets array with unique ids of implications  y <= b or y >= b for x == 0 or x == 1 of given active problem variable x,
 *  there are no implications for nonbinary variable x.
 *  If an implication is a shortcut, i.e., it was added as part of the transitive closure of another implication,
 *  its id is negative, otherwise it is nonnegative.
 */
int* SCIPvarGetImplIds(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for implications for x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);
   assert(SCIPvarIsActive(var));

   return SCIPimplicsGetIds(var->implics, varfixing);
}

/** gets number of cliques, the active variable is contained in */
int SCIPvarGetNCliques(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);

   return SCIPcliquelistGetNCliques(var->cliquelist, varfixing);
}

/** gets array of cliques, the active variable is contained in */
SCIP_CLIQUE** SCIPvarGetCliques(
   SCIP_VAR*             var,                /**< active problem variable */
   SCIP_Bool             varfixing           /**< FALSE for cliques containing x == 0, TRUE for x == 1 */
   )
{
   assert(var != NULL);

   return SCIPcliquelistGetCliques(var->cliquelist, varfixing);
}

/** gets primal LP solution value of variable */
SCIP_Real SCIPvarGetLPSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIPcolGetPrimsol(var->data.col);
   else
      return SCIPvarGetLPSol_rec(var);
}

/** gets exact primal LP solution value of variable */
void SCIPvarGetLPSolExact(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_RATIONAL*        res                 /**< resulting value */
   )
{
   assert(var != NULL);

   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_COLUMN )
      SCIPrationalSetRational(res, SCIPcolExactGetPrimsol(var->exactdata->colexact));
   else
      SCIPvarGetLPSolExact_rec(var, res);
}

/** gets primal NLP solution value of variable */
SCIP_Real SCIPvarGetNLPSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN || SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE) )
      return var->nlpsol;
   else
      return SCIPvarGetNLPSol_rec(var);
}

/** return lower bound change info at requested position */
SCIP_BDCHGINFO* SCIPvarGetBdchgInfoLb(
   SCIP_VAR*             var,                /**< problem variable */
   int                   pos                 /**< requested position */
   )
{
   assert(pos >= 0);
   assert(pos < var->nlbchginfos);

   return &var->lbchginfos[pos];
}

/** gets the number of lower bound change info array */
int SCIPvarGetNBdchgInfosLb(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   return var->nlbchginfos;
}

/** return upper bound change info at requested position */
SCIP_BDCHGINFO* SCIPvarGetBdchgInfoUb(
   SCIP_VAR*             var,                /**< problem variable */
   int                   pos                 /**< requested position */
   )
{
   assert(pos >= 0);
   assert(pos < var->nubchginfos);

   return &var->ubchginfos[pos];
}

/** gets the number upper bound change info array */
int SCIPvarGetNBdchgInfosUb(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->nubchginfos;
}

/** returns the value based history for the variable */
SCIP_VALUEHISTORY* SCIPvarGetValuehistory(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   return var->valuehistory;
}

/** gets pseudo solution value of variable */
SCIP_Real SCIPvarGetPseudoSol(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIPvarGetBestBoundLocal(var);
   else
      return SCIPvarGetPseudoSol_rec(var);
}

/** gets exact pseudo solution value of variable */
SCIP_RATIONAL* SCIPvarGetPseudoSolExact(
   SCIP_VAR*             var                 /**< problem variable */
   )
{
   assert(var != NULL);

   if( SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatusExact(var) == SCIP_VARSTATUS_COLUMN )
      return SCIPvarGetBestBoundLocalExact(var);
   else
      return SCIPvarGetPseudoSolExact_rec(var);
}

/** returns the variable's VSIDS score */
SCIP_Real SCIPvarGetVSIDS(
   SCIP_VAR*             var,                /**< problem variable */
   SCIP_STAT*            stat,               /**< problem statistics */
   SCIP_BRANCHDIR        dir                 /**< branching direction (downwards, or upwards) */
   )
{
   assert(var != NULL);

   if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_LOOSE || SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN )
      return SCIPhistoryGetVSIDS(var->history, dir)/stat->vsidsweight;
   else
      return SCIPvarGetVSIDS_rec(var, stat, dir);
}

/** includes event handler with given data in variable's event filter */
SCIP_RETCODE SCIPvarCatchEvent(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTTYPE        eventtype,          /**< event type to catch */
   SCIP_EVENTHDLR*       eventhdlr,          /**< event handler to call for the event processing */
   SCIP_EVENTDATA*       eventdata,          /**< event data to pass to the event handler for the event processing */
   int*                  filterpos           /**< pointer to store position of event filter entry, or NULL */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(var->eventfilter != NULL);
   assert((eventtype & ~SCIP_EVENTTYPE_VARCHANGED) == 0);
   assert((eventtype & SCIP_EVENTTYPE_VARCHANGED) != 0);
   assert(SCIPvarIsTransformed(var));

   SCIPsetDebugMsg(set, "catch event of type 0x%" SCIP_EVENTTYPE_FORMAT " of variable <%s> with handler %p and data %p\n",
      eventtype, var->name, (void*)eventhdlr, (void*)eventdata);

   SCIP_CALL( SCIPeventfilterAdd(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );

   return SCIP_OKAY;
}

/** deletes event handler with given data from variable's event filter */
SCIP_RETCODE SCIPvarDropEvent(
   SCIP_VAR*             var,                /**< problem variable */
   BMS_BLKMEM*           blkmem,             /**< block memory */
   SCIP_SET*             set,                /**< global SCIP settings */
   SCIP_EVENTTYPE        eventtype,          /**< event type mask of dropped event */
   SCIP_EVENTHDLR*       eventhdlr,          /**< event handler to call for the event processing */
   SCIP_EVENTDATA*       eventdata,          /**< event data to pass to the event handler for the event processing */
   int                   filterpos           /**< position of event filter entry returned by SCIPvarCatchEvent(), or -1 */
   )
{
   assert(var != NULL);
   assert(set != NULL);
   assert(var->scip == set->scip);
   assert(var->eventfilter != NULL);
   assert(SCIPvarIsTransformed(var));

   SCIPsetDebugMsg(set, "drop event of variable <%s> with handler %p and data %p\n", var->name, (void*)eventhdlr,
         (void*)eventdata);

   SCIP_CALL( SCIPeventfilterDel(var->eventfilter, blkmem, set, eventtype, eventhdlr, eventdata, filterpos) );

   return SCIP_OKAY;
}

/** returns the position of the bound change index */
int SCIPbdchgidxGetPos(
   SCIP_BDCHGIDX*        bdchgidx            /**< bound change index */
   )
{
   assert(bdchgidx != NULL);

   return bdchgidx->pos;
}

/** returns the depth of the bound change index */
int SCIPbdchgidxGetDepth(
   SCIP_BDCHGIDX*        bdchgidx            /**< bound change index */
   )
{
   assert(bdchgidx != NULL);

   return bdchgidx->depth;
}

/** returns whether first bound change index belongs to an earlier applied bound change than second one */
SCIP_Bool SCIPbdchgidxIsEarlierNonNull(
   SCIP_BDCHGIDX*        bdchgidx1,          /**< first bound change index */
   SCIP_BDCHGIDX*        bdchgidx2           /**< second bound change index */
   )
{
   assert(bdchgidx1 != NULL);
   assert(bdchgidx1->depth >= -2);
   assert(bdchgidx1->pos >= 0);
   assert(bdchgidx2 != NULL);
   assert(bdchgidx2->depth >= -2);
   assert(bdchgidx2->pos >= 0);

   return (bdchgidx1->depth < bdchgidx2->depth)
      || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
}

/** returns whether first bound change index belongs to an earlier applied bound change than second one;
 *  if a bound change index is NULL, the bound change index represents the current time, i.e. the time after the
 *  last bound change was applied to the current node
 */
SCIP_Bool SCIPbdchgidxIsEarlier(
   SCIP_BDCHGIDX*        bdchgidx1,          /**< first bound change index, or NULL */
   SCIP_BDCHGIDX*        bdchgidx2           /**< second bound change index, or NULL */
   )
{
   assert(bdchgidx1 == NULL || bdchgidx1->depth >= -2);
   assert(bdchgidx1 == NULL || bdchgidx1->pos >= 0);
   assert(bdchgidx2 == NULL || bdchgidx2->depth >= -2);
   assert(bdchgidx2 == NULL || bdchgidx2->pos >= 0);

   if( bdchgidx1 == NULL )
      return FALSE;
   else if( bdchgidx2 == NULL )
      return TRUE;
   else
      return (bdchgidx1->depth < bdchgidx2->depth)
         || (bdchgidx1->depth == bdchgidx2->depth && (bdchgidx1->pos < bdchgidx2->pos));
}

/** returns old bound that was overwritten for given bound change information */
SCIP_Real SCIPbdchginfoGetOldbound(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return bdchginfo->oldbound;
}

/** returns new bound installed for given bound change information */
SCIP_Real SCIPbdchginfoGetNewbound(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return bdchginfo->newbound;
}

/** returns variable that belongs to the given bound change information */
SCIP_VAR* SCIPbdchginfoGetVar(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return bdchginfo->var;
}

/** returns whether the bound change information belongs to a branching decision or a deduction */
SCIP_BOUNDCHGTYPE SCIPbdchginfoGetChgtype(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return (SCIP_BOUNDCHGTYPE)(bdchginfo->boundchgtype);
}

/** returns whether the bound change information belongs to a lower or upper bound change */
SCIP_BOUNDTYPE SCIPbdchginfoGetBoundtype(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return (SCIP_BOUNDTYPE)(bdchginfo->boundtype);
}

/** returns depth level of given bound change information */
int SCIPbdchginfoGetDepth(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return bdchginfo->bdchgidx.depth;
}

/** returns bound change position in its depth level of given bound change information */
int SCIPbdchginfoGetPos(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return bdchginfo->bdchgidx.pos;
}

/** returns bound change index of given bound change information */
SCIP_BDCHGIDX* SCIPbdchginfoGetIdx(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return &bdchginfo->bdchgidx;
}

/** returns inference variable of given bound change information */
SCIP_VAR* SCIPbdchginfoGetInferVar(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
      || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);

   return bdchginfo->inferencedata.var;
}

/** returns inference constraint of given bound change information */
SCIP_CONS* SCIPbdchginfoGetInferCons(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER);
   assert(bdchginfo->inferencedata.reason.cons != NULL);

   return bdchginfo->inferencedata.reason.cons;
}

/** returns inference propagator of given bound change information, or NULL if no propagator was responsible */
SCIP_PROP* SCIPbdchginfoGetInferProp(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);

   return bdchginfo->inferencedata.reason.prop;
}

/** returns inference user information of given bound change information */
int SCIPbdchginfoGetInferInfo(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
      || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);

   return bdchginfo->inferencedata.info;
}

/** returns inference bound of inference variable of given bound change information */
SCIP_BOUNDTYPE SCIPbdchginfoGetInferBoundtype(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER
      || (SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER);

   return (SCIP_BOUNDTYPE)(bdchginfo->inferboundtype);
}

/** returns the relaxed bound change type */
SCIP_Real SCIPbdchginfoGetRelaxedBound(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change to add to the conflict set */
   )
{
   return ((SCIP_BOUNDTYPE)(bdchginfo->boundtype) == SCIP_BOUNDTYPE_LOWER ? bdchginfo->var->conflictrelaxedlb : bdchginfo->var->conflictrelaxedub);
}


/** returns whether the bound change information belongs to a redundant bound change */
SCIP_Bool SCIPbdchginfoIsRedundant(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);
   assert(bdchginfo->redundant == (bdchginfo->oldbound == bdchginfo->newbound)); /*lint !e777*/

   return bdchginfo->redundant;
}

/** returns whether the bound change has an inference reason (constraint or propagator), that can be resolved */
SCIP_Bool SCIPbdchginfoHasInferenceReason(
   SCIP_BDCHGINFO*       bdchginfo           /**< bound change information */
   )
{
   assert(bdchginfo != NULL);

   return ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_CONSINFER)
      || ((SCIP_BOUNDCHGTYPE)bdchginfo->boundchgtype == SCIP_BOUNDCHGTYPE_PROPINFER
         && bdchginfo->inferencedata.reason.prop != NULL);
}

/** for two bound change informations belonging to the same variable and bound, returns whether the first bound change
 *  has a tighter new bound as the second bound change
 */
SCIP_Bool SCIPbdchginfoIsTighter(
   SCIP_BDCHGINFO*       bdchginfo1,         /**< first bound change information */
   SCIP_BDCHGINFO*       bdchginfo2          /**< second bound change information */
   )
{
   assert(bdchginfo1 != NULL);
   assert(bdchginfo2 != NULL);
   assert(bdchginfo1->var == bdchginfo2->var);
   assert(bdchginfo1->boundtype == bdchginfo2->boundtype);

   return (SCIPbdchginfoGetBoundtype(bdchginfo1) == SCIP_BOUNDTYPE_LOWER
      ? bdchginfo1->newbound > bdchginfo2->newbound
      : bdchginfo1->newbound < bdchginfo2->newbound);
}

/** returns position of variable in certificate */
int SCIPvarGetCertificateIndex(
   SCIP_VAR*             var                 /**< variable to get index for */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);

   return var->exactdata->certificateindex;
}

/** sets index of variable in certificate */
void SCIPvarSetCertificateIndex(
   SCIP_VAR*             var,                /**< variable to set index for */
   int                   certidx             /**< the index */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(certidx >= 0);

   var->exactdata->certificateindex = certidx;
}

/** sets index of variable in certificate */
void SCIPvarSetUbCertificateIndexGlobal(
   SCIP_VAR*             var,                /**< variable to set index for */
   SCIP_Longint          certidx             /**< the index */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(certidx >= 0);

   var->exactdata->glbdom.ubcertificateidx = certidx;
   var->exactdata->locdom.ubcertificateidx = certidx;
}

/** sets index of variable in certificate */
void SCIPvarSetUbCertificateIndexLocal(
   SCIP_VAR*             var,                /**< variable to set index for */
   SCIP_Longint          certidx             /**< the index */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(certidx >= 0);

   var->exactdata->locdom.ubcertificateidx = certidx;
}

/** sets index of variable in certificate */
void SCIPvarSetLbCertificateIndexLocal(
   SCIP_VAR*             var,                /**< variable to set index for */
   SCIP_Longint          certidx             /**< the index */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(certidx >= 0);

   var->exactdata->locdom.lbcertificateidx = certidx;
}

/** sets index of variable in certificate */
void SCIPvarSetLbCertificateIndexGlobal(
   SCIP_VAR*             var,                /**< variable to set index for */
   SCIP_Longint          certidx             /**< the index */
   )
{
   assert(var != NULL);
   assert(var->exactdata != NULL);
   assert(certidx >= 0);

   var->exactdata->glbdom.lbcertificateidx = certidx;
   var->exactdata->locdom.lbcertificateidx = certidx;
}

/** returns index of variable bound in vipr certificate */
SCIP_Longint SCIPvarGetLbCertificateIndexLocal(
   SCIP_VAR*             var                 /**< variable to get index for */
   )
{
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.lbcertificateidx != -1);
   assert(var->exactdata->locdom.lbcertificateidx <= SCIPcertificateGetCurrentIndex(SCIPgetCertificate(var->scip)) && var->exactdata->locdom.lbcertificateidx >= 0);

   return var->exactdata->locdom.lbcertificateidx;
}

/** returns index of variable bound in vipr certificate */
SCIP_Longint SCIPvarGetUbCertificateIndexLocal(
   SCIP_VAR*             var                 /**< variable to get index for */
   )
{
   assert(var->exactdata != NULL);
   assert(var->exactdata->locdom.ubcertificateidx != -1);
   assert(var->exactdata->locdom.ubcertificateidx <= SCIPcertificateGetCurrentIndex(SCIPgetCertificate(var->scip)) && var->exactdata->locdom.ubcertificateidx >= 0);

   return var->exactdata->locdom.ubcertificateidx;
}

/** returns index of variable bound in vipr certificate */
SCIP_Longint SCIPvarGetLbCertificateIndexGlobal(
   SCIP_VAR*             var                 /**< variable to get index for */
   )
{
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.lbcertificateidx != -1);
   assert(var->exactdata->glbdom.lbcertificateidx <= SCIPcertificateGetCurrentIndex(SCIPgetCertificate(var->scip)) && var->exactdata->glbdom.lbcertificateidx >= 0);

   return var->exactdata->glbdom.lbcertificateidx;
}

/** returns index of variable bound in vipr certificate */
SCIP_Longint SCIPvarGetUbCertificateIndexGlobal(
   SCIP_VAR*             var                 /**< variable to get index for */
   )
{
   assert(var->exactdata != NULL);
   assert(var->exactdata->glbdom.ubcertificateidx != -1);
   assert(var->exactdata->glbdom.ubcertificateidx <= SCIPcertificateGetCurrentIndex(SCIPgetCertificate(var->scip)) && var->exactdata->glbdom.ubcertificateidx >= 0);

   return var->exactdata->glbdom.ubcertificateidx;
}
