poly-ne.c 5.87 KB
/* poly-ne(C, X, y) == sum(C . X) != y */

static int fd_poly_ne_filter(fd_constraint this)
{
  int ub, lb;
  int min, max;
  int terms = this->nconstants;
  int *mins, *maxs;
  int unset = 0;		// non-singleton variables
  int i;
#ifdef CONSTRAINT_TEMPS
  int *base;

  assert(!fd__constraint_data_valid(this));

  if (!constraint_memory[this->index])
    constraint_memory[this->index] = malloc((2 * terms + 5) * sizeof(int));

  base = constraint_memory[this->index];

  mins = base + 5;
  maxs = mins + terms;
#else
  mins = alloca(terms * sizeof(*mins));
  maxs = alloca(terms * sizeof(*maxs));
#endif

  lb = _fd_var_min(VAR(this, this->nvariables - 1));	// lower bound
  ub = _fd_var_max(VAR(this, this->nvariables - 1));	// upper bound

  // sum the minima and the maxima of the terms
  min = max = 0;

  for (i = 0; i < terms; ++i)
    {
      int vl, vh;
      int c = this->constants[i];

      if (c > 0)
	{
	  vl = mins[i] = _fd_var_min(VAR(this, i));
	  vh = maxs[i] = _fd_var_max(VAR(this, i));
	}
      else
	{
	  vl = maxs[i] = _fd_var_max(VAR(this, i));
	  vh = mins[i] = _fd_var_min(VAR(this, i));
	}

      if (c && vl != vh)
	unset++;

      min += c * vl;
      max += c * vh;
    }

  if (min > ub || max < lb)
    {
      fd__constraint_set_entailed(this);

      return FD_OK;
    }

  if (min == max)
    {
      fd_update_domain_and_check(del_val, min, VAR(this, this->nvariables - 1));

      fd__constraint_set_entailed(this);
    }
  else if (unset == 1 && lb == ub)
    {
      int m, v;

      // find out which is the non-singleton variable
      for (i = terms - 1; i >= 0; --i)
	if (this->constants[i] && mins[i] != maxs[i])
	  break;

      if (this->constants[i] > 0)
	m = min - this->constants[i] * mins[i];
      else
	m = min - this->constants[i] * maxs[i];

      v = (lb - m) / this->constants[i];

      if (v * this->constants[i] + m == lb)
	fd_update_domain_and_check(del_val, v, VAR(this, i));

      fd__constraint_set_entailed(this);
    }

#ifdef CONSTRAINT_TEMPS
  // save values
  *base = lb;
  *(base + 1) = ub;
  *(base + 2) = min;
  *(base + 3) = max;
  *(base + 4) = unset;

  fd__constraint_remember(this);
#endif

  return FD_OK;
}

static int fd_poly_ne_propagate2(fd_constraint this, fd_int culprit)
{
#ifdef CONSTRAINT_TEMPS
  int ub, lb;
  int min, max;
  int terms = this->nconstants;
  int *mins, *maxs;
  int unset;
  int i;
  int *base;
  int x, c;
  int nmin, nmin_x, nmax, nmax_x;

  if (!fd__constraint_data_valid(this))
    return fd_poly_ne_filter(this);	// ignores culprit

  // bounds filtering

  base = constraint_memory[this->index];

  mins = base + 5;
  maxs = mins + terms;

  lb = *base;
  ub = *(base + 1);

  min = *(base + 2);
  max = *(base + 3);

  unset = *(base + 4);

  if (culprit == VAR(this, this->nvariables - 1))
    {
      // the culprit is the sum variable
      int nlb, nub;

      nlb = _fd_var_min(culprit);
      nub = _fd_var_max(culprit);

      if (nlb == lb && nub == ub)
	return FD_OK;

      if (min > nub || max < nlb)
	fd__constraint_set_entailed(this);

      if (unset == 1 && nlb == nub)
	{
	  int m, v;

	  // find out which is the non-singleton variable
	  for (i = this->nvariables - 2; i >= 0; --i)
	    if (this->constants[i] && mins[i] != maxs[i])
	      break;

	  if (this->constants[i] > 0)
	    m = min - this->constants[i] * mins[i];
	  else
	    m = min - this->constants[i] * maxs[i];

	  v = (nlb - m) / this->constants[i];

	  if (v * this->constants[i] + m == nlb)
	    fd_update_domain_and_check(del_val, v, VAR(this, i));

	  fd__constraint_set_entailed(this);
	}

      *base = nlb;
      *(base + 1) = nub;

      return FD_OK;
    }

  // the culprit appears in one of the terms, find out which one(s)
  for (x = 0; culprit != VAR(this, x); ++x)
    ;

  nmin_x = _fd_var_min(VAR(this, x));
  nmax_x = _fd_var_max(VAR(this, x));

  if (nmin_x == mins[x] && nmax_x == maxs[x])
    return FD_OK;

  nmin = min;
  nmax = max;

  do
    {
      c = this->constants[x];

      if (c && nmin_x == nmax_x)
	unset--;

      if (c > 0)
	{
	  nmin = nmin + (nmin_x - mins[x]) * c;
	  nmax = nmax - (maxs[x] - nmax_x) * c;
	}
      else if (c < 0)
	{
	  nmin = nmin - (maxs[x] - nmax_x) * c;
	  nmax = nmax + (nmin_x - mins[x]) * c;
	}

      mins[x] = nmin_x;
      maxs[x] = nmax_x;

      while (++x < terms && culprit != VAR(this, x))
	;
    }
  while (x < terms);

  if (nmin > ub || nmax < lb)
    {
      fd__constraint_set_entailed(this);

      return FD_OK;
    }

  if (nmin == nmax)
    {
      fd_update_domain_and_check(del_val, nmin, VAR(this, this->nvariables - 1));

      fd__constraint_set_entailed(this);
    }
  else if (unset == 1 && lb == ub)
    {
      int m, v;

      // find out which is the non-singleton variable
      for (i = terms - 1; i >= 0; --i)
	if (this->constants[i] && mins[i] != maxs[i])
	  break;

      if (this->constants[i] > 0)
	m = min - this->constants[i] * mins[i];
      else
	m = min - this->constants[i] * maxs[i];

      v = (lb - m) / this->constants[i];

      if (v * this->constants[i] + m == lb)
	fd_update_domain_and_check(del_val, v, VAR(this, i));

      fd__constraint_set_entailed(this);
    }

  *(base + 2) = nmin;
  *(base + 3) = nmax;
  *(base + 4) = unset;

  return FD_OK;
#else /* CONSTRAINT_TEMPS */
  return fd_poly_ne_filter(this);	// ignores culprit
#endif /* CONSTRAINT_TEMPS */
}

fd_constraint fd_poly_ne(int cs[], fd_int xs[], int nterms, fd_int y)
{
  fd_constraint c = _fd_constraint_new(nterms + 1, nterms);
  int i;

  if (c)
    {
      for (i = 0; i < nterms; ++i)
	c->variables[i] = FD_INT2C_VAR(xs[i]);
      c->variables[nterms] = FD_INT2C_VAR(y);
      for (i = 0; i < nterms; ++i)
	c->constants[i] = cs[i];
#ifdef CONSTRAINT_CLASS
      c->kind = FD_CONSTR_POLY_NE;
#else /* CONSTRAINT_CLASS */
      c->propagator2 = fd_poly_ne_propagate2;
#endif /* CONSTRAINT_CLASS */

      for (i = 0; i < c->nvariables; ++i)
	_fd_var_add_constraint(VAR(c, i), c);

      _fd_add_constraint(c);
    }

  return c;
}