////////////////////////////////////////////////////////////////////////////////
// 
// TriangResult.hh 
//
//    produced: 07/08/25 jr
//
////////////////////////////////////////////////////////////////////////////////
#ifndef TRIANGRESULT_HH
#define TRIANGRESULT_HH

#include "LabelSet.hh"
#include "SimplicialComplex.hh"

#include "CommandlineOptions.hh"

#include "ConfData.hh"
#include "RegularityCheck.hh"
#include "Symmetry.hh"
#include "Flip.hh"

namespace topcom {

  // the following class collects everything that one might want to know
  // about a found triangulation;
  // it depends on the CommandlineOptions and the algorithm,
  // what information is actually generated:
  class TriangResult {
  private:

    // configuration information, owned by some other class:
    const ConfData&          _confdata;
    
    // minimal triangulation information, owned by some other class:
    const int                _ID; // a unique ID
    const int                _workerID; // the ID of the worker that generated this triangulation
    const int                _runID; // the ID of the run in which this triangulation was found
    const int                _inorbitID; // the ID inside one orbit, if orbits must be traversed
    const SimplicialComplex& _triang; // the actual triangulation

    // derivable information (zero pointers mean information is not yet available):
    const LabelSet*          _supportptr; // the support of the triangulation
    const parameter_type*    _supportcardptr; // the number of simplices in the triangulation
    const size_type*         _cardptr; // the number of simplices in the triangulation
    const SymmetryGroup*     _triang_symmetriesptr; // the symmetries of the triangulation
    const bool*              _has_required_symsptr; // whether _triang has required symmetries
    const bool*              _is_regularptr; // whether _triang is regular
    const bool*              _is_unimodularptr; // whether _triang is unimodular
    const bool*              _is_targetequivptr; // whether _triang is equivalent to a given target triangulation
    const Vector*            _heightsptr; // heights from regularity check (zero until regularity has been established)
    const Vector*            _gkzvecptr; // GKZ coordinates of _triang
    const size_type*         _flipdistptr; // can be set by flip-exploration algorithms
  public:
    
    // constructors:
    inline TriangResult() = delete;
    inline TriangResult(const ConfData&,
			const int,
			const int,
			const int,
			const int,
			const SimplicialComplex&);
    inline TriangResult(const TriangResult&);
    inline TriangResult(TriangResult&&);

    // destructor:
    inline ~TriangResult();

    // assignments:
    inline TriangResult& operator=(const TriangResult&) = delete;
    inline TriangResult& operator=(TriangResult&&) = delete;

    // availability:
    inline const bool               has_support()           const;
    inline const bool               has_supportcard()       const;
    inline const bool               has_card()              const;
    inline const bool               has_triang_symmetries() const;
    inline const bool               has_has_required_syms() const;
    inline const bool               has_is_regular()        const;
    inline const bool               has_is_unimodular()     const;
    inline const bool               has_is_targetequiv()    const;
    inline const bool               has_heights()           const;
    inline const bool               has_gkzvec()            const;
    inline const bool               has_flipdist()          const;

    // accessors:
    inline const int                ID()                    const;
    inline const int                workerID()              const;
    inline const int                runID()                 const;
    inline const int                inorbitID()             const;
    inline const SimplicialComplex& triang()                const;
    inline const LabelSet&          support()               const;
    inline const parameter_type     supportcard()           const;
    inline const size_type          card()                  const;
    inline const SymmetryGroup&     triang_symmetries()     const;
    inline const bool               has_required_syms()     const;
    inline const bool               is_regular()            const;
    inline const bool               is_unimodular()         const;
    inline const bool               is_targetequiv()        const;
    inline const Vector&            heights()               const;
    inline const Vector&            gkzvec()                const;
    inline const size_type          flipdist()              const;
   
    // modifiers:
    inline void                     set_support(const LabelSet& support);
    inline void                     set_supportcard(const parameter_type& card);
    inline void                     set_card(const size_type& card);
    inline void                     set_triang_symmetries(const SymmetryGroup& triang_symmetries);
    inline void                     set_has_required_syms(const bool& has_required_syms);
    inline void                     set_is_regular(const bool& is_regular);
    inline void                     set_is_unimodular(const bool& is_unimodular);
    inline void                     set_is_targetequiv(const bool& is_targetequiv);
    inline void                     set_heights(const Vector& heights);
    inline void                     set_gkzvec(const Vector& gkzvec);
    inline void                     set_flipdist(const size_type& flipdist);

    // internal data computation:
    inline const LabelSet&          make_support();
    inline const parameter_type          make_supportcard();
    inline const size_type          make_card();
    inline const SymmetryGroup&     make_triang_symmetries();
    inline const bool               make_has_required_syms();
    inline const bool               make_is_regular();
    inline const bool               make_is_unimodular();
    inline const bool               make_is_targetequiv();
    inline const Vector&            make_heights();
    inline const Vector&            make_gkzvec();
    inline const size_type          make_flipdist();

    // comparison operators:
    inline bool operator==(const TriangResult&) const;

  public:
    // stream output/input:
    inline std::ostream& write(std::ostream& ost) const;
    inline std::istream& read(std::istream& ist);
    friend inline std::ostream& operator<<(std::ostream&, const TriangResult&);
    friend inline std::istream& operator>>(std::istream&, TriangResult&);
  };
   
  // constructors:
  inline TriangResult::TriangResult(const ConfData&          confdata,
				    const int                ID,
				    const int                workerID,
				    const int                runID,
				    const int                inorbitID,
				    const SimplicialComplex& triang) :
    _confdata(confdata),
    _ID(ID),
    _workerID(workerID),
    _runID(runID),
    _inorbitID(inorbitID),
    _triang(triang),
    _supportptr(0),
    _supportcardptr(0),
    _cardptr(0),
    _triang_symmetriesptr(0),
    _has_required_symsptr(0),
    _is_regularptr(0),
    _is_unimodularptr(0),
    _is_targetequivptr(0),
    _heightsptr(0),
    _gkzvecptr(0),
    _flipdistptr(0) {}
  
  inline TriangResult::TriangResult(const TriangResult& tr) :
    _confdata(tr._confdata),
    _ID(tr._ID),
    _workerID(tr._workerID),
    _runID(tr._runID),
    _inorbitID(tr._inorbitID),
    _triang(tr._triang),
    _supportptr(tr._supportptr ? new LabelSet(*tr._supportptr) : 0),
    _supportcardptr(tr._supportcardptr ? new parameter_type(*tr._supportcardptr) : 0),
    _cardptr(tr._cardptr ? new size_type(*tr._cardptr) : 0),
    _triang_symmetriesptr(tr._triang_symmetriesptr ? new SymmetryGroup(*tr._triang_symmetriesptr) : 0),
    _has_required_symsptr(tr._has_required_symsptr ? new bool(*tr._has_required_symsptr) : 0),
    _is_regularptr(tr._is_regularptr ? new bool(*tr._is_regularptr) : 0),
    _is_unimodularptr(tr._is_unimodularptr ? new bool(*tr._is_unimodularptr) : 0),
    _is_targetequivptr(tr._is_targetequivptr ? new bool(*tr._is_targetequivptr) : 0),
    _heightsptr(tr._heightsptr ? new Vector(*tr._heightsptr) : 0),
    _gkzvecptr(tr._gkzvecptr ? new Vector(*tr._gkzvecptr) : 0),
    _flipdistptr(tr._flipdistptr ? new size_type(*tr._flipdistptr) : 0) {}
  
  inline TriangResult::TriangResult(TriangResult&& tr) :
    _confdata(tr._confdata),
    _ID(tr._ID),
    _workerID(tr._workerID),
    _runID(tr._runID),
    _inorbitID(tr._inorbitID),
    _triang(tr._triang),
    _supportptr(tr._supportptr),
    _supportcardptr(tr._supportcardptr),
    _cardptr(tr._cardptr),
    _triang_symmetriesptr(tr._triang_symmetriesptr),
    _has_required_symsptr(tr._has_required_symsptr),
    _is_regularptr(tr._is_regularptr),
    _is_unimodularptr(tr._is_unimodularptr),
    _is_targetequivptr(tr._is_targetequivptr),
    _heightsptr(tr._heightsptr),
    _gkzvecptr(tr._gkzvecptr),
    _flipdistptr(tr._flipdistptr) {}
  
  // destructor:
  inline TriangResult::~TriangResult() {
    if (_flipdistptr) {
      delete _flipdistptr;
    }
    if (_gkzvecptr) {
      delete _gkzvecptr;
    }
    if (_heightsptr) {
      delete _heightsptr;
    }
    if (_is_targetequivptr) {
      delete _is_targetequivptr;
    }
    if (_is_unimodularptr) {
      delete _is_unimodularptr;
    }
    if (_is_regularptr) {
      delete _is_regularptr;
    }
    if (_has_required_symsptr) {
      delete _has_required_symsptr;
    }
    if (_triang_symmetriesptr) {
      delete _triang_symmetriesptr;
    }
    if (_cardptr) {
      delete _cardptr;
    }
    if (_supportcardptr) {
      delete _supportcardptr;
    }
    if (_supportptr) {
      delete _supportptr;
    }
  }

  // availability:
  inline const bool TriangResult::has_support() const {
    return _supportptr;
  }
  inline const bool TriangResult::has_supportcard() const {
    return _cardptr;
  }
  inline const bool TriangResult::has_card() const {
    return _cardptr;
  }
  inline const bool TriangResult::has_triang_symmetries() const {
    return _triang_symmetriesptr;
  }
  inline const bool TriangResult::has_has_required_syms() const {
    return _has_required_symsptr;
  }
  inline const bool TriangResult::has_is_regular() const {
    return _is_regularptr;
  }
  inline const bool TriangResult::has_is_unimodular() const {
    return _is_unimodularptr;
  }
  inline const bool TriangResult::has_is_targetequiv() const {
    return _is_targetequivptr;
  }
  inline const bool TriangResult::has_heights() const {
    return _heightsptr;
  }
  inline const bool TriangResult::has_gkzvec() const {
    return _gkzvecptr;
  }
  inline const bool TriangResult::has_flipdist() const {
    return _flipdistptr;
  }

  // accessors:
  inline const int TriangResult::ID() const {
    return _ID;
  }
  inline const int TriangResult::workerID() const {
    return _workerID;
  }
  inline const int TriangResult::runID() const {
    return _runID;
  }
  inline const int TriangResult::inorbitID() const {
    return _inorbitID;
  }
  inline const SimplicialComplex& TriangResult::triang() const {
    return _triang;
  }
  inline const LabelSet& TriangResult::support() const {
    if (!_supportptr) {
      MessageStreams::forced() << "TriangResult::support(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_supportptr;
  }
  inline const parameter_type TriangResult::supportcard() const {
    if (!_supportcardptr) {
      MessageStreams::forced() << "TriangResult::supportcard(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_supportcardptr;
  }
  inline const size_type TriangResult::card() const {
    if (!_cardptr) {
      MessageStreams::forced() << "TriangResult::card(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_cardptr;
  }
  inline const SymmetryGroup& TriangResult::triang_symmetries() const {
    if (!_triang_symmetriesptr) {
      MessageStreams::forced() << "TriangResult::triang_symmetries(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_triang_symmetriesptr;
  }
  inline const bool TriangResult::has_required_syms() const {
    if (!_has_required_symsptr) {
      MessageStreams::forced() << "TriangResult::has_required_syms(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_has_required_symsptr;
  }
  inline const bool TriangResult::is_regular() const {
    if (!_is_regularptr) {
      MessageStreams::forced() << "TriangResult::is_regular(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_is_regularptr;
  }
  inline const bool TriangResult::is_unimodular() const {
    if (!_is_unimodularptr) {
      MessageStreams::forced() << "TriangResult::is_unimodular(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_is_unimodularptr;
  }
  inline const bool TriangResult::is_targetequiv() const {
    if (!_is_targetequivptr) {
      MessageStreams::forced() << "TriangResult::is_targetequiv(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_is_targetequivptr;
  }
  inline const Vector& TriangResult::heights() const {
    if (!_heightsptr) {
      MessageStreams::forced() << "TriangResult::heights(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_heightsptr;
  }
  inline const Vector& TriangResult::gkzvec() const {
    if (!_gkzvecptr) {
      MessageStreams::forced() << "TriangResult::gkzvec(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_gkzvecptr;
  }
  inline const size_type TriangResult::flipdist() const {
    if (!_flipdistptr) {
      MessageStreams::forced() << "TriangResult::flipdist(): not available - exiting" << std::endl;
      exit(1);
    }
    return *_flipdistptr;
  }

  // modifiers:
  inline void TriangResult::set_support(const LabelSet& support) {
    _supportptr = new LabelSet(support);
  }

  inline void TriangResult::set_supportcard(const parameter_type& supportcard) {
    _supportcardptr = new parameter_type(supportcard);
  }

  inline void TriangResult::set_card(const size_type& card) {
    _cardptr = new size_type(card);
  }

  inline void TriangResult::set_triang_symmetries(const SymmetryGroup& triang_symmetries) {
    _triang_symmetriesptr = new SymmetryGroup(triang_symmetries);
  }
  
  inline void TriangResult::set_has_required_syms(const bool& has_required_syms) {
    _has_required_symsptr = new bool(has_required_syms);
  }

  inline void TriangResult::set_is_regular(const bool& is_regular) {
    _is_regularptr = new bool(is_regular);
  }
  
  inline void TriangResult::set_is_unimodular(const bool& is_unimodular) {
    _is_unimodularptr = new bool(is_unimodular);
  }

  inline void TriangResult::set_is_targetequiv(const bool& is_targetequiv) {
    _is_targetequivptr = new bool(is_targetequiv);
  }
  
  inline void TriangResult::set_heights(const Vector& heights) {
    _heightsptr = new Vector(heights);
  }
  
  inline void TriangResult::set_gkzvec(const Vector& gkzvec) {
    _gkzvecptr = new Vector(gkzvec);
  }

  inline void TriangResult::set_flipdist(const size_type& flipdist) {
    _flipdistptr = new size_type(flipdist);
  }

  // internal data computation:
  inline const LabelSet& TriangResult::make_support() {
    if (!_supportptr) {
      _supportptr = new LabelSet(_triang.support());
    }
    return *_supportptr;
  }

  inline const parameter_type TriangResult::make_supportcard() {
    if (!_supportcardptr) {
      _supportcardptr = new parameter_type(make_support().card());
    }
    return *_supportcardptr;
  }

  inline const size_type TriangResult::make_card() {
    if (!_cardptr) {
      _cardptr = new size_type(_triang.card());
    }
    return *_cardptr;
  }

  inline const SymmetryGroup& TriangResult::make_triang_symmetries() {
    if (!_triang_symmetriesptr) {
      if (_confdata.symmetries().complete()) {
	_triang_symmetriesptr = new SymmetryGroup(_confdata.symmetries().stabilizer(_triang));
      }
      else {
	SymmetryGroup stabsyms(_confdata.symmetries());
	stabsyms.closure();
	_triang_symmetriesptr = new SymmetryGroup(stabsyms.stabilizer(_triang));
      }
    }
    return *_triang_symmetriesptr;
  }

  inline const bool TriangResult::make_has_required_syms() {
    if (!_has_required_symsptr) {
      _has_required_symsptr = new bool(_confdata.required_symmetries().fixes(_triang));
    }
    return *_has_required_symsptr;
  }
  
  inline const bool TriangResult::make_is_regular() {
    if (!_is_regularptr) {
      RegularityCheck regchecker(_confdata, _triang);
      _is_regularptr = new bool(regchecker.is_regular());
    }
    return *_is_regularptr;
  }
  
  inline const bool TriangResult::make_is_unimodular() {
    if (!_is_unimodularptr) {
      _is_unimodularptr = new bool((_confdata.volume() == _triang.card()));
    }
    return *_is_unimodularptr;    
  }

  inline const bool TriangResult::make_is_targetequiv() {
    if (!_is_targetequivptr) {
      MessageStreams::forced() << "TriangResult::make_is_targetequiv: cannot be computed ex-post without access to target triangulation - exiting" << std::endl;
      exit(1);
    }
    return *_is_targetequivptr;    
  }
  
  inline const Vector& TriangResult::make_heights() {
    if (!_heightsptr) {
      RegularityCheck regchecker(_confdata, _triang);
      if (_is_regularptr) {
	delete _is_regularptr;
      }
      _is_regularptr = new bool(regchecker.is_regular());
      _heightsptr = new Vector(regchecker.heights());
    }
    return *_heightsptr;
  }
  
  inline const Vector& TriangResult::make_gkzvec() {
    if (!_gkzvecptr) {
      _gkzvecptr = new Vector(_confdata.gkz(_triang));
    }
    return *_gkzvecptr;
  }

  inline const size_type TriangResult::make_flipdist() {
    if (!_flipdistptr) {
      MessageStreams::forced() << "TriangResult::flipdist(): cannot be computed ex-post - exiting" << std::endl;
      exit(1);
    }
    return *_flipdistptr;
  }

  // comparison operators:
  inline bool TriangResult::operator==(const TriangResult& tr) const {
    return ((_confdata == tr._confdata)
	    && (_ID == tr._ID)
	    && (_workerID == tr._workerID)
	    && (_runID == tr._runID)
	    && (_inorbitID == tr._inorbitID)
	    && (_triang == tr._triang));
  }

  // stream output/input:
  inline std::ostream& TriangResult::write(std::ostream& ost) const {
    ost << std::boolalpha;
    ost << ContainerChars::list_start_char
	<< "triangID:" << _ID
	<< ContainerChars::delim_char
	<< "workerID:" << _workerID
	<< ContainerChars::delim_char
	<< "runID:" << _runID
	<< ContainerChars::delim_char
	<< "inorbitID:" << _inorbitID
	<< ContainerChars::delim_char
	<< "triang:" << _triang;
    if (has_support()) {
      ost << ContainerChars::delim_char;
      ost << "support:" << support();
    }      
    if (has_supportcard()) {
      ost << ContainerChars::delim_char;
      ost << "supportcard:" << supportcard();
    }      
    if (has_card()) {
      ost << ContainerChars::delim_char;
      ost << "card:" << card();
    }      
    if (has_triang_symmetries()) {
      ost << ContainerChars::delim_char;
      ost << "triang_symmetries:" << triang_symmetries();
    }      
    if (has_has_required_syms()) {
      ost << ContainerChars::delim_char;
      ost << "has_required_syms:" << has_required_syms();
    }      
    if (has_is_regular()) {
      ost << ContainerChars::delim_char;
      ost << "is_regular:" << is_regular();
    }      
    if (has_is_unimodular()) {
      ost << ContainerChars::delim_char;
      ost << "is_unimodular:" << is_unimodular();
    }      
    if (has_is_targetequiv()) {
      ost << ContainerChars::delim_char;
      ost << "is_targetequiv:" << is_targetequiv();
    }      
    if (has_heights()) {
      ost << ContainerChars::delim_char;
      ost << "heights:" << heights();
    }      
    if (has_gkzvec()) {
      ost << ContainerChars::delim_char;
      ost << "gkzvec:" << gkzvec();
    }      
    if (has_flipdist()) {
      ost << ContainerChars::delim_char;
      ost << "flipdist:" << flipdist();
    }      
    ost << ContainerChars::list_end_char;
    return ost;
  }
  
  inline std::istream& TriangResult::read(std::istream& ist) {
    MessageStreams::forced() << "TriangResult::read(std::istream&): no read functionality implemented - exiting." << std::endl;
    exit(1);
    return ist;
  }
  
  inline std::ostream& operator<<(std::ostream& ost, const TriangResult& tr) {
    return tr.write(ost);
  }
  
  inline std::istream& operator>>(std::istream& ist, TriangResult& tr) {
    return tr.read(ist);
  }

  
}; // namespace topcom

#endif

// eof TriangResult.hh
