element-var.c 3.48 KB
/* element-var */
/* element-var(X, y, z) == X[y] = z (0 <= y < |X|) */

static int fd_element_var_filter(fd_constraint this)
{
  int length = this->nvariables - 2;
  fd_int index, value;
  int imax, min, max;
  int v;
  int i;

  // do some filtering...

  index = VAR(this, this->nvariables - 2);

  // check the index variable
  if (_fd_var_del_ge(length, index))
    {
      if (fd_domain_empty(index))
	return FD_NOSOLUTION;

      _fd_revise_connected(this, index);
    }

  value = VAR(this, this->nvariables - 1);

  // see if the index is already fixed
  if (fd_var_single(index, &v))
    {
      if (_fd_var_intersect(VAR(this, v), value))
	{
	  if (fd_domain_empty(VAR(this, v)))
	    return FD_NOSOLUTION;

	  _fd_revise_connected(this, VAR(this, v));
	}

      if (_fd_var_intersect(value, VAR(this, v)))
	{
	  if (fd_domain_empty(value))
	    return FD_NOSOLUTION;

	  _fd_revise_connected(this, value);
	}

      return FD_OK;
    }

  // do some bounds filtering on the value variable
  imax = _fd_var_max(index);

  min = _fd_var_min(VAR(this, imax));
  max = _fd_var_max(VAR(this, imax));

  for (i = _fd_var_min(index); i < imax; ++i)
    {
      int m;

      if ((m = _fd_var_min(VAR(this, i))) < min)
	min = m;
      else if ((m = _fd_var_max(VAR(this, i))) > max)
	max = m;
    }

  if (_fd_var_del_lt(min, value) | _fd_var_del_gt(max, value))
    {
      if (fd_domain_empty(value))
	return FD_NOSOLUTION;

      _fd_revise_connected(this, value);
    }

  // XXX: more...

  return FD_OK;
}

static int fd_element_var_propagate2(fd_constraint this, fd_int culprit)
{
  int length = this->nvariables - 2;
  fd_int index, value;
  int i;

  // do some filtering...

  index = VAR(this, this->nvariables - 2);
  value = VAR(this, this->nvariables - 1);

  // see if the culprit is the index variable
  if (culprit == index)
    {
      if (fd_var_single(index, &i))
	{
	  if (_fd_var_intersect(VAR(this, i), value))
	    {
	      if (fd_domain_empty(VAR(this, i)))
		return FD_NOSOLUTION;

	      _fd_revise_connected(this, VAR(this, i));
	    }

	  if (_fd_var_intersect(value, VAR(this, i)))
	    {
	      if (fd_domain_empty(value))
		return FD_NOSOLUTION;

	      _fd_revise_connected(this, value);
	    }
	}

      return FD_OK;
    }

  // see if the culprit is the value variable
  if (culprit == value)
    {
      int k;

      if (fd_var_single(value, &k))
	{
	  // check which variables have k in their domain
	  int imax = _fd_var_max(index);

	  for (i = _fd_var_min(index); i <= imax; ++i)
	    if (!_fd_var_contains_val(VAR(this, i), k) &&
		_fd_var_del_val(i, index))
	      {
		if (fd_domain_empty(index))
		  return FD_NOSOLUTION;

		_fd_revise_connected(NULL, index);
	      }
	}

      return FD_OK;
    }

  // XXX: filter for non-index and non-value variables

  return FD_OK;
}

fd_constraint fd_element_var(fd_int *variables, int nvariables, fd_int index,
			     fd_int value)
{
  fd_constraint c = _fd_constraint_new(nvariables + 2, 0);
  int i;

  // XXX: add a constraint 0 <= INDEX < nvariables

  if (c)
    {
      for (i = 0; i < nvariables; ++i)
	c->variables[i] = FD_INT2C_VAR(variables[i]);
      c->variables[nvariables] = FD_INT2C_VAR(index);
      c->variables[nvariables + 1] = FD_INT2C_VAR(value);
#ifdef CONSTRAINT_CLASS
      c->kind = FD_CONSTR_ELEMENT_VAR;
#else /* CONSTRAINT_CLASS */
      c->propagator2 = fd_element_var_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;
}