/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
 *  Main authors:
 *     Christian Schulte <schulte@gecode.org>
 *
 *  Copyright:
 *     Christian Schulte, 2010
 *
 *  Last modified:
 *     $Date: 2014-08-28 14:29:11 +0200 (Thu, 28 Aug 2014) $ by $Author: schulte $
 *     $Revision: 14203 $
 *
 *  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 "test/int.hh"

#include <gecode/minimodel.hh>
#include <climits>

namespace Test { namespace Int {
  
  /// %Tests for bin-packing constraint
  namespace BinPacking {

    /**
     * \defgroup TaskTestIntBinPacking Bin-packing constraints
     * \ingroup TaskTestInt
     */
    //@{
    /// Generate load and bin assignments
    class LoadBinAssignment : public Assignment {
    protected:
      /// Number of bins
      int n_bins;
      /// Number of items
      int n_items;
      /// Domain for load variables
      Gecode::IntSet d_load;
      /// Domain for bin variables
      Gecode::IntSet d_bin;
      /// Load to generate (unless -1)
      int load;
      /// Iterator for each variable
      Gecode::IntSetValues* dsv; 
    public:
      /// Initialize assignments for load and bin variables
      LoadBinAssignment(int m, const Gecode::IntSet& d_m,
                        int n, const Gecode::IntSet& d_n,
                        int l)
        : Assignment(m+n,d_m),
          n_bins(m), n_items(n), d_load(d_m), d_bin(d_n), load(l),
          dsv(new Gecode::IntSetValues[static_cast<unsigned int>(m+n)]) {
        for (int i=n_bins; i--; )
          dsv[i].init(d_load);
        for (int i=n_items; i--; )
          dsv[n_bins+i].init(d_bin);
      }
      /// Test whether all assignments have been iterated
      virtual bool operator()(void) const {
        return dsv[0]();
      }
      /// Move to next assignment
      virtual void operator++(void) {
        // Try to generate next bin assignment
        {
          int i = n_items-1;
          while (i >= 0) {
            ++dsv[n_bins+i];
            if (dsv[n_bins+i]())
              return;
            dsv[n_bins+(i--)].init(d_bin);
          }
        }
        // Try to generate next load assignment
        {
        retry:
          int i = n_bins-1;
          while (true) {
            ++dsv[i];
            if (dsv[i]() || (i == 0)) {
              if (dsv[i]() && (load >= 0)) {
                int l = 0;
                for (int k=0;k<n_bins; k++)
                  l += dsv[k].val();
                if (load != l)
                  goto retry;
              }
              return;
            }
            dsv[i--].init(d_load);
          }
        }
      }
      /// Return value for variable \a i
      virtual int operator[](int i) const {
        assert((i>=0) && (i<n_bins+n_items));
        return dsv[i].val();
      }
      /// Destructor
      virtual ~LoadBinAssignment(void) {
        delete [] dsv;
      }
    };

    /// %Test with different bin loads and items
    class BPT : public Test {
    protected:
      /// Number of bins
      int m;
      /// Item sizes
      Gecode::IntArgs s;
      /// Whether to generate only valid loads
      bool valid;
      /// Total item sizes
      int t;
      /// Array of sufficient size for computing item loads
      mutable int il[8];
      /// Compute total size
      static int total(const Gecode::IntArgs& s) {
        int t = 0;
        for (int i=s.size(); i--; )
          t += s[i];
        return t;
      }
    public:
      /// Create and register test for \a m bins and item sizes \a s
      BPT(int m0, const Gecode::IntArgs& s0, bool v=true) 
        : Test("BinPacking::"+str(m0)+"::"+str(s0)+"::"+(v ? "+" : "-"),
               m0+s0.size(), 0, 100), 
          m(m0), s(s0), valid(v), t(total(s)) {
        testsearch = false;
      }
      /// Create assignment
      virtual Assignment* assignment(void) const {
        // Compute plausible bin sizes
        int a = t / m;
        return new LoadBinAssignment(m,Gecode::IntSet(a-1,a+2),
                                     s.size(),Gecode::IntSet(0,m-1),
                                     valid ? t : -1);
      }
      /// %Test whether \a x is solution
      virtual bool solution(const Assignment& x) const {
        // Loads are from 0 to m-1, after that are items
        // Check whether loads sum up to total size
        int l=0;
        for (int j=m; j--; )
          l += x[j];
        if (l != t)
          return false;
        // Compute whether items add up
        for (int j=m; j--; )
          il[j] = 0;
        for (int i=s.size(); i--; )
          il[x[m+i]] += s[i];
        for (int j=m; j--; )
          if (il[j] != x[j])
            return false;
        return true;
      }
      /// Post constraint on \a x
      virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
        using namespace Gecode;
        IntVarArgs l(m);
        IntVarArgs b(s.size());
        for (int j=m; j--; )
          l[j]=x[j];
        for (int i=s.size(); i--; )
          b[i]=x[m+i];
        binpacking(home, l, b, s);
      }
    };
    
    /// %Test with different bin loads and items
    class MBPT : public Test {
    protected:
      /// Dimension
      int d;
      /// Number of bins
      int m;
      /// Item sizes
      Gecode::IntArgs s;
      /// Bin capacities
      Gecode::IntArgs c;
      /// Array of sufficient size for computing item loads
      mutable int il[4][8];
    public:
      /// Create and register test for \a d0 dimensions, \a m0 bins, item sizes \a s0, and capacities \a c0
      MBPT(int d0, int m0, 
           const Gecode::IntArgs& s0, const Gecode::IntArgs& c0) 
        : Test("MultiBinPacking::"+str(d0)+"::"+str(m0)+"::"+
               str(s0)+"::"+str(c0), s0.size() / d0, 0, m0-1), 
          d(d0), m(m0), s(s0), c(c0) {
        testsearch = false;
        testfix = false;
      }
      /// %Test whether \a x is solution
      virtual bool solution(const Assignment& x) const {
        // x are the bin variables
        for (int k=d; k--; )
          for (int j=m; j--; )
            il[k][j] = 0;
        for (int k=d; k--; )
          for (int i=x.size(); i--; )
            il[k][x[i]] += s[i*d+k];
        for (int k=d; k--; )
          for (int j=m; j--; )
            if (il[k][j] > c[k])
              return false;
        return true;
      }
      /// Post constraint on \a x
      virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
        using namespace Gecode;
        IntVarArgs l(d*m);
        for (int j=m*d; j--; )
          l[j]=IntVar(home, 0, Gecode::Int::Limits::max);
        binpacking(home, d, l, x, s, c);
      }
    };
    
    /// Test for testing the max-clique finding for multi bin-packing
    class CliqueMBPT : public Base {
    protected:
      /// Number of items
      int n_items;
      /// Expected clique
      Gecode::IntArgs clique;
      /// Simple test space class
      class TestSpace : public Gecode::Space {
      public:
        // Constructor
        TestSpace(void) {}
        // Copy function
        virtual Gecode::Space* copy(bool share) {
          return NULL;
        }
      };
    public:
      /// Test for number of items \a n expected clique \a c
      CliqueMBPT(const Gecode::IntArgs& c)
        : Base("Int::MultiBinPacking::Clique::"+Test::str(c)), clique(c) {}
      /// Run the actual test
      virtual bool run(void) {
        using namespace Gecode;
        TestSpace* home = new TestSpace;
        /* 
         * Set up a multi-dimensional bin packing problems of dimension 2
         * where the item sizes in one dimension are all one but for some
         * random items and two in the other dimension if the item is 
         * included in the clique  and where the capacity in both dimensions 
         * is 3.
         */
        // Number of items
        int n_items = clique[clique.size()-1] + 1;
        // Capacity
        IntArgs c(2, 3,3);
        // Item sizes
        IntArgs s(2*n_items);
        for (int i=2*n_items; i--; )
          s[i]=1;
        // Create some random conflicts
        for (int i=clique.size()-1; i--; )
          s[rand(n_items)*2+0]=2;
        // Create conflicts corresponding to the clique
        for (int i=clique.size(); i--; )
          s[clique[i]*2+1]=2;
        // Load and bin variables
        IntVarArgs b(*home, n_items, 0, n_items-1);
        IntVarArgs l(*home, 2*n_items, 0, 3);
        IntSet mc = binpacking(*home, 2, l, b, s, c);
        if (home->status() == SS_FAILED) {
          delete home;
          return false;
        }
        if (clique.size() != mc.size()) {
          delete home;
          return false;
        }
        for (int i=clique.size(); i--; )
          if (!mc.in(clique[i])) {
            delete home;
            return false;
          }
        delete home;
        return true;
      }
    };

    /// Help class to create and register tests
    class Create {
    public:
      /// Perform creation and registration
      Create(void) {
        using namespace Gecode;

        {
          IntArgs s1(3, 2,1,1);
          IntArgs s2(4, 1,2,3,4);
          IntArgs s3(4, 4,3,2,1);
          IntArgs s4(4, 1,2,4,8);
          IntArgs s5(4, 1,1,1,1);
          IntArgs s6(4, 1,1,2,2);
          IntArgs s7(4, 1,3,3,4);
          IntArgs s8(6, 1,3,3,0,4,0);
          IntArgs s9(6, 1,2,4,8,16,32);
          
          for (int m=1; m<4; m++) {
            (void) new BPT(m, s1);
            (void) new BPT(m, s2);
            (void) new BPT(m, s3);
            (void) new BPT(m, s4);
            (void) new BPT(m, s5);
            (void) new BPT(m, s6);
            (void) new BPT(m, s7);
            (void) new BPT(m, s8);
            (void) new BPT(m, s9);
            (void) new BPT(m, s1, false);
          }
        }

        {
          IntArgs s1(2*4, 1,2, 2,1, 1,2, 2,1);
          IntArgs c1(2, 3,3);
          (void) new MBPT(2, 4, s1, c1);
          (void) new MBPT(2, 6, s1, c1);
          IntArgs s2(2*3, 1,1, 1,1, 1,1);
          IntArgs c21(2, 1,1);
          IntArgs c22(2, 2,2);
          (void) new MBPT(2, 6, s2, c21);
          (void) new MBPT(2, 6, s2, c22);
          IntArgs s3(3*4, 1,2,3, 3,2,1, 2,1,3, 1,3,2);
          IntArgs c31(3, 3,3,3);
          IntArgs c32(3, 4,4,4);
          IntArgs c33(3, 6,6,6);
          (void) new MBPT(3, 4, s3, c31);
          (void) new MBPT(3, 4, s3, c32);
          (void) new MBPT(3, 4, s3, c33);
          (void) new MBPT(3, 5, s3, c31);
          (void) new MBPT(3, 5, s3, c32);
          (void) new MBPT(3, 5, s3, c33);
        }

        {
          IntArgs c1(4, 0,2,4,6);
          IntArgs c2(8, 1,2,3,4,5,6,7,8);
          IntArgs c3(8, 1,3,7,10,15,22,27,97);
          IntArgs c41(8, 1,2,3,4,5,6,7,14);
          IntArgs c42(8, 1,2,3,4,5,6,7,15);
          IntArgs c43(8, 1,2,3,4,5,6,7,16);
          IntArgs c44(8, 1,2,3,4,5,6,7,30);
          IntArgs c45(8, 1,2,3,4,5,6,7,31);
          IntArgs c46(8, 1,2,3,4,5,6,7,32);
          IntArgs c47(8, 1,2,3,4,5,6,7,62);
          IntArgs c48(8, 1,2,3,4,5,6,7,63);
          IntArgs c49(8, 1,2,3,4,5,6,7,64);

          (void) new CliqueMBPT(c1);
          (void) new CliqueMBPT(c2);
          (void) new CliqueMBPT(c3);
          (void) new CliqueMBPT(c41);
          (void) new CliqueMBPT(c42);
          (void) new CliqueMBPT(c43);
          (void) new CliqueMBPT(c44);
          (void) new CliqueMBPT(c45);
          (void) new CliqueMBPT(c46);
          (void) new CliqueMBPT(c47);
          (void) new CliqueMBPT(c48);
          (void) new CliqueMBPT(c49);
        }
      }
    };
    
    Create c;
    
    //@}
    
  }

}}


// STATISTICS: test-int

