diff --git a/README b/README index e5e6ffa..e93807a 100644 --- a/README +++ b/README @@ -173,6 +173,9 @@ Global constraints fd_sum_prod(fd_int X[], fd_int Y[], int n, int k) X . Y = k fd_poly_eq(int C[], fd_int Y[], int n, fd_int z) z <= C . Y <= z + fd_poly_eq_k(int C[], fd_int Y[], int n, int k) C . Y = k + fd_poly_ne(int C[], fd_int Y[], int n, fd_int z) C . Y != z + fd_poly_ne_k(int C[], fd_int Y[], int n, int k) C . Y != k fd_knapsack2(fd_int X[], fd_int Y[], int n, fd_int z) z <= X . Y <= z fd_exactly(fd_int X[], int n, int c, int k) #{ i | X[i] = k } = c diff --git a/src/constraints.c b/src/constraints.c index 50dc9eb..b7a487d 100644 --- a/src/constraints.c +++ b/src/constraints.c @@ -149,6 +149,9 @@ void fd__constraint_free_memory() #include #include #include +#include +#include +#include #ifdef CONSTRAINT_CLASS @@ -178,6 +181,9 @@ _FD_CONSTRAINT_INITIALISATION(max, FD_CONSTR_MAX) _FD_CONSTRAINT_INITIALISATION(poly_eq, FD_CONSTR_POLY_EQ) _FD_CONSTRAINT_INITIALISATION(element_var, FD_CONSTR_ELEMENT_VAR) _FD_CONSTRAINT_INITIALISATION(exactly_vars, FD_CONSTR_EXACTLY_VARS) +_FD_CONSTRAINT_INITIALISATION(poly_eq_k, FD_CONSTR_POLY_EQ_K) +_FD_CONSTRAINT_INITIALISATION(poly_ne, FD_CONSTR_POLY_NE) +_FD_CONSTRAINT_INITIALISATION(poly_ne_k, FD_CONSTR_POLY_NE_K) void _fd_init_constraints() { @@ -212,6 +218,9 @@ void _fd_init_constraints() _fd_init_constraint(poly_eq); _fd_init_constraint(element_var); _fd_init_constraint(exactly_vars); + _fd_init_constraint(poly_eq_k); + _fd_init_constraint(poly_ne); + _fd_init_constraint(poly_ne_k); done = 1; } diff --git a/src/constraints.h b/src/constraints.h index b02d19f..0061390 100644 --- a/src/constraints.h +++ b/src/constraints.h @@ -33,6 +33,9 @@ typedef enum { FD_CONSTR_POLY_EQ, FD_CONSTR_ELEMENT_VAR, FD_CONSTR_EXACTLY_VARS, + FD_CONSTR_POLY_EQ_K, + FD_CONSTR_POLY_NE, + FD_CONSTR_POLY_NE_K, FD_CONSTR_KINDS // number of kinds of constraints } _fd_constraint_kind; diff --git a/src/constraints/poly-eq-k.c b/src/constraints/poly-eq-k.c new file mode 100644 index 0000000..81e22af --- /dev/null +++ b/src/constraints/poly-eq-k.c @@ -0,0 +1,469 @@ +/* poly-eq-k(C, X, k) == sum(C . X) = k */ + +static int fd_poly_eq_k_filter(fd_constraint this) +{ + int k; + int min, max; + int terms = this->nvariables; + int *mins, *maxs; + 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 + 2) * sizeof(int)); + + base = constraint_memory[this->index]; + + mins = base + 2; + maxs = mins + terms; +#else + mins = alloca(terms * sizeof(*mins)); + maxs = alloca(terms * sizeof(*maxs)); +#endif + + k = this->constants[terms]; + + // sum the minima and the maxima of the terms + min = max = 0; + + for (i = 0; i < terms; ++i) + { + int vl, vh; + + if (this->constants[i] > 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)); + } + + min += this->constants[i] * vl; + max += this->constants[i] * vh; + } + + if (min > k || max < k) + return FD_NOSOLUTION; + + if (min == max) + return FD_OK; + + // XXX: poor man's propagation + if (min == k) + { + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + + if (c && mins[i] != maxs[i]) + { + if (c > 0) + _fd_var_del_gt(mins[i], VAR(this, i)); + else + _fd_var_del_lt(maxs[i], VAR(this, i)); + + // check needed for when variables occur more than once + // and with opposite signs + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + + fd__constraint_set_entailed(this); + } + else if (max == k) + { + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + + if (c && mins[i] != maxs[i]) + { + if (c > 0) + _fd_var_del_lt(maxs[i], VAR(this, i)); + else + _fd_var_del_gt(mins[i], VAR(this, i)); + + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + + fd__constraint_set_entailed(this); + } + else + { + if (max > k) + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + int xmin = mins[i]; + int xmax = maxs[i]; + + if (c > 0) + { + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k + + if ((xmax - xmin) * c > k - min) + { + // xmax = floor((k - min) / c) + xmin + xmax = (k - min) / c + xmin; + + _fd_var_del_gt(xmax, VAR(this, i)); + + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + else if (c < 0) + { + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k + + if ((xmin - xmax) * c > k - min) + { + // xmin = ceil((k - min) / c) + xmax + xmin = (k - min) / c + xmax; + + _fd_var_del_lt(xmin, VAR(this, i)); + + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + } + + if (min < k) + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + int xmin = mins[i]; + int xmax = maxs[i]; + + if (c > 0) + { + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k + + if ((xmax - xmin) * c > max - k) + { + // xmin = ceil((k - max) / c) + xmax + xmin = (k - max) / c + xmax; + + _fd_var_del_lt(xmin, VAR(this, i)); + + if (fd_domain_empty(VAR(this, i))) // domains may have holes + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + else if (c < 0) + { + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k + + if ((xmax - xmin) * c < k - max) + { + // xmax = floor((k - max) / c) + xmin + xmax = (k - max) / c + xmin; + + _fd_var_del_gt(xmax, VAR(this, i)); + + if (fd_domain_empty(VAR(this, i))) // domains may have holes + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + } + } + +#ifdef CONSTRAINT_TEMPS + // save values + *base = min; + *(base + 1) = max; + + fd__constraint_remember(this); +#endif + + return FD_OK; +} + +static int fd_poly_eq_k_propagate2(fd_constraint this, fd_int culprit) +{ +#ifdef CONSTRAINT_TEMPS + int k; + int min, max; + int terms = this->nvariables; + int *mins, *maxs; + int i; + int *base; + int x, c; + int nmin, nmin_x, nmax, nmax_x; + + if (!fd__constraint_data_valid(this)) + return fd_poly_eq_k_filter(this); // ignores culprit + + // bounds filtering + + base = constraint_memory[this->index]; + + mins = base + 2; + maxs = mins + terms; + + min = *base; + max = *(base + 1); + + k = this->constants[terms]; + + // 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 > 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; + } + + if (nmin > k || nmax < k) + return FD_NOSOLUTION; + + mins[x] = nmin_x; + maxs[x] = nmax_x; + + while (++x < terms && culprit != VAR(this, x)) + ; + } + while (x < terms); + + // XXX: poor man's propagation + if (nmin == k) + { + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + + if (c && mins[i] != maxs[i]) + { + if (c > 0) + { + _fd_var_del_gt(mins[i], VAR(this, i)); + + maxs[i] = mins[i]; + } + else + { + _fd_var_del_lt(maxs[i], VAR(this, i)); + + mins[i] = maxs[i]; + } + + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + + fd__constraint_set_entailed(this); + } + else if (nmax == k) + { + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + + if (c && mins[i] != maxs[i]) + { + if (c > 0) + { + _fd_var_del_lt(maxs[i], VAR(this, i)); + + mins[i] = maxs[i]; + } + else + { + _fd_var_del_gt(mins[i], VAR(this, i)); + + maxs[i] = mins[i]; + } + + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + } + + fd__constraint_set_entailed(this); + } + +#if 0 // this is too expensive + { + if (nmax < max && nmax > k) + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + int xmin = mins[i]; + int xmax = maxs[i]; + + if (c > 0) + { + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k + + if ((xmax - xmin) * c > k - nmin) + { + // xmax = floor((k - nmin) / c) + xmin + xmax = (k - nmin) / c + xmin; + + if (_fd_var_del_gt(xmax, VAR(this, i))) + { + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + + maxs[i] = xmax; + } + } + else if (c < 0) + { + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k + + if ((xmin - xmax) * c > k - nmin) + { + // xmin = ceil((k - nmin) / c) + xmax + xmin = (k - nmin) / c + xmax; + + if (_fd_var_del_lt(xmin, VAR(this, i))) + { + if (fd_domain_empty(VAR(this, i))) + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + + mins[i] = xmin; + } + } + } + + if (nmin > min && nmin < k) + for (i = 0; i < terms; ++i) + { + int c = this->constants[i]; + int xmin = mins[i]; + int xmax = maxs[i]; + + if (c > 0) + { + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k + + if ((xmax - xmin) * c > nmax - k) + { + // xmin = ceil((k - nmax) / c) + xmax + xmin = (k - nmax) / c + xmax; + + if (_fd_var_del_lt(xmin, VAR(this, i))) + { + + if (fd_domain_empty(VAR(this, i))) // domains may have holes + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + + mins[i] = xmin; + } + } + else if (c < 0) + { + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k + + if ((xmax - xmin) * c < k - nmax) + { + // xmax = floor((k - nmax) / c) + xmin + xmax = (k - nmax) / c + xmin; + + if (_fd_var_del_gt(xmax, VAR(this, i))) + { + if (fd_domain_empty(VAR(this, i))) // domains may have holes + return FD_NOSOLUTION; + + _fd_revise_connected(this, VAR(this, i)); + } + + maxs[i] = xmax; + } + } + } + } +#endif + + *base = nmin; + *(base + 1) = nmax; + + return FD_OK; +#else /* CONSTRAINT_TEMPS */ + return fd_poly_eq_k_filter(this); // ignores culprit +#endif /* CONSTRAINT_TEMPS */ +} + +fd_constraint fd_poly_eq_k(int cs[], fd_int xs[], int nterms, int k) +{ + fd_constraint c = _fd_constraint_new(nterms, nterms + 1); + int i; + + if (c) + { + for (i = 0; i < nterms; ++i) + c->variables[i] = FD_INT2C_VAR(xs[i]); + for (i = 0; i < nterms; ++i) + c->constants[i] = cs[i]; + c->constants[nterms] = k; +#ifdef CONSTRAINT_CLASS + c->kind = FD_CONSTR_POLY_EQ_K; +#else /* CONSTRAINT_CLASS */ + c->propagator2 = fd_poly_eq_k_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; +} diff --git a/src/constraints/poly-ne-k.c b/src/constraints/poly-ne-k.c new file mode 100644 index 0000000..6fefc55 --- /dev/null +++ b/src/constraints/poly-ne-k.c @@ -0,0 +1,250 @@ +/* poly-ne-k(C, X, k) == sum(C . X) != k */ + +static int fd_poly_ne_k_filter(fd_constraint this) +{ + int k; + int min, max; + int terms = this->nvariables; + 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 + 3) * sizeof(int)); + + base = constraint_memory[this->index]; + + mins = base + 3; + maxs = mins + terms; +#else + mins = alloca(terms * sizeof(*mins)); + maxs = alloca(terms * sizeof(*maxs)); +#endif + + k = this->constants[terms]; + + // 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 > k || max < k) + { + fd__constraint_set_entailed(this); + + return FD_OK; + } + + if (min == max) + { + if (min == k) + return FD_NOSOLUTION; + + fd__constraint_set_entailed(this); + + return FD_OK; + } + + if (unset == 1) + { + 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 = (k - m) / this->constants[i]; + + if (v * this->constants[i] + m == k) + fd_update_domain_and_check(del_val, v, VAR(this, i)); + + fd__constraint_set_entailed(this); + } + +#ifdef CONSTRAINT_TEMPS + // save values + *base = min; + *(base + 1) = max; + *(base + 2) = unset; + + fd__constraint_remember(this); +#endif + + return FD_OK; +} + +static int fd_poly_ne_k_propagate2(fd_constraint this, fd_int culprit) +{ +#ifdef CONSTRAINT_TEMPS + int k; + int min, max; + int terms = this->nvariables; + 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_k_filter(this); // ignores culprit + + // bounds filtering + + base = constraint_memory[this->index]; + + mins = base + 3; + maxs = mins + terms; + + k = this->constants[terms]; + + min = *base; + max = *(base + 1); + + unset = *(base + 2); + + // 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 > k || nmax < k) + { + fd__constraint_set_entailed(this); + + return FD_OK; + } + + if (nmin == nmax) + { + if (nmin == k) + return FD_NOSOLUTION; + + fd__constraint_set_entailed(this); + + return FD_OK; + } + + if (unset == 1) + { + 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 = (k - m) / this->constants[i]; + + if (v * this->constants[i] + m == k) + fd_update_domain_and_check(del_val, v, VAR(this, i)); + + fd__constraint_set_entailed(this); + } + + *base = nmin; + *(base + 1) = nmax; + *(base + 2) = unset; + + return FD_OK; +#else /* CONSTRAINT_TEMPS */ + return fd_poly_ne_k_filter(this); // ignores culprit +#endif /* CONSTRAINT_TEMPS */ +} + +fd_constraint fd_poly_ne_k(int cs[], fd_int xs[], int nterms, int k) +{ + fd_constraint c = _fd_constraint_new(nterms, nterms + 1); + int i; + + if (c) + { + for (i = 0; i < nterms; ++i) + c->variables[i] = FD_INT2C_VAR(xs[i]); + for (i = 0; i < nterms; ++i) + c->constants[i] = cs[i]; + c->constants[nterms] = k; +#ifdef CONSTRAINT_CLASS + c->kind = FD_CONSTR_POLY_NE_K; +#else /* CONSTRAINT_CLASS */ + c->propagator2 = fd_poly_ne_k_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; +} diff --git a/src/constraints/poly-ne.c b/src/constraints/poly-ne.c new file mode 100644 index 0000000..70b3a39 --- /dev/null +++ b/src/constraints/poly-ne.c @@ -0,0 +1,288 @@ +/* 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; +} -- libgit2 0.21.2