ExecStatus
  IntBase<VY>::prune_lower(Space& home, int* dis, int n_dis) {
    assert(n_dis > 0);

    // At least one more value will be needed
    GECODE_ME_CHECK(y.gq(home,vs.size() + 1));

    Region r(home);

    // Only one additional value is allowed
    if (y.max() == vs.size() + 1) {
      // Compute possible values
      ViewRanges<IntView>* r_dis = r.alloc<ViewRanges<IntView> >(n_dis);
      for (int i=n_dis; i--; )
        r_dis[i] = ViewRanges<IntView>(x[dis[i]]);
      Iter::Ranges::NaryInter iv(r, r_dis, n_dis);
      // Is there a common value at all?
      if (!iv())
        return ES_FAILED;
      ValSet::Ranges vsr(vs);
      Iter::Ranges::NaryUnion pv(r,iv,vsr);
      // Enforce common values
      for (int i=x.size(); i--; ) {
        pv.reset();
        GECODE_ME_CHECK(x[i].inter_r(home, pv, false));
      }
      return ES_OK;
    }

    // Compute independent set for lower bound
    // ovl is a bit-matrix defining whether two views overlap
    SymBitMatrix ovl(r,x.size());
    // deg[i] is the degree of x[i]
    int* deg = r.alloc<int>(x.size());
    // ovl_i[i] is an array of indices j such that x[j] overlaps with x[i]
    int** ovl_i = r.alloc<int*>(x.size());
    // n_ovl_i[i] defines how many integers are stored for ovl_i[i]
    int* n_ovl_i = r.alloc<int>(x.size());
    {
#ifndef NDEBUG
      // Initialize all to null pointers so that things crash ;-)
      for (int i=x.size(); i--; )
        ovl_i[i] = NULL;
#endif
      // For each i there can be at most n_dis-1 entries in ovl_i[i]
      int* m = r.alloc<int>(n_dis*(n_dis-1));
      for (int i=n_dis; i--; ) {
        deg[dis[i]] = 0; 
        ovl_i[dis[i]] = m; m += n_dis-1;
      }
    }
    
    // Initialize overlap matrix by analyzing the view ranges
    {
      // Compute how many events are needed
      // One event for the end marker
      int n_re = 1;
      // Two events for each range
      for (int i=n_dis; i--; )
        for (ViewRanges<IntView> rx(x[dis[i]]); rx(); ++rx)
          n_re += 2;

      // Allocate and initialize events
      RangeEvent* re = r.alloc<RangeEvent>(n_re);
      int j=0;
      for (int i=n_dis; i--; )
        for (ViewRanges<IntView> rx(x[dis[i]]); rx(); ++rx) {
          // Event when a range starts
          re[j].ret=RET_FST; re[j].val=rx.min(); re[j].view=dis[i]; j++;
          // Event when a range ends
          re[j].ret=RET_LST; re[j].val=rx.max(); re[j].view=dis[i]; j++;
        }
      // Make this the last event
      re[j].ret=RET_END; re[j].val=Int::Limits::infinity;
      assert(j+1 == n_re);
      // Sort and process events
      Support::quicksort(re,n_re);

      // Current views with a range being active
      Support::BitSet<Region> cur(r,static_cast<unsigned int>(x.size()));
      // Process all events
      for (int i=0; true; i++)
        switch (re[i].ret) {
        case RET_FST:
          // Process all overlapping views
          for (Iter::Values::BitSet<Support::BitSet<Region> > j(cur); 
               j(); ++j) {
            int di = re[i].view, dj = j.val();
            if (!ovl.get(di,dj))  {
              ovl.set(di,dj);
              ovl_i[di][deg[di]++] = dj; 
              ovl_i[dj][deg[dj]++] = di; 
            }
          }
          cur.set(static_cast<unsigned int>(re[i].view));
          break;
        case RET_LST:
          cur.clear(static_cast<unsigned int>(re[i].view));
          break;
        case RET_END:
          goto done;
        default:
          GECODE_NEVER;
        }
    done:
      r.free<RangeEvent>(re,n_re);
    }

    // While deg changes, n_ovl_i remains unchanged and is needed, so copy it
    for (int i=n_dis; i--; ) {
      assert(deg[dis[i]] < n_dis);
      n_ovl_i[dis[i]] = deg[dis[i]];
    }
    
    // Views in the independent set
    int* ind = r.alloc<int>(n_dis);
    int n_ind = 0;

    while (n_dis > 0) {
      int i_min = n_dis-1;
      int d_min = deg[dis[i_min]];
      unsigned int s_min = x[dis[i_min]].size(); 

      // Find view with smallest (degree,size)
      for (int i=n_dis-1; i--; )
        if ((d_min > deg[dis[i]]) || 
            ((d_min == deg[dis[i]]) && (s_min > x[dis[i]].size()))) {
          i_min = i;
          d_min = deg[dis[i]];
          s_min = x[dis[i]].size();
        }

      // i_min refers to view with smallest (degree,size)
      ind[n_ind++] = dis[i_min]; dis[i_min] = dis[--n_dis];
      
      // Filter out non disjoint views
      for (int i=n_dis; i--; )
        if (ovl.get(dis[i],ind[n_ind-1])) {
          // Update degree information
          for (int j=n_ovl_i[dis[i]]; j--; )
            deg[ovl_i[dis[i]][j]]--;
          // Eliminate view
          dis[i] = dis[--n_dis];
        }
    }
    // Enforce lower bound
    GECODE_ME_CHECK(y.gq(home,vs.size() + n_ind));

    // Prune, if possible
    if (vs.size() + n_ind == y.max()) {
      // Only values from the indepent set a can be taken
      ViewRanges<IntView>* r_ind = r.alloc<ViewRanges<IntView> >(n_ind);
      for (int i=n_ind; i--; )
        r_ind[i] = ViewRanges<IntView>(x[ind[i]]);
      Iter::Ranges::NaryUnion v_ind(r, r_ind, n_ind);
      ValSet::Ranges vsr(vs);
      v_ind |= vsr;
      for (int i=x.size(); i--; ) {
        v_ind.reset();
        GECODE_ME_CHECK(x[i].inter_r(home,v_ind,false));
      }
    } 
    return ES_OK;
  }
void FluidSim::solve_viscosity(float dt) {
   int ni = liquid_phi.ni;
   int nj = liquid_phi.nj;
   
   //static obstacles for simplicity - for moving objects, 
   //use a spatially varying 2d array, and modify the linear system appropriately
   float u_obj = 0;
   float v_obj = 0;

   Array2c u_state(ni+1,nj,(const char&)0);
   Array2c v_state(ni,nj+1,(const char&)0);
   const int SOLID = 1;
   const int FLUID = 0;

   printf("Determining states\n");
   //just determine if the face position is inside the wall! That's it.
   for(int j = 0; j < nj; ++j) {
      for(int i = 0; i < ni+1; ++i) {
         if(i - 1 < 0 || i >= ni || (nodal_solid_phi(i,j+1) + nodal_solid_phi(i,j))/2 <= 0)
            u_state(i,j) = SOLID;
         else
            u_state(i,j) = FLUID;
      }
   }


   for(int j = 0; j < nj+1; ++j)  {
      for(int i = 0; i < ni; ++i) {
         if(j - 1 < 0 || j >= nj || (nodal_solid_phi(i+1,j) + nodal_solid_phi(i,j))/2 <= 0)
            v_state(i,j) = SOLID;
         else 
            v_state(i,j) = FLUID;
      }
   }
   
   printf("Building matrix\n");
   int elts = (ni+1)*nj + ni*(nj+1);
   if(vrhs.size() != elts) {
      vrhs.resize(elts);
      velocities.resize(elts);
      vmatrix.resize(elts);
   }
   vmatrix.zero();
   
   float factor = dt/sqr(dx);
   for(int j = 1; j < nj-1; ++j) for(int i = 1; i < ni-1; ++i) {
      if(u_state(i,j) == FLUID ) {
         int index = u_ind(i,j);      
         
         vrhs[index] = u_vol(i,j) * u(i,j);
         vmatrix.set_element(index,index,u_vol(i,j));
         
         //uxx terms
         float visc_right = viscosity(i,j);
         float visc_left = viscosity(i-1,j);
         float vol_right = c_vol(i,j);
         float vol_left = c_vol(i-1,j);

        //u_x_right
         vmatrix.add_to_element(index,index, 2*factor*visc_right*vol_right);
         if(u_state(i+1,j) == FLUID)
            vmatrix.add_to_element(index,u_ind(i+1,j), -2*factor*visc_right*vol_right);
         else if(u_state(i+1,j) == SOLID)
            vrhs[index] -= -2*factor*visc_right*vol_right*u_obj;

         //u_x_left
         vmatrix.add_to_element(index,index, 2*factor*visc_left*vol_left);
         if(u_state(i-1,j) == FLUID)
            vmatrix.add_to_element(index,u_ind(i-1,j), -2*factor*visc_left*vol_left);
         else if(u_state(i-1,j) == SOLID)
            vrhs[index] -= -2*factor*visc_left*vol_left*u_obj;
         
         //uyy terms
         float visc_top = 0.25f*(viscosity(i-1,j+1) + viscosity(i-1,j) + viscosity(i,j+1) + viscosity(i,j));
         float visc_bottom = 0.25f*(viscosity(i-1,j) + viscosity(i-1,j-1) + viscosity(i,j) + viscosity(i,j-1));
         float vol_top = n_vol(i,j+1);
         float vol_bottom = n_vol(i,j);

         //u_y_top
         vmatrix.add_to_element(index,index, +factor*visc_top*vol_top);
         if(u_state(i,j+1) == FLUID)
            vmatrix.add_to_element(index,u_ind(i,j+1), -factor*visc_top*vol_top);
         else if(u_state(i,j+1) == SOLID)
            vrhs[index] -= -u_obj*factor*visc_top*vol_top;
      
         //u_y_bottom
         vmatrix.add_to_element(index,index, +factor*visc_bottom*vol_bottom);
         if(u_state(i,j-1) == FLUID)
            vmatrix.add_to_element(index,u_ind(i,j-1), -factor*visc_bottom*vol_bottom);
         else if(u_state(i,j-1) == SOLID)
            vrhs[index] -= -u_obj*factor*visc_bottom*vol_bottom;
      
         //vxy terms
         //v_x_top
         if(v_state(i,j+1) == FLUID)
            vmatrix.add_to_element(index,v_ind(i,j+1), -factor*visc_top*vol_top);
         else if(v_state(i,j+1) == SOLID)
            vrhs[index] -= -v_obj*factor*visc_top*vol_top;
         
         if(v_state(i-1,j+1) == FLUID)
            vmatrix.add_to_element(index,v_ind(i-1,j+1), factor*visc_top*vol_top);
         else if(v_state(i-1,j+1) == SOLID)
            vrhs[index] -= v_obj*factor*visc_top*vol_top;
     
         //v_x_bottom
         if(v_state(i,j) == FLUID)
            vmatrix.add_to_element(index,v_ind(i,j), +factor*visc_bottom*vol_bottom);
         else if(v_state(i,j) == SOLID)
            vrhs[index] -= v_obj*factor*visc_bottom*vol_bottom;
         
         if(v_state(i-1,j) == FLUID)
            vmatrix.add_to_element(index,v_ind(i-1,j), -factor*visc_bottom*vol_bottom);
         else if(v_state(i-1,j) == SOLID)
            vrhs[index] -= -v_obj*factor*visc_bottom*vol_bottom;
      

      }
   }
   
   for(int j = 1; j < nj; ++j) for(int i = 1; i < ni-1; ++i) {
      if(v_state(i,j) == FLUID) {
         int index = v_ind(i,j);      
         
         vrhs[index] = v_vol(i,j)*v(i,j);
         vmatrix.set_element(index, index, v_vol(i,j));

         //vyy
         float visc_top = viscosity(i,j);
         float visc_bottom = viscosity(i,j-1);
         float vol_top = c_vol(i,j);
         float vol_bottom = c_vol(i,j-1);

         //vy_top
         vmatrix.add_to_element(index,index, +2*factor*visc_top*vol_top);
         if(v_state(i,j+1) == FLUID)
            vmatrix.add_to_element(index,v_ind(i,j+1), -2*factor*visc_top*vol_top);
         else if (v_state(i,j+1) == SOLID)
            vrhs[index] -= -2*factor*visc_top*vol_top*v_obj;
         
         //vy_bottom
         vmatrix.add_to_element(index,index, +2*factor*visc_bottom*vol_bottom);
         if(v_state(i,j-1) == FLUID)
            vmatrix.add_to_element(index,v_ind(i,j-1), -2*factor*visc_bottom*vol_bottom);
         else if(v_state(i,j-1) == SOLID)
            vrhs[index] -= -2*factor*visc_bottom*vol_bottom*v_obj;
         
         //vxx terms
         float visc_right = 0.25f*(viscosity(i,j-1) + viscosity(i+1,j-1) + viscosity(i,j) + viscosity(i+1,j));
         float visc_left = 0.25f*(viscosity(i,j-1) + viscosity(i-1,j-1) + viscosity(i,j) + viscosity(i-1,j));
         float vol_right = n_vol(i+1,j);
         float vol_left = n_vol(i,j);

         //v_x_right
         vmatrix.add_to_element(index,index, +factor*visc_right*vol_right);
         if(v_state(i+1,j) == FLUID)
            vmatrix.add_to_element(index,v_ind(i+1,j), -factor*visc_right*vol_right);
         else if(v_state(i+1,j) == SOLID)
            vrhs[index] -= -v_obj*factor*visc_right*vol_right;
      
         //v_x_left
         vmatrix.add_to_element(index,index, +factor*visc_left*vol_left);
         if(v_state(i-1,j) == FLUID)
            vmatrix.add_to_element(index,v_ind(i-1,j), -factor*visc_left*vol_left);
         else if(v_state(i-1,j) == SOLID)
            vrhs[index] -= -v_obj*factor*visc_left*vol_left;

         //uyx

         //u_y_right
         if(u_state(i+1,j) == FLUID)
            vmatrix.add_to_element(index,u_ind(i+1,j), -factor*visc_right*vol_right);
         else if(u_state(i+1,j) == SOLID)
            vrhs[index] -= -u_obj*factor*visc_right*vol_right;
         
         if(u_state(i+1,j-1) == FLUID)
            vmatrix.add_to_element(index,u_ind(i+1,j-1), factor*visc_right*vol_right);
         else if(u_state(i+1,j-1) == SOLID)
            vrhs[index] -= u_obj*factor*visc_right*vol_right;
      
         //u_y_left
         if(u_state(i,j) == FLUID)
            vmatrix.add_to_element(index,u_ind(i,j), factor*visc_left*vol_left);
         else if(u_state(i,j) == SOLID)
            vrhs[index] -= u_obj*factor*visc_left*vol_left;
          
         if(u_state(i,j-1) == FLUID)
            vmatrix.add_to_element(index,u_ind(i,j-1), -factor*visc_left*vol_left);
         else if(u_state(i,j-1) == SOLID)
            vrhs[index] -= -u_obj*factor*visc_left*vol_left;
      
      }
   }

   
   double res_out;
   int iter_out;
   
   solver.solve(vmatrix, vrhs, velocities, res_out, iter_out);
   
   for(int j = 0; j < nj; ++j)
      for(int i = 0; i < ni+1; ++i)
         if(u_state(i,j) == FLUID)
            u(i,j) = (float)velocities[u_ind(i,j)];
         else if(u_state(i,j) == SOLID) 
            u(i,j) = u_obj;
         

   
   for(int j = 0; j < nj+1; ++j)
      for(int i = 0; i < ni; ++i)
         if(v_state(i,j) == FLUID)
            v(i,j) = (float)velocities[v_ind(i,j)];
         else if(v_state(i,j) == SOLID) 
            v(i,j) = v_obj;
}