// MathicGB copyright 2012 all rights reserved. MathicGB comes with ABSOLUTELY
// NO WARRANTY and is licensed as GPL v2.0 or later - see LICENSE.txt.
#ifndef MATHICGB_MODULE_MONO_SET_GUARD
#define MATHICGB_MODULE_MONO_SET_GUARD

#include "PolyRing.hpp"
#include <vector>
#include <iostream>
#include <algorithm>

MATHICGB_NAMESPACE_BEGIN

/// Represents the ideal generated by a set of module monomials.
class ModuleMonoSet
{
public:
  typedef PolyRing::Monoid Monoid;
  typedef Monoid::ConstMonoRef ConstMonoRef;
  typedef Monoid::ConstMonoPtr ConstMonoPtr;

  virtual ~ModuleMonoSet();

  /// Inserts mono into this set if it is not already a member of the
  /// ideal generated by this set. Returns true if mono was inserted.
  /// If this set was constructed to allow removals, any other module
  /// monomials divisible by mono will be removed. A reference to
  /// mono is retained but ownership is not passed.
  virtual bool insert(ConstMonoRef mono) = 0;

  /// Returns true if mono is a member of the ideal generated by this set.
  virtual bool member(ConstMonoRef mono) = 0;

  /// Prints a human-readable representation of this set to out.
  virtual void display(std::ostream& out) const = 0;

  /// Returns the number of module monomials in this set. Take care not to
  /// confuse this with the number of components.
  virtual size_t elementCount() const = 0;

  class EntryOutput {
  public:
    virtual void proceed(ConstMonoRef mono) = 0;
  };

  /// Calls consumer.proceed(mono) for each mono in this set.
  virtual void forAllVirtual(EntryOutput& consumer) = 0;

  /// Calls func(mono) for eac mono in this set.
  template<class Func>
  void forAll(Func&& func) {
    struct Wrap : EntryOutput {
      Wrap(Func&& func): mFunc(std::forward<Func>(func)) {}
      virtual void proceed(ConstMonoRef mono) {mFunc(mono);}
      Func&& mFunc;
    } wrap(std::forward<Func>(func));
    forAllVirtual(wrap);
  }

  /// Returns a descriptive name for the implementation of this set.
  virtual std::string name() const = 0;

  virtual size_t getMemoryUse() const = 0;

  /// Prints a human-readable description of the type codes for the
  /// implementations of this interface.
  static void displayCodes(std::ostream& out);

  /// This is how you should instantiate classes that implement this interface.
  static std::unique_ptr<ModuleMonoSet> make(
    const Monoid& monoid,
    int type, // the type code - which implementation do you want?
    size_t componentCount,
    bool allowRemovals
  );
};

MATHICGB_NAMESPACE_END
#endif
