//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Mar 13 20:26:11 PST 2002
// Last Modified: Wed Mar 13 20:26:17 PST 2002
// Filename:      ...sig/examples/all/esac2hum.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/esac2hum.cpp
// Syntax:        C++; museinfo
//
// Description:   Converts an EsAC file into Humdrum.
//

#include "humdrum.h"

#include <string.h>
#include <stdio.h>
#include <math.h>

#ifndef OLDCPP
   #include <iostream>
   #include <fstream>
   using namespace std;
#else
   #include <iostream.h>
   #include <fstream.h>
#endif

typedef Array<int> ArrayInt;

class char16 {
   public:
      char* getBase(void) { return buffer; }
      char buffer[16];
};


// function declarations:
void      checkOptions(Options& opts, int argc, char** argv);
void      example(void);
void      usage(const char* command);
void      parseIndex(const char* filename, Array<char16>& ids,
                                 Array<int>& startp,Array<ArrayInt>& intervals);
int       generateScore(Array<int>& song1, Array<int>& song2, 
                                 int kernul);
void      findBest(int index, Array<char16>& ids, 
                                 Array<ArrayInt>& intervals, int kernul, 
                                 double cutoff);
int       comparePosition(Array<int>& song1, int i, Array<int>& song2, 
                                 int j, int kernul);
int       scorecompare(const void* a, const void* b);
int       generateScoreLinear(Array<int>& song1, Array<int>& song2, 
                                 int kernul);
int       comparePositionContinuous(Array<int>& song1, int i, Array<int>& song2,
                                 int j, int kernul);
int       generateScoreLinearSum(Array<int>& song1, Array<int>& song2, 
                                 int kernul);


// User interface variables:
Options   options;
int       debugQ = 0;          // used with the --debug option
int       verboseQ = 0;        // used with the -v option
int       kernel = 4;          // number of notes in sequence that are
                               // required to generate a match.
double    cutoff = 0.1;        // used with the -c option
int       ncutoff = 0;         // used with the -n option
int       rawQ = 0;            // used with the -r option

//////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv) {
   // process the command-line options
   Array<ArrayInt> intervals;
   Array<char16>   ids;
   Array<int>      startp;

   checkOptions(options, argc, argv);
   parseIndex(options.getArg(1), ids, startp, intervals);
   if (verboseQ) {
      cout << "ids: " << ids.getSize()
           << "\tstartp: " << startp.getSize()
           << "\tintervals: " << intervals.getSize()
           << endl;
   }

   int i;
   for (i=0; i<intervals.getSize(); i++) {
      findBest(i, ids, intervals, kernel, cutoff);
   }

   return 0;
}


//////////////////////////////////////////////////////////////////////////

class scorepair {
   public:
      double score;
      double score1;
      double score2;
      double score3;
      int index;
};


//////////////////////////////
//
// findBest --
//

void findBest(int index, Array<char16>& ids, Array<ArrayInt>& intervals, 
      int kernul, double cutoff) {
   double y0 = 0.0;
   double y1 = 0.0;
   double y2 = 0.0;
   double sco;
   int i;
   Array<scorepair> scores;
   scores.setSize(intervals.getSize());
   for (i=0; i<intervals.getSize(); i++) {
      scores[i].score1 = generateScoreLinear(intervals[index], intervals[i], 
            kernul+4);
      scores[i].score2 = generateScoreLinear(intervals[index], intervals[i], 
            kernul+2);
      scores[i].score3 = generateScoreLinear(intervals[index], intervals[i], 
            kernul+0);
      scores[i].index = i;
      scores[i].score = scores[i].score1;
   }

   // sort scores here.
   qsort(scores.getBase(), scores.getSize(), sizeof(scorepair), scorecompare);

   double basevalue1 = scores[0].score1;
   double basevalue2 = scores[0].score2;
   double basevalue3 = scores[0].score3;
   for (i=0; i<scores.getSize(); i++) {
      y0 = scores[i].score * 1.0 / basevalue1;
      y1 = scores[i].score2 * 1.0 / basevalue2;
      y2 = scores[i].score3 * 1.0 / basevalue3;
      sco = 100 * (y0 + y1/2 + y2/4 - (y1-y0) - (y2-y0) - 0.75);
      sco = (sco + 45.0) / 145.0 * 100.0;
      scores[i].score = sco;
   }

   qsort(scores.getBase(), scores.getSize(), sizeof(scorepair), scorecompare);

   cout << ids[index].getBase() << "\t";

   if (ncutoff > 0) {
      for (i=0; i<ncutoff; i++) {
         cout << ids[scores[i].index].getBase() 
              << "(" << scores[i].score << ")\t";
      }
   } else {
      i = 0;
      while (i < ids.getSize() && scores[i].score >= cutoff) {
         cout << ids[scores[i].index].getBase() <<"("<< scores[i].score << ")\t";
         i++;
      }
   } 

   cout << endl;
}



//////////////////////////////
//
// generateScore -- make a score for the two songs
//

int generateScore(Array& song1, Array& song2, int kernul) {
   int score = 0;
   int linescore = 0;
   int tempscore = 0;
   int i, j;
   int seq = 0;
   for (i=0; i<song1.getSize() - kernul; i++) {
      j = 0;
      linescore = 0;
      while (j < song2.getSize() - kernul) {
         tempscore = 0;
         seq = 0;
         while (j < song2.getSize() - kernul && 
                i + seq + kernul < song1.getSize() &&
               comparePosition(song1, i+seq, song2, j, kernul)) {
            tempscore++;
            j++;
            seq++;
         }
         j++;
         if (tempscore > linescore) {
            linescore = tempscore;
         }
      }
      score += linescore;
   }
   return score;
}



//////////////////////////////
//
// generateScoreLinear -- make a score for the two songs by finding
//    every pattern in song1 anywhere in song2
//

int generateScoreLinear(Array& song1, Array& song2, int kernul) {
   int score = 0;
   int i, j;
   for (i=0; i<song1.getSize() - kernul; i++) {
      j = 0;
      while (j < song2.getSize() - kernul) {
         if (comparePosition(song1, i, song2, j, kernul)) {
            score++;
         }
         j++;
      }
   }
   return score;
}



//////////////////////////////
//
// generateScoreLinear -- make a score for the two songs by finding
//    every pattern in song1 anywhere in song2
//

int generateScoreLinearSum(Array& song1, Array& song2, int kernul) {
   int score = 0;
   int tempscore = 0;
   int maxscore = 0;
   int i, j;
   for (i=0; i<song1.getSize() - kernul; i++) {
      j = 0;
      maxscore = 0;
      tempscore = 0;
      while (j < song2.getSize() - kernul) {
         tempscore = comparePositionContinuous(song1, i, song2, j, kernul);
         if (tempscore == kernul) {
            maxscore = tempscore;
            break;
         }
         if (maxscore < tempscore) {
            maxscore = tempscore;
         }
         j++;
      }
      score += tempscore;
   }
   return score;
}



//////////////////////////////
//
// comparePositionContinuous --
//

int comparePositionContinuous(Array<int>& song1, int i, Array<int>& song2, 
      int j, int kernul) {
   int a;
   int score = 0;
   for (a=0; a<kernul; a++) {
      if (song1[i+a] == song2[j+a]) {
         score++;
      }
   }

   return score;
}



//////////////////////////////
//
// comparePosition --
//

int comparePosition(Array<int>& song1, int i, Array<int>& song2, int j, 
      int kernul) {
   int a;
   for (a=0; a<kernul; a++) {
      if (song1[i+a] != song2[j+a]) {
         return 0;
      }
   }

   return 1;
}



//////////////////////////////
//
// parseIndex --
//

void parseIndex(const char* filename, Array<char16>& ids, Array<int>& startp, 
      Array<ArrayInt>& intervals) {

   #ifndef OLDCPP
      ifstream infile(filename);
   #else
      ifstream infile(filename, ios::nocreate);
   #endif

   if (!infile.is_open()) {
      cerr << "Error opening file: " << filename << endl;
      exit(1);
   }
   char buffer[10001] = {0};
   Array<int> templist;
   templist.setSize(1000);
   templist.setGrowth(1000);
   templist.setSize(0);
 
   ids.setSize(10000);
   ids.setGrowth(10000);
   ids.setSize(0);

   char16 tempid;
 
   startp.setSize(10000);
   startp.setGrowth(10000);
   startp.setSize(0);
   int pitch;
 
   intervals.setSize(10000);
   intervals.setGrowth(10000);
   intervals.setSize(0);
   int inter;

   Array<int> tempinter;
   tempinter.setSize(1000);
   tempinter.setGrowth(1000);
   tempinter.setSize(0);

   int lineno = 0;
   infile.getline(buffer, 10000, '\n');
   lineno++;
   char* ptr;
   while (!infile.eof()) {
      // read id number
      ptr = strtok(buffer, "\t\n: ");
      if (ptr == NULL) {
         break;
      }
      strcpy(tempid.getBase(), ptr);
      ids.append(tempid);

      // read starting pitch
      ptr = strtok(NULL, "\t\n: ");
      if (ptr == NULL) {
         cerr << "Error on line: " << lineno << endl;
         exit(1);
      }
      pitch = atoi(ptr);
      startp.append(pitch);

      tempinter.setSize(0);
      ptr = strtok(NULL, "\t\n: ");
      while (ptr != NULL) {
         inter = atoi(ptr);
         tempinter.append(inter);         
         ptr = strtok(NULL, "\t\n: ");
      }
      intervals.append(tempinter);

      infile.getline(buffer, 10000, '\n');
      lineno++;
   }
}



//////////////////////////////
//
// checkOptions -- 
//

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("debug=b",        "print debug information"); 
   opts.define("v|verbose=b",    "verbose output"); 
   opts.define("k|kernel=i:3",   "matching sequence length");
   opts.define("c|cutoff=d:0.5", "cut-off for matched displays");
   opts.define("n|number=i:0",   "number of best matches to display");
   opts.define("r|raw=b",        "print raw score with no scaling");

   opts.define("author=b",  "author of program"); 
   opts.define("version=b", "compilation info");
   opts.define("example=b", "example usages");   
   opts.define("help=b",  "short description");
   opts.process(argc, argv);
   
   // handle basic options:
   if (opts.getBoolean("author")) {
      cout << "Written by Craig Stuart Sapp, "
           << "craig@ccrma.stanford.edu, March 2002" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 15 March 2002" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << MUSEINFO_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }
   
   debugQ      = opts.getBoolean("debug");
   verboseQ    = opts.getBoolean("verbose");
   kernel      = opts.getInteger("kernel");
   cutoff      = opts.getDouble("cutoff");
   ncutoff     = opts.getInteger("number");
   rawQ        = opts.getBoolean("raw");
}



//////////////////////////////
//
// example --
//

void example(void) {


}



//////////////////////////////
//
// usage --
//

void usage(const char* command) {

}



//////////////////////////////
//
// scorecompare -- compare two integers for ordering
//

int scorecompare(const void* a, const void* b) {
   scorepair& A = *((scorepair*)a);
   scorepair& B = *((scorepair*)b);

   if (A.score > B.score) {
      return -1;
   } else if (A.score < B.score) {
      return 1;
   } else {
      return 0;
   }
}




// md5sum: d0a542d39a5553960e187e9f5738d8e2 plmatch.cpp [20050403]