// Lemur OLAP library (c) 2003 National Research Council of Canada by Daniel Lemire, and Owen Kaser
 /**
 *  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 (version 2). 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, write to the Free Software Foundation,
 *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "normalizationscommon.h"

#include "normalutil.h"
#include "normalizations.h"
#include "pearsonnormalization.h"
#include "perlc-interface.h"
#include "slicesortingnormalization.h"
#include "perfectnormalization.h"
#include "greedyfrequencysort.h"
#include "randomclusters.h"
#include "cubestatistics.h"

//
// This code is meant to run tests over simple cases
int main(int argc, char * argv[]) {
    srand( (unsigned)time( NULL ) ); // whenever we need random data
    if(argc < 5) {
      cout << "Usage is : " << argv[0] << " NumberOfRows NumberOfCols NumberAllocated NumberOfTrials" << endl;
      cout << " or " << endl;
        cout << "Usage is : " << argv[0] << 
          " NumberOfRows NumberOfCols NumberAllocated NumberOfTrials RowsChunking ColsChunking " << endl; 
      return 0;
    }
    const int NumberOfRows = atoi(argv[1]);
    const int NumberOfCols = atoi(argv[2]);
    cout << "Number of rows = " << NumberOfRows << " Number of cols = "<< NumberOfCols << endl;
    assert(NumberOfRows > 0); 
    assert(NumberOfCols > 0);
    //assert(NumberOfRows < 100); 
    //assert(NumberOfCols < 100);
    const int NumberAllocated = atoi(argv[3]);
    assert(NumberAllocated >= 0); 
    assert(NumberAllocated <= NumberOfCols * NumberOfRows);
    int RowChunking = 2;
    int ColChunking = 2;
    const int NumberOfTrials = atoi(argv[4]);
    if(argc >=7) {
      RowChunking = atoi(argv[5]);
      ColChunking = atoi(argv[6]);
    }
    vector<int> ChunkShape(2);
    ChunkShape[0] = RowChunking;
    ChunkShape[1] = ColChunking;
    //
    //
    //
    
    //
    vector< Normalization<int,int64> * > n;
    n.push_back( new Normalization<int,int64> ()); 
    n.push_back( new GreedyFrequencySort<int,int64> ());  
    if((NumberOfRows * NumberOfCols < 32)/* || (NumberOfTrials == 1)*/)
      n.push_back( new PerfectNormalization<int,int64> (ChunkShape));
    else 
      cout << "Disabling PerfectNormalization." <<endl;
    //n.push_back( new FrequencySort<int,int64> ());
    n.push_back( new SliceSort<int,int64>()); 
    if(false) n.push_back(new IteratedSliceCluster<int,int64>(ChunkShape));
    n.push_back(new GreedyIterSort<int,int64> ());  
    vector<pair<double,string> > results(n.size());
    for(uint i = 0; i < n.size(); ++i) 
      results[i] = pair<double,string>(0.0,n[i]->getTextName() );
    //int NumberOfTrials = 1000;
    for(int trial = 0; trial < NumberOfTrials; ++trial) {
      if((trial % 100 == 0) && (trial>0)) cout << "trial = " << trial << " out of " << NumberOfTrials << endl;
      vector<bool> data( NumberOfCols * NumberOfRows , false);
      for(int k = 0; k < NumberAllocated ; ++k) data[k] = true;
      random_shuffle(data.begin(), data.end());
      vector<int> shape(2);
      shape[0] = NumberOfCols;
      shape[1] = NumberOfRows;
      RAMCube<int,int64> DC(shape);
      DC.open(/*"anything"*/);// no need for a string
      DC.fillWithZeroes();
#if 1
      for(int k = 0; k < NumberOfCols * NumberOfRows  ; ++k) 
        if(data[k]) 
          DC.put(1,k%NumberOfCols, k/NumberOfCols);
#else
      srand(trial+1);  // debugging
      RAMCube<int,int64> tempDC(shape);
      tempDC.open(/*"junk"*/); //no need for a string
      tempDC.fillWithZeroes();

      if (NumberOfCols != 32)
        addUniformClusters<int,int64>(tempDC,3,DC.getVolume()/2, 0.2, 0.9);
      else
        addUniformCluster<int,int64>(tempDC,vector<int>(2,3), vector<int>(2,20), 0.9); 

      vector<vector<int> > scrambler = PermutationUtil::randomPermutation(shape);
      vector<vector<int> > unscrambler = PermutationUtil::invert(scrambler); // later: add a cheater normalization
      vector<vector<int> > identity = PermutationUtil::identity(shape);

      NormalUtil<int,int64>::copyTo(tempDC, DC,  scrambler /* identity */  );
      
#endif
      // next we actually compute the normalizations
      vector<vector<int> > normal[n.size()];
      for (uint i = 0; i < n.size(); ++i) {
        normal[i] = n[i]->computeNormal(DC);
        if( NumberOfTrials == 1) {
          cout << "Normalization " + n[i]->getTextName() << endl;
          NormalUtil<int,int64>::printSmall2d(DC,normal[i],2);
        }
        const double cost = HOLAPUtil<int,int64>::cost(DC,ChunkShape,normal[i]); 
        results[i].first += cost / (double) NumberOfTrials;         
//				cout << results[i].second << " -> " << cost << endl;
      }
      //CubeStatistics<int,int64>::independenceProduct(DC);  
      DC.close();
    }
    sort(results.begin(), results.end()); 
    cout << "Number of trials = " << NumberOfTrials << endl;
    for (uint i = 0; i < n.size(); ++i) {
            cout << results[i].second << " = ";
            cout << results[i].first;
            cout << " (";
            cout << results[i].first / (float) results[results.size()-1].first * 100;
            cout << " %)";
            cout << endl;
    }
    //
    // We must clean up!
    //
    //
    for (uint i = 0; i < n.size(); ++i) delete n[i];
}

