/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Christian Schulte * * Copyright: * Christian Schulte, 2002 * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include namespace Gecode { namespace Int { namespace Linear { /// Eliminate assigned views forceinline void eliminate(Term* t, int &n, long long int& d) { for (int i=n; i--; ) if (t[i].x.assigned()) { long long int ax = t[i].a * static_cast(t[i].x.val()); if (Limits::overflow_sub(d,ax)) throw OutOfLimits("Int::linear"); d=d-ax; t[i]=t[--n]; } } /// Rewrite all inequations in terms of IRT_LQ forceinline void rewrite(IntRelType &irt, long long int &d, Term* &t_p, int &n_p, Term* &t_n, int &n_n) { switch (irt) { case IRT_EQ: case IRT_NQ: case IRT_LQ: break; case IRT_LE: d--; irt = IRT_LQ; break; case IRT_GR: d++; /* fall through */ case IRT_GQ: irt = IRT_LQ; std::swap(n_p,n_n); std::swap(t_p,t_n); d = -d; break; default: throw UnknownRelation("Int::linear"); } } /// Decide the required precision and check for overflow forceinline bool precision(Term* t_p, int n_p, Term* t_n, int n_n, long long int d) { long long int sl = 0; long long int su = 0; for (int i=0; i(t_p[i].x.min()); if (Limits::overflow_add(sl,axmin)) throw OutOfLimits("Int::linear"); sl = sl + axmin; long long int axmax = t_p[i].a * static_cast(t_p[i].x.max()); if (Limits::overflow_add(sl,axmax)) throw OutOfLimits("Int::linear"); su = su + axmax; } for (int i=0; i(t_n[i].x.max()); if (Limits::overflow_sub(sl,axmax)) throw OutOfLimits("Int::linear"); sl = sl - axmax; long long int axmin = t_n[i].a * static_cast(t_n[i].x.min()); if (Limits::overflow_sub(su,axmin)) throw OutOfLimits("Int::linear"); su = su - axmin; } bool is_ip = (sl >= Limits::min) && (su <= Limits::max); if (Limits::overflow_sub(sl,d)) throw OutOfLimits("Int::linear"); sl = sl - d; if (Limits::overflow_sub(su,d)) throw OutOfLimits("Int::linear"); su = su - d; is_ip = is_ip && (sl >= Limits::min) && (su <= Limits::max); for (int i=0; i(t_p[i].x.min()); if (Limits::overflow_sub(sl,axmin)) throw OutOfLimits("Int::linear"); if (sl - axmin < Limits::min) is_ip = false; long long int axmax = t_p[i].a * static_cast(t_p[i].x.max()); if (Limits::overflow_sub(su,axmax)) throw OutOfLimits("Int::linear"); if (su - axmax > Limits::max) is_ip = false; } for (int i=0; i(t_n[i].x.min()); if (Limits::overflow_add(sl,axmin)) throw OutOfLimits("Int::linear"); if (sl + axmin < Limits::min) is_ip = false; long long int axmax = t_n[i].a * static_cast(t_n[i].x.max()); if (Limits::overflow_add(su,axmax)) throw OutOfLimits("Int::linear"); if (su + axmax > Limits::max) is_ip = false; } return is_ip; } /** * \brief Posting n-ary propagators * */ template forceinline void post_nary(Home home, ViewArray& x, ViewArray& y, IntRelType irt, Val c) { switch (irt) { case IRT_EQ: GECODE_ES_FAIL((Eq::post(home,x,y,c))); break; case IRT_NQ: GECODE_ES_FAIL((Nq::post(home,x,y,c))); break; case IRT_LQ: GECODE_ES_FAIL((Lq::post(home,x,y,c))); break; default: GECODE_NEVER; } } /// Macro for posting binary special cases for linear constraints #define GECODE_INT_PL_BIN(CLASS) \ switch (n_p) { \ case 2: \ GECODE_ES_FAIL((CLASS::post \ (home,t_p[0].x,t_p[1].x,c))); \ break; \ case 1: \ GECODE_ES_FAIL((CLASS::post \ (home,t_p[0].x,MinusView(t_n[0].x),c))); \ break; \ case 0: \ GECODE_ES_FAIL((CLASS::post \ (home,MinusView(t_n[0].x),MinusView(t_n[1].x),c))); \ break; \ default: GECODE_NEVER; \ } /// Macro for posting ternary special cases for linear constraints #define GECODE_INT_PL_TER(CLASS) \ switch (n_p) { \ case 3: \ GECODE_ES_FAIL((CLASS::post \ (home,t_p[0].x,t_p[1].x,t_p[2].x,c))); \ break; \ case 2: \ GECODE_ES_FAIL((CLASS::post \ (home,t_p[0].x,t_p[1].x, \ MinusView(t_n[0].x),c))); \ break; \ case 1: \ GECODE_ES_FAIL((CLASS::post \ (home,t_p[0].x, \ MinusView(t_n[0].x),MinusView(t_n[1].x),c))); \ break; \ case 0: \ GECODE_ES_FAIL((CLASS::post \ (home,MinusView(t_n[0].x), \ MinusView(t_n[1].x),MinusView(t_n[2].x),c))); \ break; \ default: GECODE_NEVER; \ } void post(Home home, Term* t, int n, IntRelType irt, int c, IntPropLevel ipl) { Limits::check(c,"Int::linear"); long long int d = c; eliminate(t,n,d); Term *t_p, *t_n; int n_p, n_n, gcd=1; bool is_unit = normalize(t,n,t_p,n_p,t_n,n_n,gcd); rewrite(irt,d,t_p,n_p,t_n,n_n); // Divide by gcd if (gcd > 1) { switch (irt) { case IRT_EQ: if ((d % gcd) != 0) { home.fail(); return; } d /= gcd; break; case IRT_NQ: if ((d % gcd) != 0) return; d /= gcd; break; case IRT_LQ: d = floor_div_xp(d,static_cast(gcd)); break; default: GECODE_NEVER; } } if (n == 0) { switch (irt) { case IRT_EQ: if (d != 0) home.fail(); break; case IRT_NQ: if (d == 0) home.fail(); break; case IRT_LQ: if (d < 0) home.fail(); break; default: GECODE_NEVER; } return; } if (n == 1) { if (n_p == 1) { LLongScaleView y(t_p[0].a,t_p[0].x); switch (irt) { case IRT_EQ: GECODE_ME_FAIL(y.eq(home,d)); break; case IRT_NQ: GECODE_ME_FAIL(y.nq(home,d)); break; case IRT_LQ: GECODE_ME_FAIL(y.lq(home,d)); break; default: GECODE_NEVER; } } else { LLongScaleView y(t_n[0].a,t_n[0].x); switch (irt) { case IRT_EQ: GECODE_ME_FAIL(y.eq(home,-d)); break; case IRT_NQ: GECODE_ME_FAIL(y.nq(home,-d)); break; case IRT_LQ: GECODE_ME_FAIL(y.gq(home,-d)); break; default: GECODE_NEVER; } } return; } // Check this special case here, as there can be no overflow if ((n == 2) && is_unit && ((vbd(ipl) == IPL_DOM) || (vbd(ipl) == IPL_DEF)) && (irt == IRT_EQ) && (d == 0)) { switch (n_p) { case 2: { IntView x(t_p[0].x); MinusView y(t_p[1].x); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } case 1: { IntView x(t_p[0].x); IntView y(t_n[0].x); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } case 0: { IntView x(t_n[0].x); MinusView y(t_n[1].x); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } default: GECODE_NEVER; } return; } bool is_ip = precision(t_p,n_p,t_n,n_n,d); if (is_unit && is_ip && (vbd(ipl) != IPL_DOM) && (vbd(ipl) != IPL_DEF)) { // Unit coefficients with integer precision c = static_cast(d); if (n == 2) { switch (irt) { case IRT_EQ: GECODE_INT_PL_BIN(EqBin); break; case IRT_NQ: GECODE_INT_PL_BIN(NqBin); break; case IRT_LQ: GECODE_INT_PL_BIN(LqBin); break; default: GECODE_NEVER; } } else if (n == 3) { switch (irt) { case IRT_EQ: GECODE_INT_PL_TER(EqTer); break; case IRT_NQ: GECODE_INT_PL_TER(NqTer); break; case IRT_LQ: GECODE_INT_PL_TER(LqTer); break; default: GECODE_NEVER; } } else { ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i(home,x,y,irt,c); } } else if (is_ip) { if ((n==2) && is_unit && ((vbd(ipl) == IPL_DOM) || (vbd(ipl) == IPL_DEF)) && (irt == IRT_EQ)) { // Binary domain-consistent equality c = static_cast(d); assert(c != 0); switch (n_p) { case 2: { MinusView x(t_p[0].x); OffsetView y(t_p[1].x, -c); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } case 1: { IntView x(t_p[0].x); OffsetView y(t_n[0].x, c); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } case 0: { MinusView x(t_n[0].x); OffsetView y(t_n[1].x, c); GECODE_ES_FAIL((Rel::EqDom::post(home,x,y))); break; } default: GECODE_NEVER; } } else { // Arbitrary coefficients with integer precision c = static_cast(d); ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i::post(home,x,y,c))); } else { post_nary(home,x,y,irt,c); } } } else { // Arbitrary coefficients with long long precision ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i ::post(home,x,y,d))); } else { post_nary(home,x,y,irt,d); } } } #undef GECODE_INT_PL_BIN #undef GECODE_INT_PL_TER /** * \brief Posting reified n-ary propagators * */ template forceinline void post_nary(Home home, ViewArray& x, ViewArray& y, IntRelType irt, Val c, Reify r) { switch (irt) { case IRT_EQ: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,r.var()))); break; default: GECODE_NEVER; } break; case IRT_NQ: { NegBoolView n(r.var()); switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,n))); break; case RM_IMP: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,n))); break; case RM_PMI: GECODE_ES_FAIL((ReEq:: post(home,x,y,c,n))); break; default: GECODE_NEVER; } } break; case IRT_LQ: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReLq:: post(home,x,y,c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReLq:: post(home,x,y,c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReLq:: post(home,x,y,c,r.var()))); break; default: GECODE_NEVER; } break; default: GECODE_NEVER; } } template forceinline void posteqint(Home home, IntView& x, int c, CtrlView b, ReifyMode rm, IntPropLevel ipl) { if ((vbd(ipl) == IPL_DOM) || (vbd(ipl) == IPL_DEF)) { switch (rm) { case RM_EQV: GECODE_ES_FAIL((Rel::ReEqDomInt:: post(home,x,c,b))); break; case RM_IMP: GECODE_ES_FAIL((Rel::ReEqDomInt:: post(home,x,c,b))); break; case RM_PMI: GECODE_ES_FAIL((Rel::ReEqDomInt:: post(home,x,c,b))); break; default: GECODE_NEVER; } } else { switch (rm) { case RM_EQV: GECODE_ES_FAIL((Rel::ReEqBndInt:: post(home,x,c,b))); break; case RM_IMP: GECODE_ES_FAIL((Rel::ReEqBndInt:: post(home,x,c,b))); break; case RM_PMI: GECODE_ES_FAIL((Rel::ReEqBndInt:: post(home,x,c,b))); break; default: GECODE_NEVER; } } } void post(Home home, Term* t, int n, IntRelType irt, int c, Reify r, IntPropLevel ipl) { Limits::check(c,"Int::linear"); long long int d = c; eliminate(t,n,d); Term *t_p, *t_n; int n_p, n_n, gcd=1; bool is_unit = normalize(t,n,t_p,n_p,t_n,n_n,gcd); rewrite(irt,d,t_p,n_p,t_n,n_n); // Divide by gcd if (gcd > 1) { switch (irt) { case IRT_EQ: if ((d % gcd) != 0) { if (r.mode() != RM_PMI) GECODE_ME_FAIL(BoolView(r.var()).zero(home)); return; } d /= gcd; break; case IRT_NQ: if ((d % gcd) != 0) { if (r.mode() != RM_IMP) GECODE_ME_FAIL(BoolView(r.var()).one(home)); return; } d /= gcd; break; case IRT_LQ: d = floor_div_xp(d,static_cast(gcd)); break; default: GECODE_NEVER; } } if (n == 0) { bool fail = false; switch (irt) { case IRT_EQ: fail = (d != 0); break; case IRT_NQ: fail = (d == 0); break; case IRT_LQ: fail = (0 > d); break; default: GECODE_NEVER; } if (fail) { if (r.mode() != RM_PMI) GECODE_ME_FAIL(BoolView(r.var()).zero(home)); } else { if (r.mode() != RM_IMP) GECODE_ME_FAIL(BoolView(r.var()).one(home)); } return; } bool is_ip = precision(t_p,n_p,t_n,n_n,d); if (is_unit && is_ip) { c = static_cast(d); if (n == 1) { switch (irt) { case IRT_EQ: if (n_p == 1) { posteqint(home,t_p[0].x,c,r.var(),r.mode(),ipl); } else { posteqint(home,t_p[0].x,-c,r.var(),r.mode(),ipl); } break; case IRT_NQ: { NegBoolView nb(r.var()); ReifyMode rm = r.mode(); switch (rm) { case RM_IMP: rm = RM_PMI; break; case RM_PMI: rm = RM_IMP; break; default: ; } if (n_p == 1) { posteqint(home,t_p[0].x,c,nb,rm,ipl); } else { posteqint(home,t_p[0].x,-c,nb,rm,ipl); } } break; case IRT_LQ: if (n_p == 1) { switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_p[0].x,c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_p[0].x,c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_p[0].x,c,r.var()))); break; default: GECODE_NEVER; } } else { NegBoolView nb(r.var()); switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_n[0].x,-c-1,nb))); break; case RM_IMP: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_n[0].x,-c-1,nb))); break; case RM_PMI: GECODE_ES_FAIL((Rel::ReLqInt:: post(home,t_n[0].x,-c-1,nb))); break; default: GECODE_NEVER; } } break; default: GECODE_NEVER; } } else if (n == 2) { switch (irt) { case IRT_EQ: switch (n_p) { case 2: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; default: GECODE_NEVER; } break; case 1: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; default: GECODE_NEVER; } break; case 0: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_n[0].x,t_n[1].x,-c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_n[0].x,t_n[1].x,-c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_n[0].x,t_n[1].x,-c,r.var()))); break; default: GECODE_NEVER; } break; default: GECODE_NEVER; } break; case IRT_NQ: { NegBoolView nb(r.var()); switch (n_p) { case 2: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,nb))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,nb))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,c,nb))); break; default: GECODE_NEVER; } break; case 1: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c,nb))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c,nb))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c,nb))); break; default: GECODE_NEVER; } break; case 0: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,-c,nb))); break; case RM_IMP: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,-c,nb))); break; case RM_PMI: GECODE_ES_FAIL((ReEqBin:: post(home,t_p[0].x,t_p[1].x,-c,nb))); break; default: GECODE_NEVER; } break; default: GECODE_NEVER; } } break; case IRT_LQ: switch (n_p) { case 2: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,t_p[1].x,c,r.var()))); break; default: GECODE_NEVER; } break; case 1: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReLqBin:: post(home,t_p[0].x,MinusView(t_n[0].x),c, r.var()))); break; default: GECODE_NEVER; } break; case 0: switch (r.mode()) { case RM_EQV: GECODE_ES_FAIL((ReLqBin:: post(home,MinusView(t_n[0].x), MinusView(t_n[1].x),c,r.var()))); break; case RM_IMP: GECODE_ES_FAIL((ReLqBin:: post(home,MinusView(t_n[0].x), MinusView(t_n[1].x),c,r.var()))); break; case RM_PMI: GECODE_ES_FAIL((ReLqBin:: post(home,MinusView(t_n[0].x), MinusView(t_n[1].x),c,r.var()))); break; default: GECODE_NEVER; } break; default: GECODE_NEVER; } break; default: GECODE_NEVER; } } else { ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i(home,x,y,irt,c,r); } } else if (is_ip) { // Arbitrary coefficients with integer precision c = static_cast(d); ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i(home,x,y,irt,c,r); } else { // Arbitrary coefficients with long long precision ViewArray x(home,n_p); for (int i=0; i y(home,n_n); for (int i=0; i(home,x,y,irt,d,r); } } }}} // STATISTICS: int-post