/* Frobby: Software for monomial ideal computations.
   Copyright (C) 2007 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 SLICE_FACADE_GUARD
#define SLICE_FACADE_GUARD

#include "SplitStrategy.h"
#include "Ideal.h"
#include "Facade.h"
#include "SliceParams.h"
#include "TermConsumer.h"
#include "CoefTermConsumer.h"
#include "CommonParamsHelper.h"

#include <vector>
#include <cstdio>
#include <string>

class BigIdeal;
class BigTermConsumer;
class CoefBigTermConsumer;
class SliceStrategy;
class IOHandler;
class DataType;

/** A facade for operations on monomial ideals using the Slice
 Algorithm.

 @ingroup Facade
*/
class SliceFacade : public Facade {
 public:
  SliceFacade(const SliceParams& params, const DataType& output);
  SliceFacade(const SliceParams& params,
              const BigIdeal& ideal,
              BigTermConsumer& consumer);
  SliceFacade(const SliceParams& params,
              const BigIdeal& ideal,
              CoefBigTermConsumer& consumer);

  ~SliceFacade();

  /** Compute the numerator of the multigraded Hilbert-Poincare
   series.

   This series is the sum of all monomials not in the ideal.

   The multigraded Hilbert-Poincare series can be written as a single
   fraction with a polynomial as the numerator and the product of
   (1-x) as the denominator where x varies over all the variable in
   the ambient ring. It is this numerator that is computed.
  */
  void computeMultigradedHilbertSeries();

  /** Compute the numerator of the univariate Hilbert-Poincare
   series.

   This is defined as the multivariate Hilbert-Poincare series where
   every variable has been substituted with the same single variable
   t. See the documentation for computeMultigradedHilbertSeries() for
   more information.

   The terms of the output polynomial are provided in ascending order
   according to exponent.
  */
  void computeUnivariateHilbertSeries();

  /** Compute the unique irredundant set of irreducible ideals whose
   intersection equals ideal.

   If encode is false, then each irreducible component is output as a
   separate ideal, which is only supported when writing to a file. If
   encode is true, then each irreducible component is provided as a
   term, where missing pure powers are encoded as a zero exponent.

   The irreducible components are provided in arbitrary order.

   @param encode Specifies whether to encode the irreducible component
   as monomials.

   @todo Check whether encode being false is really only supported for
   writing to a file. If so, fix it and update this documention.
  */
  void computeIrreducibleDecomposition(bool encode);

  /** Compute the Krull dimension of ideal. By convention, this is -1
      if ideal is generated by the identity. Compute the codimension
      if the codimension is true, which is the number of variables in
      the ambient ring minus the dimension.
  */
  mpz_class computeDimension(bool codimension = false);

  /** Compute the dimension of ideal. This is defined as the dimension
      of the zero set of the ideal. The ideal generated by the
      identity has the special property of having an empty zero set,
      and we define that to have dimension -1.

	  @todo Nope, it is the Krull dimension. although that may be the
	  same. Fix this doc.
	  @todo why are there two dimension methods? */
  void computeDimension(mpz_class& dimension);

  /** Compute the unique "nicest" primary decomposition of the
   ideal.

   This is defined as the primary decomposition where each
   primary component is the intersection of the irreducible components
   with that same support.

   Each primary component is provided as a separate ideal in arbitrary
   order.
  */
  void computePrimaryDecomposition();

  /** Compute the maximal staircase monomials of the ideal.
   A monomial m is a staircase monomial if it belongs to the ideal,
   while the monomial m : (x_1 ... x_n) does not. A monomial m is a
   maximal staircase monomial if m is a staircase monomial and m * x_i
   is not for every variable x_i.

   The output monomials are provided in arbitrary order.
  */
  void computeMaximalStaircaseMonomials();

  /** Compute the maximal standard monomials of the ideal.
   A monomial m is a standard monomial if it does not belong to the
   ideal. A monomial m is a maximal standard monomial if it is
   standard and m * x_i is not standard for every variable x_i. The
   output monomials are provided in arbitrary order.
  */
  void computeMaximalStandardMonomials();

  /** Compute the Alexander dual of the ideal.
   The output monomials are provided in arbitrary order.

   It is an error if any minimal generator of ideal does not divide
   point. It is acceptable for a non-minimal generator to not divide
   point.

   @param point The point to dualize on.
  */
  void computeAlexanderDual(const vector<mpz_class>& point);

  /** Compute the Alexander dual of the ideal.
   The point to dualize on is the least common multiple of the minimal
   generators of the ideal.

   The output monomials are provided in arbitrary order.
  */
  void computeAlexanderDual();

  /** Compute the associated primes of the ideal. These are
   represented as generators of an ideal and are provided in arbitrary
   order.

   @todo Add a bool encode option to choose between output as actual
   ideals or as it is being done now.
  */
  void computeAssociatedPrimes();

  /** Solve an optimization program over maximal standard monomials.
   The optimization program being solved is

    maximize \f$\textrm{v} * \textrm{grading}\f$ subject to \f$x ^
    \textrm{v}\f$ being a maximal standard monomial of the ideal.

   @param grading The vector to optimize.
   @param value Will be set to the value of the program, if any.
   @param reportAllSolutions Output all optimal solutions if true,
     otherwise report some optimal solution if there are any.
   @returns true if there are any maximal standard monomials.
  */
  bool solveStandardProgram
    (const vector<mpz_class>& grading,
     mpz_class& value,
     bool reportAllSolutions);

  /** Solve an optimization program over irreducible components.
   The optimization program being solved is

    maximize \f$\textrm{v} * \textrm{grading}\f$ subject to \p v
    encoding an irreducible component of the ideal.

   @param grading The vector to optimize.
   @param optimalValue Will be set to the value of the program, if any.
   @param reportAllSolutions Output all optimal solutions if true,
     otherwise report some single optimal solution if there is one.
   @returns true if there are any irreducible components.
  */
  bool solveIrreducibleDecompositionProgram
    (const vector<mpz_class>& grading,
     mpz_class& optimalValue,
     bool reportAllSolutions);

 private:
  void produceEncodedIrrDecom(TermConsumer& consumer);

  bool solveProgram(const vector<mpz_class>& grading,
                    mpz_class& optimalValue,
                    bool reportAllSolutions);

  bool isFirstComputation() const;

  void takeRadical();

  void getLcmOfIdeal(vector<mpz_class>& lcm);

  void runSliceAlgorithmWithOptions(SliceStrategy& strategy);

  SliceParams _params;
  CommonParamsHelper _common;
  unique_ptr<SplitStrategy> _split;
};

#endif
