/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
 *  Main authors:
 *     Guido Tack <tack@gecode.org>
 *
 *  Copyright:
 *     Guido Tack, 2008
 *
 *  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 <gecode/int/linear.hh>

namespace Gecode { namespace Int { namespace Arithmetic {

  /*
   * Positive bounds consistent division
   *
   */

  template<class VA, class VB, class VC>
  forceinline
  DivPlusBnd<VA,VB,VC>::DivPlusBnd(Home home, VA x0, VB x1, VC x2)
    : MixTernaryPropagator<VA,PC_INT_BND,VB,PC_INT_BND,VC,PC_INT_BND>
  (home,x0,x1,x2) {}

  template<class VA, class VB, class VC>
  forceinline
  DivPlusBnd<VA,VB,VC>::DivPlusBnd(Space& home, DivPlusBnd<VA,VB,VC>& p)
    : MixTernaryPropagator<VA,PC_INT_BND,VB,PC_INT_BND,VC,PC_INT_BND>
  (home,p) {}

  template<class VA, class VB, class VC>
  Actor*
  DivPlusBnd<VA,VB,VC>::copy(Space& home) {
    return new (home) DivPlusBnd<VA,VB,VC>(home,*this);
  }

  template<class VA, class VB, class VC>
  ExecStatus
  DivPlusBnd<VA,VB,VC>::propagate(Space& home, const ModEventDelta&) {
    assert(pos(x0) && pos(x1) && !neg(x2));
    bool mod;
    do {
      mod = false;
      GECODE_ME_CHECK_MODIFIED(mod,x2.lq(home,
                                         floor_div_pp(x0.max(),x1.min())));
      GECODE_ME_CHECK_MODIFIED(mod,x2.gq(home,
                                         floor_div_px(x0.min(),x1.max())));
      GECODE_ME_CHECK_MODIFIED(mod,x0.le(home,mll(x1.max(),ill(x2.max()))));
      GECODE_ME_CHECK_MODIFIED(mod,x0.gq(home,mll(x1.min(),x2.min())));
      if (x2.min() > 0) {
          GECODE_ME_CHECK_MODIFIED(mod,
            x1.lq(home,floor_div_pp(x0.max(),x2.min())));
      }
      GECODE_ME_CHECK_MODIFIED(mod,x1.gq(home,ceil_div_pp(ll(x0.min()),
                                                          ill(x2.max()))));
    } while (mod);
    return x0.assigned() && x1.assigned() ?
      home.ES_SUBSUMED(*this) : ES_FIX;
  }

  template<class VA, class VB, class VC>
  forceinline ExecStatus
  DivPlusBnd<VA,VB,VC>::post(Home home, VA x0, VB x1, VC x2) {
    GECODE_ME_CHECK(x0.gr(home,0));
    GECODE_ME_CHECK(x1.gr(home,0));
    GECODE_ME_CHECK(x2.gq(home,floor_div_pp(x0.min(),x1.max())));
    (void) new (home) DivPlusBnd<VA,VB,VC>(home,x0,x1,x2);
    return ES_OK;
  }


  /*
   * Bounds consistent division
   *
   */
  template<class View>
  forceinline
  DivBnd<View>::DivBnd(Home home, View x0, View x1, View x2)
    : TernaryPropagator<View,PC_INT_BND>(home,x0,x1,x2) {}

  template<class View>
  forceinline
  DivBnd<View>::DivBnd(Space& home, DivBnd<View>& p)
    : TernaryPropagator<View,PC_INT_BND>(home,p) {}

  template<class View>
  Actor*
  DivBnd<View>::copy(Space& home) {
    return new (home) DivBnd<View>(home,*this);
  }

  template<class View>
  ExecStatus
  DivBnd<View>::propagate(Space& home, const ModEventDelta&) {
    if (pos(x1)) {
      if (pos(x2) || pos(x0)) goto rewrite_ppp;
      if (neg(x2) || neg(x0)) goto rewrite_npn;
      goto prop_xpx;
    }
    if (neg(x1)) {
      if (neg(x2) || pos(x0)) goto rewrite_pnn;
      if (pos(x2) || neg(x0)) goto rewrite_nnp;
      goto prop_xnx;
    }
    if (pos(x2)) {
      if (pos(x0)) goto rewrite_ppp;
      if (neg(x0)) goto rewrite_nnp;
      goto prop_xxp;
    }
    if (neg(x2)) {
      if (pos(x0)) goto rewrite_pnn;
      if (neg(x0)) goto rewrite_npn;
      goto prop_xxn;
    }
    assert(any(x1) && any(x2));
    GECODE_ME_CHECK(x0.lq(home,std::max(mll(x1.max(),ill(x2.max()))-1,
                                        mll(x1.min(),dll(x2.min()))-1)));
    GECODE_ME_CHECK(x0.gq(home,std::min(mll(x1.min(),ill(x2.max())),
                                        mll(x1.max(),dll(x2.min())))));
    return ES_NOFIX;

  prop_xxp:
    assert(any(x0) && any(x1) && pos(x2));

    GECODE_ME_CHECK(x0.le(home, mll(x1.max(),ill(x2.max()))));
    GECODE_ME_CHECK(x0.gq(home, mll(x1.min(),ill(x2.max()))));

    if (pos(x0)) goto rewrite_ppp;
    if (neg(x0)) goto rewrite_nnp;

    GECODE_ME_CHECK(x1.lq(home,floor_div_pp(x0.max(),x2.min())));
    GECODE_ME_CHECK(x1.gq(home,ceil_div_xp(x0.min(),x2.min())));

    if (x0.assigned() && x1.assigned())
      goto subsumed;
    return ES_NOFIX;

  prop_xpx:
    assert(any(x0) && pos(x1) && any(x2));

    GECODE_ME_CHECK(x0.le(home, mll(x1.max(),ill(x2.max()))));
    GECODE_ME_CHECK(x0.gq(home, mll(x1.max(),dll(x2.min()))));

    if (pos(x0)) goto rewrite_ppp;
    if (neg(x0)) goto rewrite_npn;

    GECODE_ME_CHECK(x2.lq(home,floor_div_xp(x0.max(),x1.min())));
    GECODE_ME_CHECK(x2.gq(home,floor_div_xp(x0.min(),x1.min())));

    if (x0.assigned() && x1.assigned())
      goto subsumed;
    return ES_NOFIX;

  prop_xxn:
    assert(any(x0) && any(x1) && neg(x2));

    GECODE_ME_CHECK(x0.lq(home, mll(x1.min(),dll(x2.min()))));
    GECODE_ME_CHECK(x0.gq(home, mll(x1.max(),dll(x2.min()))));

    if (pos(x0)) goto rewrite_pnn;
    if (neg(x0)) goto rewrite_npn;

    if (x2.max() != -1)
      GECODE_ME_CHECK(x1.lq(home,ceil_div_xx(ll(x0.min()),ill(x2.max()))));
    if (x2.max() != -1)
      GECODE_ME_CHECK(x1.gq(home,ceil_div_xx(ll(x0.max()),ill(x2.max()))));

    if (x0.assigned() && x1.assigned())
      goto subsumed;
    return ES_NOFIX;

  prop_xnx:
    assert(any(x0) && neg(x1) && any(x2));

    GECODE_ME_CHECK(x0.lq(home, mll(x1.min(),dll(x2.min()))));
    GECODE_ME_CHECK(x0.gq(home, mll(x1.min(),ill(x2.max()))));

    if (pos(x0)) goto rewrite_pnn;
    if (neg(x0)) goto rewrite_nnp;

    GECODE_ME_CHECK(x2.lq(home,floor_div_xx(x0.min(),x1.max())));
    GECODE_ME_CHECK(x2.gq(home,floor_div_xx(x0.max(),x1.max())));

    if (x0.assigned() && x1.assigned())
      goto subsumed;
    return ES_NOFIX;

  rewrite_ppp:
    GECODE_REWRITE(*this,(DivPlusBnd<IntView,IntView,IntView>
                          ::post(home(*this),x0,x1,x2)));
  rewrite_nnp:
    GECODE_REWRITE(*this,(DivPlusBnd<MinusView,MinusView,IntView>
                          ::post(home(*this),MinusView(x0),MinusView(x1),x2)));
  rewrite_pnn:
    GECODE_REWRITE(*this,(DivPlusBnd<IntView,MinusView,MinusView>
                          ::post(home(*this),x0,MinusView(x1),MinusView(x2))));
  rewrite_npn:
    GECODE_REWRITE(*this,(DivPlusBnd<MinusView,IntView,MinusView>
                          ::post(home(*this),MinusView(x0),x1,MinusView(x2))));
  subsumed:
    assert(x0.assigned() && x1.assigned());
    int result = std::abs(x0.val()) / std::abs(x1.val());
    if (x0.val()/x1.val() < 0)
      result = -result;
    GECODE_ME_CHECK(x2.eq(home,result));
    return home.ES_SUBSUMED(*this);
  }

  template<class View>
  ExecStatus
  DivBnd<View>::post(Home home, View x0, View x1, View x2) {
    GECODE_ME_CHECK(x1.nq(home, 0));
    if (pos(x0)) {
      if (pos(x1) || pos(x2)) goto post_ppp;
      if (neg(x1) || neg(x2)) goto post_pnn;
    } else if (neg(x0)) {
      if (neg(x1) || pos(x2)) goto post_nnp;
      if (pos(x1) || neg(x2)) goto post_npn;
    } else if (pos(x1)) {
      if (pos(x2)) goto post_ppp;
      if (neg(x2)) goto post_npn;
    } else if (neg(x1)) {
      if (pos(x2)) goto post_nnp;
      if (neg(x2)) goto post_pnn;
    }
    (void) new (home) DivBnd<View>(home,x0,x1,x2);
    return ES_OK;

  post_ppp:
    return DivPlusBnd<IntView,IntView,IntView>
      ::post(home,x0,x1,x2);
  post_nnp:
    return DivPlusBnd<MinusView,MinusView,IntView>
      ::post(home,MinusView(x0),MinusView(x1),x2);
  post_pnn:
    return DivPlusBnd<IntView,MinusView,MinusView>
      ::post(home,x0,MinusView(x1),MinusView(x2));
  post_npn:
    return DivPlusBnd<MinusView,IntView,MinusView>
      ::post(home,MinusView(x0),x1,MinusView(x2));
  }


  /*
   * Propagator for x0 != 0 /\ (x1 != 0 => x0*x1>0) /\ abs(x1)<abs(x0)
   *
   */

  template<class View>
  forceinline
  DivMod<View>::DivMod(Home home, View x0, View x1, View x2)
    : TernaryPropagator<View,PC_INT_BND>(home,x0,x1,x2) {}

  template<class View>
  forceinline ExecStatus
  DivMod<View>::post(Home home, View x0, View x1, View x2) {
    GECODE_ME_CHECK(x1.nq(home,0));
    (void) new (home) DivMod<View>(home,x0,x1,x2);
    return ES_OK;
  }

  template<class View>
  forceinline
  DivMod<View>::DivMod(Space& home, DivMod<View>& p)
  : TernaryPropagator<View,PC_INT_BND>(home,p) {}

  template<class View>
  Actor*
  DivMod<View>::copy(Space& home) {
    return new (home) DivMod<View>(home,*this);
  }

  template<class View>
  ExecStatus
  DivMod<View>::propagate(Space& home, const ModEventDelta&) {
    bool signIsSame;
    do {
      signIsSame = true;
      // The sign of x0 and x2 is the same
      if (x0.min() >= 0) {
        GECODE_ME_CHECK(x2.gq(home, 0));
      } else if (x0.max() <= 0) {
        GECODE_ME_CHECK(x2.lq(home, 0));
      } else if (x2.min() > 0) {
        GECODE_ME_CHECK(x0.gq(home, 0));
      } else if (x2.max() < 0) {
        GECODE_ME_CHECK(x0.lq(home, 0));
      } else {
        signIsSame = false;
      }

      // abs(x2) is less than abs(x1)
      int x1max = std::max(x1.max(),std::max(-x1.max(),
                           std::max(x1.min(),-x1.min())));
      GECODE_ME_CHECK(x2.le(home, x1max));
      GECODE_ME_CHECK(x2.gr(home, -x1max));

      int x2absmin = any(x2) ? 0 : (pos(x2) ? x2.min() : -x2.max());
      Iter::Ranges::Singleton sr(-x2absmin,x2absmin);
      GECODE_ME_CHECK(x1.minus_r(home,sr,false));
    } while (!signIsSame &&
             (x0.min() > 0 || x0.max() < 0 || x2.min() > 0 || x2.max() < 0));

    if (signIsSame) {
      int x2max = std::max(x2.max(),std::max(-x2.max(),
                           std::max(x2.min(),-x2.min())));
      int x1absmin = any(x1) ? 0 : (pos(x1) ? x1.min() : -x1.max());
      if (x2max < x1absmin)
        return home.ES_SUBSUMED(*this);
    }
    return ES_FIX;
  }

}}}

// STATISTICS: int-prop
