//
//  Utils4.cpp
//  
//
//  Created by Yufeng Wu on 9/30/13.
//
//

#include "Utils4.h"
#include <cmath>
#include <fstream>

// template functios goes to header file
#if 0

// utilities
template<class TYPE1, class TYPE2>
void CreateMapFromTwoVec( const vector<TYPE1> &vecKey, const vector<TYPE2> &vecval, map<TYPE1,TYPE2> &mapCreated  )
{
    //
    YW_ASSERT_INFO( vecKey.size() == vecval.size(), "veckey has different size as vecval" );
    mapCreated.clear();
    for( int i=0; i<(int)vecKey.size(); ++i )
    {
        //
        mapCreated.insert(map<TYPE1,TYPE2> :: value_type(vecKey[i], vecval[i] ) );
    }
}

template<class TYPE1,class TYPE2>
void KeepCommonInMaps( map<TYPE1,TYPE2> &mapSubtracted, const map<TYPE1,TYPE2> &mapToSub )
{
    // only keep those that is also in the second map
    map<TYPE1, TYPE2> mapNew;
    for( typename map<TYPE1,TYPE2> :: iterator it = mapSubtracted.begin(); it != mapSubtracted.end(); ++it )
    {
        //
        if( mapToSub.find(it->first) != mapToSub.end() )
        {
            // appear in second map so keep
            mapNew.insert( map<TYPE1,TYPE2> :: value_type(it->first, it->second) );
        }
    }
    mapSubtracted = mapNew;
}

template<class TYPE1,class TYPE2>
void KeepCommonInMapsSet( map<TYPE1,TYPE2> &mapSubtracted, const set<TYPE1> &setKept )
{
    // only keep those that is also in the second map
    map<TYPE1, TYPE2> mapNew;
    for( typename map<TYPE1,TYPE2> :: iterator it = mapSubtracted.begin(); it != mapSubtracted.end(); ++it )
    {
        //
        if( setKept.find(it->first) != setKept.end() )
        {
            // appear in second map so keep
            mapNew.insert( map<TYPE1,TYPE2> :: value_type(it->first, it->second) );
        }
    }
    mapSubtracted = mapNew;

}

template<class TYPE1, class TYPE2>
void CreateTwoVecFromMap(const map<TYPE1,TYPE2> &mapIn, vector<TYPE1> &vecKey, vector<TYPE2> &vecval )
{
    vecKey.clear();
    vecval.clear();
    for( typename map<TYPE1,TYPE2> :: iterator it = mapIn.begin(); it != mapIn.end(); ++it )
    {
        vecKey.push_back( it->first );
        vecval.push_back( it->second );
    }
}

#endif



int GetZeroOneDiff(int x, int y)
{
    if( x == y )
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

void GetMatchingPosIntVec(const int val, const vector<int> &listVals, vector<int> &listPos)
{
    listPos.clear();
    for(int i=0; i<(int)listVals.size(); ++i)
    {
        if( val == listVals[i])
        {
            listPos.push_back(i);
        }
    }
}

void FormUnitVector(int numItems, int posUnit, vector<int> &vecUnit)
{
    //
    YW_ASSERT_INFO(posUnit<numItems, "Wrong");
    vecUnit.clear();
    for(int i=0; i<numItems; ++i)
    {
        //
        vecUnit.push_back(0);
    }
    vecUnit[posUnit] = 1;
}

void FormZeroVector(int numItems, vector<int> &vecZero)
{
    //
    vecZero.clear();
    for(int i=0; i<numItems; ++i)
    {
        //
        vecZero.push_back(0);
    }
}

bool AreTwoSetsCompatible( const set<int> &set1, const set<int> &set2)
{
    // are two sets either disjoint or one contins another
    set<int> sint;
    JoinSets(set1, set2, sint);
    if( sint.size() == 0 || sint.size() == set1.size() || sint.size() == set2.size() )
    {
        return true;
    }
    return false;
}

bool IsSetCompatibleWithSets( const set<int> &set1, const set< set<int> > &setSets)
{
    bool res = true;
    for( set<set<int> > :: const_iterator it = setSets.begin(); it != setSets.end(); ++it )
    {
        if( AreTwoSetsCompatible(set1, *it) == false )
        {
            res = false;
            break;
        }
    }
    return res;
}

bool AreTwoSetsCompatible( const set<int> &set1, const set<int> &set2, int numTotalElem)
{
    // are two sets either disjoint or one contins another
    set<int> sint;
    JoinSets(set1, set2, sint);
    if( sint.size() == 0 || sint.size() == set1.size() || sint.size() == set2.size() )
    {
        return true;
    }
    set<int> ssTot = set1;
    UnionSets(ssTot, set2);
    if( (int)ssTot.size() == numTotalElem )
    {
        return true;
    }
    return false;
}

bool IsSetCompatibleWithSets( const set<int> &set1, const set< set<int> > &setSets, int numTotalElem)
{
    bool res = true;
    for( set<set<int> > :: const_iterator it = setSets.begin(); it != setSets.end(); ++it )
    {
        if( AreTwoSetsCompatible(set1, *it, numTotalElem) == false )
        {
            res = false;
            break;
        }
    }
    return res;
}


bool IsSignificantFraction(int totNum, int numTypes, int numOneType, double minFrac )
{
    // test whether the num of one type occupies a siinficant portion of the totNum (composed of numTypes types)
    if( minFrac >=0.0)
    {
        return numOneType >= totNum*minFrac;
    }
    // if not specific fraction is givn, then use the following rule based on the number of types
    // basicallly require appearing at least two times
    return numOneType >= 2;
}

void OffsetAllNumInVecBy( vector<int> &vec, int diff) { for(unsigned int i=0; i<vec.size(); ++i) vec[i] += diff; }

void IncAllNumInSet( set<int> &sint )
{
    //
    set<int> res;
    for(set<int> :: iterator it = sint.begin(); it != sint.end(); ++it)
    {
        res.insert( *it+1);
    }
    sint = res;
}

void DecAllNumInSet( set<int> &sint )
{
    //
    set<int> res;
    for(set<int> :: iterator it = sint.begin(); it != sint.end(); ++it)
    {
        res.insert( *it-1);
    }
    sint = res;
}

void IncAllNumInSets( set< set<int> > &setInts)
{
    //
    set<set<int> > res;
    for( set<set<int> > :: iterator it = setInts.begin(); it != setInts.end(); ++it )
    {
        set<int> sint = *it;
        IncAllNumInSet(sint);
        res.insert( sint);
    }
    setInts = res;
}

void GetNonZeroPosofVec( const vector<int> &vec, set<int> &setpos)
{
    //
    setpos.clear();
    for( int i=0; i<(int)vec.size(); ++i )
    {
        if( vec[i] != 0 )
        {
            setpos.insert(i);
        }
    }
}

int GetSegIndex(int val, const vector<int> &listSegSizes)
{
    //
    int res = -1;
    int szSoFar = 0;
    while( val >= szSoFar  && res < (int)listSegSizes.size() )
    {
        ++res;
        szSoFar += listSegSizes[res];
    }
    return res;
}

// Prob related utilties
double CalcPoisonProb(double rate, int numEvts)
{
    //
    double res = exp(-1.0*rate);
    for(int i=1; i<=numEvts; ++i)
    {
        res *= rate/i;
    }
    return res;
}

void GetDiffPosOfTwoVec(const vector<int> &vec1, const vector<int> &vec2, set<int> &setpos)
{
    //
    YW_ASSERT_INFO(vec1.size() == vec2.size(), "Size: mismatch");
    setpos.clear();
    for(int i=0; i<(int)vec1.size(); ++i)
    {
        if( vec1[i] != vec2[i])
        {
            setpos.insert( i );
        }
    }
}

void ComplementBoolVec(vector<bool> &listVals)
{
    // T->F and vice versa
    for(int i=0; i<(int)listVals.size(); ++i)
    {
        if( listVals[i] == true)
        {
            listVals[i] = false;
        }
        else
        {
            listVals[i] = true;
        }
    }
}

void GetAllGridPoints( int gridLB, int gridUB, int dimGrid, set< vector<int> > &setGridPts )
{
    //  get all grid points whose num is within the range [lb,ub]
    YW_ASSERT_INFO( gridLB <= gridUB, "Bounds wrong");
    YW_ASSERT_INFO( dimGrid >=1, "Dimension must be positive");
    // apply recurrence
    setGridPts.clear();
    if( dimGrid == 1 )
    {
        for( int v=gridLB; v<= gridUB; ++v )
        {
            //
            vector<int> vec;
            vec.push_back( v );
            setGridPts.insert(vec);
        }
    }
    else
    {
        //
        set< vector<int> > setGridPtsSmall;
        GetAllGridPoints( gridLB, gridUB, dimGrid-1, setGridPtsSmall );
        for( set< vector<int> > :: iterator it = setGridPtsSmall.begin(); it != setGridPtsSmall.end(); ++it)
        {
            //
            for( int v=gridLB; v<= gridUB; ++v )
            {
                //
                vector<int> vec = *it;
                vec.push_back( v );
                setGridPts.insert(vec);
            }
        }
    }
}

void MapIntListToAnother( const vector<int> &vec1, const vector<int> &vec2, map<int,int> &mapVec1IndexToVec2)
{
    // given two vectors, e.g. vec1 = [2,1,3] and vec2 = [3,2,1]. Create a map from vec1's index to vec2
    // map = [0,1], [1,2], [2,0]
    // we assume there is no dupllicate for now
//cout << "MapIntListToAnother: vec1: ";
//DumpIntVec(vec1);
//cout << "vec2: ";
//DumpIntVec(vec2);
    mapVec1IndexToVec2.clear();
    YW_ASSERT_INFO( vec1.size() == vec2.size(), "size: mismatch");
    map<int,int> mapValToIndex1, mapValToIndex2;
    for( int i = 0; i<(int)vec1.size(); ++i )
    {
        //
        YW_ASSERT_INFO( mapValToIndex1.find( vec1[i] ) == mapValToIndex1.end(), "Duplicate found" );
        mapValToIndex1.insert( map<int,int> :: value_type( vec1[i], i ) );
//cout << "mapValToIndex1: " << vec1[i] << ", " << i << endl;
    }
    for( int i = 0; i<(int)vec2.size(); ++i )
    {
        //
        YW_ASSERT_INFO( mapValToIndex2.find( vec2[i] ) == mapValToIndex2.end(), "Duplicate found" );
        mapValToIndex2.insert( map<int,int> :: value_type( vec2[i], i ) );
//cout << "mapValToIndex12 " << vec2[i] << ", " << i << endl;
    }
    for( map<int,int> :: iterator it = mapValToIndex1.begin(); it != mapValToIndex1.end(); ++it )
    {
        YW_ASSERT_INFO( mapValToIndex2.find(it->first) != mapVec1IndexToVec2.end(), "Two lists: not idential" );
        mapVec1IndexToVec2.insert( map<int,int> :: value_type( it->second, mapVec1IndexToVec2[it->first] ) );
    }
}

void FindEvenDistriPoints(double valMin, double valMax, double valResolution, int maxNumPoints, vector<double> &listChosenVals)
{
    // pick uniformly some number (<= maxNumPoints) of points within [valMin, valMax}, with distance no more than resolution
    // first figure out spacing
    double valSpacing = (valMax-valMin)/maxNumPoints;
    if( valSpacing < valResolution )
    {
        valSpacing = valResolution;
    }
    for(int i=0; i<(int) (valMax-valMin)/valSpacing ; ++i)
    {
        //
        double val = (i+0.5)*valSpacing;
        listChosenVals.push_back(val);
    }
}

// bits operation
bool IsBitSetInt(int val, int posBit)
{
    //
    // for an index of AC, which src populaiton is a leave
    int mask = (0x1 << posBit);
    // assume only two populaitons for now
    bool res = false;
    if( ( val & mask ) != 0 )
    {
        res = true;
    }
    return res;
}

int ToggleBitInt(int val, int posBit)
{
    //
    return val ^ (1 << posBit);
}

double StrToDouble(const string &s)
{
    double d;
    stringstream ss(s); //turn the string into a stream
    ss >> d; //convert
    return d;
}

double CalcProductBetween(int lb, int ub)
{
    double res = 1.0;
    for(int i=lb; i<=ub; ++i)
    {
        res *= i;
    }
    return res;
}

void CreateClustersFromMultisets( const multiset< multiset<int> > &setMultisets, map<multiset<int>, vector<  multiset<int> > > &mapMultisetClusters )
{
cout << "CreateClustersFromMultisets: DONOT WORK YET\n";
    // give multisets S1, S2, .... Sn
    // find the ancestral (clustering) relations among them
    // i.e. if S1 contains S2 and S3 (as the smallest enclosing), then we have: S1 (S2, S3) and so on
    mapMultisetClusters.clear();
    
    // this refers to the smallest container set
    map<multiset<int>, multiset<int> > mapSmallestContainer;
    
    //
    //YW: TBD: issue: there may be duplicate clusters
    //TBDDDDDDDDDDDDDDDDDDD
    
    for( multiset< multiset<int> > :: const_iterator it1 = setMultisets.begin(); it1 != setMultisets.end(); ++it1 )
    {
        //
        for( multiset< multiset<int> > :: const_iterator it2 = setMultisets.begin(); it2 != setMultisets.end(); ++it2 )
        {
            //
            if( it1 == it2)
            {
                continue;
            }
            
            // is s1 contained by s2? and also cannot allow the two becomes the same
            if( IsMultisetContainedIn( *it1, *it2 ) == true && it1->size() < it2->size() )
            {
                //
                if( mapSmallestContainer.find( *it1) == mapSmallestContainer.end() )
                {
                    mapSmallestContainer.insert( map<multiset<int>, multiset<int> > :: value_type(*it1, *it2)  );
                }
                else if( mapSmallestContainer[*it1].size() > it2->size()  )
                {
                    mapSmallestContainer[*it1] = *it2;
                }
            }
        }
    }
cout << "here...\n";
    // now from the smallest container, create the clusters
    for( map<multiset<int>, multiset<int> > :: iterator it = mapSmallestContainer.begin(); it != mapSmallestContainer.end(); ++it )
    {
        if( mapMultisetClusters.find(it->second) == mapMultisetClusters.end() )
        {
            vector< multiset<int> > listMSs;
            mapMultisetClusters.insert( map<multiset<int>, vector<  multiset<int> > >:: value_type( it->second, listMSs ) );
        }
        mapMultisetClusters[it->second].push_back(it->first);
    }
}

void CountMultiset( const multiset<int> &s1, map<int, int> &msMap )
{
    for(multiset<int> :: const_iterator it = s1.begin(); it != s1.end(); ++it)
    {
        if( msMap.find(*it) == msMap.end() )
        {
            msMap.insert( map<int,int> :: value_type(*it,0));
        }
        ++msMap[*it];
    }
}

bool IsMultisetContainedIn( const multiset<int> &s1, const multiset<int> &s2 )
{
    map<int,int> msMap1, msMap2;
    CountMultiset(s1, msMap1);
    CountMultiset(s2, msMap2);
    for( map<int,int> :: iterator it1 = msMap1.begin(); it1 != msMap1.end(); ++it1)
    {
        if( msMap2.find(it1->first) == msMap2.end() || it1->second > msMap2[it1->first]  )
        {
            return false;
        }
    }
    return true;
}

void DumpIntMultiset(const multiset<int> & ms)
{
    for(multiset<int> :: const_iterator it = ms.begin(); it != ms.end(); ++it )
    {
        cout << *it << "  ";
    }
    cout << endl;
}

void OutputStringsToFile(const char *filename, const vector<string> &listStrsOut)
{
    ofstream outFile( filename );
    if(outFile.is_open() == false)
	{
		cout << "Fatal error: Can not open output file: "<< filename <<  endl;
		exit(1);
	}
    
	for(int i=0; i<(int)listStrsOut.size(); ++i)
	{
        outFile << listStrsOut[i] << endl;
	}
	outFile.close();
}


unsigned int ConvVecToIntGen( const vector<int> &vec, int base)
{
	// assume vec[0] is least siginicant
	unsigned int res = 0;
    
	for(int i=(int)vec.size()-1; i >= 0; --i)
	{
        YW_ASSERT_INFO( vec[i] >= 0 && vec[i] < base, "In ConvVecToIntGen, vector value overflow."  );
        //cout << "res = " << res << endl;
        
        res += vec[i];
		if( i > 0)
		{
			res = res * base;
		}
	}
    
	return res;
}

unsigned int ConvVecToIntGenMSB( const vector<int> &vec, int base)
{
    vector<int> vecMSB = vec;
    //cout << "vec = ";
    //DumpIntVec( vec );
    ReverseIntVec(vecMSB);
    //cout << "vec = ";
    //DumpIntVec( vec );
    return ConvVecToIntGen(vecMSB, base);
}

int ConvVecToIntGenBounds( const vector<int> &vec, const vector<int> &bounds)
{
    // bound[i]: the largest value a digit can reach at position i
    // assume vec[0] is least siginicant
	unsigned int res = 0;
    
	for(int i=(int)vec.size()-1; i >= 0; --i)
	{
        YW_ASSERT_INFO( vec[i] >= 0 && vec[i] <= bounds[i], "In ConvVecToIntGen, vector value overflow."  );
        //cout << "res = " << res << endl;
        
        res += vec[i];
		if( i > 0)
		{
			res = res * (bounds[i-1]+1);
		}
	}
    
	return res;
}

void ConvIntToVecGen( int val, const vector<int> &bounds, vector<int> &vec )
{
    vec.clear();

    int numBits = bounds.size();
    YW_ASSERT_INFO( numBits < 30, "Overflow000" );
    
    // we would store the least significant bit as vec[0]
    for(int i=0; i<numBits; ++i)
    {
        int bound0 = bounds[i];
        YW_ASSERT_INFO(bound0 >=0, "Cannot be too small");
        int val2 = val % (bound0+1);
        vec.push_back(val2);
        val = (val -  val2)/(bound0+1);
    }
}

int ConvRowMajorPosVecToIntGenBounds( const vector<int> &vec, const vector<int> &bounds)
{
    // different from above: bound b means that max value is actaully b-1 (like those)
    // bound[i]: the largest value a digit can reach at position i
    // assume vec[0] is least siginicant
	unsigned int res = 0;
    
	for(int i=0; i<(int)vec.size(); ++i)
	{
		if( i > 0)
		{
			res = res * (bounds[i]);
		}
        YW_ASSERT_INFO( vec[i] >= 0 && vec[i] <= bounds[i], "In ConvVecToIntGen, vector value overflow."  );
        //cout << "res = " << res << endl;
        res += vec[i];
	}
    
	return res;
}

void ConvRowMajorIntPosToVecGen( int val, const vector<int> &bounds, vector<int> &vec )
{
    //
    vec.clear();
    
    int numBits = bounds.size();
    YW_ASSERT_INFO( numBits < 30, "Overflow000" );
    
    // we would store the least significant bit as vec[0]
    for(int i=numBits-1; i>=0; --i)
    {
        int bound0 = bounds[i];
        YW_ASSERT_INFO(bound0 >=1, "Cannot be too small");
        int val2 = val % (bound0);
        vec.push_back(val2);
        val = (val -  val2)/(bound0);
    }
    ReverseIntVec( vec );
}

// utility
class ClusterPosition
{
public:
    ClusterPosition() { pos = 0; }
    ClusterPosition(const ClusterPosition &rhs) : pos(rhs.pos) {}
    ClusterPosition(int posIn) {pos = posIn;}
    int GetPosition() const {return pos;}
    
private:
    int pos;
};

void ClusterLinearPoints( const vector<double> &listPoints, double ratioMaxInOutCmp, vector<int> &listBkpts )
{
    // assume points are sorted!!!
    if( listPoints.size() <= 1 )
    {
        // nothing to cluster
        return;
    }
    
    // rationInOutCmp: the max ratio btwn inside cluster and outside cluster that we will merge two groups
    map< pair<int,int>, double > mapClusterInfo;    // current max distance within group
    map<int, pair<int,int> > mapPointMembership;
    
    // init each point to self
    for(int i=0; i<(int)listPoints.size(); ++i)
    {
        pair<int,int> pp(i,i);
        mapClusterInfo.insert( map< pair<int,int>, double > :: value_type(pp, 0.0) );
        mapPointMembership.insert( map<int,pair<int,int> > :: value_type(i, pp) );
    }
    // sort the values
    vector<ClusterPosition> vecPosRecords;
    for(int i=0; i<(int)listPoints.size(); ++i)
    {
        ClusterPosition cp(i);
        vecPosRecords.push_back(cp);
    }
    vector< pair<double, void *> > listPointsSortedWithPos;
    for(int i=0; i<(int)listPoints.size()-1; ++i)
    {
        pair<double, void *> pp( listPoints[i+1]-listPoints[i], &vecPosRecords[i] );
        listPointsSortedWithPos.push_back(pp);
    }
    SortPairsByNumsDouble( listPointsSortedWithPos );
    for(int i=0; i<(int)listPointsSortedWithPos.size(); ++i)
    {
        //
        double diststep = listPointsSortedWithPos[i].first;
        ClusterPosition *ptr = (ClusterPosition *)( listPointsSortedWithPos[i].second );
        int pos = ptr->GetPosition();
        int posNext = pos+1;
        YW_ASSERT_INFO( mapPointMembership.find(pos) != mapPointMembership.end(), "Fail" );
        YW_ASSERT_INFO( mapPointMembership.find(posNext) != mapPointMembership.end(), "Fail" );
        pair<int,int> pp1 = mapPointMembership[pos];
        pair<int,int> pp2 = mapPointMembership[posNext];
        // should we merge the two; do so if the current distance
        bool fMerge1 = true;
        if( pp1.second > pp1.first  )
        {
            YW_ASSERT_INFO( mapClusterInfo.find(pp1) != mapClusterInfo.end(), "Fail to find" );
            double distCur = mapClusterInfo[pp1];
            if( diststep <= distCur* ratioMaxInOutCmp )
            {
                fMerge1 = true;
            }
            else
            {
                fMerge1 = false;
            }
        }
        bool fMerge2 = true;
        if( pp2.second > pp2.first  )
        {
            YW_ASSERT_INFO( mapClusterInfo.find(pp2) != mapClusterInfo.end(), "Fail to find" );
            double distCur = mapClusterInfo[pp2];
            if( diststep <= distCur* ratioMaxInOutCmp )
            {
                fMerge2 = true;
            }
            else
            {
                fMerge2 = false;
            }
        }
        if( fMerge1 && fMerge2  )
        {
cout << "Merging: (" << pp1.first << ", " << pp1.second << "): and (" << pp2.first << "," << pp2.second << ")\n";
            // merge
            pair<int,int> ppnew(pp1.first, pp2.second);
            double distMaxNew = std::max( diststep, std::max( mapClusterInfo[pp1], mapClusterInfo[pp2] ) );
            mapClusterInfo.insert( map<pair<int,int>, double> :: value_type( ppnew, distMaxNew ) );
            mapClusterInfo.erase(pp1);
            mapClusterInfo.erase(pp2);
            for(int s=ppnew.first; s<=ppnew.second; ++s)
            {
                mapPointMembership.erase(s);
                mapPointMembership.insert( map<int, pair<int,int> > :: value_type(s, ppnew) );
            }
        }
    }
    // now insert all segments
    for( map< pair<int,int>, double > :: iterator it = mapClusterInfo.begin(); it != mapClusterInfo.end(); ++it )
    {
        int bkptRight = it->first.second;
        if( bkptRight < (int) listPoints.size()-1 )
        {
            listBkpts.push_back( bkptRight );
        }
    }
}

void FindConsecutiveIntervals(const set<int> &setItems, vector< pair<int,int> > &listIVs )
{
    listIVs.clear();
    if( setItems.size() == 0 )
    {
        return;
    }
    int itemStart = *setItems.begin();
    int itemPrev = itemStart;
    set<int> :: const_iterator it = setItems.begin();
    ++it;
    while(it != setItems.end() )
    {
        if( *it != itemPrev+1)
        {
            // this is an IV
            pair<int,int> pp(itemStart, itemPrev );
            listIVs.push_back(pp);
            itemStart = *it;
        }

        itemPrev = *it;
        ++it;
        if( it == setItems.end() )
        {
            // ouput the prev
            pair<int,int> pp(itemStart, itemPrev);
            listIVs.push_back(pp);
        }
    }
}

void ComplementIntSet(int numTot, set<int> &setToComp)
{
    // YW: assume numbers start from 0 to numTot-1
    set<int> ssTot;
    PopulateSetWithInterval(ssTot, 0, numTot-1);
    SubtractSets( ssTot, setToComp );
    setToComp = ssTot;
}

void ComplementIntVec(int numTot, vector<int> &vecToComp)
{
    //
    set<int> ssInt;
    PopulateSetByVec(ssInt, vecToComp);
    ComplementIntSet(numTot, ssInt);
    PopulateVecBySet(vecToComp, ssInt);
}

void ComplementIntSetIfMiss(int numTot, int nid, set<int> &setToComp)
{
    // if nid is not in the set, complement it
    if( setToComp.find(nid) == setToComp.end() )
    {
        ComplementIntSet(numTot, setToComp);
    }
}

void GetCountsItems(int range, const set<int> &listNumbers, vector<int> &listCnts)
{
    // count occurance of numbers: listCnts[k] = # of items that is smaller or equal to k in the set
    YW_ASSERT_INFO(range >= 0, "Must be positive");
    listCnts.clear();
    listCnts.resize(range+1);
    int cntTot = 0;
    int posLast = -1;
    for(set<int> :: const_iterator it = listNumbers.begin(); it != listNumbers.end(); ++it)
    {
        int val = *it;
        YW_ASSERT_INFO( val <= range, "Wrong" );
        for(int i=posLast+1; i< val; ++i)
        {
            listCnts[i] = cntTot;
        }
        ++cntTot;
        listCnts[val] = cntTot;
        posLast = val;
    }
}

void FindGapBlocksWithinPosVec(const vector<int> &posvec, int numItemsEnum, int numItemsGap, vector< pair<int,int> > &listSegs)
{
    // in a position vector (i.e. subset of positions 0, 1, ..., k; find gaps in between the chosen positions
    // gaps are re-ordered to consecutive from 0, 1, ...
    listSegs.clear();
    vector<int> listGapLens;
    for(int i=0; i<(int)posvec.size(); ++i)
    {
        int posLast = -1;
        if( i > 0 )
        {
            posLast = posvec[i-1];
        }
        int len = posvec[i] - posLast-1;
        listGapLens.push_back(len);
    }
//cout << "numItemsEnum: " << numItemsEnum << ", listGapLens: ";
//DumpIntVec(listGapLens);
    // last segment
    int posFinal = numItemsEnum+numItemsGap-1;
    int posFirst = 0;
    if( posvec.size() > 0 )
    {
        posFirst = posvec[ posvec.size()-1 ];
    }
    else
    {
        posFinal = numItemsGap;
    }
    int lenFinal = posFinal - posFirst;
//cout << "posFirst: " << posFirst << ", posFinal: " << posFinal << ", lenFinal: " << lenFinal << endl;
    YW_ASSERT_INFO(lenFinal >= 0, "Cannot be negative");
    listGapLens.push_back( lenFinal );
    int posCur = 0;
    for(int i=0; i<(int)listGapLens.size(); ++i)
    {
        pair<int,int> pp;
        pp.first = posCur;
        pp.second =  posCur + listGapLens[i];
        
        if( pp.first > numItemsGap )
        {
            pp.first = -1;
        }
        if( pp.second > numItemsGap )
        {
            pp.second = -1;
        }
        
        listSegs.push_back(pp);
        
        // note: consecutive IV overlaps
        posCur = pp.second;
    }
}

void GetSetsIntParts(const set<int> &set1, const set<int> &set2, const set<int> &setAll, set<int> &set1Only, set<int> &set2Only, set<int> &set12, set<int> &setNone )
{
    //
    set1Only = set1;
    SubtractSets(set1Only, set2);
    set2Only = set2;
    SubtractSets(set2Only, set1);
    set12 = set1;
    UnionSets(set12, set2);
    setNone = setAll;
    SubtractSets(setNone, set12);
}

void ReadFromFileListOfIntsSingleLine( ifstream &fileIn, vector<int> &listInts )
{
    //
    char buf[1024000];
    fileIn.getline(buf, 1024000);
    listInts.clear();
    std::stringstream  lineStream(buf);
    
    int value;
    // Read an integer at a time from the line
    while(lineStream >> value)
    {
        // Add the integers from a line to a 1D array (vector)
        listInts.push_back(value);
    }
}

// region search utility
LeftRightSearch :: LeftRightSearch(int boundLeftIn, int boundRightIn, int posStartIn) : boundLeft(boundLeftIn), boundRight(boundRightIn), posStart(posStartIn), posCur(posStartIn), dirSearch(0)
{
}
void LeftRightSearch :: Init()
{
    posCur = posStart;
    dirSearch = 0;
}
void LeftRightSearch :: Next()
{
    if( dirSearch == 0  )
    {
        ++posCur;
    }
    else
    {
        --posCur;
    }
    if( posCur > boundRight )
    {
        dirSearch = 1;
        posCur = posStart-1;
    }
}
bool LeftRightSearch :: IsDone() const
{
    return posCur < boundLeft || posCur > boundRight;
}
int LeftRightSearch :: GetCurPos() const
{
    return posCur;
}

// automatically search around the center

LeftRightSearchV2 :: LeftRightSearchV2(int boundLeftIn, int boundRightIn, int posStartIn) : boundLeft(boundLeftIn), boundRight(boundRightIn), posCur(posStartIn), posCurLeft(posStartIn), posCurRight(posStartIn), dirSearch(0)
{
    Init();
}
void LeftRightSearchV2 :: Init()
{
    // do nothing for now
}
void LeftRightSearchV2 :: Next()
{
    if(dirSearch == 0 )
    {
        dirSearch = 1;
    }
    else
    {
        dirSearch = 0;
    }
    if( dirSearch == 0)
    {
        // check to see if we can move left
        if( posCurLeft <= boundLeft)
        {
            dirSearch = 1;
        }
    }
    if( dirSearch == 1)
    {
        // check to see if we can move left
        if( posCurRight >= boundRight)
        {
            dirSearch = 0;
        }
    }
    if(dirSearch == 0 )
    {
        --posCurLeft;
        posCur = posCurLeft;
    }
    else
    {
        ++posCurRight;
        posCur = posCurRight;
    }
}
bool LeftRightSearchV2 :: IsDone() const
{
    return posCurLeft <= boundLeft && posCurRight >= boundRight;
}
int LeftRightSearchV2 :: GetCurPos() const
{
    return posCur;
}


void ConsDupPosForVec(const vector<int> &vecDup, vector<set<int> > &vecDupPos)
{
    // vecDup: a list of vectors with duplicates. Assume with integers consecutively 0, 1, 2, 1, 3, 3
    // vecDupPos: (0) (1,3), (2), (4,5). That is, for each distinct number, the set of positions with that number
    set<int> setNoDups;
    PopulateSetByVec( setNoDups, vecDup );
    YW_ASSERT_INFO( setNoDups.size() == 0 || ( *(setNoDups.begin()) == 0 && *(setNoDups.rbegin()) == (int)setNoDups.size()-1  ), "Wrong in ConsDupPosForVec" );
    vecDupPos.clear();
    vecDupPos.resize( setNoDups.size() );
    for(int i=0; i<(int)vecDup.size(); ++i)
    {
        vecDupPos[ vecDup[i] ].insert(i);
    }
}

void GetAllSubsets(const set<int> &setCurr, set<set<int> > &setAllSS)
{
    // find all subsets of the set
    setAllSS.clear();
    vector<int> vecCurr;
    PopulateVecBySet(vecCurr, setCurr);
    int numSS = 0x1 << setCurr.size();
    for(int v=0; v<numSS; ++v)
    {
        set<int> ss;
        ConvPositionToIntSet(v, ss);
        set<int> ssconv;
        for(set<int> :: iterator it2 = ss.begin(); it2 != ss.end(); ++it2)
        {
            ssconv.insert(vecCurr[*it2]);
        }
        setAllSS.insert(ssconv);
    }
}

void ConvConsecutiveVecTo(const vector<int> &vecMap, const vector<int> &vecToMap, vector<int> &vecMapped)
{
    // map a sub-vector (in the range of [0,1,..., n-1]) based on a mapping vector
    // example: vecMap=(1,3,4,2,0), vecToMap=(2,3), then vecMapped=(4,2)
    vecMapped.clear();
    for(unsigned int i=0; i<vecToMap.size(); ++i)
    {
        vecMapped.push_back(vecMap[vecToMap[i]]);
    }
}

static double CalcSumDoublesPreciseImp(const vector<double> &listNums, int posStart, int posEnd)
{
    if( posStart == posEnd )
    {
        return listNums[posStart];
    }
    if( posStart +1 == posEnd )
    {
        return listNums[posStart] + listNums[posEnd];
    }
    // divide and conquer
    int mid = (posStart+posEnd)/2;
    return CalcSumDoublesPreciseImp(listNums, posStart, mid) + CalcSumDoublesPreciseImp(listNums, mid+1, posEnd);
}

double CalcSumDoublesPrecise(const vector<double> &listNums)
{
    // assume: these numbers are close; want to have more precise sum of these values to avoid rounding errors when adding large and small floating numbers
    // apporach: divide and conquer
    return CalcSumDoublesPreciseImp( listNums, 0, (int)listNums.size()-1 );
}
