/* Frobby: Software for monomial ideal computations.
   Copyright (C) 2009 Bjarke Hammersholt Roune (www.broune.com)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see http://www.gnu.org/licenses/.
*/
#ifndef SIZE_MAX_INDEP_SET_ALG
#define SIZE_MAX_INDEP_SET_ALG

#include "Ideal.h"

/** Encapsulates an algorithm for computing size-maximal independent
    sets of a hypergraph.

    Currently the only functionality is to compute the size of such a
    set. This amounts to finding the dimension of a monomial ideal,
    which can be done by taking the radical and then using this
    algorithm.

    The algorithm is a branch-and-bound algorithm lifted from Macaulay
    2, though the implementation here is much faster as of July 2009.
    I don't know why it is so much faster, since it is nearly
    equivalent to what Macalay 2 does.

    The state of the algortihm is a partially-decided set of nodes of
    the hypergraph, where each node can be definitely in the set,
    definitely not in the set or undecided. In the beginning every
    variable is undecided.

    The algorithm then scans through the edges. For each edge, it may
    be that it definitely does not make the current set dependent. If
    so, we skip past the edge. It may also definitely make the current
    set dependent. If so, we discard the current set. If it is
    undecided whether the edge makes the set dependent, we choose some
    undecided node that makes this happen, and recurse on the
    possibility where it is not in the set (and so makes the set not
    dependent according to this edge). When we are done, we then
    consider the case where the node is definitely in the set,
    restarting the case-distinctions above.

    The bound used is simply the number of definitely excluded
    nodes. If this is more than or equal to the least number of
    excluded nodes seen so far, then there is no reason to consider
    such a set further, since it can lead to no improvement.

    Note that in the base case, not every independent set considered
    is maximal according to inclusion, but since we only consider the
    size-maximal ones, the ones we care about are maximal with respect
    to inclusion.
 */
class SizeMaxIndepSetAlg {
 public:
  /** Run the algorithm on ideal. Should work but has not been tested
      when called more than once on the same object. May rearrange the
      generators of ideal. ideal must be square free and minimally
      generated. Thus ideal is the edge ideal of some hypergraph, and
      it is this hypergraph that the algorithm runs on.
  */
  void run(Ideal& ideal);

  /** Returns the largest size over all independent sets of the
      hypergraph passed to \ref run.

      Returns varCount + 1 if the hypergraph has no independent sets,
      which is to say that ideal is generated by the identity. The
      returned value is undefined if run has not been called first.
  */
  mpz_class getMaxIndepSetSize();

 private:
  /** Is used for encoding the state of a partially-decided set of
      nodes/variables in the backtracking algorithm.
  */
  enum VarState {
    IsMaybeInSet = 0,
    IsNotInSet = 1,
    IsInSet = 2
  };

  /** Returns true if \ref _state is an independent set according to
      every term in _edges from pos and forward.
  */
  bool isIndependentIncludingMaybe(size_t pos);

  /** Returns true if the term encoded in _edges between pos and
      nextPos makes or might make the set in _state dependent. If so,
      maybeCount is set to the number of IsMaybeInSet from _state on
      the support of that term.
   */
  bool couldBeDependence(size_t pos, size_t nextPos, size_t& maybeCount);

  /** The recursive method that implements the algortihm. See the
      description for this class for a description of the
      algortihm. Pos is the current term, while excluded is the number
      of definitely excluded elements, and also an index into _undo,
      which works since it increases by one at each recursive
      sub-call.
  */
  void recurse(size_t pos, size_t excluded);

  /** The number of variables in the ideal passed to \ref
      run. I.e. the number of nodes in the hypergraph.
  */
  size_t _varCount;

  /** The minimal number of excluded elements in any independent set
      found so far. */
  size_t _minExcluded;

  /** The current state of the backtracking algorithm. Recurse changes
      this as it runs, and then restores the previous value using
      information stored in \ref _undo.
  */
  vector<VarState> _state;

  /** Stores information on how to restore the state of _state after
      having done backtracking on one possibility.
   */
  vector<vector<size_t> > _undo;

  /** Is true if there are no independent sets for the argument to
      \ref run.
  */
  bool _noIndependentSets;

  /** Sparse encoding of the parameter to \ref run. The encoding is
      that the first entry is the size \f$n\f$ of the support of a
      term/edge, and the next \f$n\f$ entries are the variables/nodes
      that are included in that term. Then the next term follows,
      encoded in the same way. It is efficient to store all of this in
      one place to increase locality of reference for the cache.
  */
  vector<size_t> _edges;

  /** The size of _edges. Stored like this for efficiency. */
  size_t _endPos;
};

#endif
