//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Aug 30 10:51:26 PDT 2011
// Last Modified: Fri Sep  2 18:25:34 PDT 2011
// Last Modified: Tue Sep 13 13:33:52 PDT 2011 added -k option
// Last Modified: Thu Sep 15 01:36:49 PDT 2011 added -D option
// Last Modified: Thu Oct 20 22:23:27 PDT 2011 fixed init bug
// Filename:      ...sig/examples/all/notearray.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/notearray.cpp
// Syntax:        C++; museinfo
// 
// Description:   Generate a two-dimensional numeric array containing
//                notes in the score in base-40, base-12, or base-7 
//                representation.  Each data line represents a sonority
//                with attacked notes in that sonority being positive
//                numbers, and sustained notes from previous sonorities
//                being negative.
//

#include "humdrum.h"

#ifndef OLDCPP
   #include <iostream>
   using namespace std;
#else
   #include <iostream.h>
#endif
   
#define STYLE_BASE40 40
#define STYLE_BASE12 12
#define STYLE_BASE7   7

#define TYPE_MIN         999
#define TYPE_NOTES	1000
#define TYPE_KERN       1000
#define TYPE_LINE	(2000-1)  /* +1 will be added later to make 2000 */
#define TYPE_MEASURE	3000
#define TYPE_BEAT 	4000
#define TYPE_ABSOLUTE	5000
#define TYPE_LINEDUR    6000
#define TYPE_ATTACK	7100
#define TYPE_LAST    	7200
#define TYPE_NEXT	7300

// function declarations
void getNoteArray(Array<Array<int> >& notes, Array<int>& measpos, 
                             Array<int>& linenum, HumdrumFile& infile, 
                             int base, int flags);
void printNoteArray(Array<Array<int> >& notes, Array<int>& measpos, 
                             Array<int>& linenum, HumdrumFile& infile);
void printComments(HumdrumFile& infile, int startline, int stopline, 
                             int style);
void printExclusiveInterpretations(int basecount);
void printLine(Array<Array<int> >& notes, 
                             Array<Array<int> >& attacks, 
                             Array<Array<int> >& lasts, 
                             Array<Array<int> >& nexts, Array<int>& measpos, 
                             Array<int>& linenum, HumdrumFile& infile, 
                             int index, int style);
void usage(const char* command);
void example(void);
void checkOptions(Options& opts, int argc, char* argv[]);
void getNoteAttackIndexes(Array<Array<int> >& attacks, 
                             Array<Array<int> >& notes, int offst);
void getLastAttackIndexes(Array<Array<int> >& lasts, 
                             Array<Array<int> >& notes, int offset);
void getNextAttackIndexes(Array<Array<int> >& lasts, 
                             Array<Array<int> >& notes, int offset);
int  noteStartMarker(Array<Array<int> >& notes, int line, int column);
int  noteEndMarker(Array<Array<int> >& notes, int line, int column);
int  noteContinueMarker(Array<Array<int> >& notes, int line, int column);
int  singleNote(Array<Array<int> >& notes, int line, int column);


// global variables
Options   options;             // database for command-line arguments
int       humdrumQ  = 0;       // used with -H option
int       base7Q    = 0;       // used with -d option
int       base12Q   = 0;       // used with -m option
int       base40Q   = 1;       // default output type
int       base      = STYLE_BASE40;
int       measureQ  = 1;       // used with -M option
int       beatQ     = 1;       // used with -B option
int       commentQ  = 1;       // used with -C option
int       rationalQ = 0;       // used with -r option
int       fractionQ = 0;       // used with -f option
int       absoluteQ = 0;       // used with -a option
int       linedurQ  = 0;       // used with -D option
int       doubleQ   = 0;       // used with --double option
int       lineQ     = 0;       // used with -l option
int       mathQ     = 0;       // used with --mathematica option
int       susxQ     = 1;       // used with -S option
int       bibQ      = 0;       // used with -b option
int       infoQ     = 1;       // used with -I option
int       octadj    = 0;       // used with -o option
int       endQ      = 0;       // used with -e option
int       typeQ     = 0;       // used with -c option
int       oneQ      = 0;       // used with -1 option
int       sepQ      = 0;       // used with --sep option
int       Offset    = 0;       // used with -1/--offset option
int       OffsetSum = 0;       // used for multiple input files
int       attackQ   = 0;       // used with --attack option
int       nextQ     = 0;       // used with --last option
int       lastQ     = 0;       // used with --next option
int       indexQ    = 0;       // used with -i option
int       saQ       = 0;       // used with --sa option
int       quoteQ    = 0;       // used with --quote option
int       Count     = 0;       // count of how many input files
int       Current   = 0;       // used with --math option
int       moQ       = 0;       // used with --mo option
int       Measure   = 0;       // additive value for measure number
int       Mincrement= 0;       // used to increment between pieces/movements
int       kernQ     = 0;       // used with -k option
int       kerntieQ  = 1;       // used with --no-tie option
int       doubletieQ= 0;       // used with -T option
RationalNumber Absoffset;      // used with --sa option

const char* commentStart = "#";
const char* commentStop  = "";
const char* mathvar  = "data"; // used with --mathematica option
const char* beatbase = "";     // used with -t option


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

int main(int argc, char** argv) {

   Array<Array<int> > notelist;
   Array<double>      metpos;
   Array<int>         measpos;
   Array<int>         linenum;
 
   HumdrumFile infile;

   // process the command-line options
   checkOptions(options, argc, argv);

   // figure out the number of input files to process
   int numinputs = options.getArgCount();
   Count = numinputs;

   Absoffset = 0;

   for (int i=0; i<numinputs || i==0; i++) {
      Current = i;
      if (moQ) {
         Measure = (i+1) * Mincrement;
      }
      infile.clear();

      // if no command-line arguments read data file from standard input
      if (numinputs < 1) {
         infile.read(cin);
      } else {
         infile.read(options.getArg(i+1));
      }
      // analyze the input file according to command-line options
      infile.analyzeRhythm(beatbase);

      getNoteArray(notelist, measpos, linenum, infile, base, doubleQ);
      printNoteArray(notelist, measpos, linenum, infile);
      OffsetSum += notelist.getSize();
    
      if (!saQ) {
         Absoffset += infile.getTotalDurationR();
      }

      if (sepQ && (Count > 1) && (Current < Count - 1)) {
         // add a separate between input file analyses:
         if (mathQ) {
            cout << "(* ********** *)\n";
         } else if (humdrumQ) {
            cout << "!!!!!!!!!!\n";         
         } else {
            cout << "##########\n";
         }
      }
   }
   
   return 0;
}


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


//////////////////////////////
//
// getNoteArray -- 
//    style:
//       STYLE_BASE40 40: print as base-40 pitches
//       STYLE_BASE12 12: print as base-12 pitches
//       STYLE_BASE7   7: print as base-7  pitches
//
//    flags:
//    	0: all off:
//    	1: turn on double-barline rest markers.
//

void getNoteArray(Array<Array<int> >& notes, Array<int>& measpos, 
      Array<int>& linenum, HumdrumFile& infile, int style, int flags) {

   notes.setSize(infile.getNumLines());
   notes.setSize(0);

   measpos.setSize(infile.getNumLines());
   measpos.setSize(0);

   linenum.setSize(infile.getNumLines());
   linenum.setSize(0);

   int measnum = 0;
   int rest    = 0;
   int i, j, ii, jj;
   int negQ    = 0;

   // prestorage for note lists so that --no-sustain option can be applied
   Array<int> templist;
   templist.setSize(1000);
   templist.setSize(0);

   Array<int> templistI;
   templistI.setSize(1000);
   templistI.setSize(0);

   int value;
   int firstQ = 1;
   if (typeQ) {
      // store even if lineQ is zero!
      value = TYPE_LINE;
      linenum.append(value);
   }
   if (typeQ) {
      value = TYPE_MEASURE;
      measpos.append(value);
   }

   // increment measure number in case of pickups
   measnum = Measure;
   for (i=0; i<infile.getNumLines(); i++) {

      if (infile[i].isMeasure()) {
         if (doubleQ && (templist.getSize() == 0)) {
            if (strstr(infile[i][0], "||") != NULL) {
               // insert a rest sonority to break music
               // from previous measure
               notes.setSize(notes.getSize() + 1);
               notes.last().setSize(infile[i].getFieldCount());
               notes.last().setSize(0);
               for (j=0; j<infile[i].getFieldCount(); j++) {
                  if (!infile[i].isExInterp(j, "**kern")) {
                     continue;
                  }
                  notes.last().append(rest);
                  linenum.append(i);
               }
               measpos.append(measnum);
            }
         }

         // store new measure number (avoiding double code above)
         sscanf(infile[i][0], "=%d", &measnum);
         measnum += Measure;
      }
      if (!infile[i].isData()) {
         continue;
      }

      templist.setSize(0);


      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (!infile[i].isExInterp(j, "**kern")) {
            continue;
         }
         if (strchr(infile[i].getSpineInfo(j), 'b') != NULL) {
            // ignore notes non-primary tracks
            continue;
         }
         int attack = 1;
         if (strcmp(infile[i][j],".")==0) {
            attack = -1;
            ii = infile[i].getDotLine(j);
            jj = infile[i].getDotSpine(j);
         } else {
            ii = i;
            jj = j;
         }
         int baseval = Convert::kernToBase40(infile[ii][jj]);
         baseval += 40 * octadj;
         if (strchr(infile[ii][jj], 'r') != NULL) {
            baseval = 0;
         } else {
            // now storing base-40, and converting to base-12/base-7 later
            // switch (style) {
            //    case STYLE_BASE12: 
            //       baseval = Convert::base40ToMidiNoteNumber(baseval);
            //       break;
            //    case STYLE_BASE7: 
            //       baseval = Convert::base40ToDiatonic(baseval);
            //       break;
            // }
         }
         baseval = attack * baseval;
         templist.append(baseval);
      }

      negQ = 1;
      if (susxQ) {
         // if all notes are negative, then do not store the line
         for (int m=0; m<templist.getSize(); m++) {
            if (templist[m] >= 0) {
               negQ = 0;
               break;
            }
         }
         if (negQ) {
            continue;
         }
      }

      if (firstQ && typeQ) {
         firstQ = 0;
         templistI.setSize(templist.getSize());
         int value = TYPE_NOTES;
         switch (style) {
            case STYLE_BASE40: value += 40; break;
            case STYLE_BASE12: value += 12; break;
            case STYLE_BASE7:  value +=  7; break;
         }
         templistI.setAll(value);
         notes.increase();
         notes.last() = templistI;
      }

      notes.increase();
      notes.last() = templist;
      measpos.append(measnum);
      linenum.append(i);

   }


   if (endQ) {
      notes.increase();
      notes.last().setSize(notes.last(-1).getSize());
      notes.last().zero();
      // sustains instead of rests (have to copy .last(-1):
      // for (i=0; i<notes.last().getSize(); i++) {
      //    if (notes.last()[i] > 0) {
      //       notes.last()[i] *= -1;
      //    }
      // }
      measpos.append(measpos.last());
      linenum.append(linenum.last());
      // store the data termination line as the line number of the sonority
      for (i=infile.getNumLines()-1; i>=0; i--) {
         if (infile[i].isInterpretation()) {
            linenum.last() = i;
            break;
         }
      }
 
      // increment the measure number if the beat of the end
      // is at 1.  This will work unless the last sonority is a 
      // grace note.
      if (infile[linenum.last()].getBeat() == 1.0) {
         measpos.last()++; 
      }
   }

}



//////////////////////////////
//
// printNoteArray --
//

void printNoteArray(Array<Array<int> >& notes, Array<int>& measpos,
      Array<int>& linenum, HumdrumFile& infile) {

   linenum.allowGrowth(0);
   measpos.allowGrowth(0);
   notes.allowGrowth(0);

   Array<Array<int> > attacks;
   Array<Array<int> > lasts;
   Array<Array<int> > nexts;
   if (attackQ) {
      getNoteAttackIndexes(attacks, notes, Offset + OffsetSum);
   }
   if (lastQ) {
      getLastAttackIndexes(lasts, notes, Offset + OffsetSum);
   }
   if (nextQ) {
      getNextAttackIndexes(nexts, notes, Offset + OffsetSum);
   }

   int i, j;
   for (i=0; i<notes.getSize(); i++) {
      if (i == 0) { 
         if (commentQ && !typeQ) {
            printComments(infile, 0, linenum[0], humdrumQ);
         } else if (commentQ && typeQ) {
            printComments(infile, 0, linenum[1], humdrumQ);
         }
         if (infoQ) {
            printExclusiveInterpretations(notes[0].getSize());
         } else if (humdrumQ) {
            // always print exclusive interpretations if Humdrum output
            printExclusiveInterpretations(notes[0].getSize());
         }
         if (mathQ) {
            if (Count <= 1) {
               cout << mathvar << " = {\n";
            } else if (Current == 0) {
               cout << mathvar << " = {{\n";
            } else {
               cout << "}, {\n";
            }
         }
      } else if (commentQ) {
         printComments(infile, linenum[i-1]+1, linenum[i], humdrumQ);
      }
      if (typeQ && i == 0) {
         printLine(notes, attacks, lasts, nexts, measpos, linenum, infile,i,1);
      } else {
         printLine(notes, attacks, lasts, nexts, measpos, linenum, infile,i,0);
      }
   }

   if (humdrumQ) {
      int width = 1;
      if (attackQ) { width++; }
      if (lastQ)   { width++; }
      if (nextQ)   { width++; }
      if (kernQ)   { width++; }
         
      int counter = notes[0].getSize();
      counter *= width;
      counter += indexQ + lineQ + measureQ + beatQ + absoluteQ + linedurQ;
      // if (attackQ) { counter += notes[0].getSize(); }
      // if (lastQ) { counter += notes[0].getSize(); }
      // if (nextQ) { counter += notes[0].getSize(); }
      for (j=0; j<counter; j++) {
         cout << "*-";
         if (j < counter-1) {
            cout << "\t";
         }
      }
      cout << endl;
   }

   if (mathQ) {
      if (Count <= 1) {
         cout << "};\n";
      } else if (Current <= Count - 2) {
         // cout << "}},\n";
      } else if (Current == Count - 1) {
         cout << "}};\n";
      }
   }

   if (commentQ) {
      printComments(infile, linenum.last(), infile.getNumLines()-1, humdrumQ);
   }
}



//////////////////////////////
//
// getLastAttackIndexes -- return an index of the attack portion
//     of notes (or the first rest in a series of rests) in each voice.
//

void getLastAttackIndexes(Array<Array<int> >& lasts, 
      Array<Array<int> >& notes, int offset) {

   int start = 0;
   lasts.setSize(notes.getSize());
   if (typeQ) {
      lasts[0].setAll(TYPE_LAST);
      start = 1;
   }

   int i, j;

   lasts[start].setAll(-1);

   if (notes.getSize() == start+1) {
      return;
   }

   Array<int> states;
   states.setSize(notes[0].getSize());
   if (typeQ) {
      states.setAll(offset+1);
   } else {
      states.setAll(offset);
   }

   for (i=0; i<notes.getSize(); i++) {
      lasts[i].setSize(notes[i].getSize());
      if (i <= start) {
         lasts[i].setAll(-1);
         if (typeQ && (i==0)) {
            lasts[i].setAll(TYPE_LAST);
         }
         continue;
      }

      for (j=0; j<notes[i].getSize(); j++) {
         if (notes[i][j] > 0) {
            // a new note attack, store the note attack index in
            // the states array after recording the index of the previous note
            lasts[i][j] = states[j];
            states[j] = i + offset;
         } else if (notes[i][j] < 0) {
           // note is sustaining, so copy the index of the last attack
           // from the previous line
           if (i > start) {
              lasts[i][j] = lasts[i-1][j];
           } else {
              lasts[i][j] = -1;
           }
         } else {
           // rest: if last line was a rest then this is a rest sustain:
           if (i > start) {
              if (notes[i-1][j] == 0) {
                 // rest sustain
                 lasts[i][j] = lasts[i-1][j];
              } else {
                 // rest attack
                 lasts[i][j] = states[j];
                 states[j] = i + offset;
              }  
           } else {
              lasts[i][j] = -1;
           }
         }
      }
   }

}



//////////////////////////////
//
// getNextAttackIndexes -- return an index of the attack portion
//     of notes (or the first rest in a series of rests) in each voice.
//

void getNextAttackIndexes(Array<Array<int> >& nexts, 
      Array<Array<int> >& notes, int offset) {

   int start = 0;
   nexts.setSize(notes.getSize());
   if (typeQ) {
      nexts[0].setSize(notes[0].getSize());
      nexts[0].setAll(TYPE_NEXT);
      start = 1;
   }

   int i, j;

   nexts.last().setSize(notes.last().getSize());
   nexts.last().setAll(-1);

   if (notes.getSize() == start+1) {
      return;
   }

   for (i=notes.getSize()-2; i>=start; i--) {
      nexts[i].setSize(notes[i].getSize());
      for (j=0; j<notes[i].getSize(); j++) {
         if ((notes[i][j] != 0) && (notes[i+1][j] == 0)) {
            // next note is a "rest attack"
            nexts[i][j] = i + 1 + offset;
         } else if (notes[i+1][j] > 0) {
            // a new note attack, store the note attack index in
            // the states array after recording the index of the previous note
            nexts[i][j] = i + 1 + offset;
         } else {
            nexts[i][j] = nexts[i+1][j];
         }

      }
   }

}



//////////////////////////////
//
// getNoteAttackIndexes -- return an index of the attack portion
//     of notes (or the first rest in a series of rests) in each voice.
//

void getNoteAttackIndexes(Array<Array<int> >& attacks, 
      Array<Array<int> >& notes, int offset) {

   attacks.setSize(notes.getSize());
   int i, j;

   attacks[0].setSize(notes[0].getSize());
   attacks[0].setAll(offset);

   for (i=1; i<attacks.getSize(); i++) {
      attacks[i].setSize(notes[i].getSize());
      attacks[i].allowGrowth(0);
      for (j=0; j<attacks[i].getSize(); j++) {
         if (notes[i][j] < 0) {
            // a sustained note, so store index of attack note
            attacks[i][j] = attacks[i-1][j];
         } else if (notes[i][j] > 0) {
            // note being attacked, so store its index
            attacks[i][j] = i + offset;
         } else {
            // a rest: check to see if last position was a rest.
            // if so, then this is a "tied rest"; otherwise it is
            // a "attacked rest".
            if (notes[i-1][j] == 0) {
               attacks[i][j] = attacks[i-1][j];
            } else {
               attacks[i][j] = i + offset;
            }
         }
      }
   }

   if (typeQ) {
      attacks[0].setAll(TYPE_ATTACK);
   }
}



//////////////////////////////
//
// printLine -- print a line of the note array.
//    style == 0: regular line
//    style == 1: index line (don't extract beat or absbeat from infile)
//

void printLine(Array<Array<int> >& notes, Array<Array<int> >& attacks, 
      Array<Array<int> >& lasts, Array<Array<int> >& nexts, 
      Array<int>& measpos, Array<int>& linenum, HumdrumFile& infile, 
      int index, int style) {

   int& i = index;
   int j;

   if (mathQ) {
      cout << "{";
   }

   if (indexQ) { 
      cout << i + Offset + OffsetSum;
   }

   if (lineQ) { 
      if (indexQ) {
         if (mathQ) {
            cout << ",";
         }
         cout << "\t";
      }
      cout << linenum[i]+1;
   }

   if (measureQ) { 
      if (indexQ || lineQ) {
         if (mathQ) {
            cout << ",";
         }
         cout << "\t";
      }
      cout << measpos[i];
   }

   if (beatQ) {
      if (indexQ || lineQ || measureQ) {
         if (mathQ) {
            cout << ",";
         }
         cout << "\t";
      }
      if (style == 1) {
         cout << TYPE_BEAT;
      } else if (rationalQ) {
         if (fractionQ) {
            cout << infile[linenum[i]].getBeatR();
         } else {
            infile[linenum[i]].getBeatR().printTwoPart(cout);
         }
      } else {
         // print beat position as a floating-point number:
         cout << infile[linenum[i]].getBeat();
      }
   }

   if (absoluteQ) {
      if (indexQ || lineQ || measureQ || beatQ) {
         if (mathQ) {
            cout << ",";
         }
         cout << "\t";
      }
      if (style == 1) {
         cout << TYPE_ABSOLUTE;
      } else if (rationalQ) {
         if (fractionQ) {
            cout << (Absoffset + infile[linenum[i]].getAbsBeatR());
         } else {
            RationalNumber sum = Absoffset + infile[linenum[i]].getAbsBeatR();
            sum.printTwoPart(cout);
         }
      } else {
         // print beat position as a floating-point number:
         cout << infile[linenum[i]].getAbsBeat() + Absoffset.getFloat();
      }
 
   }

   if (linedurQ) {
      if (indexQ || lineQ || measureQ || beatQ || absoluteQ) {
         if (mathQ) {
            cout << ",";
         }
         cout << "\t";
      }
      if (style == 1) {
         cout << TYPE_LINEDUR;
      } else if (rationalQ) {
         if (fractionQ) {
            cout << (Absoffset + infile[linenum[i]].getDurationR());
         } else {
            RationalNumber num = infile[linenum[i]].getDurationR();
            num.printTwoPart(cout);
         }
      } else {
         // print beat position as a floating-point number:
         cout << infile[linenum[i]].getDuration();
      }
 
   }
   if (indexQ || lineQ || measureQ || beatQ || absoluteQ || linedurQ) {
      if (mathQ) {
         cout << ",";
      }
   }


   Array<int> values;
   values.setSize(notes.getSize() * 10);
   values.setSize(0);

   int vv;
   int sign;
   for (j=0; j<notes[i].getSize(); j++) {
      vv = notes[i][j];
      //if (kernQ && (style == 1) && typeQ) {
      // int ww = TYPE_KERN;
      //   values.append(ww);
      //} 
      if (vv < TYPE_NOTES) {
         if (vv < 0) {
            sign = -1;
            vv = -vv;
         } else {
            sign = +1;
         }
         if (vv != 0) { 
            switch (style) {
               case STYLE_BASE12: 
                  vv = Convert::base40ToMidiNoteNumber(vv);
                break;
               case STYLE_BASE7: 
                  vv = Convert::base40ToDiatonic(vv);
                  break;
            }
         }
         vv *= sign;
         values.append(vv);
      } else {
         values.append(vv);
      }

      if (attackQ) {
         values.append(attacks[i][j]);
      }
      if (lastQ) {
         values.append(lasts[i][j]);
      }
      if (nextQ) {
         values.append(nexts[i][j]);
      }
   }

   int width = 1;  // notes
   if (attackQ) { width++; }
   if (lastQ)   { width++; }
   if (nextQ)   { width++; }

   char buffer[1024] = {0};

   for (j=0; j<values.getSize(); j++) {
      if (kernQ && ((j % width) == 0)) {
         //if (mathQ) {
         //   cout << ",";
         //}
         cout << "\t";
         if ((style == 1) && (i == 0)) {
            cout << TYPE_KERN;         
         } else {
            if (mathQ || quoteQ) {
               cout << "\"";
            }

            if (kerntieQ && noteStartMarker(notes, i, j / width)) {
               cout << "[";
            } 
            if (doubletieQ && singleNote(notes, i, j / width)) {
               cout << "["; 
            }

            if (values[j] == 0) {
               cout << "r";
            } else {
               cout << Convert::base40ToKern(buffer, abs(values[j]));
            }

            if (kerntieQ && noteContinueMarker(notes, i, j / width)) {
               cout << "_";
            } else if (kerntieQ && noteEndMarker(notes, i, j / width)) {
               cout << "]";
            } 
            if (doubletieQ && singleNote(notes, i, j / width)) {
               cout << "]"; 
            }

            if (mathQ || quoteQ) {
               cout << "\"";
            }
         }
         if (mathQ) {
            cout << ",";
         }
      }
      cout << "\t" << values[j];

      if (j < values.getSize()-1) {
         if (mathQ) {
            cout << ",";
         }
         //cout << "\t";
      }
   }
   if (mathQ) {
      cout << "}";
      if (i < linenum.getSize() - 1) {
         cout << ",";
      }
   }
   cout << endl;
}



//////////////////////////////
//
// singleNote -- true if the note in the column/line is uniq
//

int singleNote(Array >& notes, int line, int column) {
   int start = 0;
   if (typeQ) {
      start = 1;
   }
   if (line < start) {
      return 0;
   }

   int pitch = notes[line][column];
   int nextpitch = -1;
   int lastpitch = -1;

   if ((line > start) && (notes.getSize() > 1+start)) {
      lastpitch = notes[line-1][column];
   }
   if ((notes.getSize() > start+1) && (line < notes.getSize() - 1)) {
      nextpitch = notes[line+1][column];
   }
 
   if (pitch > TYPE_MIN) {
      return 0;
   }

   if (pitch < 0) {
      // note is a sustain, so not considered
      return 0;
   } else if (pitch == 0) {
      // if the rest is surrounded by non-rests, then mark
      if ((pitch != lastpitch) && (pitch != nextpitch)) {
         return 1;
      } else {
         return 0;
      }
   } else {
      if (pitch != -nextpitch) {
         // next sonority does not contain a sustain of the note
         return 1;
      } else {
         return 0;
      }
   }
}



//////////////////////////////
//
// noteStartMarker --
//

int noteStartMarker(Array >& notes, int line, int column) {
   int start = 0;
   if (typeQ) {
      start = 1;
   }
   if (line < start) {
      return 0;
   }

   int pitch = notes[line][column];
   int nextpitch = -1;
   int lastpitch = -1;

   if ((line > start) && (notes.getSize() > 1+start)) {
      lastpitch = notes[line-1][column];
   }
   if ((notes.getSize() > start+1) && (line < notes.getSize() - 1)) {
      nextpitch = notes[line+1][column];
   }

   if (pitch == 0) {
      // if the first rest in a row then true
      if ((lastpitch != 0) && (nextpitch == 0)) {
         // don't include rests which start and stop on the same line
         return 1;
      } else {
         return 0;
      }
   } else if (pitch < 0) {
      return 0;
   } else {
      if ((nextpitch < 0) && (nextpitch != -1)) {
         return 1;
      } else {
         return 0;
      }
   }
}



//////////////////////////////
//
// noteContinueMarker --
//

int noteContinueMarker(Array >& notes, int line, int column) {
   int start = 0;
   if (typeQ) {
      start = 1;
   }
   if (line < start) {
      return 0;
   }

   int pitch = notes[line][column];
   int nextpitch = -1;
   int lastpitch = -1;

   if ((line > start) && (notes.getSize() > 1+start)) {
      lastpitch = notes[line-1][column];
   }
   if ((notes.getSize() > start+1) && (line < notes.getSize() - 1)) {
      nextpitch = notes[line+1][column];
   }

   if (pitch == 0) {
      // if not the first or the last zero than a continue
      if ((lastpitch == 0) && (nextpitch == 0)) {
         return 1;
      } else {
         return 0;
      }
   } else if (pitch > 0) {
      return 0;
   } else {
      if (pitch == nextpitch ) {
         return 1;
      } else {
         return 0;
      }
   }
}



//////////////////////////////
//
// noteEndMarker --
//

int noteEndMarker(Array >& notes, int line, int column) {
   int start = 0;
   if (typeQ) {
      start = 1;
   }
   if (line < start) {
      return 0;
   }

   int pitch = notes[line][column];
   int nextpitch = -1;
   int lastpitch = -1;

   if ((line > start) && (notes.getSize() > 1+start)) {
      lastpitch = notes[line-1][column];
   }
   if ((notes.getSize() > start+1) && (line < notes.getSize() - 1)) {
      nextpitch = notes[line+1][column];
   }

   if (pitch == 0) {
      // if not the first or the last zero than a continue
      if (nextpitch != 0) {
         return 1;
      } else {
         return 0;
      }
   } else if (pitch > 0) {
      return 0;
   } else {
      if (pitch != nextpitch ) {
         return 1;
      } else {
         return 0;
      }
   }
}



//////////////////////////////
//
// printExclusiveInterpretations -- print ** markers at the start of 
//      each column of data.
//      
//      Order of optional prefix columns:
//      lineQ
//      measureQ
//      beatQ
//      absoluteQ
//      linedurQ
//

void printExclusiveInterpretations(int basecount) {

   char basename[1024] = {0};

   const char* prefix = "##";
   if (humdrumQ || mathQ) {
      prefix = "**";
   } 

   if (kernQ) {
      strcat(basename, "kern");
      strcat(basename, "\t");
   }

   if (base7Q) {
      // strcat(basename, prefix);
      strcat(basename, "b7");
   } else if (base12Q) {
      // strcat(basename, prefix);
      strcat(basename, "b12");
   } else {
      // strcat(basename, prefix);
      strcat(basename, "b40");
   }

   if (attackQ) {
      strcat(basename, "\t");
      strcat(basename, prefix);
      strcat(basename, "attk");
   }
   if (lastQ) {
      strcat(basename, "\t");
      strcat(basename, prefix);
      strcat(basename, "last");
   }
   if (nextQ) {
      strcat(basename, "\t");
      strcat(basename, prefix);
      strcat(basename, "next");
   }

   int startmark = 0;

   if (indexQ) {
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "idx\t"; 
   }
   if (lineQ) { 
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "line\t"; 
   }
   if (measureQ) { 
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "bar\t"; 
   }

   if (beatQ) { 
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "beat\t"; 
   }

   if (absoluteQ) { 
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "abs\t"; 
   }

   if (linedurQ) { 
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << "ldur\t"; 
   }

   int i;
   for (i=0; i<basecount; i++) {
      if ((startmark == 0) && mathQ) {
         cout << "(*";
         startmark++;
      } else {
         cout << prefix;
      }
      cout << basename;
      if (i < basecount - 1) {
         cout << "\t";
      }
   }
   if (mathQ) {
      cout << " *)";
   }
   cout << "\n";

}



//////////////////////////////
//
//  
// printComments -- print any comments
//

void printComments(HumdrumFile& infile, int startline, int stopline, 
      int style) {

   int i;
   for (i=startline; i<=stopline; i++) {
      if (!infile[i].isComment()) {
         continue;
      }
      if (bibQ && infile[i].isGlobalComment()) {
         continue;
      }
      if (infile[i].isLocalComment()) {
         // don't know how to store local comments, ignore for now.
         continue;
      }
      if (style) {
         // print in Humdrum format:
         cout << infile[i] << "\n";
      } else {
         // print in Matlab format:
         cout << commentStart << infile[i] << commentStop << "\n";
      }
   }
}



//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|absolute=b",    "print absolute beat numbers");
   opts.define("b|bib|bibliographic|reference=b", "display only bib record");
   opts.define("c|type=b",        "add a numeric index at start of data array");
   opts.define("d|diatonic=b",    "print output in absolute diatonic");
   opts.define("f|fraction=b",    "display rational number as fraction");
   opts.define("e|end|end-rest=b","store ending rest");
   opts.define("i|index=b",       "print a column with the index value");
   opts.define("j|josquin=b",     "default settings for josquin project");
   opts.define("k|kern=b",        "display kern spine");
   opts.define("no-tie|no-ties=b", "don't display kern tie information");
   opts.define("l|line=b",        "print original line of sonority in data");
   opts.define("m|midi=b",        "print output as MIDI key numbers");
   opts.define("o|octave=i:0",    "octave adjustment value");
   opts.define("r|rational=b",    "display metric position as rational number");
   opts.define("t|beat=s:",       "metric base for constant beat analysis");
   opts.define("1|one=b",         "offset index values by one");
   opts.define("M|no-measures=b", "don't print measure number column");
   opts.define("B|no-beats=b",    "don't print beat value column");
   opts.define("C|no-comments=b", "don't print comments in file");
   opts.define("D|linedur=b",     "display duration of line");
   opts.define("A|all=b",         "display all options prefix columns");
   opts.define("H|humdrum=b",     "print output in Humdrum format");
   opts.define("I|no-info=b",     "do not display information header");
   opts.define("S|no-sustain=b",  "suppress sonorities that are only sustains");
   opts.define("T|all-tie|all-ties=b",  "start/stop tie marks on all notes");
   opts.define("N|no-cols=b",     "turn off all information columns");
   opts.define("attack=b",        "display note-attack index values");
   opts.define("double=b",        "add rests at double barlines");
   opts.define("last=b",          "display previous note-attack index values");
   opts.define("math|mathematica=s:data", "print output data as Matlab array");
   opts.define("mel|melodic=b",    "display melodic note index columns");
   opts.define("mo|measure-offset=i:0", "bar num increase per file");
   opts.define("next=b",          "display following note-attack index values");
   opts.define("offset=i:0",      "starting index for first row");
   opts.define("sa|separate-absolute=b", "single absolute beat positions");
   opts.define("sep|separator=b",  "print a separator between input analyses");
   opts.define("quote=b",          "print quotes around kern names");

   opts.define("debug=b");        // determine bad input line num
   opts.define("author=b");       // author of program
   opts.define("version=b");      // compilation info
   opts.define("example=b");      // example usages
   opts.define("h|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, Aug 2011" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 30 August 2011" << 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);
   }

   if (opts.getBoolean("josquin")) {
      beatbase  = "1";  // beat is the whole note.
      doubleQ   = 1;
   } else {
      beatbase  = opts.getString("beat");
      doubleQ   = opts.getBoolean("double");
   }

   humdrumQ = opts.getBoolean("humdrum");
   base7Q   = opts.getBoolean("diatonic");
   base12Q  = opts.getBoolean("midi");
   if (base7Q) {
      base7Q  = 1;
      base12Q = 0;
      base40Q = 0;
   } else if (base12Q) {
      base7Q  = 0;
      base12Q = 1;
      base40Q = 0;
   } else {
      base7Q  = 0;
      base12Q = 0;
      base40Q = 1;
   }

   if (base7Q) {
      base = STYLE_BASE7;
   } else if (base12Q) {
      base = STYLE_BASE12;
   } else {
      base = STYLE_BASE40;
   }

   mathQ     =  opts.getBoolean("mathematica");
   mathvar   =  opts.getString("mathematica");
   commentStart = "(* ";
   commentStop  = " *)";

   if (!mathQ) {
      commentStart = "# ";
      commentStop  = "";
   }

   lineQ     =  opts.getBoolean("line");
   measureQ  = !opts.getBoolean("no-measures");
   beatQ     = !opts.getBoolean("no-beats");
   absoluteQ =  opts.getBoolean("absolute");
   linedurQ  =  opts.getBoolean("linedur");
   commentQ  = !opts.getBoolean("no-comments");
   rationalQ =  opts.getBoolean("rational");
   fractionQ =  opts.getBoolean("fraction");
   susxQ     =  opts.getBoolean("no-sustain");
   kernQ     =  opts.getBoolean("kern");
   kerntieQ  = !opts.getBoolean("no-tie");
   bibQ      =  opts.getBoolean("bibliographic");
   infoQ     = !opts.getBoolean("no-info");
   endQ      =  opts.getBoolean("end-rest");
   octadj    =  opts.getInteger("octave");
   typeQ     =  opts.getBoolean("type");
   attackQ   =  opts.getBoolean("attack");
   nextQ     =  opts.getBoolean("last");
   lastQ     =  opts.getBoolean("next");
   indexQ    =  opts.getBoolean("index");
   sepQ      =  opts.getBoolean("sep");
   saQ       =  opts.getBoolean("separate-absolute");
   Mincrement=  opts.getInteger("measure-offset");
   moQ       =  opts.getBoolean("measure-offset");
   Offset    =  opts.getInteger("offset");
   quoteQ    =  opts.getBoolean("quote");
   doubletieQ=  opts.getBoolean("all-tie");
   if (doubletieQ) {
      kerntieQ = 1;
   }
   if (Offset < 0) {
      Offset = 0;
   }
   if (opts.getBoolean("mel")) {
      attackQ = 1;
      nextQ   = 1;
      lastQ   = 1;
   }

   if (opts.getBoolean("1")) {
      Offset = 1;
   }

   if (fractionQ) {
      rationalQ = 1;
   }

   if (opts.getBoolean("no-cols")) {
      measureQ = 0;
      beatQ    = 0;
   }

   if (opts.getBoolean("all")) {
      lineQ = measureQ = beatQ = absoluteQ = linedurQ = 1;
   }

}



//////////////////////////////
//
// example -- example usage of the quality program
//

void example(void) {
   cout <<
   "                                                                         \n"
   << endl;
}



//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//

void usage(const char* command) {
   cout <<
   "                                                                         \n"
   << endl;
}


// md5sum: cae22ba3e86ede60cf34a672bb901dbd notearray.cpp [20111105]