#ifndef BINARY_MATRIX_H
#define BINARY_MATRIX_H

#include <list>
#include <vector>
#include <set>
#include <string>
#include <iostream>
#include <fstream>
#include <map> 
using namespace std;

#include "BioSequenceMatrix.h"
#include "Utils3.h"
#include "UnWeightedGraph.h"

typedef  vector < set<int>   >  COLUMN_EQUIV_CLASS;

// ***************************************************************************
// Define a reusable binary matrix class
// ***************************************************************************
class BinaryMatrix : public BioSequenceMatrix
{
public:
	BinaryMatrix();
	~BinaryMatrix(); 
	BinaryMatrix(int nr, int nc); 

	// Support assignment/copy constructor
	BinaryMatrix( const BinaryMatrix &rhs); 
	BinaryMatrix &operator=(const BinaryMatrix &rhs); 

    // Important interface functions we need
    virtual bool IsDataValid(int val) ;      // check to see if this data is good for this class
                                                // e.g. for genotype data, 0, 1, 2


	// Matrix editing functions specific to Binary (i.e. haplotype) Matrix
	void TrimDupSites(set<int>* pRemovedSites = NULL, bool fTrimSubsumed = false);
	int  FindDupRow(  );
	void FindNonInformativeSites( set<int> &sitesNoinfo );
	bool TrimNonInformativeSites(set<int> *pRemovedSet = NULL);
    void TrimUniformSites(set<int> *pRemovedSet = NULL);
    void FindUniformSites(set<int> &sitesUniform) const;
	void TrimFullyCompatibleSites(set<int> *pRemovedSet = NULL);
	virtual void TrimNgbrDupCompSites(set<int> *pRemovedSet = NULL);
    void TrimSubsumedRows();
    bool IsRowSubsumedBy( int r1, int r2 ) ;
    bool IsColSubsumedBy( int c1, int c2 ) ;
    void FindSubsumedSites( set<int> &ssSites );

	// Matrix property checking
    bool IsColNonInformative(int c, int *singletonState);
    bool IsColNonInformative(int c) const;
	bool IsColTrivial (int c);
    void GetTrivialSites( vector<int> &trivSites );
	bool IsCompatible( int c1, int c2) const;
    bool IsCompatibleRooted(int c1, int c2, int rallele1, int rallele2);
    bool IsCladeCompatibleWithSite( const set<int> &clade, int c ) const;
	bool IsSiteCompatibleWithRegion( int s,  int rc1, int rc2);
	bool IsRegionFullyCompatible( int rc1, int rc2);
	void GetGamates( int c1, int c2, bool &f00, bool &f01, bool &f10, bool &f11);
	virtual bool IsColComplement( int c1, int c2);
	virtual bool IsColDuplicate( int c1, int c2);
	bool IsPerfectPhylogeny(); 
	bool IsZeroColumn( int c);
	bool IsAllColumnsUnique(); 
	int GetZeroColNum();
    void GetAllIncompatiblePairs(set< pair<int,int> > &incompatibles);
    virtual int GetMajorityState( int site) ;
    int GetMinorStateNum( int site, int &minorState) const;
    void GetMinorStateRows( int site, int &minorState, set<int> &listRowsWMinor) const;
    void GetRowsWithAllele(int site, int alleleState, set<int> &setRows) const;
    static int GetTheOtherAllele(int allele);

	// Construct interval-speceific equivalance row classes, 
	// i.e. sets of row indexes that are same
	void BuildColEquivClasses(); 
	void GetUniqueColsInRange( int c1, int c2, set<int> &setUniques  ); 
	bool IsSequencesMatch( int r1, int r2, vector<int>& seqColPos  ); 
	void GetSequencesDiffSites( int r1, int r2, set<int>& seqColDiffs  ) const; 

	// Ohter utilities
	void ConstructConflictGraph( UnWeightedGraph &graph ); 
	void ConflictGraphComponents( vector<BinaryMatrix>& listSubMatrix);
	void ConfigZeroMajSeq();	// make majority elem all-0 for each position
    void ConfigZeroAncesSeq(const vector<int> &seqAnces);       // make the matrix s.t. the ancestral state is always 0 in matrix
	void DumpConvGenotypes( );
    void GreedyRemoveIncompatSites(BinaryMatrix &matReduced);       // greedily remove incompatible sites (i.e. first remove site that is incompatible w/ most sites and continue)
    void CalcSFS( vector<double> &listSFSFrac ) const;
    int GetDiffSitesForTwoRows(int r1, int r2) const;
    int GetDiffSitesForTwoRowsRegion(int r1, int r2, int c1, int c2) const;
    double CalcAvePairRowsDiffRegion(const set<int> &setRows, int c1, int c2) const;
    double CalcAvePairRowsDiff() const;
    double CalcAvePairRowsDiffBetween(const set<int> &rowsSet1, const set<int> &rowsSet2, double &valMindiffOut) const;
    void CollectAllPairwiseDiffs( const set<int> &rowsSet1, const set<int> &rowsSet2, vector<double> &listRowPairsDiff ) const;
	
	// Missing data utilities
	bool IsColumnBinary(int c) const;
	bool IsRowBinary(int r) const;
	void TrimNonBinaryRows();
	bool IsRowRangeBinary( int r, int left, int right  ); 

    // Lower/upper recombination bound utilities
    int ComputeHKBound();
    int ComputeFastHapBound();
    int ComputeFastRecombUpperBound();
    int ComputeMinRecombWeight( int rowIndex, vector<pair<int,int> > *pListSegs = NULL );
private:

	// Interval-based equivlance classes
	COLUMN_EQUIV_CLASS setColEquiv; 	
};



// some other useful functions
// this structure defines what rows to keep and what not to, and for each removed row, which row it comes from (i.e. duplicate)
// NOTE: we are dealing with the current rows only. THat is, the removal may be in stages
// in each stage, we only consider what we have so far
typedef struct
{
	set<int> rowsRemoved;
	vector< pair<int,int> > pairsRmKeepRows;
} REMOVED_ROWS_INFO;

void GetNoninformativeRowsInMat(const BinaryMatrix &mat, set<int> &trimedRows, vector<REMOVED_ROWS_INFO> &trimedRowInfo, set<int> &trimedCols,
								BinaryMatrix &matUpdated, bool fRmDup = false );
void SplitMatrixIntoMaximalFullyCompatRegs(const BinaryMatrix &mat, vector<pair<int,int> > &listFullyCompatRegs);

void ReadSitePosFromFirstRowInFile(const char *filename, int numSites, vector<double> &listSitePos);

void ConsBinMatrixFromSplits( BinaryMatrix &matBin, int numRows, const set<set<int> > &setClades );


#endif //BINARY_MATRIX_H
