//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Jul 19 12:09:12 PDT 2003
// Last Modified: Tue Jul 22 21:01:22 PDT 2003
// Last Modified: Sun Sep  7 10:42:09 PDT 2003 (added multiple systems)
// Last Modified: Mon Dec  6 22:09:12 PST 2004 (minor improvements)
// Filename:      ...sig/examples/all/chor2scor.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/score/chor2scor.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert a Humdrum chorale file into a SCORE data file.
//

#include "humdrum.h"

#include <string.h>
#include <ctype.h>

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


// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   recheckOptions(Options& opts, char* command, const char* string);
void   example(void);
void   usage(const char* command);
void   convertToScore(ostream& output, HumdrumFile& infile, int segment, 
                          Array<int>& breaks);
void   printNote(ostream& output, const char* data, double hpos, 
                          HumdrumFile& infile, int line, int staff, 
                          int direction, int voice);
void   printStaffInfo(ostream& output, HumdrumFile& infile, int segment);
int    noteheadtype(double duration);
void   printMeasure(ostream& output, HumdrumFile& infile, int line, 
                          double hpos, int measurecount, int systemno);
void   printHarm(ostream& output, const char* text, double hpos, 
                          HumdrumFile& infile, int harmspine);
void   printTrailer(ostream& output);
int    getdots(double duration, int voice);
int    getflags(double duration);
double makeaccidental(HumdrumFile& infile, int line, int voice, 
                          Array<int>& lkey);
void   resetkey(void);
int    peekAccidental(HumdrumFile& infile, int line, int voice, 
                          Array<int>& lkey);
double getHpos(HumdrumFile& infile, int line, double start = -1,
                          double ending = -1);
void   generateBeams(ostream& output, HumdrumFile& infile, int voice,
                          int startline, int stopline);
void   checkForBeam(ostream& output, HumdrumFile& infile, int voice, 
                          Array<int>& index, int i);
double getBeamStart(double vpos1, double vpos2, int dir);
double getBeamEnd(double vpos1, double vpos2, int dir);
void   fillnindex(HumdrumFile& infile, Array<Array<int> >& nindex);
void   initoffset(Array<Array<double> >& offset, 
                          Array<Array<int> >& nindex);
void   storeoffset(double offset, int voice, int line);
double getoffset(const char* lower, const char* upper);
void   printTitle(ostream& output, const char* title);
void   printKeyInfo(ostream& output, const char* key, double hpos, 
                          double vpos);
void   calculateBreaks(Array<int>& breaks, HumdrumFile& infile, int lines);
void   makeFilename(char* buffer, const char* zbasename, char* ext, 
                          int total, int n);
int    findHarmSpine(HumdrumFile& infile);
int    findHarmSpine(HumdrumRecord& aRecord);

// global variables
Options   options;                // database for command-line arguments
int       debugQ          = 0;    // used with --debug option
int       quietQ          = 0;    // used with -q option
double    scale           = 0.6;  // used with -s option
int       readQ           = 1;    // used with -R option
int       harmonyQ        = 1;    // used with -H option
int       lines           = 1;    // used with -l option
char      inputbase[1024] = {0};  // used with -o option (for %b expansion)
char      zbasename[1024] = {0};  // used with -o option
char      extension[1024] = {0};  // used with -o option

Array<double>         tie(4);     // for keeping track of ties
Array<int>            key(7);     // for keeping track of key
Array<Array<int> >    vkey(4);    // for keeping track of accidentals
Array<Array<double> > offsets(4); // for keeping track of beam offsets
Array<Array<int> >    nindex(4);  // for keeping track of beam offsets
Array<int>            breaks;     // for keeping track of line breaks

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

int main(int argc, char* argv[]) {
   checkOptions(options, argc, argv);

   HumdrumFile infile;
   char* filenameIn  = options.getArg(1);
   infile.read(filenameIn);
   infile.analyzeRhythm();

   int i;
   if (readQ) {
      for (i=0; i<infile.getNumLines(); i++) {
         if (infile[i].getType() == E_humrec_global_comment) {
            if (strncmp(infile[i][0], "!!chor2scor:", 12) == 0) {
               recheckOptions(options, (char*)argv[0], &infile[i][0][12]);
            }
         }
      }
   }

   tie.setAll(0.0);
   key.setAll(0);
   for (i=0; i<3; i++) {
      vkey[i].setSize(7);
      vkey[i].setAll(0);
   }

   fillnindex(infile, nindex);
   initoffset(offsets, nindex);
   calculateBreaks(breaks, infile, lines);
   
   ofstream* output;
   char buffer[1024] = {0};
   for (i=0; i<lines; i++) {
      if (zbasename[0] != '\0') {
         makeFilename(buffer, zbasename, extension, lines, i);
         if (!quietQ) {
            cout  << "ZZZ Creating file " << buffer << "..." << endl;
         }
         output = new ofstream;
         output->open(buffer);
         convertToScore(*output, infile, i, breaks);
         output->close();
         delete output;
      } else {
         if (!quietQ) {
            cout  << "ZZZ System " << i+1 << "/" << lines
                  << " ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" << endl;
         }
         convertToScore(cout, infile, i, breaks);
      }
   }

   return 0;
}


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


//////////////////////////////
//
// makeFilename --
//

void makeFilename(char* buffer, const char* zbasename, char* ext, int total, 
      int n) {
   if (total > 10 && n < 10) {
      sprintf(buffer, "%s0%d.%s", zbasename, n+1, ext);
   } else if (total > 100 && n < 10) {
      sprintf(buffer, "%s00%d.%s", zbasename, n+1, ext);
   } else if (total > 100 && n < 100) {
      sprintf(buffer, "%s00%d.%s", zbasename, n+1, ext);
   } else {
      sprintf(buffer, "%s%d.%s", zbasename, n+1, ext);
   }
}



///////////////////////////////
//
// calculateBreaks -- determine line breaks in the music
//

void calculateBreaks(Array& breaks, HumdrumFile& infile, int lines) {
   Array<int> items;
   items.setSize(infile.getNumLines());
   items.setSize(0);

   if (lines <= 1) {
      return;
   }

   int temp;
   Array<int> explicitbreaks;
   explicitbreaks.setSize(infile.getNumLines());
   explicitbreaks.setSize(0);
   int i;
   for (i=1; i<infile.getNumLines()-1; i++) {
      if (infile[i].getType() == E_humrec_global_comment) {
         if (strncmp(infile[i][0], "!!linebreak", 11) == 0)  {
cout << "!!LINEBREAK FOUND ON LINE " << i + 1 << endl;
            if (infile[i-1].getType() == E_humrec_data_measure) {
               temp = i-1;
               explicitbreaks.append(temp);
            } else {
               temp = i+1;
               explicitbreaks.append(temp);
            }
         }
      }
   }
   
cout << "LINES = " << lines << " CHECK = " << explicitbreaks.getSize() + 1 << endl;
   if (explicitbreaks.getSize() + 1 == lines) {
cout << "COPYING BREAKS " << endl;
      breaks.setSize(explicitbreaks.getSize());
      for (i=0; i<explicitbreaks.getSize(); i++) {
         breaks[i] = explicitbreaks[i];
      }

      if (!quietQ) {
         for (i=0; i<breaks.getSize(); i++) {
            cout << "ZZZ Linebreak " << i+1 << " is on line " 
                 << breaks[i]+1 << endl;
         }
      }

      return;
      
   }


   // get a list of the horizontal items (data and measures)
   for (i=0; i<infile.getNumLines(); i++) {
      switch (infile[i].getType()) {
         case E_humrec_data:
            if (infile[i].equalFieldsQ("**kern", ".") == 0) {
               // ignoring null-token lines in **kern data
               items.append(i);
            }
            break;
         case E_humrec_data_measure:
            if (strchr(infile[i][0], '-') == NULL) {
               items.append(i);
            }
            break;
      }
   }

   breaks.setSize(lines);
   breaks.setSize(0);

   int division = 0;
   int j;
   double duron     = 0.0;
   double durbefore = 0.0;
   double durafter  = 0.0;
   int divbefore = 0;
   int divafter  = 0;
   double dis1, dis2;
   for (i=0; i<lines-1; i++) {
      division = (int)(1.0 * items.getSize() / lines + 0.5);
      duron = infile[items[division]].getAbsBeat();
      durbefore = duron;
      durafter  = duron;

      for (j=division; j<items.getSize(); j++) {
         if (infile[items[j]].getType() == E_humrec_data_measure) {
            durafter = infile[items[j]].getAbsBeat();
            divafter = j;
            break;
         }
      }

      for (j=division; j>=0; j--) {
         if (infile[items[j]].getType() == E_humrec_data_measure) {
            durafter = infile[items[j]].getAbsBeat();
            divbefore = j;
            break;
         }
      }

      dis1 = duron - durbefore;
      dis2 = durafter - duron;
      if (dis1 < dis2) {
         breaks.append(items[divbefore]);
      } else {
         breaks.append(items[divafter]);
      }
   }

   if (!quietQ) {
      for (i=0; i<breaks.getSize(); i++) {
         cout << "ZZZ Break " << i+1 << ": " << breaks[i]+1 << endl;
      }
   }

}



//////////////////////////////
//
// printTitle --
//

void printTitle(ostream& output, const char* title) {
   int i;
   int length = strlen(title);

   for (i=0; i<length; i++) {
      if ((title[i] == 'a') && (title[i+1] == 'e')) {
         output << "%%a";
         i++;
      } else if ((title[i] == 'o') && (title[i+1] == 'e')) {
         output << "%%o";
         i++;
      } else if ((title[i] == 'u') && (title[i+1] == 'e')) {
         output << "%%u";
         i++;
      } else {
         output << title[i];
      }
   }
}



//////////////////////////////
//
// initoffset --
//

void initoffset(Array >& offsets, Array >& nindex) {
   int i;
   offsets.setSize(4);
   for (i=0; i<4; i++) {
      offsets[i].setSize(nindex[i].getSize());
      offsets[i].setAll(0.0);
   }
}



//////////////////////////////
//
// fillnindex --  create an index of the notes in each voice
//     for beam maintenance.
//

void fillnindex(HumdrumFile& infile, Array >& nindex) {
   int i, j;
   for (i=0; i<4; i++) {
      nindex[i].setSize(infile.getNumLines());
      nindex[i].setSize(0);
      for (j=0; j<infile.getNumLines(); j++) {
         if (infile[j].isData()) {
            if (strcmp(infile[j][i], ".") == 0) {
               continue;
            }
            if (strchr(infile[j][i], 'r') != NULL) {
               continue;
            }
            nindex[i].append(j);
         }
      }
   }
}



//////////////////////////////
//
// resetkey -- copy the global key into the temporary voice key for
//     the measure.
//

void resetkey(void) {
   int i;
   int j;
   for (i=0; i<4; i++) {
      for (j=0; j<7; j++) {
         vkey[i][j] = key[j];
      }
   }
}



//////////////////////////////
//
// setKey -- set the default key signature accidentals
//

void setKey(int keynum) {
   if (keynum < 0) {
      if (keynum <= -7) key[3] = -1; // F
      if (keynum <= -6) key[0] = -1; // C
      if (keynum <= -5) key[4] = -1; // G
      if (keynum <= -4) key[1] = -1; // D
      if (keynum <= -3) key[5] = -1; // A
      if (keynum <= -2) key[2] = -1; // E
      if (keynum <= -1) key[6] = -1; // B
   } else if (keynum > 0) {
      if (keynum >= 1) key[3] = 1; // F
      if (keynum >= 2) key[0] = 1; // C
      if (keynum >= 3) key[4] = 1; // G
      if (keynum >= 4) key[1] = 1; // D
      if (keynum >= 5) key[5] = 1; // A
      if (keynum >= 6) key[2] = 1; // E
      if (keynum >= 7) key[6] = 1; // B
   }
}



//////////////////////////////
//
// printStaffInfo -- print staff, keysignature, Title, and meter
//
// 40%  p4 = 2:-45 3:-67 4:-112
// 50%  p4 = 2:-36 3:-48 4:-84
// 60%  p4 = 2:-30 3:-37 4:-67
// 100% p4 = 2:-18 3:-12 4:-30
// 
// staff 2 (tenor) vertical position calculated from this formula:
//    p4 = -18/p5
// staff 3 (alto) vertical position calculated from this formula (approximate)
//    p4 = -6*2/(p5*p5)
// staff 3 (soprano) vertical position calculated from this formula:
//    p4 = -6*2/(p5*p5) - 18/p5
//

void printStaffInfo(ostream& output, HumdrumFile& infile, int segment) {
   double trebleoffset = (int)(-6.0 * 2.0 / (scale*scale) + 0.5);

   // fix approximation for 60% and 40% scalings:
   if (scale == 0.6) {
      trebleoffset = -37.0;
   } else if (scale == 0.4) {
      trebleoffset = -67.0;
   }

   // print bass staff:
   output << "8 1 0 0 " << scale << " 200\n";
   // print tenor staff:
   output << "8 2 0 " << (-18.0/scale) << " " << scale << " 200\n";
   // print alto staff:
   output << "8 3 0 " << trebleoffset << " " << scale << " 200\n";
   // print treble staff:
   output << "8 4 0 " << (trebleoffset - 18.0/scale) << " " << scale << " 200\n";

   // print bass clef:
   output << "3 1 2 0 1\n";
   // print bass clef:
   output << "3 2 2 0 1\n";
   // print treble clef:
   output << "3 3 2 0 0\n";
   // print treble clef:
   output << "3 4 2 0 0\n";

   // print opening barlines
   output << "14 1 0 4 0\n";
   output << "14 1 0 4 8\n";

   // print key signatures 
   int i;
   int keynum = 0;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isInterpretation()) {
         if (strncmp(infile[i][0], "*k[", 3) == 0) {
            keynum = Convert::kernKeyToNumber(infile[i][0]);
         }
      }
   }
   setKey(keynum);
   vkey[0] = key;
   vkey[1] = key;
   vkey[2] = key;
   vkey[3] = key;

   if (keynum != 0) {
      // print bass key signature
      output << "17 1 4 0 " << keynum << " 1\n";
      // print bass key signature
      output << "17 2 4 0 " << keynum << " 1\n";
      // print treble key signature
      output << "17 3 4 0 " << keynum << " 0\n";
      // print treble key signature
      output << "17 4 4 0 " << keynum << " 0\n";
   } 

   // print time signatures if the first line of music
   if (segment == 0) {
      int top = 5;
      int bottom = 5;
      for (i=0; i<infile.getNumLines(); i++) {
         if (infile[i].isInterpretation()) {
            if (sscanf(infile[i][0], "*M%d/%d", &top, &bottom) == 2) {
               break;
            }
         }
      }

      int metc = 0;
      for (i=0; i<infile.getNumLines(); i++) {
         if (infile[i].isInterpretation()) {
            if (strcmp(infile[i][0], "*met(C)") == 0) {
               metc = 1;
               break;
            }
         }

      }

      if (metc) {
         // print the bass time signature
         output << "18 1 7 0 " << 99 << " " << 1 << "\n";
         // print the bass time signature
         output << "18 2 7 0 " << 99 << " " << 1 << "\n";
         // print the treble time signature
         output << "18 3 7 0 " << 99 << " " << 1 << "\n";
         // print the treble time signature
         output << "18 4 7 0 " << 99 << " " << 1 << "\n";
      } else {
         // print the bass time signature
         output << "18 1 7 0 " << top << " " << bottom << "\n";
         // print the bass time signature
         output << "18 2 7 0 " << top << " " << bottom << "\n";
         // print the treble time signature
         output << "18 3 7 0 " << top << " " << bottom << "\n";
         // print the treble time signature
         output << "18 4 7 0 " << top << " " << bottom << "\n";
      }
   }

   if (segment == 0) {
      // print title information
      const char* title = "";
      for (i=0; i<infile.getNumLines(); i++) {
         if (infile[i].isBibliographic()) {
            if (strncmp(infile[i][0], "!!!OTL", 6) == 0) {
               if (strchr(infile[i][0], ':') != NULL) {
                  title = strchr(infile[i][0], ':') + 1;
                  while (title[0] == ' ' || title[0] == '\n') {
                     if (title[0] == '\0') {
                        break;
                     }
                     title++;
                  }
                  break;  // only get the first title in the file
               }
            }
         }
      }
      int bhnum = 0;
      for (i=0; i<infile.getNumLines(); i++) {
         if (infile[i].isBibliographic()) {
            if (strncmp(infile[i][0], "!!!SCT: BH", 10) == 0) {
               sscanf(infile[i][0], "!!!SCT: BH %d", &bhnum);
            }
         }
      }

      output << "t 4 0 20 0 1.5\n";
      if (bhnum > 0) {
         output << "_00" << bhnum << ".   ";
         printTitle(output, title);
         output << "\n";
      } else {
         output << "_00";
         printTitle(output, title);
         output << "\n";
      }
   }

// // print final barline
// output << "14 1 200 4 2\n";

}



//////////////////////////////
//
// convertToScore -- convert humdrum data into a SCORE file
//

void convertToScore(ostream& output, HumdrumFile& infile, int segment, 
      Array<int>& breaks) {
   int i;
   double hpos = 0.0;
   double lastharmhpos = 0.0;
   const char* nextkey = NULL;

   printStaffInfo(output, infile, segment);
   int startline = 0;
   int stopline = 0;

   if (breaks.getSize() == 0) {
      startline = 0;
      stopline = infile.getNumLines() - 1;
   } else {
      if (segment == 0) {
         startline = 0;
      } else {
         startline = breaks[segment-1];
      }
      if (segment == lines - 1) {
         stopline = infile.getNumLines() - 1;
      } else {
         stopline = breaks[segment];
      }
   }
   int measureitem = 0;
   int harmspine = findHarmSpine(infile);
   int staff = 0;
   int direction = 0;
   int voice = 0;
   int length;
   for (i=startline; i<=stopline; i++) {
      hpos = getHpos(infile, i, infile[startline].getAbsBeat(),
         infile[stopline].getAbsBeat());

      if (infile[i].isInterpretation()) {
         if (infile[i].isInterpretation()) {
            length = strlen(infile[i][0]);
            if ((infile[i][0][length-1] == ':') && 
                (Convert::kernToBase40(infile[i][0]) > 0)) {
               nextkey = infile[i][0];
            }
         }

      } else if (infile[i].isMeasure()) {
         printMeasure(output, infile, i, hpos, measureitem++, segment);
      } else if (infile[i].isData()) {
         // output << infile[i].getAbsBeat() << ":\t" << hpos << endl;

         // check bass note
         if (strcmp(infile[i][0], ".") != 0) {
            staff = 1;
            voice = 0;
            direction = 20;
            printNote(output, infile[i][0], hpos, infile, i, 
                  staff, direction, voice);
         }
         // check tenor note
         if (strcmp(infile[i][1], ".") != 0) {
            staff = 2;
            voice = 1;
            direction = 10;
            printNote(output, infile[i][1], hpos, infile, i, 
                  staff, direction, voice);
         }
         // check alto note
         if (strcmp(infile[i][2], ".") != 0) {
            staff = 3;
            voice = 2;
            direction = 20;
            printNote(output, infile[i][2], hpos, infile, i, 
                  staff, direction, voice);
         }
         // check soprano note
         if (strcmp(infile[i][3], ".") != 0) {
            staff = 4;
            voice = 3;
            direction = 10;
            printNote(output, infile[i][3], hpos, infile, i, 
                  staff, direction, voice);
         }

         // print **harm data
         harmspine = findHarmSpine(infile[i]);;
         if ((harmspine >= 0) && (strcmp(infile[i][harmspine], ".") != 0)) {
            if (nextkey != NULL) {
               printKeyInfo(output, nextkey, 
                     (hpos - lastharmhpos)/2.0+lastharmhpos+scale, -9);
               nextkey = NULL;
            }
            printHarm(output, infile[i][harmspine], hpos, infile, harmspine);
            lastharmhpos = hpos;
         }

      }
   }

   for (i=0; i<4; i++) {
      generateBeams(output, infile, i, startline, stopline);
   }

   printTrailer(output);
}



//////////////////////////////
//
// findHarmSpine -- find the spine number for the **harm data
//

int findHarmSpine(HumdrumRecord& aRecord) {
   int output = -1;
   if (harmonyQ == 0) {
      return output;
   }

   int i;
   for (i=0; i<aRecord.getFieldCount(); i++) {
      if (strcmp(aRecord.getExInterp(i), "**harm") == 0) {
         output = i;
         break;
      }
   }

   return output;
}


int findHarmSpine(HumdrumFile& infile) {
   int output = -1;
   if (harmonyQ == 0) {
      return output;
   }

   int i, j;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() != E_humrec_interpretation) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (strcmp(infile[i].getExInterp(j), "**harm") == 0) {
            output = j;
            return output;
         }
      }
   }

   return output;
}



//////////////////////////////
//
// printKeyInfo -- print the analysis key
//

void printKeyInfo(ostream& output, const char* key, double hpos, 
      double vpos) {
   int i;
   int length = strlen(key);

   // double offset = length <= 3 ? -4 : -4.6;
   // output << "t 1 " << hpos << " " << vpos << " 0 1.0 1.25 0 0 0 "
   //      << offset
   //      << "\n";

   output << "t 1 " << hpos << " " << vpos << " 0 1.0 1.25\n";
   output << "_01" ;


   for (i=1; i<length-1; i++) {
      if (key[i] == '-') {
         output << "?1";
      } else if (key[i] == '#') {
         output << "?2";
      } else {
         output << key[i]; 
      }
   }
   output << ":\n";
}



//////////////////////////////
//
// printTrailer --
//

void printTrailer(ostream& output) {
   output << 
//      "IF p1=6 and p7>19 and (p4<8 or p5<8) then p4=p4+0.5, p5=p5+0.5\n"
//      "IF p1=6 and p7>19 and (p4<6 or p5<6) then p4=p4+0.5, p5=p5+0.5\n"
//      "IF p1=6 and p7>19 and (p4<4 or p5<4) then p4=p4+0.5, p5=p5+0.5\n"
      "AD 99\n"
      "STM 99\n"
      "LJ\n"
      "1 4\n\n\n"
      // These next lines fix extra low fermatas so that they do not collide 
      // with the harmonic analysis line. These commands have to go below the
      // STM 99 command:
      "IF p1=1 and p2=1 and p11=-14 and p4=2  then p8=-3\n"
      "IF p1=1 and p2=1 and p11=-14 and p4=1  then p8=-3\n"
      "IF p1=1 and p2=1 and p11=-14 and p4=0  then p8=-3, p14=0.5\n"
      "IF p1=1 and p2=1 and p11=-14 and p4=-1 then p8=-3.75, p14=0.75\n"
   ;

}



//////////////////////////////
//
// printHarm -- print Harmonic analysis
//

void printHarm(ostream& output, const char* text, double hpos, 
      HumdrumFile& infile, int harmspine) {
   if (strchr(text, 'r') != NULL) {
      return;
   }

   output << "t 1 " << hpos << " -7 0 0 0 0 90\n";
   output << "_29" ;
   int i;
   int length = strlen(text);
   for (i=0; i<length; i++) {
      if (text[i] == '/') {
         output << '\\';
      } else if (text[i] == ';') {
         // don't print fermata
      } else if (text[i] == '[') {
         output << "?[";
      } else if (text[i] == ']') {
         output << "?]";
      } else {
         output << text[i];
      }
   }
   output << "\n";
}



//////////////////////////////
//
// printMeasure --
//

void printMeasure(ostream& output, HumdrumFile& infile, int line, double hpos,
      int measurecount, int systemno) {
   int btype;
   const char* data = infile[line][0];

   if (strchr(data, '-') != NULL) {
      return;  // this is a non-printing barline 
   } else if (strncmp(data, "==", 2) == 0) {   // final barline
      btype = 2;
   } else if (strstr(data, ":|!|:") != NULL) {
      btype = 5;
   } else if (strstr(data, ":!!:") != NULL) {
      btype = 6;
   } else if (strstr(data, ":|!") != NULL) {
      btype = 3;
   } else if (strstr(data, "!|:") != NULL) {
      btype = 4;
   } else if (strstr(data, "||") != NULL) {
      btype = 1;
   } else {
      // plain measure
      btype = 0;
   }

   // offset the position so that the first beat of the next
   // measure does not collide with the measure
   double hposition = hpos;
   if (hposition < 199.80) {
      // only shift the measure line back if not at the end of a line (200)
      hposition -= 1.00;
   }
   if ((systemno > 0) && (measurecount == 0)) {
      // don't draw the measure line if it is of type 0
      switch (btype) {
         case 0:
         case 1:
         case 3:
             // do nothing -- supress printing of this barline
             break;
         case 6:
         case 5:
            btype = 4;
            // no break;
         case 4:
            output << "14 1 " << hposition << " 4 " << btype << "\n";
            break;
      }
   } else {
      output << "14 1 " << hposition << " 4 " << btype << "\n";
   }

   int measureno = 0;
   // print measure number, if it is there
   if ((systemno > 0) && (measurecount == 0)) {
      // print measure number at start of system before clefs
      if (sscanf(data, "=%d", &measureno)) {
         output << "10 4 " << 0 << " 13 " << measureno << " 0.8 1\n";
         output << "I\nCM\n\n";
      }
   } else if (hposition < 199.8) {
      if (sscanf(data, "=%d", &measureno)) {
         output << "10 4 " << hposition << " 13 " << measureno << " 0.8 1\n";
         output << "I\nCM\n\n";
      }
   }

   resetkey();
}



//////////////////////////////
//
// getoffset -- return the note offset to avoid collisions due
//     to closeness of two voices on the same staff.
//

double getoffset(const char* lower, const char* upper) {
   if (strcmp(lower, ".") == 0) {
      return 0;
   }
   if (strcmp(upper, ".") == 0) {
      return 0;
   }

   int lowp = Convert::kernToBase40(lower);
   int upp  = Convert::kernToBase40(upper);
   if ((upp - lowp > 0) && (upp - lowp < 10)) {
      return 10;
   }

   int lowtype = noteheadtype(Convert::kernToDuration(lower));
   int uptype  = noteheadtype(Convert::kernToDuration(upper));
   
   if ((upp - lowp >= 0) && (upp - lowp < 5)) {
      if (lowtype != uptype) {
         return 10;
      }
   }

   return 0;
}



//////////////////////////////
//
// makeaccidental --
//

double makeaccidental(HumdrumFile& infile, int line, int voice, 
   Array<int>& lkey) {

   int acc = 0;
   int length = strlen(infile[line][voice]);
   int i;
   for (i=0; i<length; i++) {
      if (infile[line][voice][i] == '-') {
         acc += -1;
      } else if (infile[line][voice][i] == '#') {
         acc += 1;
      }
   }

   int base40   = Convert::kernToBase40(infile[line][voice]);
   int diatonic = Convert::base40ToDiatonic(base40) % 7;

   // don't write an accidental which is in the current key set
   if ((acc == lkey[diatonic]) && 
       (strchr(infile[line][voice], 'n') == NULL) &&
       (strchr(infile[line][voice], 'y') == NULL) 
      ) {
      return 0;
   }

   lkey[diatonic] = acc;
   int output = 0;
  
   if (strchr(infile[line][voice], 'n') != NULL) {
      output = 3;  // natural sign
   } else {
      switch (acc) {
         case -2: output = 4; break; // double flat
         case -1: output = 1; break; // flat
         case 0:  output = 3; break; // natural sign
         case 1:  output = 2; break; // sharp sign
         case 2:  output = 5; break; // double sharp sign
      }
   }

   // check for moving the accidental if they will collide

   double fraction = 0.0;
   if ((voice == 1) || (voice == 3)) {
      return output + fraction;
   } 

   // check on bass and alto voices

   int poutput = 0;
   if (voice == 0) {
      poutput = peekAccidental(infile, line, 1, vkey[1]);  // look at tenor acc.
   } else {
      poutput = peekAccidental(infile, line, 3, vkey[3]);  // look at sop. acc.
   }

   if (poutput == 0) {
      return output + fraction;
   }

   int b40u = 0;
   int b40d = 0;

   if (voice == 0) {
      b40u = Convert::kernToBase40(infile[line][1]);
      b40d = Convert::kernToBase40(infile[line][0]);
   } else {
      b40u = Convert::kernToBase40(infile[line][3]);
      b40d = Convert::kernToBase40(infile[line][2]);
   }

   // the function does not check properly if the voices are inverted
   if ((b40u - b40d < 0) || (b40u - b40d >= 40)) {
      return output + fraction; 
   }
   
   int dup = Convert::base40ToDiatonic(b40u) % 7;
   int ddn = Convert::base40ToDiatonic(b40d) % 7;

   int dint = dup - ddn;
   if (dint < 0) {
      dint += 7;
   }

   //  Here are the accidental offset separations by diatonic interval for
   //  sharps, flats and naturals (doubles to be done later, if ever)
   //
   //   bot top    2    3    4    5    6    7
   //    b   b    0.24 0.24 0.15 0.15 0.12 0.00
   //    b   s    0.25 0.25 0.25 0.20 0.18 0.11
   //    b   n    0.22 0.22 0.18 0.12 0.12 0.00
   //    s   b    0.24 0.24 0.24 0.18 0.00 0.00
   //    s   s    0.29 0.29 0.29 0.22 0.20 0.00
   //    s   n    0.24 0.24 0.23 0.11 0.11 0.00
   //    n   b    0.24 0.24 0.20 0.13 0.00 0.00
   //    n   s    0.26 0.26 0.25 0.20 0.12 0.00
   //    n   n    0.22 0.22 0.16 0.12 0.00 0.00
   //

   // ignoring spacing when dealing with double sharps or double flats
   if ((poutput > 3) || (output > 3)) {
      return output + fraction;
   }

   // ignoring spacing for unison accidentals:
   if (dint == 0) {
      return output + fraction;
   }

   switch (output * 10 + poutput) {
      case 11: 
         //    b   b    0.24 0.24 0.15 0.15 0.12 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.24; break;
            case 3: case 4: fraction = 0.15; break;
            case 5: fraction = 0.12; break; 
            case 6: fraction = 0.12; break; 
         }
         break;
      case 12:
         //    b   s    0.25 0.25 0.25 0.20 0.18 0.11
         switch (dint) { 
            case 1: case 2: case 3: fraction = 0.25; break;
            case 4: fraction = 0.20; break;
            case 5: fraction = 0.18; break;
            case 6: fraction = 0.11; break;
         }
         break;
      case 13:
         //    b   n    0.22 0.22 0.18 0.12 0.12 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.22; break;
            case 3: fraction = 0.18; break;
            case 4: case 5: fraction = 0.12; break;
         }
         break;
      case 21:
         //    s   b    0.24 0.24 0.24 0.18 0.00 0.00
         switch (dint) { 
            case 1: case 2: case 3: fraction = 0.24; break;
            case 4: fraction = 0.18; break;
         }
         break;
      case 22:
         //    s   s    0.29 0.29 0.29 0.22 0.20 0.00
         switch (dint) { 
            case 1: case 2: case 3: fraction = 0.29; break;
            case 4: fraction = 0.22; break;
            case 5: fraction = 0.20; break;
         }
         break;
      case 23:
         //    s   n    0.24 0.24 0.23 0.11 0.11 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.24; break;
            case 3: fraction = 0.22; break;
            case 4: case 5: fraction = 0.11; break;
         }
         break;
      case 31:
         //    n   b    0.24 0.24 0.20 0.13 0.00 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.24; break;
            case 3: fraction = 0.20; break;
            case 4: fraction = 0.13; break;
         }
         break;
      case 32:
         //    n   s    0.26 0.26 0.25 0.20 0.12 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.26; break;
            case 3: fraction = 0.25; break;
            case 4: fraction = 0.20; break;
            case 5: fraction = 0.12; break;
         }
         break;
      case 33:
         //    n   n    0.22 0.22 0.16 0.12 0.00 0.00
         switch (dint) { 
            case 1: case 2: fraction = 0.22; break;
            case 3: fraction = 0.16; break;
            case 4: fraction = 0.12; break;
         }
         break;
   }

   return output + fraction;
}



//////////////////////////////
//
// peekAccidental --
//

int peekAccidental(HumdrumFile& infile, int line, int voice, 
   Array<int>& lkey) {

   if (strcmp(infile[line][voice], ".") == 0) {
      return 0;
   }

   int acc = 0;
   int length = strlen(infile[line][voice]);
   int i;
   for (i=0; i<length; i++) {
      if (infile[line][voice][i] == '-') {
         acc += -1;
      } else if (infile[line][voice][i] == '#') {
         acc += 1;
      }
   }

   int base40   = Convert::kernToBase40(infile[line][voice]);
   int diatonic = Convert::base40ToDiatonic(base40) % 7;

   // don't write an accidental which is in the current key set
   if ((acc == lkey[diatonic]) && (strchr(infile[line][voice], 'n') == NULL)) {
      return 0;
   }

   int output = 0;
  
   if (strchr(infile[line][voice], 'n') != NULL) {
      output = 3;  // natural sign
   } else {
      switch (acc) {
         case -2: output = 4; break; // double flat
         case -1: output = 1; break; // flat
         case 0:  output = 3; break; // natural sign
         case 1:  output = 2; break; // sharp sign
         case 2:  output = 5; break; // double sharp sign
      }
   }

   return output;
}



//////////////////////////////
//
// printNote --
//

void printNote(ostream& output, const char* data, double hpos, 
      HumdrumFile& infile, int line, int staff, int direction, int voice) {
   double duration = Convert::kernToDuration(data);
   int pitch = Convert::kernToBase40(data);
   int vpos = 0;
   int clef = 0;
   double offset = 0.0;
   double accidental = 0.0;
   switch (staff) {
      case 1: clef = 1; break;
      case 2: clef = 1; break;
      case 3: clef = 0; break;
      case 4: clef = 0; break;
   }
   double tiedir = 0;
   switch (voice) {
      case 0: tiedir = -1; break;
      case 1: tiedir = 1;  break;
      case 2: tiedir = -1; break;
      case 3: tiedir = 1;  break;
   }
   if (strchr(data, 'r') != NULL) {
      switch (voice) {
         case 0: case 2:
            output << "2 " << staff << " " << hpos << " -4 0 0 " 
                 << duration << endl;
            break;
         case 1: case 3:
            output << "2 " << staff << " " << hpos << " 4 0 0 " 
                 << duration << endl;
            break;
      }
   } else {
      accidental = makeaccidental(infile, line, voice, vkey[voice]);
      offset = 0.0;
      if (voice == 2) {
         offset = getoffset(infile[line][2], infile[line][3]);
         if (offset != 0) {
            storeoffset(offset, voice, line);
         }
      } else if (voice == 0) {
         offset = getoffset(infile[line][0], infile[line][1]);
         if (offset != 0) {
            storeoffset(offset, voice, line);
         }
      } else if (voice == 1) {
         // if the tenor is below the bass,
         // then offset the tenor by a small amount so that note
         // heads do not collide with the other voices stem.
         if (Convert::kernToBase40(infile[line][1]) < 
             Convert::kernToBase40(infile[line][0])) {
            offset = 0.9;
            storeoffset(offset, voice, line);
         }
      } else if (voice == 3) {
         // if the soprano is below the alto 
         // then offset the soprano by a small amount so that note
         // heads do not collide with the other voices stem.
         if (Convert::kernToBase40(infile[line][3]) < 
             Convert::kernToBase40(infile[line][2])) {
            offset = 0.9;
            storeoffset(offset, voice, line);
         }
      }


      double stemadj = 0.0;
      double fermataadj = 0.0;
      vpos = Convert::base40ToScoreVPos(pitch, clef);
      if ((strchr(data, ';') != NULL) && (voice == 0)) {
         // these conditions don't really matter, because
         // the stm 99 and ad 99 commands will erase this data.
         // appended if statments to trailer to fix extra low fermatas
         if (vpos < 2) {
            stemadj = -3.5;  // LJ erases this data anyway
         } 
         if (vpos < 1) {
            stemadj = -4.0;  // LJ erases this data anyway
         }
         if (vpos < 1) {
            fermataadj = 0.5;
         }
         if (vpos < 0) {
            fermataadj = 1.0;
         }
         if (vpos < -1) {
            fermataadj = 1.5;
         }
      }
      output << "1 " << staff << " " << hpos << " " << vpos << " "
           << direction + accidental << " " << noteheadtype(duration) << " " 
           << duration << " " << stemadj << " " 
           << 10 * getdots(duration, voice) + getflags(duration)
           << " " << offset;
      if (strchr(data, ';') != NULL) {
         if (voice == 0) {
            output << " -14";
            if (fermataadj != 0.0) {
               output << " " << fermataadj;
            }
         } else if (voice == 3) {
            output << " 14";
         } else if (voice == 1) {
            if (strchr(infile[line][0], ';') == NULL) {
               output << " 14";
            }
         } else if (voice == 2) {
            if (strchr(infile[line][3], ';') == NULL) {
               output << " -14";
            }
         }
      }
      output << "\n";

      // check tie information

      double voffset = 0.0;
      if (voice == 0 || voice == 2) {
         if (vpos % 2) {
            voffset += -1;
         } else {
            voffset += -2;
         }
      } else {
         if (vpos % 2) {
            voffset += +1;
         } else {
            voffset += +2;
         }
      }

      if (offset == 10) {
         hpos += 1;
      }

      double roffset = 0.0;
      if (voice == 1 || voice == 3) {
         roffset = 0.25;
      }

      tiedir *= 0.8;
      if (tiedir > 0) {
         voffset += -0.5;
      } else {
         voffset += 0.5;
      }
      if ((tiedir > 0) && (vpos > 11) && (vpos % 2 == 0)) {
         voffset += -1;
      } else if ((tiedir > 0) && (vpos > 11) && (vpos % 2 == 1)) {
         voffset += 0.5;
      }

      if ((tiedir < 0) && (vpos < 4) && (vpos % 2 == 0)) {
         voffset += 0.5;
      } else if ((tiedir < 0) && (vpos < 2) && (vpos % 2 == 1)) {
         voffset -= 0.5;
      }

      if (strchr(data, '[') != NULL) {
         tie[voice] = hpos;
      } else if (strchr(data, '_') != NULL) {
         output << "5 " << staff << " " << tie[voice] - 0.25 << " "
              << vpos+voffset << " " << vpos+voffset << " " 
              << hpos + roffset << " " << tiedir << " -2 0 0 0 0 0.6\n";
         if ((tiedir < 0) && (vpos <= 3)) {
            output << "I\nCR\n\n";
         }
         tie[voice] = hpos;
      } else if (strchr(data, ']') != NULL) {
         output << "5 " << staff << " " << tie[voice] - 0.25 << " "
              << vpos+voffset << " " << vpos+voffset << " " 
              << hpos + roffset << " " << tiedir << " -2 0 0 0 0 0.6\n";
         if ((tiedir < 0) && (vpos <= 3)) {
            output << "I\nCR\n\n";
         }
         tie[voice] = 0.0;
      } else {
         tie[voice] = 0.0;
      }
   }
}



//////////////////////////////
//
// storeoffset -- store the note offset so that the beams can be
//    coordinated when they are written at a later stage.
//

void storeoffset(double offset, int voice, int line) {
   int index = -1;
   int i;
   for (i=0; i<nindex[voice].getSize(); i++) {
      if (nindex[voice][i] == line) {
         index = i;
      }
   }

   if (index >= 0) {
      offsets[voice][index] = offset;
   }
}



//////////////////////////////
//
// getflags --
//

int getflags(double duration) {
   if      (duration <= 1.0/128.0) { return 8; }
   else if (duration <  1.0/64.0 ) { return 7; }
   else if (duration <  1.0/32.0 ) { return 6; }
   else if (duration <  1.0/16.0 ) { return 5; }
   else if (duration <  1.0/8.0  ) { return 4; }
   else if (duration <  1.0/4.0  ) { return 3; }
   else if (duration <  1.0/2.0  ) { return 2; }
   else if (duration <  1.0/1.0  ) { return 1; }
   else                            { return 0; }
}



//////////////////////////////
//
// getdots -- return the number of rhythm augmentation dots
//

int getdots(double duration, int voice) {
   char buffer[128] = {0};
   Convert::durationToKernRhythm(buffer, duration);
   int i = 0;
   int count = 0;
   while (buffer[i] != '\0') {
      if (buffer[i] == '.') {
         count++;
      }
      i++;
   }

   if ((count > 0) && (voice == 0 || voice == 2)) {
      count += 10; // 10 multiplied later
   }

   return count;
}



//////////////////////////////
//
// noteheadtype --
//

int noteheadtype(double duration) {
   if      (duration < 1.9) { return 0; }
   else if (duration < 3.9) { return 1; }
   else if (duration < 7.9) { return 2; }
   else                     { return 3; }
}



//////////////////////////////
//
// recheckOptions -- validate and process command-line options again
//

void recheckOptions(Options& opts, char* command, const char* string) {
   char buffer[1024] = {0};
   strncpy(buffer, string, 1000);
   char* array[1024] = {0};
   int argc = 0;
   array[argc++] = command;
   array[argc++] = strtok(buffer, " \t\n");
   if (array[argc-1] != NULL) {
      while ((array[argc++] = strtok(NULL, " \t\n")) != NULL) { 
         if (argc > 1000) {
            break;
         }
      }
   }
   argc--;
   opts.process(argc, array);

   debugQ    =  opts.getBoolean("debug");
   scale     =  opts.getDouble("scale");
   lines     =  opts.getInteger("lines");
   quietQ    =  opts.getBoolean("quiet");
   readQ     = !opts.getBoolean("do-not-read-file-options");
   harmonyQ  = !opts.getBoolean("no-harmony");

   char buffer2[1024] = {0};
   char* ptr;
   if (options.getBoolean("output")) {
      strcpy(buffer2, options.getString("output"));
      if ((ptr = strrchr(buffer2, '.')) != NULL) {
         strcpy(extension, ptr+1);
         strcpy(zbasename, options.getString("output"));
         ptr = strrchr(zbasename, '.');
         ptr[0] = '\0';
      } else {
         strcpy(zbasename, options.getString("output"));
         extension[0] = '\0';
      }
   }

   if ((strcmp(zbasename, "%b") == 0) && (strlen(inputbase) > 0)) {
      strcpy(zbasename, inputbase);
   }
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("debug=b",             "trace input parsing");   
   opts.define("q|quiet=b",           "do not print line parsing info");   
   opts.define("H|no-harmony=b",      "do not print harmony under music");   
   opts.define("s|scale=d:0.6",       "staff scaling");   
   opts.define("R|do-not-read-file-options=b","prevent reading opts in file");
   opts.define("l|lines=i:1",         "number of lines");   
   opts.define("o|output=s:file.pmx", "output file template");

   opts.define("author=b",            "author of the program");   
   opts.define("version=b",           "compilation information"); 
   opts.define("example=b",           "example usage"); 
   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, July 2003" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: September 2003" << 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");
   scale     =  opts.getDouble("scale");
   lines     =  opts.getInteger("lines");
   quietQ    =  opts.getBoolean("quiet");
   readQ     = !opts.getBoolean("do-not-read-file-options");

   char buffer[1024] = {0};
   char* ptr;
   if (options.getBoolean("output")) {
      strcpy(buffer, options.getString("output"));
      if ((ptr = strrchr(buffer, '.')) != NULL) {
         strcpy(extension, ptr+1);
         strcpy(zbasename, options.getString("output"));
         ptr = strrchr(zbasename, '.');
         ptr[0] = '\0';
      } else {
         strcpy(zbasename, options.getString("output"));
         extension[0] = '\0';
      }
   }

   char tbuffer[1024] = {0};
   strcpy(tbuffer, options.getArg(1));
   ptr = strrchr(tbuffer, '.');
   if (ptr != NULL) {
      ptr[0] = '\0';
   }
   ptr = strrchr(tbuffer, '/');
   if (ptr != NULL) {
      strcpy(inputbase, &ptr[1]);
   } else {
      strcpy(inputbase, tbuffer);
   }

   if ((strcmp(zbasename, "%b") == 0) &&(strlen(inputbase) > 0)) {
      strcpy(zbasename, inputbase);
   }
}



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

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



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

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



//////////////////////////////
//
// generateBeams -- 
//

void generateBeams(ostream& output, HumdrumFile& infile, int voice,
      int startline, int stopline) {
   int i;
   Array<int> index;
   index.setSize(infile.getNumLines());
   index.setSize(0);

// fix this for loop to prevent starting at 0
// for (i=startline; i<=stopline; i++) {
   for (i=0; i<=stopline; i++) {
      if (infile[i].isData()) {
         if (strcmp(infile[i][voice], ".") == 0) {
            continue;
         }
         if (strchr(infile[i][voice], 'r') != NULL) {
            continue;
         }
         index.append(i);
      }
   }

   for (i=0; i<index.getSize(); i++) {
      if ((index[i]>=startline) && (index[i]<=stopline)) {
         checkForBeam(output, infile, voice, index, i); 
      }
   }
}



//////////////////////////////
//
// checkForBeam -- see if a beam is supposed to start here
//

void checkForBeam(ostream& output, HumdrumFile& infile, int voice, 
      Array<int>& index, int ind) {


   int startbeat = 0;
   int endbeat = 0;

   // assume that there are no pickups less than a quarter note.
   int beat = (int)(infile[index[ind]].getAbsBeat()+ 0.001);
   double fraction = infile[index[ind]].getAbsBeat() - beat;


   int tbeat = 0;
   int j;
   for (j=ind; j<index.getSize(); j++) {
      tbeat = (int)(infile[index[j]].getAbsBeat()+ 0.001);
      if (tbeat == beat) {
         endbeat = j;
      } else {
         break;
      }
   }

   for (j=ind; j>=0; j--) {
      tbeat = (int)(infile[index[j]].getAbsBeat()+ 0.001);
      if (tbeat == beat) {
         startbeat = j;
      } else {
         break;
      }
   }

   // don't beam quarter notes or larger
   if (startbeat == endbeat) {
      return;
   }

   // know that some beaming action necessary

   Array<double> durs;
   durs.setSize(0);
   durs.setSize(endbeat-startbeat+1);
   durs.setAll(0.0);

   for (j=0; j<durs.getSize(); j++) {
      durs[j] =  Convert::kernToDuration(infile[index[j+startbeat]][voice]);
   }

   // beam on a case-by-case basis
   double hpos1 = 0.0;
   double hpos2 = 0.0;
   double hpos3 = 0.0;
   double vpos1 = 0.0;
   double vpos2 = 0.0;
   double vpos3 = 0.0;

   //////////

   int pitch1;
   int pitch2;
   int pitch3;
   int dir = 0;

   // beam two eighth notes together
   if ((durs.getSize() == 2) && (durs[0] == 0.5) && (durs[1] == 0.5)
         && fraction < 0.001) {
      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[endbeat]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[endbeat]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 21;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 20)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] != 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] != 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] != 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 11;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 10)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 10)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] != 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] != 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] != 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
      }
   }


   //////////

   // beam two sixteenth notes together on the half beat
   else if ((durs.getSize() == 2) && (durs[0] == 0.25) && (durs[1] == 0.25)
         && fraction == 0.5) {
      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[endbeat]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[endbeat]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 22;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 20)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 12;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 10)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 10)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
      }
   }

   //////////

   // beam two sixteenth notes starting on the beat (long note or rest
   // on the second half of the beat
   else if (((durs.getSize() == 2) && (durs[0] == 0.25) && (durs[1] == 0.25)
         && fraction == 0.0) ||
         ((durs.getSize() == 3) && (durs[0] == 0.25) && (durs[1] == 0.25)
         && (durs[2] >= 1.0) && fraction == 0.0) 
      ) {

      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[startbeat+1]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[startbeat+1]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 22;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 20)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][startbeat+1] > 0.0) {
                   output << " " << offsets[voice][startbeat+1];
                }
            } else if (offsets[voice][startbeat+1] > 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][startbeat+1];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 12;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 10)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 10)
                 << " " << hpos2 << " " << dir;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 0 0 0 0 0 " << offsets[voice][startbeat];
                if (offsets[voice][startbeat+1] > 0.0) {
                   output << " " << offsets[voice][startbeat+1];
                }
            } else if (offsets[voice][startbeat+1] > 0.0) {
                output << " 0 0 0 0 0 0 0 " << offsets[voice][startbeat+1];
            }
            output << "\n";
            break;
      }
   }

   //////////

   // beam one eighth and two sixteenth notes together 
   else if ((durs.getSize() == 3) && (durs[0] == 0.5) && 
         (durs[1] == 0.25) && (durs[2] == 0.25) && fraction == 0.0) {
      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[startbeat+1]);
      hpos3 = getHpos(infile, index[endbeat]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[startbeat+1]][voice]);
      pitch3 = Convert::kernToBase40(infile[index[endbeat]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 0);
            }
            dir = 21;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos3, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos3, 20) - 0.5
                 << " " << hpos3 << " " << dir 
                 << " 0 0 " << 11 << " " << hpos2 << " " << hpos3;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 0);
            }
            dir = 11;
            if (vpos2 > vpos1 && vpos2 > vpos3)  {
               output << "6 " << voice+1 << " " << hpos1 << " "
                    << getBeamEnd(vpos1, vpos3, 10) + 0.5
                    << " " 
                    << getBeamEnd(vpos1, vpos3, 10) + 0.5
                    << " " << hpos3 << " " << dir 
                    << " 0 0 " << 11 << " " << hpos2 << " " << hpos3;
            } else {
               output << "6 " << voice+1 << " " << hpos1 << " "
                    << getBeamStart(vpos1, vpos3, 10)
                    << " " 
                    << getBeamEnd(vpos1, vpos3, 10) + 0.5
                    << " " << hpos3 << " " << dir 
                    << " 0 0 " << 11 << " " << hpos2 << " " << hpos3;
            }
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
      }
   }

   //////////

   // beam two sixteenths and one eighth note together 
   else if ((durs.getSize() == 3) && (durs[0] == 0.25) && 
         (durs[1] == 0.25) && (durs[2] == 0.5) && fraction == 0.0) {
      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[startbeat+1]);
      hpos3 = getHpos(infile, index[endbeat]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[startbeat+1]][voice]);
      pitch3 = Convert::kernToBase40(infile[index[endbeat]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 0);
            }
            dir = 21;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos3, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos3, 20) - 0.5
                 << " " << hpos3 << " " << dir 
                 << " 0 0 " << 11 << " " << hpos1 << " " << hpos2;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
               vpos3 = Convert::base40ToScoreVPos(pitch3, 0);
            }
            dir = 11;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos3, 10)
                 << " " 
                 << getBeamEnd(vpos1, vpos3, 10) + 0.5
                 << " " << hpos3 << " " << dir 
                 << " 0 0 " << 11 << " " << hpos1 << " " << hpos2;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
      }
   }

   //////////

   // beam dotted eight and sixteenth
   else if ((durs.getSize() == 2) && (durs[0] == 0.75) && (durs[1] == 0.25)
         && fraction == 0.0) {
      hpos1 = getHpos(infile, index[startbeat]);
      hpos2 = getHpos(infile, index[endbeat]);
      pitch1 = Convert::kernToBase40(infile[index[startbeat]][voice]);
      pitch2 = Convert::kernToBase40(infile[index[endbeat]][voice]);
      switch (voice) {
         case 0: case 2:
            if (voice == 0) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 21;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 20)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 20)
                 << " " << hpos2 << " " << dir << " 0 0 "
                 << 11 << " 0 " << hpos2;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
         case 1: case 3:
            if (voice == 1) {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 1);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 1);
            } else {
               vpos1 = Convert::base40ToScoreVPos(pitch1, 0);
               vpos2 = Convert::base40ToScoreVPos(pitch2, 0);
            }
            dir = 11;
            output << "6 " << voice+1 << " " << hpos1 << " "
                 << getBeamStart(vpos1, vpos2, 10)
                 << " " 
                 << getBeamEnd(vpos1, vpos2, 10)
                 << " " << hpos2 << " " << dir << " 0 0 "
                 << 11 << " 0 " << hpos2;
            if (offsets[voice][startbeat] > 0.0) {
                output << " 0 " << offsets[voice][startbeat];
                if (offsets[voice][endbeat] > 0.0) {
                   output << " " << offsets[voice][endbeat];
                }
            } else if (offsets[voice][endbeat] > 0.0) {
                output << " 0 0 " << offsets[voice][endbeat];
            }
            output << "\n";
            break;
      }
   }

}



//////////////////////////////
//
// getHpos -- return the horizontal postion of an item in the score
//

double getHpos(HumdrumFile& infile, int line, double start, double ending) {
   static double startDur = -1;
   static double endDur = -1;
   if (startDur < 0) {
      startDur = start;
   }
   if (endDur < 0) {
      endDur = ending;
   }

   if (start >= 0.0) {
      startDur = start;
   }
   if (ending >= 0.0) {
      endDur = ending;
   }

   double beatwidth = endDur - startDur;

   double output;
   output = (infile[line].getAbsBeat() - startDur) / beatwidth * 190 + 10;
   output = ((int)(output*100.0 + 0.5))/100.0;
   return output;

// originally:
//   output = infile[line].getAbsBeat() / infile.getTotalDuration() * 190 + 10;
//   output = ((int)(output*100.0 + 0.5))/100.0;
}



//////////////////////////////
//
// getBeamStart --
//

double getBeamStart(double vpos1, double vpos2, int dir) {

   double upbeam1[23][23] = {{0, 0, 0, 0, 0, 0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{0, 0, 0, 0, 0, 0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{0, 0, 0, 0, 0, 0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{0, 0, 0, 0, 0, 0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{1, 1, 1, 1, 1, 1, 1.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{2, 2, 2, 2, 2, 2, 2, 2.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{2, 2, 2, 2, 3, 3, 3, 3, 3.5, 3.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{3, 3, 3, 3, 3, 4, 4, 4, 4, 4.5, 4.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5.5, 5.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6.5, 6.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7.5, 7.5, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{7, 7, 7, 7, 7, 7, 7, 7, 7, 7.5, 7.5, 7.5, 7.5, 7.75, 7.75, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8, 8, 8, 8, 8.25, 8.25, 9, 10, 11, 12, 13, 14, 15},
	{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8.5, 8.5, 9, 8.5, 9, 9, 10, 11, 12, 13, 14, 15},
	{9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 10, 10, 11, 12, 13, 14, 15},
	{10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 11, 11, 12, 13, 14, 15},
	{11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 11.5, 12, 12, 13, 14, 15},
	{12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 13, 13, 14, 15},
	{13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 13.5, 14, 14, 15},
	{14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 15, 15},
	{15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5, 16},
	{16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5, 16.5},
	{17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5}};

   double downbeam1[23][23] = {{-1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5},
	{-0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5},
	{0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5},
	{1, 1, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5},
	{1, 1.5, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5},
	{1, 1.5, 2.5, 2.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5},
	{1, 1.5, 2.5, 3.5, 3.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5},
	{1, 1.5, 2.5, 3.5, 4.5, 4.5, 5.5, 5, 5.5, 5.5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 5.5, 6, 6, 6, 6, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6, 6.5, 6.5, 6.5, 6.5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 6.5, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14},
	{1, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14}};

   if (vpos1 < -3 || vpos2 < -3 || vpos1 > 22 || vpos2 > 22) {
      return vpos1;
   }

   switch (dir) {
      case 10: return upbeam1[(int)vpos1+3][(int)vpos2+3];
      case 20: return downbeam1[(int)vpos1+3][(int)vpos2+3];
   }

   return vpos1;
}



//////////////////////////////
//
// getBeamEnd --
//


double getBeamEnd(double vpos1, double vpos2, int dir) {

   double upbeam2[23][23] = {{0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0.5, 0.5, 0.5, 0.5, 1, 2, 3, 4, 4, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{0.5, 0.5, 0.5, 0.5, 1.5, 2, 3, 4, 5, 5, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{1.5, 1.5, 1.5, 1.5, 1.5, 2.5, 3, 4, 5, 6, 6, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 3.5, 4, 5, 6, 7, 7, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 4.5, 5, 6, 7, 7.5, 8.5, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 5.5, 6, 7, 7.5, 8, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 6.5, 7, 7.5, 8, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 7.5, 7.5, 8, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.75, 8, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 7.75, 8.25, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 8.25, 9, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5},
	{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13.5, 14.5, 15.5, 16.5, 17.5},
	{13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14.5, 15.5, 16.5, 17.5},
	{14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15.5, 16.5, 17.5},
	{15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16.5, 17.5}};

   double downbeam2[23][23] = {{-1.5, -0.5, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{-1.5, -0.5, 0.5, 1, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5},
	{-1.5, -0.5, 0.5, 1.5, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 2.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 3.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 4.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 5.5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 6, 6, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 6.5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6, 6.5, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 6, 6.5, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 6.5, 7, 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 7, 8, 9, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 8, 9, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 9, 10, 11, 12, 12, 13, 13, 13, 13, 13, 13},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 10, 11, 12, 13, 13, 13, 13, 13, 13, 13},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 11, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14},
	{-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5, 5.5, 7, 8, 9, 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14}};

   if (vpos1 < -3 || vpos2 < -3 || vpos1 > 22 || vpos2 > 22) {
      return vpos2;
   }

   switch (dir) {
      case 10: return upbeam2[(int)vpos1+3][(int)vpos2+3];
      case 20: return downbeam2[(int)vpos1+3][(int)vpos2+3];
   }

   return vpos2;
}



// md5sum: a5f8f916b2b688fc8807fa3e79714787 chor2scor.cpp [20121112]