//
// globs.cpp (global functions of use to everyone)
//
// Written by John A deLaubenfels, https://www.adaptune.com/steg.htm .
// Permission granted to use freely in any non-fraudulent way.
//
//
//
//
//
//
//
//



#ifdef NEED_PCH
   #include "pch.h"
#else
   #include "stdafx.h"
#endif // !NEED_PCH


#define FROM_GLOB


#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <utime.h>

// int utime(const char *filename, const struct utimbuf *times);

#include <sys/time.h>

#endif // !_WIN32

#include <list>
#include "globs.h"
#include "zlib.h"
#include "arcHelp.h"


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifdef _WIN32
#include <sys/utime.h>
#include <io.h>
#include <conio.h>
#include <direct.h>
#include <timeapi.h>
#endif // _WIN32

#include "bufin.h"
#include "sha1.h"
#include "randd.h"
#include "ssprint.h"


#ifndef _WIN32
#define _stat64 stat64
#endif // ! _WIN32









#ifdef BOZOOOO


#ifdef NEED_PCH
   #include "pch.h"
#else
   #include "stdafx.h"
#endif // !NEED_PCH


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>





// #include "randd.h"
#include <vector>

#define FROM_GLOB



#include <stdio.h>
#include <string.h>
#include <stdlib.h>    // 12-27-04 (jdl)

#ifdef _WIN32
#include <direct.h>    // 12-27-04 (jdl)
#endif // _WIN32

#include <assert.h>

#ifdef _WIN32
#include <conio.h>
#endif // _WIN32

#include <math.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <sys/utime.h>  // 07-21-06 (jdl)
#endif // _WIN32

#include <time.h>
// #include <mmsystem.h>  // out?  jdl 01-11-00
#include "globs.h"
#include "bufin.h"
// #include "mev.h"
// #include "Mioc.h"
// #include "mprintf.h"
// #include "random32.h"
#include "sha1.h"

#ifdef _WIN32
#include <windows.h>
#include <process.h>
#endif // _WIN32


#ifndef _WIN32
#define _stat64 stat64
#endif // ! _WIN32






#ifdef MAYBE_BADD

#include <stdio.h>
#include <string.h>
#include <stdlib.h>    // 12-27-04 (jdl)

#include <assert.h>

#include <math.h>
#include <sys/stat.h>

#include <time.h>
// #include <mmsystem.h>  // out?  jdl 01-11-00
#include "globs.h"
#include "bufin.h"
// #include "mev.h"
// #include "Mioc.h"
// #include "mprintf.h"
// #include "random32.h"
#include "sha1.h"

#ifdef _WIN32
#include <direct.h>    // 12-27-04 (jdl)
#include <conio.h>
#include <sys/utime.h>  // 07-21-06 (jdl)
#else
#define _stat64 stat64
#include <sys/time.h>

#endif // !_WIN32

#endif // MAYBE_BADD

#pragma warning( disable : 4996 ) 


#endif // BOZOOOO


#pragma warning( disable : 4996 ) 


//
// Set a breakpoint here to catch a function barfing with a NADA return.
//
//
// Input:
//    pcMessage (default NULL)
//    onlyIf    (default 0):   if != 0, only make noise if global flag is set.
//
//
// 09-20-17 (jdl) onlyIf (default 0).
//
// qxNADAfunc()
//

int NADAfunc(const char *pcMessage, int onlyIf) {
   bool bDoIt = (onlyIf == 0) || ((jdebug & 0x10000) != 0);

   if(bDoIt) {
      if(pcMessage == NULL) {
         // fprintf(stdout, "NADAfunc()\n");
         qprintf(fp_dbg, "NADAfunc()\n");
      } else {
         // fprintf(stdout, "NADAfunc(): %s\n", pcMessage);
         qprintf(fp_dbg, "NADAfunc(): %s\n", pcMessage);
      } // !if(pcMessage == NULL)
   } // if(bDoIt)

   return NADA;
} // int NADAfunc()


//
// new 07-23-23, from qprintf().
//
//
//
//
// qxNADAfnc2()

int NADAfnc2(const char * const pc, ...) {
//#ifdef NOT_HEER
//  fprintf(stdout, "NADAfnc2(): ");
//  va_list args;
//  va_start (args, pc);
//  int j = vfprintf (stdout, pc, args);
//  va_end (args);
//  fprintf(stdout, "\n");
//#endif // NOT_HEER

  if(fp_dbg != NULL) {
     qprintf(fp_dbg, "NADAfnc2(): ");
     va_list arg2;
     va_start (arg2, pc);
     int k = vfprintf (fp_dbg, pc, arg2);
     va_end (arg2);
     qprintf(fp_dbg, "\n");
  } // if(fp_dbg != NULL)

  return NADA;
} // int NADAfnc2(const char * const pc, ...)





// qxjassert()

void jassert(int iBOOL) {
   if(!iBOOL) {
      // always, before 02-26-05; now just in command-line version.
      // mprintf(fp_std, "\njassert() FAILED!\n"); // 04-12-03
      qprintf(fp_dbg, "jassert() FAILED!\n");
      fclose(fp_dbg);
      if(fp_db2 != NULL) fclose(fp_db2);
   } // if(!iBOOL) 

   return;
} // end of jassert()






//
// new 10-07-98: skip lines w/ '#' or zero substantive len...
//
//
//
//
//
//
//
// qxgetBipMeat()
//

const char *getBipMeat(Bufin *bip) {
   int done = 0;
   const char *cp_buf;

   while(!done) {
      if((cp_buf=bip->get_line()) == NULL) return cp_buf;  // eof
      if(strlen(cp_buf) == 0) continue;
      if(cp_buf[0] == '#') continue;

      if(non_space_count(cp_buf) == 0) continue;
      return cp_buf;
   } // while(!done)

   // we'll never get here:
   return NULL;
} // end of getBipMeat()








/*
 * modification history:
 *    09-24-95 (jdl): trim_sp().
 *    03-27-95 (jdl): rcalcstr(); prototypes.
 *    03-05-95 (jdl): new code.
 *
 * purpose:
 *    Provide string manipulation utilities...
 *
 *
 *
 *
 */



//
// from and to are counted from zero: "123456", 1, 2 -> "23"
//
//
//
//
//
//
//
// qxjsubstr()
//

int jsubstr(const char *str_in, int from, int to, char *str_out) {
   char *cp, *cp2, *cp3;
   const char *cpc, *cpc2;

   cp = cp2 = NULL;

   /* in case fail, return empty string: */
   *str_out = '\0';

   /* zero-length out (min poss) comes from to = from-1: */
   if(to < (from-1)) return NADA;

   /* make sure we're asking for a string withing the original: */
   for(cpc=str_in, cpc2=str_in+from; cpc<=cpc2; cpc++) {
      if((*cpc) == '\0') return NADA;
   }

   for(cpc=str_in+from, cpc2=str_in+to, cp3=str_out; cpc<=cpc2; cpc++, cp3++) {
      if((*cp3 = *cpc) == '\0') break;
   }
   *cp3 = '\0';


   return (int)(cp3 - str_out);

} /* end of jsubstr() */


/*
 * In:    val    divisor
 * 123    123    1
 * 123.4  1234   10
 *
 *
 */

#define TEN 10L

// qxjcalcstr()

int jcalcstr(char *str_in, long *val_out, long *div_out) {
   char c;
   int done = 0;
   long val = 0;
   long divisor = 0;
   long neggie = 1;
   char *cp = str_in;

   while(!done) {
      c = *(cp++);
      switch(c) {
         case '\0':
            done = 1;   /* no chance for reprieve, as with spaces, etc... */
            break;
         case '+':
            neggie =  1;
            break;
         case '-':
            neggie = -1;
            break;
         case '.':
            if(divisor) {
               /* error: more than one decimal... */
               done = 1;
            } else {
               divisor = 1;
            }
            break;
         case '0': case '1': case '2': case '3': case '4':
         case '5': case '6': case '7': case '8': case '9':
            val = TEN * val + (c - '0');
            if(divisor) divisor *= TEN;
            break;
         default:
            if(val) {
               done = 1;
            } else {
               /* scan past leading blanks, etc... */
            }
            break;
      }
   }
   if(!divisor) divisor = 1;
   val *= neggie;

   *val_out = val;
   *div_out = divisor;

   return 0;

} /* end of jcalcstr() */




// qxrcalcstr()

double rcalcstr(char *str_in) {
   char c;
   int done = 0;
   long val = 0;
   long divisor = 0;
   long neggie = 1;
   char *cp = str_in;

   while(!done) {
      c = *(cp++);
      switch(c) {
         case '\0':
            done = 1;   /* no chance for reprieve, as with spaces, etc... */
            break;
         case '+':
            neggie =  1;
            break;
         case '-':
            neggie = -1;
            break;
         case '.':
            if(divisor) {
               /* error: more than one decimal... */
               done = 1;
            } else {
               divisor = 1;
            }
            break;
         case '0': case '1': case '2': case '3': case '4':
         case '5': case '6': case '7': case '8': case '9':
            val = TEN * val + (c - '0');
            if(divisor) divisor *= TEN;
            break;
         default:
            if(val) {
               done = 1;
            } else {
               /* scan past leading blanks, etc... */
            }
            break;
      }
   }
   if(!divisor) divisor = 1;
   val *= neggie;

   return (double)val/(double)divisor;

} /* end of rcalcstr() */






//
// new 09-28-98, from trim_sp()
//
//
// in the following, str_out MAY be the same as str_in... 
//
//
//
// qxreplace_char()
//

int replace_char(const char *str_in, char *str_out, int char0, int char1) {
   char c;
   unsigned char *ustr_in  = (unsigned char *)str_in;
   unsigned char *ustr_out = (unsigned char *)str_out;
   const unsigned char *ucp1;
   unsigned char *ucp2;

   for(ucp1=ustr_in, ucp2=ustr_out;; ) {
      if((c=*(ucp1++)) == '\0') {
         *(ucp2++) = (unsigned char)c;
         break;                         // all done!
      } // if((c=*(ucp1++)) == 0)

      if((int)c == char0) c = (char)char1;

      if(c < 0) {
         // swallow it!
      } else {
         *(ucp2++) = (unsigned char)c;
      }
   } // for(cp1=str_in, cp2=str_out;; )

   return 0;
} /* end of replace_char() */










//
// new 09-29-98, from replace_char()
//
//
// in the following, str_out MAY be the same as str_in... 
//
//
//
// qxreplace_string()
//

int replace_string(const char *str_in, char *str_out, const char *cold, const char *cnew, int ntries) {
   int i;
   char *cp;

   // sanity checks:
   if(str_in  == NULL) return NADA;
   if(str_out == NULL) return NADA;
   if(cold    == NULL) return NADA;
   if(cnew    == NULL) return NADA;

   strcpy(str_out, str_in);

   // if ntries <= 0, we look forever...
   for(i=ntries; ; ) {
      cp = strstr(str_out, cold);
      if(cp == NULL) break;               // no point in going on

      char *cdum0 = new char[strlen(str_out)+8];
      char *cdum1 = new char[strlen(str_out)+8];

      jsubstr(str_out, 0, (int)(cp-str_out-1), cdum0);
      strcpy(cdum1, cp+strlen(cold));

      sprintf(str_out, "%s%s%s", cdum0, cnew, cdum1);

      delete [] cdum0;
      delete [] cdum1;

      if(i > 0) {
         i--;
         if(i == 0) break;
      } // if(i > 0)
   } // for(i=ntries; ; )

   return 0;
} /* end of replace_string() */






//
//
//
//
// qxnon_space_count()
//

int non_space_count(const char *str_in) {
   int c=0;
   int non_sp=0;
   const char *cp;


   for(cp=str_in; (c=*cp)!='\0'; cp++) {
      if(c != ' ') non_sp++;
   }

   return non_sp;
} // end of non_space_count()





// qxstr_to_long()

long str_to_long(const char *str_in, int col1, int col2) {
   char cbuf[128];

   jsubstr(str_in, col1-1, col2-1, cbuf);
   return atol(cbuf);
} /* end of str_to_long() */





//
//
//
//
// 01-30-01: added err_return
//
// qxstr_to_doub()
//

double str_to_doub(const char *str_in, int col1, int col2, double err_return) {
   char cbuf[128];

   // new sanity checks 01-30-01
   if(strlen(str_in) < (size_t)col2) return err_return;
   if(col2 < col1)           return err_return;
   if(col1 < 1)              return err_return;

   jsubstr(str_in, col1-1, col2-1, cbuf);
   return atof(cbuf);
} /* end of str_to_doub() */





//
//
// NOTE!!  We do NOT expect to find "0x" in the string; we add it
// ourselves!!!
//
//
//
//
// qxstr_to_hexi()
//

unsigned int str_to_hexi(const char *str_in, int col1, int col2) {    // for some reason, UINT not found though jgraph.cpp does...
   char cbuf[128];

   sprintf(cbuf, "%s", "0x");   // clue to jatof()...

   jsubstr(str_in, col1-1, col2-1, cbuf+2);
   trim_sp(cbuf+2, cbuf+2);

   double dHex = jatof(cbuf);   // 0x guaranteed...

   int    iSgn = SGN(dHex);
   JASSERT(iSgn >= 0);                // 11-20-01
   double dAbs = ABS(dHex);

   // UINT lRound = (UINT)(dAbs + 0.5);
   unsigned int lRound = (unsigned int)(dAbs + 0.5);
   // lRound      *= iSgn;           // out 11-20-01

   return lRound;
} // end of str_to_hexi()...




// qxiHash()

unsigned int iHash(const std::vector<unsigned char> &vcData) {
   size_t slen = vcData.size();
   if(slen == 0)
      return 0;

   CSHA1 aCsp;
   const unsigned char *pcData = ADDR_ZEROTH(vcData);

   if(slen < BIG_U32) {
      aCsp.Update(pcData, (unsigned int)slen);
   } else {
      // Bite me!  Need to cut into pieces.  And shit, they can't be 2^32 in length; that won't fit in a UINT.
      unsigned long long maxUI = BIG_U32 - 1;
      unsigned long long nFull = slen / maxUI;
      unsigned long long nLast = slen % maxUI;

      for(unsigned long long ll=0; ll<nFull; ll++) {
         const unsigned char *pcStart = pcData + ll * maxUI;
         aCsp.Update(pcStart, (unsigned int)maxUI);
      } // for(unsigned long long ll=0; ll<nFull; ll++)

      if(nLast > 0) {
         const unsigned char *pcStart = pcData + nFull * maxUI;
         aCsp.Update(pcStart, (unsigned int)nLast);
      } // if(nLast > 0)
   } // !if(slen < BIG_U32)

   aCsp.Final();

   unsigned int iArr[5];
   unsigned char *pucHash = (unsigned char *)iArr;
   aCsp.GetHash(pucHash);

   return iArr[0];
} // unsigned int iHash(const std::vector<unsigned char> &vcData)






//
//
//
//
// qxscrambledHash()

const unsigned char *scrambledHash(const unsigned char *cNamePlusFive, int lenPlusFive) {
   int i;
   static unsigned char cRet[4];

   CSHA1 *csp = new CSHA1;
   csp->Update(cNamePlusFive, lenPlusFive);
   csp->Final();
   // char cReport[256];
   // csp->ReportHash(cReport, CSHA1::REPORT_HEX);
   // fprintf(stdout, "hash \"%s\".\n", cReport);
   unsigned char cUs[64];
   cUs[0] = 'x';
   cUs[1] = 'y';
   cUs[2] = 'z';
   csp->GetHash(cUs+3);                    // reserve first 3 chars for hard-coded prefix

   // rather than just compare to raw hash output, scramble it:
   csp->Reset();
   csp->Update(cUs, 15);   // some arbitrary number of chars, first three being prefix.
   csp->Final();
   csp->GetHash(cUs);

   // copy
   for(i=0; i<4; i++) {
      cRet[i] = cUs[i+4];   // some arbitrary offset...
   } // for(i=0; i<4; i++)

   // clean up:
   delete csp; csp=NULL;  // out w/ function thing.

   return (const unsigned char *)cRet;
} // end of scrambledHash()...






//
// new 07-25-04 (jdl)
//
//
//
// return:
//    0: BAD, no match.
//    1: GOOD, key is valid.
//
//
// qxisCombinedKeyValid()
//

int isCombinedKeyValid(const unsigned char *cBoth, int lenBoth) {
   int i;

   // sanity check:
   char *cIn = (char *)cBoth;
   int iLen0 = (int)strlen(cIn);
   JASSERT(iLen0 == (lenBoth - 9));   // null terminator plus 8 bytes
   JASSERT(lenBoth == (iLen0 + 9));   // null terminator plus 8 bytes

   // ok, we want to do the secure hash on iLen0 + 5 chars, then manipulate that & compare the result to the
   // last 4 bytes input.

   // const unsigned char *scrambledHash(const unsigned char *cGuyPlusFive, int lenPlusFive)
   const unsigned char *cHash = scrambledHash(cBoth, (iLen0 + 5));

   // compare
   int iBad = 0;
   for(i=0; i<4; i++) {
      if(cHash[i] != cBoth[i+iLen0+5]) iBad++;
   } // for(i=0; i<4; i++)

   int iGood = !iBad;   // or (iBad == 0);

   // clean up:
   // delete csp; csp=NULL;  // out w/ function thing.

   return iGood;
} // end of isCombinedKeyValid()...



#define LIC_FILE  (".\\ppLicense.txt")   // qxtemp?
#define LIC_FIL2  ("abcdefgh.bin")       // name will be scrambled later...
#define DIR_ONE   ("C:\\")
#define DIR_ONX   ("D:\\")
#define DIR_TWO   ("C:\\windows\\system32")
#define DIR_TWX   ("D:\\windows\\system32")
const int maxLen = 256;                  // moved from below 07-20-06 (jdl)





//
// the following is convenient in that the calling side need
// not watch argc; it can pretend that the argv backbone is
// "null-terminated."
//
// as set up, if *which_arg begins at 0, we first return
// argv[1].
//
//
// qxarg_extract()
//

const char *arg_extract(int argc, const char **argv, int *which_arg) {
   if(++(*which_arg) >= argc) return NULL;
   return (argv[(*which_arg)]);
} // end of arg_extract()








// qxdbgOpen()
int dbgOpen(int iAppend) {
   dbgClose();

   if(iAppend) {
      fp_dbg = fopen("blah.dbg", "a");
   } else {
      fp_dbg = fopen("blah.dbg", "w");
   }

   return 0;
} // end of dbgOpen()








// qxdbgClose()
int dbgClose() {

   if(fp_dbg == NULL) return 0;
   if(fp_dbg == stdout) return 0;

   fclose(fp_dbg);
   fp_dbg = NULL;

   return 0;
} // end of dbgClose()








// qxstrjncpy()

char *strjncpy(char *dest, const char *src, size_t count) {
   /* we don't like strncpy(): it copies EXACTLY count at all times,
    * never adding a '\0' if src is longer than count.
    *
    * Confusion and madness: if count == 10, do we copy 9 plus '\0',
    * or 10 plus '\0'?  For consistency w/ strncpy(), 9 plus '\0'.
    */
   if(count < 1) return NULL;
   strncpy(dest, src, count-1);
   dest[count-1] = '\0';      /* really the count'th entry... */
   return dest;
} // end of strjncpy()












//
// this routine cuts a name like "abc.dat" into "abc", ".", and "dat".
//
//
// Examples:
//
//    Input      Output------------------------->
//    file_in    body_out     dot_out     ext_out
//    -------    --------     -------     -------
//    abc.dat       abc           .         dat
//      blah       blah          ""          ""     ("" means empty)
//     blah.       blah           .          ""
//     a.b.c       a.b            .           c
//    c:/b.a      c:/b            .           a
//    /a.b/c      /a.b/c         ""          ""
//
//
// qxbreak_file_name()
//

int break_file_name(const char *file_in, char *body_out, char *dot_out, char *ext_out) {
   int i, c;
   int found_something;

   sprintf(body_out, "%s", file_in);
   *ext_out = '\0';  // default...
   *dot_out = '\0';  // default...

   found_something = 0;
   for(i = (int)strlen(body_out); i>=0; i--) {
      switch(c = body_out[i]) {
         case '.':
            found_something = 1;
            body_out[i] = '\0';
            sprintf(ext_out, "%s", body_out+i+1);
            sprintf(dot_out, "%c", c);
            break;
         case '\\':
         case '/':
            // didn't find any dots in the file name...
            found_something = 1;    // stay w/ default values...
            break;
         default:
            break;
      }
      if(found_something) break;
   }
   if(!found_something) {
      // stay w/ defaults...
   }

   return 0;
} // end of break_file_name()...




//
//
//
// qxbreak_file_name()

int break_file_name(const char *file_in, std::string &ssBodOut, std::string &ssDotOut, std::string &ssExtOut) {
   SMARTA(pcBod, char, (strlen(file_in) + 32), smaBod);
   SMARTA(pcDot, char, (strlen(file_in) + 32), smaDot);
   SMARTA(pcExt, char, (strlen(file_in) + 32), smaExt);

   int iRet = break_file_name(file_in, pcBod, pcDot, pcExt);
   ssBodOut = pcBod;
   ssDotOut = pcDot;
   ssExtOut = pcExt;

   return iRet;
} // int break_file_name(const char *name_in, std::string &ssDirOut, std::string ssFilOut)







//
// This routine outputs dir_out WITHOUT TRAILING SLASH.  When the
// directory is root, it returns "/.", etc.  So: always recombine
// w/ a slash...
//
// 11-19-97: allow dir_out passed in to be NULL
// 06-02-26: if name_in ends with a slash, skip that.
//
//
// qxcut_file_name()

int cut_file_name(const char *name_in, char *dir_out, char *file_out) {
   int c;
   const char *cp, *cp1;
   char *cp2;
   char *doh = dir_out;
   const char *name_loc = name_in;
// static char dir_loc[MAX_CBUF];
// #define SMARTA(name, closs, nElem, nam2)   closs *name = new closs[nElem]; SmartParr<closs> nam2(name);
   int lenNeed = (int)strlen(name_in) + 32;
   SMARTA(dir_loc, char, lenNeed, smaCp);  // qxl: don't allocate unless we need.

   // 11-19-97: allow dir_out passed in to be NULL:
   if(dir_out == NULL) {
      dir_out = doh = dir_loc;   // just throw the output bytes away..
   } // if(dir_out == NULL)

   /* Uhhh... if we find no slash, dir_out is dot (".").  If we
    * DO find a slash, IT IS LEFT OUT of the dir_out, so that things
    * won't barf trying to open a directory with a final slash.  So,
    * when re-forming a full name, use dir_out, slash, file_out.
    *
    * Oh shit: what about root directory???  For now, "/." (or "\.")
    *
    * Oh WORSE shit: DOS drive specifiers!  Oh FLOG ME.
    *
    *    C:filename.dos  : the drive name is "C:.", and we reconstruct "C:.\filename.dos"  qxl: CHECK THIS!!!
    *    C:\filename.dos : the drive name is "C:\.", and we reconstruct "C:\.\filename.dos" CHECK THIS TOO!!!
    *
    * Silly me: this is easy: pass out drive_out also: empty string for Unix, "A:", etc, for DOS.
    * Well, recombine it internally into dir_out...
    * Aha: even easier: increment the passed pointers!!
    *
    */

   if(jdebug & 0x0001) qprintf(fp_dbg, "cut_file_name(): name_in=\"%s\".\n", name_in);

   if((strlen(name_in) >= 2) && (name_in[1] == ':')) {
      /* drive has been specified; copy and skip past... */
      *(dir_out++) = *(name_in++);
      *(dir_out++) = *(name_in++);
   } else {
      /* no drive specification; start at beginning: */
   }

   // 06-02-26: if final char is slash, skip over it:
   cp = name_in + strlen(name_in);;
   if((strlen(name_in) > 0) && (is_slash(*(cp-1))))
      cp--;

   // qprintf(stdout, "strlen(cp) = %d.\n", strlen(cp));   // qxtemp!

   for(;;) {
   // for(cp = name_in + strlen(name_in);;) {
      /* if we kept going, we'd be in somebody else's space! */
      if(cp <= name_in) break;

      if(is_slash(c = *(--cp)) ) {             // 04-15-98
         // qprintf(stdout, "%c is Slash.\n", c);
         /* that's what we're looking for! */
         if(cp == name_in) {
            /* root directory: would come out as nothing, because it's all "trailing slash" */
            sprintf(dir_out, "%c%s", c, ".");    /*  "/."  or  "\."   */
         } else {
            for(cp1 = name_in, cp2 = dir_out; cp1 != cp;) {  /* don't copy final slash */
               *(cp2++) = *(cp1++);
            }
            *(cp2++) = '\0';
         }
         sprintf(file_out, "%s", cp+1);   /* no slash in file_out either... */
         if(jdebug & 0x0001) qprintf(fp_dbg, "now name_in=\"%s\"; dir_out=\"%s\"; file_out=\"%s\".\n", name_loc, doh, file_out);
         return 1;
      } else {
         // qprintf(stdout, "%c is not slash.\n", c);
      }
   }

   // qprintf(stdout, "No slashes found.\n");

   /* no slashes found */
   sprintf(dir_out, "%s", ".");
   sprintf(file_out, "%s", name_in);

   // qprintf(stdout, "Leaving cut_file_name().\n");
   // qprintf(stdout, "now name_in=\"%s\"; dir_out=\"%s\"; file_out=\"%s\".\n", name_loc, doh, file_out);

   if(jdebug & 0x0001) qprintf(fp_dbg, "now name_in=\"%s\"; dir_out=\"%s\"; file_out=\"%s\".\n", name_loc, doh, file_out);
   return 1;

} /* end of cut_file_name() */



//
// This routine outputs dir_out WITHOUT TRAILING SLASH.  When the
// directory is root, it returns "/.", etc.  So: always recombine
// w/ a slash...
//
//
//
// qxcut_file_name()

int cut_file_name(const char *name_in, std::string &ssDirOut, std::string &ssFilOut) {
   SMARTA(pcDir, char, (strlen(name_in) + 32), smaDir);
   SMARTA(pcFil, char, (strlen(name_in) + 32), smaFil);

   int iRet = cut_file_name(name_in, pcDir, pcFil);
   ssDirOut = pcDir;
   ssFilOut = pcFil;

   return iRet;
} // int cut_file_name(const char *name_in, std::string &ssDirOut, std::string ssFilOut)



//
// this routine expects cdir WITHOUT TRAILING SLASH; we add a slash
// here!
//
//
//
//
// 04-29-98: that lousy ROOT problem...
//
//
// qxmake_full_name()
//

int make_full_name(const char *cdir, const char *cfil, char *cful) {
   int c;

   if(jdebug & 0x0001) qprintf(fp_dbg, "This is make_full_name() w/ cdir= |%s|, cfil = |%s|.\n", cdir, cfil);

   if((cdir == NULL) || (*cdir == '\0')) {
      sprintf(cful, "%s", cfil);
   } else {
      // 04-29-98: look for trailing slash:
      if(((c=cdir[strlen(cdir)-1]) == SLASH) || (c == OTHER_SLASH)) {
         // special case:
         sprintf(cful, "%s%s", cdir, cfil);
      } else {
         // usual situation:
         sprintf(cful, "%s%c%s", cdir, SLASH, cfil);
      }
   }

   if(jdebug & 0x0001) qprintf(fp_dbg, "Leaving make_full_name() w/ cful= |%s|.\n", cful);


   return 0;
} /* end of make_full_name() */









//
// from recurse.c 08-06-99; INT_32 -> int
//
//
//
//
//
//
//
// qxcase_change()
//

int case_change(char *cp_in_out, int which_case) {
   int c;
   unsigned char *ucp = (unsigned char *)cp_in_out;
   unsigned char *ucp2;
   static unsigned char smc[] = {
      0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
      0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
      0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
      0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
      0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
      0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
      0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
      0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
      0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
      0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
      0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
      0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
      0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
      0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
   };



   /* qxl: movement in the upper half of ASCII? */
   static unsigned char upc[] = {
      0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
      0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
      0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
      0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
      0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
      0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
      0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
      0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
      0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
      0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
      0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
      0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
      0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
      0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
   };



   /* qxl: movement in the upper half of ASCII? */
   static unsigned char lwc[] = {
      0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
      0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
      0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
      0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
      0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
      0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
      0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
      0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
      0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
      0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
      0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
      0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
      0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
      0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
   };

   switch(which_case) {
      case CASE_SAME: // 0:
         ucp2 = smc;
         break;
      case CASE_UPPER: // 1:
         ucp2 = upc;
         break;
      case CASE_LOWER: // 2:
         ucp2 = lwc;
         break;
      default:
         // mprintf(fp_std, "Invalid option in case_change(): %ld\n", which_case);
         qprintf(fp_dbg, "Invalid option in case_change(): %ld\n", which_case);
         return(0);
         break;
   } /* switch(which_case) */

   while(1) {
      c = *ucp;
      if(c == 0) break;
      *(ucp++) = ucp2[c];
   }
   return 1;

} /* end of case_change() */






// qxis_a_num()

int    is_a_num(const char *cdata) { 
   // we check only for initial digit or '.'
//jmk2/1/04
//   int i, j, k;
//jmk2/1/04
   int c;

   if(cdata == NULL) return 0;
   if(((c = cdata[0]) >= '0') && (c <= '9')) return 1;
   if(c == '.') return 1;
   if(c == '-') return is_a_num(cdata + 1);
   if(c == 'm') return is_a_num(cdata + 1);    // 02-10-10: for midi input; see jatof().
   return 0;

} // end of is_a_num()





// qxis_a_flag()

int    is_a_flag(const char *cdata) {
//jmk2/1/04
//   int i, j, k;
//jmk2/1/04
   int c;

   if(cdata == NULL) return 0;
   if((c = cdata[0]) != '-') return 0;
   if(is_a_num(cdata)) return 0;
   return 1;

} // end of is_a_flag()




//
// new 09-17-99
//
// Be CAREFUL!  Don't call this twice in one statement!
//
//
//
//
// qxmsecToMinSec()

const char *msecToMinSec(int msecIn, char *cbufIn) {
   static char cbufLoc[64];
   char *cpUse = cbufLoc;
   if(cbufIn != NULL) cpUse = cbufIn;

   int iSec = msecIn / (int)MSEC_PER_SEC;
   int iFrMsec = msecIn - iSec*(int)MSEC_PER_SEC;
   int iMin = iSec / 60;
   int iFrSec = iSec - iMin*60;

   sprintf(cpUse, "%d:%02d.%03d", iMin, iFrSec, iFrMsec);

   return cpUse;
} // end of msecToMinSec()...





#ifdef NOT_IN_PB

//
// new 12-17-00
//
// Be CAREFUL!  Don't call this twice in one statement!
//
// 60 -> 5:0
//
//
//
// qxnoteToColon()

const char *noteToColon(int noteIn, char *cbufIn) {
   static char cbufLoc[64];
   char *cpUse = cbufLoc;
   if(cbufIn != NULL) cpUse = cbufIn;


   int iHii = noteIn / TWELVT;
   int iLow = noteIn % TWELVT;
   cpUse[0] = MevBase::hexChar(iHii);
   cpUse[1] = CHAR_COLON;
   cpUse[2] = MevBase::hexChar(iLow);
   cpUse[3] = '\0';

   return cpUse;
} // end of noteToColon()...

#endif // NOT_IN_PB




//
// new 06-14-00
//
//
//
//
//
//
// qxnToRight()
//

int nToRight(double rawVal, int totWidth) {
   int i;
   int iRight;
   char cloc[512];

   // what's the minimum field width rawVal can achieve?
   // sprintf(cloc, "%*.0f", totWidth, rawVal);
   sprintf(cloc, "%.0f", rawVal);
   if((i= (int)strlen(cloc)) > totWidth) return NADA;  // impossible!

   // adding a decimal sucks one out, so:
   iRight = totWidth - i - 1;

   // but there's an awkward spot:
   if(iRight < 0) iRight = 0;

   return iRight;
} // end of nToRight()...





//
// new 10-09-00, from jq.c
//
//
//
//
// qxcompress_78()
//
int compress_78(const unsigned char *data_in, int n_in, unsigned char *data_out) {
   int j;
   int n_out = 0;
   int i_cycle, n_cycle, last_cycle;
   int bit_well, one_bit, j_this;

   // if 8 bytes in, have 1 cycle; if 9, 2 cycles (last has 0 bytes out)
   n_cycle = 1 + ((n_in - 1) / 8);
   last_cycle = n_in - ((n_cycle - 1) * 8) - 1;   /* range 0 to 7 */

   for(i_cycle = 1; i_cycle <= n_cycle; i_cycle++) {
      bit_well = *(data_in++);
      j_this = 7;
      if(i_cycle == n_cycle) j_this = last_cycle;
      for(j = 0; j < j_this; j++) {
         one_bit = bit_well & 1;
         bit_well >>= 1;
         *(data_out++) = (128 * one_bit) + *(data_in++);
         n_out++;
      }
   } // for(i_cycle = 1; i_cycle <= n_cycle; i_cycle++)

   return n_out;
} // end of compress_78()...





//
// new 10-09-00, from jq.c
//
//
//
// qxexpand_87()
//

int expand_87(const unsigned char *data_in, int n_in, unsigned char *data_out) {
   int j;
   int n_out = 0;
   int i_cycle, n_cycle, last_cycle;
   int bit_well, one_bit, j_this;
   int this_byte;
   const unsigned char *hold_in;

   // if 7 bytes in, have 1 cycle, if 8, 2 cycles...
   n_cycle = 1 + ((n_in - 1) / 7);
   last_cycle = n_in - ((n_cycle - 1) * 7);   // range 1 to 7

   for(i_cycle = 1; i_cycle <= n_cycle; i_cycle++) {
      j_this = 7;
      if(i_cycle == n_cycle) j_this = last_cycle;

      // assemble bit_well; then it must go out FIRST...
      bit_well = 0;
      hold_in = data_in;
      for(j = 0; j < j_this; j++) {
         this_byte = *(data_in++);
         one_bit = (this_byte & 0x80) >> 7;
         bit_well += (1 << j) * one_bit;
      }
      data_in = hold_in;

      // now actually put the data out:
      *(data_out++) = bit_well;
      n_out++;
      for(j = 0; j < j_this; j++) {
         this_byte = *(data_in++) & 0x7F;
         *(data_out++) = this_byte;
         n_out++;
      }

   } // for(i_cycle = 1; i_cycle <= n_cycle; i_cycle++)


   return n_out;
} // end of expand_87()...





//
// new 10-11-00, from jca.cpp.
//
//
// qxis_a_hex_num()
//

int is_a_hex_num(const char *cdata) {
   int c;

   if(cdata == NULL) return 0;
   if(strlen(cdata) < 2) return 0;
   if((c = cdata[0]) != '0') return 0;
   if(((c = cdata[1]) == 'x') || (c == 'X')) return 1;

   return 0;
} // end of is_a_hex_num()








//
// new 10-11-00, from jca.cpp
//
//
// Input:
//    cdata: number expressed in string form
//
// Output:
//    return value: number as double
//
//
// we handle:
//    normal real values expressed as strings, ex "123.456" (we call atof() for these...)
//    values which begin with "0x" are interpreted as hexidecimal:
//       0x10           becomes 16.0
//       0x10.1         becomes 16.0625
//       0x-10.1        becomes -16.0625
//
//
//
// 08-25-03: look for '/' and perform divide if found.
// 10-04-08 (jdl): handle letter 'O' as zero.
// 02-10-10 (jdl): handle midi number ("m69.0").
//
// qxjatof()
//

double jatof(const char *cdata) {
   int c, byte_valid(0), this_val(0);

   int have_dot = 0;
   double dret   = 0.0;
   double dfract = 1.0;
   double dsign  = 1.0;          // may become -1.0
   const char *cpc = cdata + 2;  // skip past "0x"...

   // 02-10-10 (jdl): midi input:
   if(cdata[0] == 'm') {
      double dMidi = jatof(cdata+1);
      // double dFreq = 440.0 * pow(2.0, ((dMidi - 69.0)/12.0));
      double dFreq = MID_TO_CPS(dMidi);
      return dFreq;
   } // if(cdata[0] == 'm')


   // 08-25-03: look for '/' and if found, perform divide:
   if(strchr(cdata, '/') != NULL) {
      // make copy & work on it:
      char * cplay = new char[strlen(cdata) + 8];
      sprintf(cplay, "%s", cdata);
      char *cp = strchr(cplay, '/');
      *cp = '\0';
      double dNum = jatof(cplay);
      double dDen = jatof(cp+1);
      double dVal = dNum / dDen;

      // clean up:
      delete [] cplay; cplay=NULL;

      return dVal;
   } // if(strchr(cdata, '.') != NULL)

   if(!is_a_hex_num(cdata)) {
      // normal processing...
      return atof(cdata);
   } // if(!is_a_hex_num(cdata))

   // aha: it IS a hex number!
   // check for negative number:
   if((c=*(cdata+2)) == '-') {
      dsign = -1.0;
      cpc   = cdata + 3;         // skip extra byte...
   } else {
      // normal:
      dsign = +1.0;
      cpc   = cdata + 2;
   }

   for(; ((c=*cpc)!='\0'); cpc++) {
      switch(c) {
         case '.':
            have_dot = 1;
            continue;       // skip validity check...
            break;
         case '0': case '1': case '2': case '3':
         case '4': case '5': case '6': case '7':
         case '8': case '9':
            byte_valid = 1;
            this_val = c - '0';
            break;
         case 'A': case 'B': case 'C': case 'D':
         case 'E': case 'F':
            byte_valid = 1;
            this_val = c - 'A' + 10;
            break;
         case 'a': case 'b': case 'c': case 'd':
         case 'e': case 'f':
            byte_valid = 1;
            this_val = c - 'a' + 10;
            break;
         case 'O': case 'o':                                // 10-04-08 (jdl): support letter 'O' instead of zero
            byte_valid = 1;
            this_val = 0;
            break;
         default:
            byte_valid = 0;
      } // switch(c)

      if(!byte_valid) break;

      if(have_dot) {
         // we're past a '.' decimal place!
         dfract /= 16.0;
         dret   += dfract * (double)this_val;
      } else {
         // normal: building integer number:
         dret = 16.0 * dret + (double)this_val;
      }

   } // for(; ((c=*cpc)!='\0'); cpc++)

   if(!byte_valid) return 0.0;

   return dret*dsign;
} // end of jatof()




//
// new 10-19-16.  Rounds a double to an int, dealing with uncertainty when double is negative.
//
// See also #define INTIFY .  Does the same thing, quicker.
//
//
//
//
// qxintify()

int intify(double dVal) {
   int iRet = SGN(dVal) * (int)(0.5 + ABS(dVal));

   return iRet;
} // int intify(double dVal)





//
// new 08-18-16; made for handling 0x input.  A bit clumsy, running thru jatof(), but get over it.
//
//
//
//
//
// qxjatoi()
//

int jatoi(const char *cdata) {
   double dVal = jatof(cdata);
   int    iVal = intify(dVal);

   return iVal;
} // int jatoi(const char *cdata)






#ifdef IS_RT  // not IS_BG

//
// new 06-15-00, for rte sequence playing.
//
//
//
//
//
// qxisKeyPending()

int isKeyPending(void) {
   int i = _kbhit();

   return i;
} // end of isKeyPending()...




//
// new 06-15-00, for rte sequence playing.
//
//
//
//
//
//
// qxsuckKeysPending()
//

int suckKeysPending(void) {
   int i(0);
   int ict = 0;

   while(isKeyPending()) {
      i = _getch();
      ict++;
   } // while(isKeyPending())

   // return ict;
   if(ict) {
      return i;
   } else {
      return 0;
   }
} // end of suckKeysPending()...





//
// new 06-15-00, for rte sequence playing.
//
//
//
// 06-16-00: sleep a bit while waiting
//
// qxwaitForKey()
//

int waitForKey(void) {

   // idle till key pressed:
   while(!isKeyPending()) {
      Sleep(100);             // take some load off?
   }

   int i = suckKeysPending();

   return i;
} // end of waitForKey()...


#endif // IS_RT  // not IS_BG




//
// new 11-21-00
//
// See $nv/it.  We fill in for the formula:
//
//    y = k1 + k2 * exp(k3 * x)
//
// where x0 == 0, x1 == 1, x2 == 2, etc.
//
//    k1 is the asymptotic value at x = infinity.
//    k1 + k2 is the initial value at x = 0.
//
//
// return value:
//    0: valid; k3 < 0.0; k1, k2, k3 filled in.
//   -1: invalid; k3 > 0.0; k3 filled in; k1, k2 not.
//
//
//
//
// qxcalcDecay()
//

int calcDecay(double y0, double y1, double y2, double &k1, double &k2, double &k3) {
   double tolly = .00000000001;  // qxl: what tol??

   k1 = k2 = k3 = 0.0;  // error return;

   double d0 = y1 - y0;
   double d1 = y2 - y1;

   if(ABS(d0) < tolly) return NADA;  // qxl: what tol??

   double k4 = d1 / d0;

   if(k4 < tolly) return NADA;   // qxl: some other tol??

   k3 = log(k4);  // see $nv/it, 11-21-00
   if(k3 >= 0.0) return NADA;  // i.e., k4 >= 1.0: divergence!

   k2 = (d0 / (k4 - 1.0));
   k1 = y0 - k2;

   return 0;
} // end of calcDecay()






// new 03-18-01
//
//
// qxshowArgs()
//

int showArgs(int argc, const char **argv, FILE *fp_in) {
   int i;

   // sanity check:
   if(argc <= 0) return NADA;
   if(fp_in == NULL) return NADA;

   for(i=0; i<argc; i++) {
      if(i > 0) fprintf(fp_in, " ");
      fprintf(fp_in, "%s", argv[i]);
   } // for(i=0; i<argc; i++)

   // final line-feed:
   fprintf(fp_in, "\n");

   return 0;
} // end of showArgs()...







//
// new 11-03-01
//
// returns the closest JI ratio to centsIn, within limit passed in.
// odd/prime confusion: uhhh...  but we just use the number passed, ok?
//
// return value: error in cents.
//
//
// qxcentsToJI()
//

double centsToJI(double centsIn, int limit, int &numOut, int &denOut) {
   int inum, iden;
   double factErr = 17000.0;   // we'll keep it >= 1.0...
   double tol     = .000001;   // to definitely favor small guys...

   int doSillyWrites = (limit > 300); // used only for play tests.

   // quicker to compare after just one costly power:
   double factIn = pow(2.0, centsIn/CENT_PER_OCT);    // may be > 1.0 or < 1.0...
   if(doSillyWrites) qprintf(fp_dbg, "centsToJI(): %8.4f (%12.8f) is the goal.\n", centsIn, factIn);

   for(inum=1; inum<=limit; inum++) {
      for(iden=1; iden<=limit; iden++) {
         double factTest = (double)inum/(double)iden;
         double errRat;              // make >= 1.0:
         if(factIn > factTest) {
            errRat =  factIn / factTest;
         } else {
            errRat =  factTest / factIn;
         }
         if((errRat + tol) < factErr) {
            numOut = inum;
            denOut = iden;
            factErr = errRat;
            if(doSillyWrites) {
               // well, silly cutoff, but means we're playing w/ numbers
               qprintf(fp_dbg, "centsToJI(): %8.4f is approximately %d / %d (%12.8f)\n", centsIn, inum, iden, factTest);
            } // if(doSillyWrites)
         } // if((errRat + tol) < factErr)
      } // for(iden=1; iden<=limit; iden++)
   } // for(inum=1; inum<=limit; inum++)

   double centsGot = CENT_PER_OCT * log((double)numOut/(double)denOut) / log(2.0);
   double centsErr = centsGot - centsIn;


   return centsErr;
} // end of centsToJI()...





//
// from raw_time.c 12-31-01
//
// in this version, caller must pass buffer to receive string.
//
//
//
// qxformat_one()
//

int format_one(time_t time_val, char *cbufOut) {
   char *cp;
   struct tm *p_tm;

   p_tm = localtime(&time_val);
   cp = asctime(p_tm);
   sprintf(cbufOut, "%s", cp);
   rmlf(cbufOut);

   return 0;
} // end of format_one()...




//
// from raw_time.c
//
//
//
//
// qxrmlf()
//

int rmlf(char *str_in) {
   int  c;
   int  found=0;
   char *cp_in = str_in;
   char *cp_out= str_in;

   do {
      if((c = *(cp_in++)) != '\n') {
         *(cp_out++) = (char)c;
         found++;
      }
   } while(c != '\0');

   return found;
} // end of rmlf()...







//
// new 12-31-01.
//
// This routine formats a file's date in text, in cbufOut passed in.
//
// return value: number of bytes in file; NADA if error.
//
//
//
//
// qxfileDate()
//

int fileDate(const char *fileToRead, char *cbufOut) {
   struct stat     stbuf;

   if(stat(fileToRead, &stbuf) == -1) {
      qprintf(fp_dbg, "%s is NOT found.\n", fileToRead);
      sprintf(cbufOut, "[no file]");
      return NADA;
   }

   format_one(stbuf.st_mtime, cbufOut);

   return (int)stbuf.st_size;
} // end of fileDate()...







//
//
// Input:
//    file_name: name of file we desire info on.
//    bDirectoryOK (default false): if true, return info on a directory of this name; if false, pretend it doesn't exist.
//
//
// Output:
//    return value: 
//      -1: file not found, or is a directory...
//      else: internal file time stamp as long.
//
//
//
// 03-02-14: bDirectoryOK (default false).
//
// qxfile_t()
//

long file_t(const char *file_name, bool bDirectoryOK) {
   // int mode;
   struct stat     stbuf;

   if(stat(file_name, &stbuf) == -1) {
      // mprintf(fp_std, "%s is NOT found.\n", file_name);
      return -1;
   }

   if(bDirectoryOK) {
      // new option 03-02-14: return info on a directory file with this name.
   } else {
      // default: if the file is a directory, say it "doesn't exist":
      if((stbuf.st_mode & S_IFMT) == S_IFDIR) {
         // mprintf(fp_std, "%s is a directory.\n", file_name);
         return -1;
      }
   } // !if(bDirectoryOK)

   return (long)stbuf.st_mtime;

} // end of file_t()








//
// input:
//    file_name
//
// output:
//   exists:
//       0: file does NOT exist.
//       1: file DOES exist.
//   is_dir:
//      -1: file does not exist...
//       0: file is NOT a directory
//       1: file IS a directory
//   is_prot:
//      -1: file not found, or is a directory...
//       0: file is NOT protected.
//       1: file IS protected.
//   return value:
//      -1: file not found...
//      else: internal file time stamp as long integer...
//
//
// 08-28-98: check for NULL file_name...
//
// qxfile_t4()
//

long  file_t4(const char *file_name, short *exists, short *is_dir, short *is_prot) {
   // char c, c_hold;
   int i;
   int mode;
   // int done;
   long ret_val;
   // char *cp, *cp_sp;
   struct stat stbuf;

   // cp = file_name;


   if((file_name == NULL) || ((i = stat(file_name, &stbuf)) != 0)) {
      ret_val  = -1;
      *exists  =  0;
      *is_dir  = -1;
      *is_prot = -1;
   } else {
      ret_val = (long)stbuf.st_mtime;
      *exists  =  1;


      if((stbuf.st_mode & S_IFMT) == S_IFDIR) {
         // mprintf(fp_std, "%s is a directory.\n", file_name);
         *is_dir = 1;
      } else {
         *is_dir = 0;
      }


      mode = stbuf.st_mode;
      // mprintf(fp_std, "mode=%d; compare to %d and %d.\n", mode, _S_IREAD, _S_IWRITE);

      if((mode & ME_WRITE) != ME_WRITE) {
         *is_prot = 1;
      } else {
         *is_prot = 0;
      }
   }    // file exists...

   return ret_val;
} // end of file_t4()...





//
// added 07-24-06 (jdl)
//
//
//
//
//
// qxis_a_dir()

int is_a_dir(const char *file_name) {
   short exists, is_dir, is_prot;
   long i4;

   i4 = file_t4(file_name, &exists, &is_dir, &is_prot);

   if(exists & is_dir) return 1;
   return 0;
} // end of is_a_dir()...






//
//
//
// 12-25-02 (jdl): return time calc'd, not zero!
// 03-09-04 (jdl): don't declare clock() here!
//
// qxdo_proc_time()

int do_proc_time(FILE *fp1, FILE *fp2) {
   int i;

   clock_t my_clock;

   my_clock = clock();

   i = (int)my_clock;
   if(fp1 != NULL) fprintf(fp1, "I'm showing %d processor time.\n", i);
   if(fp2 != NULL) fprintf(fp2, "I'm showing %d processor time.\n", i);

   return i;
} // end of do_proc_time()




//
// new 05-05-03; see $nv/it.
//
//
//
//
//
// qxintersectLines()
//

int intersectLines(double xa0, double ya0, double xa1, double ya1, double xb0, double yb0, double xb1, double yb1, double &xOut, double &yOut) {
   double dxa = xa1 - xa0;
   double dya = ya1 - ya0;
   double dxb = xb1 - xb0;
   double dyb = yb1 - yb0;

   // qprintf(fp_dbg, "dxa %8.4f, dya %8.4f, dxb %8.4f, dyb%8.4f\n", dxa, dya, dxb, dyb); // qxtemp!!
   double denom = dxb * dya  -  dxa * dyb;
   double cee   = dxa * ya0  -  dya * xa0;
   double eff   = dxb * yb0  -  dyb * xb0;
   // qprintf(fp_dbg, "denom %8.4f, cee %8.4f, eff %8.4f\n", denom, cee, eff); // qxtemp!

   xOut = (dxa * eff   -  dxb * cee) / denom;
   yOut = (dya * eff   -  dyb * cee) / denom;

   return 0;
} // end of intersectLines()...




//
// new 05-05-03, along with intersectLines()
//
//
//
//
// qxdropToLine()
//

int dropToLine(double xa0, double ya0, double xa1, double ya1, double xp, double yp, double &xOut, double &yOut) {
   double dxa = xa1 - xa0;
   double dya = ya1 - ya0;
   double xb0 = xp;
   double yb0 = yp;
   double xb1 = xb0 + dya;
   double yb1 = yb0 - dxa;

   int iRet = intersectLines(xa0, ya0, xa1, ya1, xb0, yb0, xb1, yb1, xOut, yOut);

   return iRet;
} // end of dropToLine()...



// qxdist2d()

double dist2d(double x0, double y0, double x1, double y1) {
   double dx = x1 - x0;
   double dy = y1 - y0;
   double dsq = dx * dx + dy * dy;
   double ret = sqrt(dsq);

   return ret;
} // end of dist2d()




//
// new 03-18-04
//
//
// qxdist2d_sq()

double dist2d_sq(double x0, double y0, double x1, double y1) {
   double dx = x1 - x0;
   double dy = y1 - y0;
   double dsq = dx * dx + dy * dy;
   // double ret = sqrt(dsq);

   return dsq;
} // end of dist2d_sq()



// qxdot2d()

double dot2d(double dx0, double dy0, double dx1, double dy1) {
   return (dx0*dx1 + dy0*dy1);
} // end of dot2d()...




//
// new 02-05-04
//
// We return 1 if the passed file ends in ".bmp" or ".BMP" (or some mix)
//
//
//
//
// qxisBmpName()
//

int isBmpName(const char *cFile) {
   if(cFile == NULL) return 0;  // slightly unusual; usually we'd return -1, but it's NOT a .bmp name!
   char c(0);

   int ilen = (int)strlen(cFile);
   if(ilen < 4) return 0;
   if(cFile[ilen-4] != '.') return 0;
   if(((c = cFile[ilen-3]) != 'b') && (c != 'B')) return 0;
   if(((c = cFile[ilen-2]) != 'm') && (c != 'M')) return 0;
   if(((c = cFile[ilen-1]) != 'p') && (c != 'P')) return 0;

   // all tests passed!
   return 1;
} // end of isBmpName()...





//
// new 02-05-04
//
// We return 1 if the passed file ends in ".jpg" or ".JPG" (or some mix)
//
// qxl: support ".JPEG", etc.?  Not usually used in Windows world, which favors 3 char extensions.
//
//
//
// qxisJpgName()
//

int isJpgName(const char *cFile) {
   if(cFile == NULL) return 0;  // slightly unusual; usually we'd return -1, but it's NOT a .jpg name!

   if(isJpsName(cFile)) return 1;
   if(isJplName(cFile)) return 1;

   // no tests passed!
   return 0;
} // end of isJpgName()...




//
// new 02-05-04
//
// We return 1 if the passed file ends in ".jpg" or ".JPG" (or some mix)
//
// qxl: support ".JPEG", etc.?  Not usually used in Windows world, which favors 3 char extensions.
//
//
//
// qxisJpsName()
//

int isJpsName(const char *cFile) {
   if(cFile == NULL) return 0;  // slightly unusual; usually we'd return -1, but it's NOT a .jpg name!

   int ilen = (int)strlen(cFile);
   if(ilen < 4) return 0;
   if(cFile[ilen-4] != '.') return 0;
   if((cFile[ilen-3] != 'j') && (cFile[ilen-3] != 'J')) return 0;
   if((cFile[ilen-2] != 'p') && (cFile[ilen-2] != 'P')) return 0;
   if((cFile[ilen-1] != 'g') && (cFile[ilen-1] != 'G')) return 0;

   // all tests passed!
   return 1;
} // end of isJpsName()...




//
// new 08-05-25
//
// We return 1 if the passed file ends in ".jpeg" or ".JPEG" (or some mix)
//
//
//
//
// qxisJplName()
//

int isJplName(const char *cFile) {
   if(cFile == NULL) return 0;  // slightly unusual; usually we'd return -1, but it's NOT a .jpg name!

   int ilen = (int)strlen(cFile);
   if(ilen < 5) return 0;
   if(cFile[ilen-5] != '.') return 0;
   if((cFile[ilen-4] != 'j') && (cFile[ilen-4] != 'J')) return 0;
   if((cFile[ilen-3] != 'p') && (cFile[ilen-3] != 'P')) return 0;
   if((cFile[ilen-2] != 'e') && (cFile[ilen-2] != 'E')) return 0;
   if((cFile[ilen-1] != 'g') && (cFile[ilen-1] != 'G')) return 0;

   // all tests passed!
   return 1;
} // end of isJplName()...





//
// new 07-20-25
//
// We return 1 if the passed file ends in ".png" or ".PNG" (or some mix)
//
//
//
//
//
// qxisPngName()
//

int isPngName(const char *cFile) {
   if(cFile == NULL) return 0;  // slightly unusual; usually we'd return -1, but it's NOT a .png name!

   int ilen = (int)strlen(cFile);
   if(ilen < 4) return 0;
   if(cFile[ilen-4] != '.') return 0;
   if((cFile[ilen-3] != 'p') && (cFile[ilen-3] != 'P')) return 0;
   if((cFile[ilen-2] != 'n') && (cFile[ilen-2] != 'N')) return 0;
   if((cFile[ilen-1] != 'g') && (cFile[ilen-1] != 'G')) return 0;

   // all tests passed!
   return 1;
} // end of isPngName()...



#ifdef NOT_NEEDED

//
// new 07-21-06, from tstamp.c
//
//
//
//
//
//
// qxtstamp3()
//

int tstamp3(time_t new_time_t, char *file_to) {
   int i;
   struct stat     stbuf;

   struct _utimbuf utb;   // 10-18-96



   // make sure destination file exists...
   if(stat(file_to, &stbuf) == -1) {
      fprintf(stderr, "%s is NOT found.\n", file_to);
      return -1;
   }




   // new 10-18-96: use _utime():
   utb.actime  = new_time_t;
   utb.modtime = new_time_t;

   if((i = _utime(file_to, &utb)) != 0) {
      fprintf(stderr, "Bad _utime() return value: %d.\n", i);
      return -1;
   }



   return 0;

} // end of tstamp3()




//
// new 07-21-06, from tstamp.c
//
//
//
//
//
// qxtstamp4()
//

int tstamp4(time_t delta_time_t, char *file_to) {
   int mode;
   struct stat     stbuf;


   // make sure source file exists...
   if(stat(file_to, &stbuf) == -1) {
      fprintf(stderr, "%s is NOT found.\n", file_to);
      return -1;
   }

   stbuf.st_mtime += delta_time_t;

   mode = stbuf.st_mode;
   // fprintf(stdout, "mode=%d; compare to %d and %d.\n", mode, _S_IREAD, _S_IWRITE);


   return tstamp3(stbuf.st_mtime, file_to);

} // end of tstamp4()


#endif // NOT_NEEDED





// qxbyte_cmp()

int byte_cmp(char *str1, char *str2, signed long lenn) {
   int i;

   for( ; --lenn>0; ) {
      if((i = (int)(*(str2++)) - (int)(*(str1++))) != 0) return SGN(i);
   }
   return 0;

} // end of byte_cmp()




// qxbyte_cpy()

int byte_cpy(const char *str1, char *str2, signed long lenn) {

   for( ; ((lenn--)>0); ) {
      *(str2++) = *(str1++);
   }
   return 0;

} // end of byte_cpy()






//
//
// Input:
//    x0, y0, x1, y1, x2, y2: three points in 2-D space defining the curve
//
// Output:
//    return value: 
//       0: all went well
//       1: Couldn't solve basic equation (zero denominator(s)); none of the output values are valid.
//       2: zero a; couldn't find max/min.  But a, b, and c are valid.
//    a, b, c: coefficiets of the curve: y = a*x^2 + b*x + c
//    xMaxMin, yMaxMin: the point at the min or max of the curve.
//
// See $nv/it, 02-13-10, for derivation.
//
//
//
//
// qxsolveQuadratic()

int solveQuadratic(double x0, double y0, double x1, double y1, double x2, double y2, double &a, double &b, double &c, double &xMaxMin, double &yMaxMin) {
   // in case we fail (return value also tells, but what the heck):
   a = b = c = xMaxMin = yMaxMin = 0.0;

   const double verySmall = 1E-23;     // pulled out of thin air...

   double x0squared = x0 * x0;
   double x1squared = x1 * x1;
   double x2squared = x2 * x2;

   double aDenom = ((((x1 - x0) * (x2squared - x1squared))  -  ((x2 - x1) * (x1squared - x0squared))));
   if(ABS(aDenom) < verySmall)
      return 1;

   double bDenom = ((x2 - x1) * (x1 - x0));
   if(ABS(bDenom) < verySmall)
      return 1;

   a = (((x1 - x0) * (y2 - y1)) - ((x2 - x1) * (y1 - y0))) / aDenom;
   b = (((x2 - x1) * (y1 - y0)) - (a * (x2 - x1) * (x1squared - x0squared))) / bDenom;
   c = y0 - a*x0squared - b*x0;

   double dDenom = 2.0 * a;
   if(ABS(dDenom) < verySmall)
      return 2;

   xMaxMin = -b / dDenom;
   yMaxMin = (xMaxMin * a + b) * xMaxMin + c;      // or yMaxMin = a * (xMaxMin * xMaxMin) + b * xMaxMin + c;

   return 0;
} // end of solveQuadratic()...







//
// taken from $bg 09-11-10
//
// We ALWAYS use random numbers.  12.3 has a 30% chance of becoming 13.
//
//
//
//
//
// qxroundy()
//

int roundy(double rawVal) {
   int iRet;
   int iVal;
   // double tolDif = 0.01;   // not used this function

   int iSgn = SGN(rawVal);
   if(iSgn == 0) return 0;

   rawVal *= (double)iSgn;              // now positive

   int iRaw = (int)rawVal;
   double dFract = rawVal - (double)iRaw;
   double dRand = randDoub();

   if(dRand > dFract) {
      // go down:
      iVal = iRaw;
   } else {
      // go up:
      iVal = iRaw + 1;
   } // !if(dRand > dFract)

   // put the sign back in:
   iRet = iVal * iSgn;

   return iRet;
} // end of roundy()...





// qxnormalizevd()

int normalizevd(std::vector<double> &vd) {
   int iRet = 0;
   double dAbsMax = -1.0;
   const double verySmall = 1E-23;     // pulled out of thin air...

   for(int j=0; j<vd.size(); j++) {
      double dTest = vd[j];
      if(ABS(dTest) > dAbsMax)
         dAbsMax = ABS(dTest);
   } // for(int j=0; j<vd.size(); j++)

   if(dAbsMax < verySmall)
      return NADA;

   for(int j=0; j<vd.size(); j++) {
      vd[j] /= dAbsMax;
   } // for(int j=0; j<vd.size(); j++)

   return 0;
} // end of int normalizevd(std::vector<double> &vd)





// qxnormalizevvd()

int normalizevvd(std::vector<std::vector<double> > &vvd) {
   int iRet = 0;

   for(int i=0; i<vvd.size(); i++) {
      std::vector<double> &vd = vvd[i];
      int ii = normalizevd(vd);
      if(ii < iRet) iRet = ii;
   } // for(int i=0; i<vvd.size(); i++)

   return iRet;
} // int normalizevvd(std::vector<std::vector<double> >)







//
// new 10-02-10 (jdl).  Solves simultaneous linear equations of n unknowns.
//
// Input:
//    coeffsIn: vector of n vectors of n+1 doubles.  Inner vector ax + by + c = 0 kinda thing, with a, b, c stored.
//    fp_wrt (default NULL).  If non-NULL, write out a bunch of info.
//
// Output:
//    return value: 0 if all ok; -1 if something went wrong.
//    unknowsOut: vector of doubles with our solution of x, y, ...
//
// Method: removes the last unknown, then calls itself recursively.
//
//
//
//
// 05-28-12 (jdl): fp_wrt (defaults to NULL)
// 05-29-12 (jdl): More clever condensation; see $nv/it.
//
// qxsolveSimultaneous()
//

int solveSimultaneous(const std::vector<std::vector<double> > &coeffsIn, std::vector<double> &unknownsOut, FILE *fp_wrt) {
   const double verySmall = 1E-23;     // pulled out of thin air...

   unknownsOut.clear();

   if(fp_wrt != NULL) {
      fprintf(fp_wrt, "\nThis is solveSimultaneous with %d unknowns.\n", (int)coeffsIn.size());
      for(int i=0; i<coeffsIn.size(); i++) {
         const std::vector<double> &vLil = coeffsIn[i];
         for(int j=0; j<vLil.size(); j++) {
            fprintf(fp_wrt, "  %10.5f", vLil[j]);
         } // for(int j=0; j<vLil.size(); j++)

         fprintf(fp_wrt, "\n");
      } // for(int i=0; i<coeffsIn.size(); i++)

      fprintf(fp_wrt, "\n");
   } // if(fp_wrt != NULL)


   int iSizCominIn = (int)coeffsIn.size();
   if(iSizCominIn == 0)
      return NADA;

   // check for just one:
   if(iSizCominIn == 1) {
      // we can get out of here, and unzip the recursive chain.
      const std::vector<double> &vLil = coeffsIn[0];

      if(vLil.size() != (2))
         return NADA;

      if(ABS(vLil[0]) < verySmall) {
         if(fp_wrt != NULL) fprintf(fp_wrt, "solveSimultaneous(): inner equation: %12.8f/%12.8f pisses me off.\n", vLil[1], vLil[0]);  // qxtemp!
         if(ABS(vLil[1]) < verySmall) {
            // indeterminite; pick 0    // qxl: refine this somehow?
            unknownsOut.push_back(0.0);
            return 0;
         } else {
            // waaaaaah!  We're FUKT!  infinite; not possible.
            return NADA;
         } // if(ABS(vLil[1]) < verySmall)
      } // if(ABS(vLil[0]) < verySmall)

      double x = -vLil[1]/vLil[0];
      unknownsOut.push_back(x);

      return 0;
   } // if(iSizCominIn == 1)


   // must recurse.  Get rid of the rightmost unknown (one field shy of the final const field)
   int iSizLess = iSizCominIn - 1;
   std::vector<std::vector<double> > coeffsLess;
   coeffsLess.resize(iSizLess);
   for(int i=0; i<iSizLess; i++) {
      coeffsLess[i].resize(iSizLess+1);
   } // for(int i=0; i<iSizLess; i++)

   // 05-29-12 (jdl): if an equation ALREADY has zero in the column we're condensing out, don't cross it with another equation (causes loss of info); just stuff it.
   int nStuffed = 0;     // we'll walk this up to iSizLess.

   // first stuffing method: any equations that already have zero in the column we're condensing out
   for(int i=0; i<iSizCominIn; i++) {
      const std::vector<double> &vecIn = coeffsIn[i];
      double dTest = vecIn[iSizLess];   // just take my word for it: iSizLess is the column we're collapsing out.
      if(ABS(dTest) < verySmall) {
         // just stuff this equation; don't cross it with another.
         std::vector<double> &vecNu = coeffsLess[nStuffed];
         for(int j=0; j<iSizLess; j++) {
            vecNu[j] = vecIn[j];
         } // for(int j=0; j<iSizLess; j++)
         vecNu[iSizLess] = vecIn[iSizCominIn];    // the const part of the equation.
         nStuffed++;

         if(nStuffed >= iSizLess) break;          // don't overflow!
      } // if(ABS(dTest) < verySmall)
   } // for(int i=0; i<iSizCominIn; i++)

   // second stuffing method (always, before 05-29-12): cross two equations.
   int iLow=NADA, iHii=NADA;
   for(int i=0; ((i<iSizCominIn) && (nStuffed < iSizLess)); i++) {
      const std::vector<double> &vecIn = coeffsIn[i];
      double dTest = vecIn[iSizLess];   // just take my word for it: iSizLess is the column we're collapsing out.
      if(ABS(dTest) < verySmall) 
         continue;                      // we will have picked this up in the loop above.

      // if we get here, we have someone we can cross with someone.
      iLow = iHii;
      iHii = i;
      if(iLow < 0)
         continue;          // we don't yet have a pair.

      // if we get here, we can stuff a pair!
      const std::vector<double> &vecLo = coeffsIn[iLow];
      const std::vector<double> &vecHi = coeffsIn[iHii];
      std::vector<double> &vecNu = coeffsLess[nStuffed];
      vecNu.resize(iSizLess+1);
      double multLo = vecHi[iSizLess];    // if 3 x 4  --> 2 x 3, we get element 2 (3rd) from the original vector.
      double multHi = vecLo[iSizLess];
      for(int j=0; j<iSizLess; j++) {
         double dd = vecLo[j] * multLo - vecHi[j] * multHi;
         vecNu[j] = dd;
      } // for(int j=0; j<iSizLess; j++)
      vecNu[iSizLess] = vecLo[iSizLess+1] * multLo - vecHi[iSizLess+1] * multHi;    // the constant value, not multiplied by an unknown.
      nStuffed++;

   } // for(int i=0; ((i<iSizCominIn) && (nStuffed < iSizLess)); i++)

   if(fp_wrt != NULL) fprintf(fp_wrt, "\nRecursing...\n\n");

   int ii   = normalizevvd(coeffsLess);

   int iRet = solveSimultaneous(coeffsLess, unknownsOut, fp_wrt);

   if(fp_wrt != NULL) fprintf(fp_wrt, "\nBack from recursing with return %d.\n\n", iRet);

   if(iRet < 0)
      return iRet;

   int uSiz = (int)unknownsOut.size();
   if(uSiz != iSizLess)
      return NADA;

   // use the values passed back in unknownsOut to add one new value to unknownsOut.

   // uhhh, which row in the original coefficient vector for the column eliminated has the highest absolute value?
   int iMax = NADA;
   double vMax = -1.0;
   for(int i=0; i<iSizCominIn; i++) {
      const std::vector<double> &vecThis = coeffsIn[i];
      double dd = vecThis[iSizLess];
      if(ABS(dd) > vMax) {
         vMax = ABS(dd);
         iMax = i;
      } // if(ABS(dd) > vMax)
   } // for(int i=0; i<iSizCominIn; i++)

   // qxl: can exit from here if vMax is small; I moved it down for diagnostics...

   const std::vector<double> &vecInUse = coeffsIn[iMax];
   double dThis = -vecInUse[iSizCominIn];                       // the const part

   for(int j=0; j<iSizCominIn-1; j++) {
      dThis -= vecInUse[j] * unknownsOut[j];
   } // for(int j=0; j<iSizCominIn-1; j++)

   if(vMax < verySmall) {
      if(fp_wrt != NULL) fprintf(fp_wrt, "solveSimultaneous(): out of %d equations, the best I could find was %12.8f/%12.8f\n", iSizCominIn, dThis, vMax);  // qxtemp!
      if(ABS(dThis) < verySmall) {
         // indeterminate; pick 0.0    // qxl: better cutoff?
         unknownsOut.push_back(0.0);
         return 0;
      } else {
         // waaaaaah!  We're FUKT!  infinite; not possible.
         return NADA;
      } // !if(ABS(dThis < verySmall)) {
   } // if(vMax < verySmall)


   dThis /= vecInUse[iSizCominIn-1];

   unknownsOut.push_back(dThis);

   if(fp_wrt != NULL) {
      fprintf(fp_wrt, "\nLeaving solveSimultaneous with %d unknowns; returning coeffs:\n", (int)coeffsIn.size());
      for(int i=0; i<unknownsOut.size(); i++) {
         fprintf(fp_wrt, "  %10.5f", unknownsOut[i]);

      } // for(int i=0; i<coeffsIn.size(); i++)

      fprintf(fp_wrt, "\n\n");
   } // if(fp_wrt != NULL)

   return 0;
} // end if solveSimultaneous()...





//
// new 10-03-10 (jdl)
//
// This is a general function but I'll speak in terms of the use I'm going to put it to: we have n frequency slots
// which represent the union of target frequencies and midi note frequencies.  We have m midi notes sounding, each
// of which has a set of energy profiles n long.  We have a target energy for each frequency slot, so n of those.
//
// We expect n > m: more frequency slots than midi notes.  So an exact solution is (probably) impossible.  We seek 
// to minimize pain: summation across n slots of deltaEnergy squared, where deltaEnergy is sum of all m midi notes
// less target energy for this frequency slot.  We set up m simultaneous equations with m unknowns, based on the
// derivative of pain with respect to one of the F(i) factors: each derivative must be zero in the usual curve-fitting
// practice.
//
// Note that we DON'T care what frequency (midi note) each slot represents, only whether it belongs to the target
// and/or one or more midi notes.  Other logic must decide whether two peaks that don't quite align should be
// grouped together or represented separately.
//
// And we DON'T care what midi note each midi note represents, only what its slot profile looks like.  Presumably
// the caller keeps track of the actual midi note pitches to be specified to match the profile(s) we've been passed.
//
// See $nv/it for derivation of equations here.
//
// Shorthand:
//    m    mMidiNotes
//    n    nFreqSlots
//
//
//
//
// Input:
//    voicePeaks: vector of m vectors of n doubles: midi notes at the outer level, then energies for each peak (at full volume).
//    targetPeaks: vector of n doubles: the desired energy for each frequency slot.
//
// Output:
//    return value: 0 if all ok; -1 if something went wrong.
//    Fout: vector of m doubles: factor times input energy for each midi note.
//    targetAchieved: vector of n doubles: what we get from Fout, the best we could find.
//    pPainFract (default NULL): if non-NULL, relative pain (0.0 perfect; 1.0 about as bad as it gets).
//
//
//
//
//
//
// qxsemiSolve()
//

int semiSolve(const std::vector<std::vector<double> > &voicePeaks, const std::vector<double> &targetPeaks, std::vector<double> &Fout, std::vector<double> &targetAchieved, double *pPainFract) {

   Fout.clear();

   int mMidiNotes = (int)voicePeaks.size();
   int nFreqSlots = (int)targetPeaks.size();


   if(mMidiNotes < 1)
      return NADA;

   if(nFreqSlots < 1)
      return NADA;


   // make sure the input vectors are the right size:
   for(int i=0; i<mMidiNotes; i++) {
      if(voicePeaks[i].size() != nFreqSlots)
         return NADA;
   } // for(int i=0; i<mMidiNotes; i++)


   // set up a vector of vectors to pass into solveSimultaneous():
   std::vector<std::vector<double> > coeffs;
   coeffs.resize(mMidiNotes);

   for(int i=0; i<mMidiNotes; i++) {
      std::vector<double> &vecNu = coeffs[i];
      vecNu.resize(mMidiNotes+1);
   } // for(int i=0; i<mMidiNotes; i++)

   // outer loop: which one we derive with respect to; see $nv/it.
   for(int ip=0; ip<mMidiNotes; ip++) {
      std::vector<double> &vecUse = coeffs[ip];
      double dd = 0.0;

      // constant:
      for(int j=0; j<nFreqSlots; j++) {
         double d0 = voicePeaks[ip][j];
         double d1 = targetPeaks[j];
         dd -= d0 * d1;                             // -sigma[j=0, n-1](k(p,j) * T(j))
      } // for(int j=0; j<nFreqSlots; j++)
      vecUse[mMidiNotes] = dd;

      // the F(p) coefficient:
      dd = 0.0;
      for(int j=0; j<nFreqSlots; j++) {
         double d = voicePeaks[ip][j];
         dd += d*d;                                 // sigma[j=0, n-1](k(p,j)^2)
      } // for(int j=0; j<nFreqSlots; j++)
      vecUse[ip] = dd;

      // the other F(i) coefficients:
      for(int i=0; i<mMidiNotes; i++) {
         if(i == ip)
            continue;

         dd = 0.0;
         for(int j=0; j<nFreqSlots; j++) {
            double d0 = voicePeaks[ip][j];
            double d1 = voicePeaks[i][j];
            dd += d0 * d1;                          // sigma[j=0, n-1](k(p,j) * k(i,j))
         } // for(int j=0; j<nFreqSlots; j++)
         vecUse[i] = dd;

      } // for(int i=0; i<mMidiNotes; i++)

   } // for(int ip=0; ip<mMidiNotes; ip++)


   int iRet = solveSimultaneous(coeffs, Fout);

   if(iRet < 0)
      return iRet;


   // how close did we come?
   targetAchieved.resize(nFreqSlots);
   for(int j=0; j<nFreqSlots; j++) targetAchieved[j] = 0.0;  // qxl: is this necessary?

   for(int i=0; i<mMidiNotes; i++) {
      double d = Fout[i];
      for(int j=0; j<nFreqSlots; j++) {
         double d2 = voicePeaks[i][j];
         targetAchieved[j] += d * d2;
      } // for(int j=0; j<nFreqSlots; j++)
   } // for(int i=0; i<mMidiNotes; i++)


   double totSq = 0.0;
   double errSq = 0.0;

   for(int j=0; j<nFreqSlots; j++) {
      double err = targetAchieved[j] - targetPeaks[j];
      errSq += err * err;
      totSq += targetPeaks[j] * targetPeaks[j];
   } // for(int j=0; j<nFreqSlots; j++)

   double relativePain = errSq / totSq;

   if(pPainFract != NULL)
      *pPainFract = relativePain;


   return 0;
} // end of semiSolve()...



//
// new 10-04-10 (jdl)
//
// A wrapper around semiSolve() when, as for notes sounding, we can't have "negative amplitude".  
// Specifically, no entry in Fout may be less than zero.
//
//
//
// Input:
//    voicePeaks: vector of m vectors of n doubles: midi notes at the outer level, then energies for each peak (at full volume).
//    targetPeaks: vector of n doubles: the desired energy for each frequency slot.
//
// Output:
//    return value: 0 if all ok; -1 if something went wrong.
//    Fout: vector of m doubles: factor times input energy for each midi note.
//    targetAchieved: vector of n doubles: what we get from Fout, the best we could find.
//    pPainFract (default NULL): if non-NULL, relative pain (0.0 perfect; 1.0 about as bad as it gets).
//
//
//
//
//
//
// qxsemiSolveNoNeg()
//

int semiSolveNoNeg(const std::vector<std::vector<double> > &voicePeaks, const std::vector<double> &targetPeaks, std::vector<double> &Fout, std::vector<double> &targetAchieved, double *pPainFract) {

   int iRet = semiSolve(voicePeaks, targetPeaks, Fout, targetAchieved, pPainFract);

   if(iRet < 0)
      return iRet;

   // make sure the solution does not include any negative amplitudes:
   double vMin = 0.0;
   int    iMin = NADA;

   int mMidiNotes = (int)voicePeaks.size();
   int nFreqSlots = (int)targetPeaks.size();

   for(int i=0; i<mMidiNotes; i++) {
      double d = Fout[i];
      if(d < vMin) {
         vMin = d;
         iMin = i;
      } // if(d < vMin)
   } // for(int i=0; i<mMidiNotes; i++)

   // chance that we're done:
   if(vMin >= 0.0)
      return 0;

   // BITE me!  Why can't they leave me alone?


   // if we're down to the last note, the entire thing is a fuster-cluck:
   if(mMidiNotes <= 1)
      return NADA;


   // get a solution with the iMin'th guy missing.
   std::vector<std::vector<double> > voicePless;        // voice peaks less (one missing)

   for(int i=0; i<mMidiNotes; i++) {
      if(i == iMin)
         continue;

      voicePless.push_back(voicePeaks[i]);
   } // for(int i=0; i<mMidiNotes; i++)

   std::vector<double> FoutLess;

   iRet = semiSolveNoNeg(voicePless, targetPeaks, FoutLess, targetAchieved, pPainFract);

   if(iRet < 0)
      return iRet;

   // ok!  recursive call(s) did the trick.  *pPainFract and targetAchieved should be good.  And Fout 
   // should be dimensioned.

   // flesh out Fout, mostly from FoutLess, but with one forced zero:
   for(int i=0; i<mMidiNotes; i++) {
      if(i == iMin) {
         Fout[i] = 0.0;    // this is the negative guy we pushed to zero.
      } else {
         int iLess = ((i>iMin) ? (i-1) : (i));
         Fout[i] = FoutLess[iLess];
      } // if(i == iMin)
   } // for(int i=0; i<mMidiNotes; i++)

   // we're done!

   return 0;
} // end of semiSolveNoNeg()...





//
// new 12-28-10 (jdl)
//
// Input:
//    yValuesIn: vector of doubles.  We assume that x values run from 0 to yValuesIn.size()-1.  Must contain at least 4 values.
//
//
//
// Output:
//    return value: 0 if all went well, -1 otherwise.
//    coeffsOut: vector of four coefficients a, b, c, d, where y ~= a*x^3 + b*x^2 + c*x + d.
//
//
//
//
// qxsemiSolveCubic()
//

int semiSolveCubic(const std::vector<double> &yValuesIn, std::vector<double> &coeffsOut) {
   coeffsOut.clear();   // even if we fail, we'll do this.

   int nPointsIn = (int)yValuesIn.size();

   if(nPointsIn < 4)
      return NADA;

   double sigmaI[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

   // calculate sigma[i=0, nPoints-1](i^6)   (etc.)
   for(int i=0; i<nPointsIn; i++) {
      double dPwr = 1.0;
      for(int j=0; j<7; j++) {
         sigmaI[j] += dPwr;
         dPwr      *= (double)i;
      } // for(int j=0; j<7; j++)
   } // for(int i=0; i<nPointsIn; i++)

   // see $nv/it for the derivation of the following:
   const int nEquations = 4;
   std::vector<std::vector<double> > vvCoeffs;
   vvCoeffs.resize(nEquations);
   std::vector<double>               unknownsOut;    // to pass to solveSimultaneous()...
   for(int jEq=0; jEq<nEquations; jEq++) {
      std::vector<double> &vRef = vvCoeffs[jEq];
      vRef.resize(nEquations+1);                     // 5...
      for(int j=0; j<nEquations; j++) {
         // this is weird:
         // vRef[j] = sigmaI[jEq + j];               // would work if we reversed a, b, c, d, but confusing.
         vRef[j] = sigmaI[(nEquations-1) + jEq - j];
      } // for(int j=0; j<nEquations; j++)

      // now the fifth element in each equation, which takes a bit more work:
      vRef[nEquations] = 0.0;
      for(int i=0; i<nPointsIn; i++) {
         int iToPower = 1;
         for(int j=0; j<jEq; j++) {
            iToPower *= i;
         } // for(int j=0; j<jEq; j++)
         vRef[nEquations] -= yValuesIn[i] * iToPower;
      } // for(int i=0; i<nPointsIn; i++)
   } // for(int jEq=0; jEq<nEquations; jEq++)

   int iRet = solveSimultaneous(vvCoeffs, unknownsOut);

   if(iRet != 0)
      return iRet;     // failed!

   coeffsOut = unknownsOut;

   return 0;
} // end of semiSolveCubic()...







//
// new 03-18-11 (jdl), from semiSolveCubic().
//
// Input:
//    yValuesIn: vector of doubles.  We assume that x values run from 0 to yValuesIn.size()-1.  Must contain at least 3 values.
//
//
//
// Output:
//    return value: 0 if all went well, -1 otherwise.
//    coeffsOut: vector of three coefficients a, b, c, where y ~= a*x^2 + b*x + c.
//
//
//
//
// qxsemiSolveQuadratic()
//

int semiSolveQuadratic(const std::vector<double> &yValuesIn, std::vector<double> &coeffsOut) {
   coeffsOut.clear();   // even if we fail, we'll do this.

   int nPointsIn = (int)yValuesIn.size();

   if(nPointsIn < 3)
      return NADA;

   double sigmaI[5] = {0.0, 0.0, 0.0, 0.0, 0.0};

   // calculate sigma[i=0, nPoints-1](i^6)   (etc.)
   for(int i=0; i<nPointsIn; i++) {
      double dPwr = 1.0;
      for(int j=0; j<5; j++) {
         sigmaI[j] += dPwr;
         dPwr      *= (double)i;
      } // for(int j=0; j<7; j++)
   } // for(int i=0; i<nPointsIn; i++)

   // see $nv/it for the derivation of the following:
   const int nEquations = 3;
   std::vector<std::vector<double> > vvCoeffs;
   vvCoeffs.resize(nEquations);
   std::vector<double>               unknownsOut;    // to pass to solveSimultaneous()...
   for(int jEq=0; jEq<nEquations; jEq++) {
      std::vector<double> &vRef = vvCoeffs[jEq];
      vRef.resize(nEquations+1);                     // 4...
      for(int j=0; j<nEquations; j++) {
         // this is weird:
         // vRef[j] = sigmaI[jEq + j];               // would work if we reversed a, b, c, but confusing.
         vRef[j] = sigmaI[(nEquations-1) + jEq - j];
      } // for(int j=0; j<nEquations; j++)

      // now the fourth element in each equation, which takes a bit more work:
      vRef[nEquations] = 0.0;
      for(int i=0; i<nPointsIn; i++) {
         int iToPower = 1;
         for(int j=0; j<jEq; j++) {
            iToPower *= i;
         } // for(int j=0; j<jEq; j++)
         vRef[nEquations] -= yValuesIn[i] * iToPower;
      } // for(int i=0; i<nPointsIn; i++)
   } // for(int jEq=0; jEq<nEquations; jEq++)

   int iRet = solveSimultaneous(vvCoeffs, unknownsOut);

   if(iRet != 0)
      return iRet;     // failed!

   coeffsOut = unknownsOut;

   return 0;
} // end of semiSolveQuadratic()...








//
// new 03-18-11 (jdl), from semiSolveQuadratic().
//
// Input:
//    yValuesIn: vector of doubles.  We assume that x values run from 0 to yValuesIn.size()-1.  Must contain at least 2 values.
//
//
//
// Output:
//    return value: 0 if all went well, -1 otherwise.
//    coeffsOut: vector of two coefficients a, b, where y ~= a*x + b.
//
//
//
//
// qxsemiSolveLinear()
//

int semiSolveLinear(const std::vector<double> &yValuesIn, std::vector<double> &coeffsOut) {
   coeffsOut.clear();   // even if we fail, we'll do this.

   int nPointsIn = (int)yValuesIn.size();

   if(nPointsIn < 2)
      return NADA;

   double sigmaI[3] = {0.0, 0.0, 0.0};

   // calculate sigma[i=0, nPoints-1](i^6)   (etc.)
   for(int i=0; i<nPointsIn; i++) {
      double dPwr = 1.0;
      for(int j=0; j<3; j++) {
         sigmaI[j] += dPwr;
         dPwr      *= (double)i;
      } // for(int j=0; j<7; j++)
   } // for(int i=0; i<nPointsIn; i++)

   // see $nv/it for the derivation of the following:
   const int nEquations = 2;
   std::vector<std::vector<double> > vvCoeffs;
   vvCoeffs.resize(nEquations);
   std::vector<double>               unknownsOut;    // to pass to solveSimultaneous()...
   for(int jEq=0; jEq<nEquations; jEq++) {
      std::vector<double> &vRef = vvCoeffs[jEq];
      vRef.resize(nEquations+1);                     // 4...
      for(int j=0; j<nEquations; j++) {
         // this is weird:
         // vRef[j] = sigmaI[jEq + j];               // would work if we reversed a, b, c, but confusing.
         vRef[j] = sigmaI[(nEquations-1) + jEq - j];
      } // for(int j=0; j<nEquations; j++)

      // now the third element in each equation, which takes a bit more work:
      vRef[nEquations] = 0.0;
      for(int i=0; i<nPointsIn; i++) {
         int iToPower = 1;
         for(int j=0; j<jEq; j++) {
            iToPower *= i;
         } // for(int j=0; j<jEq; j++)
         vRef[nEquations] -= yValuesIn[i] * iToPower;
      } // for(int i=0; i<nPointsIn; i++)
   } // for(int jEq=0; jEq<nEquations; jEq++)

   int iRet = solveSimultaneous(vvCoeffs, unknownsOut);

   if(iRet != 0)
      return iRet;     // failed!

   coeffsOut = unknownsOut;

   return 0;
} // end of semiSolveLinear()...







//
// new 09-13-11 (jdl).  Ascii to Numerator and Denominator.
//
//
// Input:
//    cdata: input string such as "3" or "13/7"
//
// Output:
//    return value: 0 if all went well; NADA otherwise.
//    iNumerOut: numerator evaluated.  May be negative.
//    iDenomOut: denominator evaluated.  Should never be negative, unless -7/-12 was entered.
//
//
//
//
//
//
//
//
// qxatond()
//

int atond(const char *cdata, int &iNumerOut, int &iDenomOut) {
   // in case we fail:
   iNumerOut = iDenomOut = 0;

   if(cdata == NULL)
      return NADA;

   if(cdata[0] == '\0')
      return NADA;

   // look for '/' and if found, perform divide:
   if(strchr(cdata, '/') == NULL) {
      iNumerOut = atoi(cdata);
      iDenomOut = 1;
   } else {
      // make copy & work on it:
      char * cplay = new char[strlen(cdata) + 8];
      sprintf(cplay, "%s", cdata);
      char *cp = strchr(cplay, '/');

      if(cp == NULL) {
         // should never happen!  We found it before we made the copy.
         delete [] cplay; cplay=NULL;

         return NADA;
      } // if(cp == NULL)

      *cp = '\0';
      iNumerOut = atoi(cplay);
      iDenomOut = atoi(cp+1);

      delete [] cplay; cplay=NULL;
   } // !if(strchr(cdata, '/') == NULL)

   return 0;
} // end of atond(const char *cdata, int &iNumerOut, int &iDenomOut)...





//
// new 09-28-11, from $jnob version...  nGuard defaults to 0.
//
//
//
// 10-18-02 (jdl): maxEmbBits; defaults to 0 (any number)
//
// qxnBitsAvail()
//

int nBitsAvail(signed short isVal, int nGuard, int maxEmbBits) {
   if(isVal < 0) isVal = ~isVal;  // 1's complement
   int ishf = 0;

   while(isVal != 0) {
      isVal = isVal >> 1;
      ishf++;
   } // while(isVal != 0)

   int iRet = ishf - 1 - ABS(nGuard);
   if(iRet < 0) iRet = 0;

   // 10-18-02: possibly limit number returned:
   if((maxEmbBits > 0) && (iRet > maxEmbBits)) iRet = maxEmbBits;

   return iRet;
} // end of nBitsAvail()...






//
// new 10-20-10 (jdl); moved from egrid.cpp 10-08-11 (jdl)
//
//
//
//
//
//
//
// qxshortify()
//

short shortify(double dVal) {

   int iSgn = SGN(dVal);
   if(iSgn == 0) return 0;

   dVal *= (double)iSgn;              // now positive

   int iVal = (int)(dVal + 0.5);      // straight round.
   iVal    *= iSgn;                  // bring back sign

   short sVal = (short)RANGE(iVal, MIN_SHORT, MAX_SHORT);

   return sVal;
} // end of shortify()...







//
// new 10-20-10 (jdl); takes int instead of double in.
//
//
//
//
//
//
//
// qxshortifi()
//

short shortifi(int iVal) {

   short sVal = (short)RANGE(iVal, MIN_SHORT, MAX_SHORT);

   return sVal;
} // end of shortifi()...








//
// new 03-02-14.  Brutally replaces the last 2 chars of a file's name (blah -> bl00; blah.dbg -> blah.d00).
//
//
//
//
//
//
//
//
// qxarcMeSimple()
//

int arcMeSimple(const char *fileName) {
   if(fileName == NULL)
      return NADA;

   int lenn = (int)strlen(fileName);
   if(lenn < 3)
      return NADA;

   // does the file exist?
   if(file_t(fileName, true) < 0)             // true: tell us about any file w/ this name, even a directory.
      return NADA;

   const int lenBuf = 2048;
   char cBuf[lenBuf];
   if(lenn > (lenBuf-2))
      return NADA;

   strcpy(cBuf, fileName);

   for(int i=0; i<1000; i++) {
      sprintf(cBuf+lenn-2, "%02d", i);
      long t = file_t(cBuf, true);             // true: tell us about any file w/ this name, even a directory.
      if(t > 0)
         continue;                             // this file already exists.

      // Yay!  Found a file name that is unused.
      int j = rename(fileName, cBuf);
      if(j == 0)
         return 0;

      // rename failed:
      return NADA;
   } // for(int i=0; i<1000; i++)

   // if we get here, we failed.
   return NADA;
} // end of int arcMeSimple(const char *fileName)...









//
// new 04-17-19: allegedly learned how to call varargs from varargs (if internal downstream available)
//
//
//
//
// qxqprintf()

int qprintf(FILE * const fp, const char * const pc, ...) {
  int iRet = 0;
  if(fp != NULL) {
     va_list args;
     va_start (args, pc);
     iRet = vfprintf (fp, pc, args);
     va_end (args);
  } // if(fp != NULL)

  return iRet;
} // int qprintf(FILE * const fp, const char * const pc, ...)



// qflush()

int qflush(FILE *fp) {
  int iRet = 0;
   if(fp != NULL)
      iRet = fflush(fp);

   return iRet;
} // int qflush(FILE *fp)







// qxfileSize()

long long fileSize(const char *file_name) {
   struct _stat64  stbu64;    // for big file sizes (>2^31).

   if(file_name == NULL)
      return NADA;

   if(_stat64(file_name, &stbu64) == -1)
      return NADA;

   if((stbu64.st_mode & S_IFMT) == S_IFDIR) 
      return NADA;

   long long totLen = (long long)stbu64.st_size;

   return totLen;
} // long long fileSize(const char *file_name)











//
// new 04-23-14.  Search for a dot.
//
//
//
//
//
//
//
//
// qxarcMeDotty()
//

int arcMeDotty(const char *fileName) {
   if(fileName == NULL)
      return NADA;

   int lenn = (int)strlen(fileName);
   if(lenn < 3)
      return NADA;

   // does the file exist?
   if(file_t(fileName, true) < 0)             // true: tell us about any file w/ this name, even a directory.
      return NADA;

   // scan backward for dot:
   int dotspot(-1);
   for(dotspot=lenn-1 ; dotspot>=0 ; dotspot--) {
      char c = fileName[dotspot];
      if(c == '.')
         break;

      if((c == SLASH) || (c == OTHER_SLASH)) {
         // don't keep searching for a dot to the left a slash!
         dotspot = -1;
         break;
      } // if((c == SLASH) || (c == OTHER_SLASH))
   } // for(dotspot=lenn ; dotspot>=0 ; dotspot--)

   // no dot??
   if(dotspot < 0)
      dotspot = lenn-2;

   // trailing dot: be careful:
   if(dotspot > (lenn-2))
      dotspot = lenn-2;

   const int lenBuf = 2048;
   char cBuf[lenBuf];
   if(lenn > (lenBuf-2))
      return NADA;

#ifdef _WIN32
   strcpy_s(cBuf, lenBuf, fileName);
#else   
   strcpy(cBuf, fileName);   
#endif   

   for(int i=0; i<10000; i++) {
#ifdef _WIN32   
      sprintf_s(cBuf+dotspot+2, lenBuf-dotspot-2, "%02d", i);
#else      
      sprintf(cBuf+dotspot+2, "%02d", i);      
#endif      
      long t = file_t(cBuf, true);             // true: tell us about any file w/ this name, even a directory.
      if(t > 0)
         continue;                             // this file already exists.

      // Yay!  Found a file name that is unused.
      int j = rename(fileName, cBuf);
      if(j == 0)
         return 0;

      // rename failed:
      return NADA;
   } // for(int i=0; i<10000; i++)

   // if we get here, we failed.
   return NADA;
} // end of int arcMeDotty(const char *fileName)...





//
// new 10-19-25
//
//
//
// returns length of string if all chars are numerical digits (0 thru 9)
// returns -1 if any char is not a numerical digit.
//
// "000" -> 3
// "a00" -> -1
//
//
//
// qxnDigits()

int nDigits(const char *pDigs) {
   if(pDigs == NULL)
      return NADA;

   int lenn = (int)strlen(pDigs);

   for(int i=0; i<lenn; i++) {
      int c = pDigs[i];
      if((c < '0') || (c > '9'))
         return NADA;
   } // for(int i=0; i<lenn; i++)

   return lenn;
} // int nDigits(const char *pDigs) {





//
// new 10-18-25.  
//
//
// abc.dat     -> abc.000.dat
// abc.000.dat -> abc.001.dat
//
//
// Test:
//    spaces in fileName.
//    slashes in fileName.
//    fileName beginning with ".".
//    fileName with no ".".
//
//
//
// qxarcMe3num()
//

int arcMe3num(const char *fileName, std::string *pSSout, bool bJustTell) {
   if(pSSout != NULL)
      *pSSout = "";

   if(fileName == NULL)
      return NADA;

   int lenn = (int)strlen(fileName);
   if(lenn < 3)
      return NADA;

   // does the file exist?
   if(file_t(fileName, true) < 0)             // true: tell us about any file w/ this name, even a directory.
      return NADA;


// int cutLineIntoTokens(const char *lineIn, const std::string &ssSeparators, std::vector<std::string> &vecTokensOut, bool bTrimSp, bool bLankFieldsOK) {

   std::string ssDot = ".";
   std::vector<std::string> vcTokens;
   bool bTrimSp       = false;
   bool bLankFieldsOK = true;

   int nTok = cutLineIntoTokens(fileName, ssDot, vcTokens, bTrimSp, bLankFieldsOK);

   bool bNuu  = false;
   int  iVal  = 0;
   int  nDigs = 3;

   if(nTok < 3) {
      bNuu = true;
   } else {
      // is the next-to-last token already a number string that we can increment?
      const char *pDigs = vcTokens[nTok-2].c_str();
      nDigs = nDigits(pDigs);
      if(nDigs < 3) {
         // not a string of digits, or shorter than 3.
         nDigs = 3;
         bNuu  = true;
      } else {
         iVal = atoi(pDigs) + 1;
         bNuu = false;
      } // !if(nDigs < 0) {
   } // !if(nTok < 3)

   // front of new filename:
   std::string ssFront;
   int nFront = ((bNuu) ? (nTok - 1) : (nTok - 2));

   for(int i=0; i<nFront; i++) {
      ssFront += vcTokens[i];
      ssFront += ".";
   } // for(int i=0; i<nFront; i++)

   std::string ssEnd = ".";
   ssEnd += vcTokens[nTok - 1];

   std::string ssNum;

   // look for new name not already used by some other irritating file:
   for(int i=0; i<10000; i++) {
      int jVal = iVal + i;
      ssprint(ssNum, "%0*d", nDigs, jVal);

      std::string ssTest = ssFront + ssNum + ssEnd;

      long t = file_t(ssTest.c_str(), true);             // true: tell us about any file w/ this name, even a directory.
      if(t > 0)
         continue;                             // this file already exists.

      // Yay!  Found a file name that is unused.
      if(pSSout != NULL)
         *pSSout = ssTest;

      if(bJustTell)
         return 0;

      int j = rename(fileName, ssTest.c_str());
      if(j == 0)
         return 0;

      // rename failed:
      return NADA;
   } // for(int i=0; i<10000; i++)

   // if we get here, we failed.
   return NADA;
} // end of int arcMe3num(const char *fileName)...





//
// converts a vector of signed char to std::string (convenient: has c_str() function, etc.).
//
//
//
//
//
// qxvschToSs()

std::string vschToSs(const std::vector<signed char> &vSc) {
   std::string ssOut;

   for(unsigned int i=0; i<vSc.size(); i++) {
      signed char sc = vSc[i];
      ssOut += sc;
   } // for(unsigned int i=0; i<vSc.size(); i++)

   return ssOut;
} // std::string vschToSs(const std::vector<signed char> &vSc)






// in the following, str_out MAY be the same as str_in... 
//
//
// 03-23-98: make str_in const...
//
// qxtrim_sp()
//

int trim_sp(const char *str_in, char *str_out) {
   if((str_in == NULL) || (str_out == NULL))
      return NADA;

   int found_non_sp;
   int done;
   char c;
   const char *cp1;
   char *cp2;
   char *cp_last_nonsp = str_out;

   for(found_non_sp=0, done=0, cp1=str_in, cp2=str_out;; ) {
      switch(c=*(cp1++)) {
         case '\0':
            if(found_non_sp) {
               *(cp_last_nonsp+1) = '\0';
            } else {
               *(str_out) = '\0';
            }
            done = 1;
            break;
         case ' ':
            /* keep scanning or copying... */
            break;
         default:
            found_non_sp = 1;
            cp_last_nonsp = cp2;      /* we're about to write it to cp2 (with POST-increment...) */
            break;
      }
      if(done) break;
      if(found_non_sp) {
         *(cp2++) = c;
      }
   }    /* for(found_non_sp=0, done=0, cp1=str_in, cp2=str_out; !done; ) */

   return 0;
} /* end of trim_sp() */




// qxtrim_sp()

int trim_sp(const std::string &ssIn, char *str_out) {
   int iRet = trim_sp(ssIn.c_str(), str_out);

   return iRet;
} // int trim_sp(const std::string ssIn, char *str_out)





// qxtrim_sp()

int trim_sp(const char *str_in, std::string &ssOut) {
   // ssOut.clear();   // BAD if ssOut is the same as str_in, eh?
   int theLen = (int)strlen(str_in)+16;
   SMARTA(cpOut, char, theLen, smaOut);
// char *cpOut = new char[strlen(str_in)+16];
   int iRet = trim_sp(str_in, cpOut);
   ssOut.clear();
   if(iRet >= 0)
      ssOut = cpOut;

// delete [] cpOut; cpOut=NULL;

   return iRet;
} // int trim_sp(const char *str_in, std::string &ssOut)




// qxtrim_sp()

int trim_sp(const std::string &ssIn, std::string &ssOut) {
   int iRet = trim_sp(ssIn.c_str(), ssOut);

   return iRet;
} // int trim_sp(const std::string &ssIn, std::string &ssOut)




//
//
// Input:
//    lineIn
//
// Output:
//    return value: -1 if error; otherwise, number of tokens output.
//    vecTokensOut: filled with tokens; see rules below
//
// Note: tokenizing rules:
//    . Anything without spaces or tabs goes in the same token.
//    . Anything inside double-quotes goes in the same token (quotes also go in);
//    . We honor the escape character to allow embedded quote char(s) within a quoted string.
//    . Except as noted above, space and tab characters are discarded.
//
//
// 09-24-20 (jdl): return number of tokens.
//
// qxcutLineIntoTokens()
//

int cutLineIntoTokens(const char *lineIn, std::vector<std::string> &vecTokensOut) {
   vecTokensOut.clear();

   if(lineIn == NULL)
      return -1;


   int lenny          = (int)strlen(lineIn);
   bool bFormingToken = false;
   bool bInsideQuotes = false;
   bool bEscape       = false;                  // set true if we hit '\\' (backslash) char.
   std::string ssGrowing;

   char c(0), cprev(0), cnext(0);
   for(int icol=0; icol<lenny; icol++) {
      cprev = c;
      c = lineIn[icol];
      cnext= lineIn[icol+1];     // we can reach the null terminator without pissing off runtime.
      switch(c) {
         case ' ':
         case ASCII_TAB:
            if(bFormingToken) {
               if(bInsideQuotes) {
                  // this space or tab goes in the string:
                  ssGrowing += c;
               } else {
                  // this space or tab terminates the token:
                  bFormingToken = false;
                  vecTokensOut.push_back(ssGrowing);
                  ssGrowing.clear();
               } // !if(bInsideQuotes)
            } else {
               // not forming a token; just skip space and tab chars.
            } // !if(bFormingToken)
            break;
         case DOUB_QUOTE:
            bFormingToken = true;
            ssGrowing += c;                    // whether we're beginning or ending, we'll include the quote char.
            if(bEscape) {
               // DON'T terminate a the string.
               bEscape = false;
            } else {
               bInsideQuotes = !bInsideQuotes;
               if(!bInsideQuotes) {
                  // save the growing string and clear it:
                  // unless the NEXT char is another double-quote: special case for .rc files:
                  if(cnext == DOUB_QUOTE) {
                     // special case!
                     bInsideQuotes = true;
                     bEscape       = true;
                  } else {
                     // normal:
                     bFormingToken = false;
                     vecTokensOut.push_back(ssGrowing);
                     ssGrowing.clear();
                  } // if(cnext == DOUB_QUOTE)
               } // if(!bInsideQuotes)
            } // !if(bEscape)
            break;
         case '\\':
            bFormingToken = true;
            bEscape  = !bEscape;               // will affect what happens if next char is quote.
            ssGrowing += c;
            break;
         default:
            bFormingToken = true;
            ssGrowing += c;
            bEscape = false;
            break;
      } // switch(c)
   } // for(int icol=0; icol<lenny; icol++)

   if(bFormingToken) {
      bFormingToken = false;
      vecTokensOut.push_back(ssGrowing);
      ssGrowing.clear();
   } // if(bFormingToken)

   int nOut = (int)vecTokensOut.size();
   return nOut;
} // end of int cutLineIntoTokens(const char *lineIn, std::vector<std::string> &vecTokensOut)






//
// new, from $95/which.cpp.  This version takes as input the separating character(s)
//
//
// Input:
//    lineIn
//    ssSeparators: one or more chars that break the current token and make it ready to start another.
//    bTrimSp (default false): if true, trim off leading and trailing spaces before pushing into output vector.
//    bLankFieldsOK (default false): if true, there may be leading, trailing, or intermediate blank fields.
//
// Output:
//    return value: -1 if error; otherwise, number of tokens output.
//    vecTokensOut: filled with tokens; see rules below
//
// Note: tokenizing rules:
//    . Anything without a defined separator goes in the same token.
//    . Anything inside double-quotes goes in the same token (quotes also go in);
//    . We honor the escape character to allow embedded quote char(s) within a quoted string.
//    . Except as noted above, separator characters are discarded.
//
//
//
// 06-29-16 (jdl): bLankFieldsOK: leading and trailing separators result in blank tokens (before: no token).
//
// qxcutLineIntoTokens()
//

int cutLineIntoTokens(const char *lineIn, const std::string &ssSeparators, std::vector<std::string> &vecTokensOut, bool bTrimSp, bool bLankFieldsOK) {
   vecTokensOut.clear();

   if(lineIn == NULL)
      return -1;


   int lenny          = (int)strlen(lineIn);
   bool bFormingToken = true;
   if(!bLankFieldsOK)
      bFormingToken = false;

   bool bInsideQuotes = false;
   bool bEscape       = false;                  // set true if we hit '\\' (backslash) char.
   std::string ssGrowing = "";

   char c(0), cprev(0), cnext(0);
   for(int icol=0; icol<lenny; icol++) {
      cprev = c;
      c = lineIn[icol];
      cnext= lineIn[icol+1];     // we can reach the null terminator without pissing off runtime.

      if(ssSeparators.find(c) != std::string::npos) {
         // c is a separator token
         if(bFormingToken) {
            if(bInsideQuotes) {
               // this separator character goes in the string:
               ssGrowing += c;
            } else {
               // this separator character terminates the token:
               if(!bLankFieldsOK)
                  bFormingToken = false;
               if(bTrimSp) trim_sp(ssGrowing, ssGrowing);
               vecTokensOut.push_back(ssGrowing);
               ssGrowing.clear();
            } // !if(bInsideQuotes)
         } else {
            // not forming a token; just skip space and tab chars.
         } // !if(bFormingToken)

         continue;
      } // if(c is in the separator list)

      switch(c) {
         case DOUB_QUOTE:
            bFormingToken = true;
            ssGrowing += c;                    // whether we're beginning or ending, we'll include the quote char.
            if(bEscape) {
               // DON'T terminate a the string.
               bEscape = false;
            } else {
               bInsideQuotes = !bInsideQuotes;
               if(!bInsideQuotes) {
                  // save the growing string and clear it:
                  // unless the NEXT char is another double-quote: special case for .rc files:
                  if(cnext == DOUB_QUOTE) {
                     // special case!
                     bInsideQuotes = true;
                     bEscape       = true;
                  } else {
                     // normal:
                     if(!bLankFieldsOK)
                        bFormingToken = false;
                     if(bTrimSp) trim_sp(ssGrowing, ssGrowing);
                     vecTokensOut.push_back(ssGrowing);
                     ssGrowing.clear();
                  } // if(cnext == DOUB_QUOTE)
               } // if(!bInsideQuotes)
            } // !if(bEscape)
            break;
         case '\\':
            bFormingToken = true;
            bEscape  = !bEscape;               // will affect what happens if next char is quote.
            ssGrowing += c;
            break;
         default:
            bFormingToken = true;
            ssGrowing += c;
            bEscape = false;
            break;
      } // switch(c)
   } // for(int icol=0; icol<lenny; icol++)

   if(bFormingToken) {
      if(!bLankFieldsOK)
         bFormingToken = false;
      if(bTrimSp) trim_sp(ssGrowing, ssGrowing);
      vecTokensOut.push_back(ssGrowing);
      ssGrowing.clear();
   } // if(bFormingToken)

   return (int)vecTokensOut.size();
} // end of int cutLineIntoTokens()...





//
// from mio.cpp 11-29-18
//
// new 11-06-98, from $mj/playmidifile.c
// added 10-14-95 from fc.c... modified: no INT_32, etc...
//
//
//
//
// qxdate_format()
//
char *date_format(time_t time_in, char *string_out) {
   char ctime_1[32], ctime_2[32];
   char buffer[64];
   char *cp;

   /* the following code results from problems passing files
    * from the NeXT to DOS: bug, bug, bug!!!
    */
   if(time_in < 0) {
      sprintf(string_out, "[Invalid date...   ]");
      return string_out;
   }


   /* ctime: documented (on NeXT at least!) in 'man ctime' */
#ifdef _WIN32
   ctime_s(buffer, 64, &time_in);
   cp = buffer;
#else
   cp = ctime(&time_in);
#endif


   /* fprintf(stdout, "From time_in %ld, we got: %s.\n", time_in, cp); */

   /* skip day of week.  move year from end to beginning: */
   /* xptl: %15s fills only to white space; %15c works: */
#ifdef _WIN32
   sscanf_s(cp, "%*4s %15c %4c", ctime_1, 32, ctime_2, 32);
#else
   sscanf(cp, "%*4s %15c %4c", ctime_1, ctime_2);
#endif


   /* change 'May  5' to 'May 05' */
   /* if(ctime_1[4] == ' ') ctime_1[4] = '0'; */

   ctime_1[15] = ctime_2[4] = '\0';
   sprintf(string_out, "%s %s", ctime_2, ctime_1);

   return string_out;

} /* end of date_format() */





//
// new 12-26-23, from jca.cpp's kprintf().
//
// Input:
//    ckorm: things like "fix 3", "sci 0", "oct 2", "hex 4", "bin 8".
//    dVal: value to be represented.
//
// Output:
//    return value: string representation of dVal in format specified.
//
//
//
// qxksprintf()
//

std::string ksprintf(const char *ckorm, double dVal) {
   int  i;
   int  iInt;
   char cfloc[256];
   char cbuff[256];
   std::string ssRet;

   // sanity checks:
   if(ckorm == NULL)     return "";
   if(strlen(ckorm) < 5) return "";

   int iFormLen = atoi(ckorm+4);  // "fix 3"
   if(iFormLen < 0) iFormLen = 0;
   if(iFormLen > 40) iFormLen = 40;

   if(strncmp(ckorm, "fix", 3) == 0) {
      sprintf(cfloc, "%%.%df ", iFormLen);
      sprintf(cbuff, cfloc, dVal);
      ssRet += cbuff;
#ifdef MAYBE_LATER
      std::string ssResult = commaDoub(dVal, -1, iFormLen);
      ssRet += "   ";
      ssRet += ssResult;
      // fprintf(fp_out, "   %s", ssResult.c_str());
#endif // MAYBE_LATER
   } else if(strncmp(ckorm, "sci", 3) == 0) {
      sprintf(cfloc, "%%.%dE ", iFormLen);
      sprintf(cbuff, cfloc, dVal);
      ssRet += cbuff;
      // fprintf(fp_out, cfloc, dVal);
   } else if((strncmp(ckorm, "hex", 3) == 0) || (strncmp(ckorm, "oct", 3) == 0) || (strncmp(ckorm, "bin", 3) == 0)) {
      double dBase = 2.0;

      if(strncmp(ckorm, "hex", 3) == 0)
         dBase = 16.0;

      if(strncmp(ckorm, "oct", 3) == 0)
         dBase = 8.0;

      // the point of this new routine; something new:
      if(dVal < 0.0) {
         ssRet += "-";
         // fprintf(fp_out, "-");  // unconventional handling of negative hex output...
         dVal = -dVal;
      } // if(dVal < 0.0)

#ifdef NOT_HELPFUL  // fails in strange cases where rounding causes ripple effects
      // integer rounding (fractional rounding is below):
      if(iFormLen <= 0) dVal += 0.5;
#endif // NOT_HELPFUL  // fails in strange cases where rounding causes ripple effects

      // perform rounding by setting up for truncation; this is more robust:
      double dRound = 0.5;
      for(i=0; i<iFormLen; i++) {
         dRound /= dBase;
      } // for(i=0; i<iFormLen; i++)
      dVal += dRound;

      // how many powers of 16 (or 2) are needed to reduce this below 1.0?
      int iPwr;
      for(iPwr=0; iPwr<700; iPwr++) {   // 700 much huger than should be needed
         if(dVal < 1.0) break;
         dVal /= dBase;
      } // for(iPwr=0; iPwr<700, iPwr++)

      // possibly output leading "0":
      if(iPwr == 0) {
         ssRet += "0";
         // fprintf(fp_out, "0");
      } // if(iPwr == 0)

      // digits to the left of the decimal point:
      for(i=0; i<iPwr; i++) {
         dVal *= dBase;
         iInt = (int)dVal;
         sprintf(cbuff, "%X", iInt);  // single hex digit
         ssRet += cbuff;
         // fprintf(fp_out, "%X", iInt);  // single hex digit
         dVal -= (double)iInt;
      } // for(i=0; i<iPwr; i++)

      if(iFormLen > 0) {
         ssRet += ".";
         // fprintf(fp_out, ".");
      } // if(iFormLen > 0) 

      // digits to the right of the decimal point:
      for(i=0; i<iFormLen; i++) {
         dVal *= dBase;

         iInt = (int)dVal;
         sprintf(cbuff, "%X", iInt);  // single hex digit
         ssRet += cbuff;
         // fprintf(fp_out, "%X", iInt);  // single hex digit
         dVal -= (double)iInt;
      } // for(i=0; i<iFormLen; i++) 
   } else {
      // unrecognized string; internal error:
      return "";
   }

   return ssRet;
} // end of ksprintf()...





// qxformatMode()

std::string formatMode(int mode, bool bRaw) {
   std::string ssRet;

   if(bRaw) {
      ssRet = ksprintf("oct 0", (double)mode);
   } else {
      ssRet += ((bitTest(mode, 14) == 0) ? ('-') : ('d'));
      ssRet += ((bitTest(mode,  8) == 0) ? ('-') : ('r'));
      ssRet += ((bitTest(mode,  7) == 0) ? ('-') : ('w'));
      ssRet += ((bitTest(mode,  6) == 0) ? ('-') : ('x'));
      ssRet += ((bitTest(mode,  5) == 0) ? ('-') : ('r'));
      ssRet += ((bitTest(mode,  4) == 0) ? ('-') : ('w'));
      ssRet += ((bitTest(mode,  3) == 0) ? ('-') : ('x'));
      ssRet += ((bitTest(mode,  2) == 0) ? ('-') : ('r'));
      ssRet += ((bitTest(mode,  1) == 0) ? ('-') : ('w'));
      ssRet += ((bitTest(mode,  0) == 0) ? ('-') : ('x'));
   } // !if(bRaw)

   return ssRet;
} // std::string formatMode(int mode)




//
// new 07-22-23, from bigCpy.cpp.
//
// Input:
//    pattern_u
//    candidate_U
//    pcEquiv:      an array of 256 chars which might equivalence 'a' and 'A', etc.
//
//
//
//
// qxmatchInner()
//

int matchInner(const unsigned char *pattern_u, const unsigned char *candidate_u, const unsigned char *pcEquiv) {
   // int i, j, k;
   int c, cx;
   int negate;
   // const unsigned char *pattern_u = (const unsigned char *)pattern;
   // const unsigned char *candidate_u = (const unsigned char *)candidate;
   int c1 = *pattern_u;
   int c2 = *candidate_u;
   const unsigned char *cp, *cpstart, *cpend;

   if(c1 == '\0') {
      /* pattern_u exhausted... */
      if(c2 == '\0') return 1;    /* both strings exhausted; match! */
      return 0;                   /* pattern_u exhausted but candidate_u not; fail */
   }

   /* at this point, candidate_u MAY be exhausted; we must be careful... */

   switch(c1) {
      case '*':
      case '@':
         /* scan to end of candidate_u string, then work back */
         for(cp=candidate_u; *cp != '\0'; cp++) {}
         while(1) {
            if(matchInner((pattern_u+1), cp, pcEquiv)) return 1;
            if(cp-- == candidate_u) return 0;     /* check candidate_u, fail if fail */
         }
         break;
      case '?':
      case '%':
         if(c2 == '\0') return 0;
         return matchInner((pattern_u+1), (candidate_u+1), pcEquiv);
         break;
      case '[':
         /* oh, I just KNEW you were going to spring one of these! */
         if(c2 == '\0') return 0;

         cpstart = pattern_u;
         for(cpend=cpstart+1; (*cpend != '\0' && *cpend != ']'); cpend++) {}
         if(*cpend == '\0') {
            qprintf(stdout, "Invalid pattern_u: no closing ']': %s \n", pattern_u);
            return 0;
         }
         if(*(cpstart+1) == '!') {
            negate = 1;
            cpstart++;
         } else {
            negate = 0;
         }
         for(cp=cpstart+1; cp<cpend; cp++) {
            switch(c = *cp) {
               case '-':
                  if((cp == cpstart+1) || (cp == cpend-1)) {
                     /* this dash is literal */
                     // if(c2 == c) {
                     // if(same_asc_nc(c2, c)) {
//                   if((*ascCmpFn)(c2, c)) {
                     if(pcEquiv[c2] == pcEquiv[c]) {
                        /* this char matches */
                        if(negate) return 0;          /* oh heck: we DIDN'T want one */
                        return matchInner((cpend+1), (candidate_u+1), pcEquiv);
                     }
                  } else {
                     /* this dash has a special meaning. */
                     int c3 = *(cp-1);
                     int c4 = *(cp+1);
                     if(c3 > c4) { // was oswap(c3, c4);
                        c4 = *(cp-1);
                        c3 = *(cp+1);
                     } // if(c3 > c4)
                     for(cx=c3; cx<=c4; cx++) {
                        // if(same_asc_nc(c2, cx)) {
//                      if((*ascCmpFn)(c2, cx)) {
                        if(pcEquiv[c2] == pcEquiv[cx]) {
                           /* this char matches */
                           if(negate) return 0;          /* oh heck: we DIDN'T want one */
                           return matchInner((cpend+1), (candidate_u+1), pcEquiv);
                        } // if(same_asc_nc(c2, cx))
                     } // for(cx=c3; cx<=c4; cx++)
                  }
                  break;
               default:
                  // if(c2 == c) {
                  // if(same_asc_nc(c2, c)) {
//                if((*ascCmpFn)(c2, c)) {
                  if(pcEquiv[c2] == pcEquiv[c]) {
                     /* this char matches */
                     if(negate) return 0;          /* oh heck: we DIDN'T want one */
                     return matchInner((cpend+1), (candidate_u+1), pcEquiv);
                  }
                  break;
            } /* switch(c = *cp) */
         }    /* for(cp=cpstart+1; cp<cpend; cp++) */
         /*  didn't find it */
         if(negate) return matchInner((cpend+1), (candidate_u+1), pcEquiv);  /* we didn't want to find it */
         return 0;
         break;
      default:
         /* if(c2 == '\0') return 0; */  /* actually not necessary here, but good practice */
         // if(c1 != c2) return 0;    /* qxl: case check? */
         // if(!same_asc_nc(c1, c2)) return 0;
//       if(!(*ascCmpFn)(c1, c2)) return 0;
         if(pcEquiv[c1] != pcEquiv[c2]) return 0;
         // int (*ascCmpFn)(int iasc1, int iasc2) = same_asc_nc;
         return matchInner((pattern_u+1), (candidate_u+1), pcEquiv);
         break;
   }

   qprintf(stdout, "Program error in matchInner(): off the edge of the earth.\n");
   return 0;

} /* end of match() */




// qxmatch()

int match(const unsigned char *pattern_u, const unsigned char *candidate_u, const unsigned char *pcEquiv) {
   int i = matchInner(pattern_u, candidate_u, pcEquiv);

// qprintf(stdout, "We got %d from \"%s\" and \"%s\".\n", i, pattern_u, candidate_u);

   return i;
} // int match(const unsigned char *pattern_u, const unsigned char *candidate_u, const unsigned char *pcEquiv)




// qxmatch()

int match(const char *pattern_u, const char *candidate_u, const unsigned char *pcEquiv) {
   int i = matchInner((const unsigned char *)pattern_u, (const unsigned char *)candidate_u, pcEquiv);

// qprintf(stdout, "We got %d from \"%s\" and \"%s\".\n", i, pattern_u, candidate_u);

   return i;
} // int match(const unsigned char *pattern_u, const unsigned char *candidate_u, const unsigned char *pcEquiv)




//
// new 10-01-20
//
//
//
//
//
// qxmkdirWrap()

int mkdirWrap(const char *pcDestDir, FILE *fp_inf) {
   if(pcDestDir == NULL)
      return NADA;

   // possible quick exit:
   if(is_a_dir(pcDestDir))
      return 0;

   // if it's only one deeper than existing, this will work:
#ifdef _WIN32   
   if(_mkdir(pcDestDir) == 0)
#else   
   if(mkdir(pcDestDir, 0777) == 0)   
#endif   
      return 0;

   // cut it up:
   std::string ssSep = "/\\";
   std::vector<std::string> vecTokensOut;
// int cutLineIntoTokens(const char *lineIn, const std::string &ssSeparators, std::vector<std::string> &vecTokensOut, bool bTrimSp, bool bLankFieldsOK) {
   int ii = cutLineIntoTokens(pcDestDir, ssSep, vecTokensOut);

   for(int i=0; i<ii; i++)
      qprintf(fp_inf, "\"%s\".\n", vecTokensOut[i].c_str());

   std::string ssGrow;
   for(int i=0; i<ii; i++) {
      ssGrow += vecTokensOut[i];
      ssGrow += SLASH;

      if(is_a_dir(ssGrow.c_str())) {
         qprintf(fp_inf, "Exists: \"%s\".\n", ssGrow.c_str());
      } else {
         qprintf(fp_inf, "NOT :   \"%s\".\n", ssGrow.c_str());
#ifdef _WIN32         
         _mkdir(ssGrow.c_str());
#else         
         mkdir(ssGrow.c_str(), 0777);         
#endif         
         if(is_a_dir(ssGrow.c_str())) {
            qprintf(fp_inf, "NOW :   \"%s\".\n", ssGrow.c_str());
         } else {
            qprintf(fp_inf, "FAILED  \"%s\".\n", ssGrow.c_str());
            return NADA;
         } // !if(is_a_dir(ssGrow.c_str()))
      } // !if(is_a_dir(ssGrow.c_str()))
   } // for(int i=0; i<ii; i++)

   int iRet = ((is_a_dir(pcDestDir)) ? 0 : -1);

   return iRet;
} // int mkdirWrap(const char *pcDestDir)




//
//
//  F:/jc/abc.c    ->   F/jc/abc.c
//  /jc/abc.c      ->   ./jc/abc.c
//
//
//
//
// qxunAbsify()

int unAbsify(const std::string &ssAbsIn, std::string &ssRelOut) {
   int iColon = (int)ssAbsIn.find(":");
   if(iColon != std::string::npos) {
      ssRelOut = ssAbsIn.substr(0, iColon) + ssAbsIn.substr(iColon+1, std::string::npos);
      return 0;
   } // if(iColon != std::string::npos)

   if((ssAbsIn.size() > 0) && (is_slash(ssAbsIn[0]))) {
      ssRelOut = ssAbsIn.substr(1, std::string::npos);
      return 0;
   } // if((ssAbsIn.size() > 0) && (is_slash(ssAbsIn[0])))

   // looks like we're relative already:
   ssRelOut = ssAbsIn;
   return 0;
} // int unAbsify(const std::string &ssAbsIn, std::string &ssRelOut)





//
// changes OTHER_SLASH to SLASH
//
//
// qxfixSlashes()

int fixSlashes(const std::string &ssIn, std::string &ssOut) {
   ssOut.clear();
   int nFixed = 0;
   
   for(int i=0; i<(int)ssIn.size(); i++) {
      char c = ssIn[i];

      if(c == OTHER_SLASH) {
         c = SLASH;
         nFixed++;
      } // if(c == OTHER_SLASH)

      ssOut += c;
   } // for(int i=0; i<(int)ssIn.size(); i++)
   
   return nFixed;
} // int fixSlashes(const std::string &ssIn, std::string &ssOut)






//
//
// Input:
//    int_in: number to be represented with commas added.
//    cbuf: buffer to hold comma'd value.
//    len_s: max number of chars cbuf can hold.
//
// Output:
//    cbuf: probably filled with representation of int_in, with commas.  LEFT-justified!
//    return value: cbuf if all is well, NULL if not.
//
//
//
//
//
// qxcomma_me()

char *comma_me(int int_in, char *cbuf, int len_s) {
   int c;
   int count;
   int comma = ',';
   int space = ' ';
   int minus = '-';
   int shrinking = int_in;
   int low_digit;
   int ive_written = 0;              /* write 3, then comma */
   int sign = 1;
   char *pc_walk = cbuf + len_s;    /*  pointer to char walk; must be PRE-DECREMENTED */

   /* wheee!  if he tells us len_s = 10, the address of the last valid 
    * slot is cbuf + 9.  Uhh, there, we'll write '\0', leaving 9 spots
    * in the range cbuf to cbuf + 8.
    */

   shrinking = int_in;
   ive_written = 0;
   pc_walk = cbuf + len_s;

   /* how many guys will this take? */
   count = 0;

   if(shrinking < 0) {
      shrinking = -shrinking;
      count++;
   }

   do {
      if(ive_written == 3) {
         /* put in a comma before writing what would be the 4th digit in a row... */
         count++;
         ive_written = 0;
      }

      low_digit = shrinking % 10;
      shrinking /= 10;

      count++;
      ive_written++;
   } while(shrinking != 0);

   if(count >= len_s) {
      /* no room for it plus '\0' */
      return NULL;
   }


   /* now, actually do it:         */

   shrinking = int_in;
   ive_written = 0;
   pc_walk = cbuf + count + 1;     /* past the final '\0' */

   *(--pc_walk) = '\0';

   if(shrinking < 0) {
      shrinking = -shrinking;
      sign   = -1;
   }

   do {
      if(ive_written == 3) {
         /* put in a comma before writing what would be the 4th digit in a row... */
         *(--pc_walk) = (char)comma;
         ive_written = 0;
      }

      low_digit = shrinking % 10;
      shrinking /= 10;

      c = '0' + low_digit;
      *(--pc_walk) = (char)c;

      ive_written++;
   } while(shrinking != 0);

   if(sign == -1) {
      /* do NOT put in a comma, even if we end up with "-123,456" */
      *(--pc_walk) = (char)minus;
   }

   /* blank out left side of string: */
   /* dead code now that we left-justify: */
   while((--pc_walk) >= cbuf) {
      *pc_walk = (char)space;
   }

   return cbuf;

}              /* end of comma_me() */





//
//
// Input:
//    ll_in: number to be represented with commas added.
//    cbuf: buffer to hold comma'd value.
//    len_s: max number of chars cbuf can hold.
//
// Output:
//    cbuf: probably filled with representation of int_in, with commas.  LEFT-justified!
//    return value: cbuf if all is well, NULL if not.
//
//
//
//
//
// qxcomma_me()

char *comma_me(long long ll_in, char *cbuf, int len_s) {
   int c;
   int count;
   int comma = ',';
   int space = ' ';
   int minus = '-';
   long long shrinking = ll_in;
   int low_digit;
   int ive_written = 0;              /* write 3, then comma */
   int sign = 1;
   char *pc_walk = cbuf + len_s;    /*  pointer to char walk; must be PRE-DECREMENTED */

   /* wheee!  if he tells us len_s = 10, the address of the last valid 
    * slot is cbuf + 9.  Uhh, there, we'll write '\0', leaving 9 spots
    * in the range cbuf to cbuf + 8.
    */

   shrinking = ll_in;
   ive_written = 0;
   pc_walk = cbuf + len_s;

   /* how many guys will this take? */
   count = 0;

   if(shrinking < 0) {
      shrinking = -shrinking;
      count++;
   }

   do {
      if(ive_written == 3) {
         /* put in a comma before writing what would be the 4th digit in a row... */
         count++;
         ive_written = 0;
      }

      low_digit = shrinking % 10;
      shrinking /= 10;

      count++;
      ive_written++;
   } while(shrinking != 0);

   if(count >= len_s) {
      /* no room for it plus '\0' */
      return NULL;
   }


   /* now, actually do it:         */

   shrinking = ll_in;
   ive_written = 0;
   pc_walk = cbuf + count + 1;     /* past the final '\0' */

   *(--pc_walk) = '\0';

   if(shrinking < 0) {
      shrinking = -shrinking;
      sign   = -1;
   }

   do {
      if(ive_written == 3) {
         /* put in a comma before writing what would be the 4th digit in a row... */
         *(--pc_walk) = (char)comma;
         ive_written = 0;
      }

      low_digit = shrinking % 10;
      shrinking /= 10;

      c = '0' + low_digit;
      *(--pc_walk) = (char)c;

      ive_written++;
   } while(shrinking != 0);

   if(sign == -1) {
      /* do NOT put in a comma, even if we end up with "-123,456" */
      *(--pc_walk) = (char)minus;
   }

   /* blank out left side of string: */
   /* dead code now that we left-justify: */
   while((--pc_walk) >= cbuf) {
      *pc_walk = (char)space;
   }

   return cbuf;

}              /* end of comma_me() */



#define CR (13)
#undef  LF
#define LF (10)


// qxrmCr()

std::string rmCr(const std::string &ssIn) {
   std::string ssBuild;
   for(int i=0; i<(int)ssIn.size(); i++) {
      char c = ssIn[i];
      if(c != CR)
         ssBuild += c;
   } // for(int i=0; i<ssIn.size(); i++)

   return ssBuild;
} // std::string rmCr(const std::string &ssIn)






//
// qxadCr()

std::string adCr(const std::string &ssIn) {
   std::string ssBuild;

   for(int i=0; i<(int)ssIn.size(); i++) {
      char c = ssIn[i];
      switch(c) {
         case CR:
            // eat any stray CR's, but add one with each LF.
            break;
         case LF:
            ssBuild += CR;
            // fall through!
         default:
            ssBuild += c;
            break;
      } // switch(c)
   } // for(int i=0; i<ssIn.size(); i++)

   return ssBuild;
} // std::string adCr(const std::string &ssIn)




// qxssVcToSs()

std::string ssVcToSs(const std::vector<std::string> &vcSsIn, bool bCr) {
   std::string ssBuild;

   for(int i=0; i<(int)vcSsIn.size(); i++) {
      const std::string &ssLin = vcSsIn[i];

      for(int j=0; j<(int)ssLin.size(); j++) {
         char c = ssLin[j];
         switch(c) {
            case CR:
            case LF:
               // eat these; we'll add one or both at the end.
               break;
            default:
               ssBuild += c;
               break;
         } // switch(c)
      } // for(int j=0; j<(int)ssLin.size(); j++)

      if(bCr)
         ssBuild += CR;

      ssBuild += LF;
   } // for(int i=0; i<(int)vcSsIn.size(); i++)

   return ssBuild;
} // std::string ssVcToSs(const std::vector<std::string> &vcSsIn, bool bCr)



// qxcountCrLf()

int countCrLf(const char *pcIn, int &nCrOut, int &nLfOut) {
   if(pcIn == NULL) {
      nCrOut = nLfOut = NADA;
      return NADA;
   } else {
      nCrOut = nLfOut = 0;
   } // !if(pcIn == NULL)

   int lenn = (int)strlen(pcIn);

   for(int i=0; i<lenn; i++) {
      char c = pcIn[i];
      switch(c) {
         case CR:
            break;
         case LF:
            break;
         default:
            break;
      } // switch(c)
   } // for(int i=0; i<lenn; i++)

   return 0;
} // int countCrLf(const char *pcIn, int &nCrOut, int &nLfOut)




//
//
// qxvcUcToSs()

int vcUcToSs(const std::vector<unsigned char> &vcIn, std::string &ssOut) {
   ssOut.clear();

   for(unsigned long long ill=0; ill<vcIn.size(); ill++)
      ssOut += (char)vcIn[ill];

   return 0;
} // int vcUcToSs(const std::vector<unsigned char> &vcIn, std::string &ssOut)





//
//
// qxvcSsToSs()

int vcSsToSs(const std::vector<std::string> &vcIn, std::string &ssOut) {
   ssOut.clear();

   for(unsigned long long ill=0; ill<vcIn.size(); ill++) {
      ssOut += vcIn[ill];
      ssOut += LF;
   } // for(unsigned long long ill=0; ill<vcIn.size(); ill++)

   return 0;
} // int vcSsToSs(const std::vector<std::string> &vcIn, std::string &ssOut)





//
//
//
//
//
//
// qxfindDenom()
//

int findDenom(double dRat, int denomLo, int denomTooHi) {
   int iDenBest(NADA);
   double minErr = 17004.0;  // start high; work down to 1.00001, etc.
   double minEr3 = 17004.0;  // error relative to denominator
   double dSlop = .00001 / (double)denomTooHi;

   // fprintf(stdout, "This is findDenom() with dRat %12.9f, maxDenom %d.\n", dRat, maxDenom);

   for(int iDen=MAX(1, denomLo); iDen<denomTooHi; iDen++) {
      double dNum = (double)iDen * dRat;
      int    iNum = (int)(dNum + 0.5);   // as close as we can get with this denominator.
      iNum        = MAX(1, iNum);
      double dRes = (double)iNum / (double)iDen;
      double dErr = dRes / dRat;
      if(dErr < 1.0) dErr = 1.0 / dErr;  // get something just above 1.0
      double dEr2 = dErr - 1.0;
      double dEr3 = dEr2 * (double)iDen;
      if(dErr < (minErr - dSlop)) {
         char cParen1 = '(';
         char cParen2 = ')';
         if(dEr3 < minEr3) {
            cParen1 = ' ';
            cParen2 = '|';
            minEr3 = dEr3;
         } // if(dEr3 < minEr3)

         // fprintf(stdout, "%cFound %5d / %5d = %12.9f, ratio %12.9f  %9.6f%c\n", cParen1, iNum, iDen, dRes, dErr, dEr3, cParen2);
         minErr   = dErr;
         iDenBest = iDen;
      } // if(dErr < minErr)
   } // for(int iDen=MAX(1, denomLo); iDen<denomTooHi; iDen++)


   return iDenBest;
} // end of findDenom()...




// qxfindNumDenom()

int findNumDenom(double dRat, int denomLo, int denomTooHi, int &numOut, int &denOut) {

   denOut = findDenom(dRat, denomLo, denomTooHi);
   numOut = MAX(1, (int)(0.5 + (double)denOut * dRat));

   return 0;
} // int findNumDenom(double dRat, int denomLo, int denomTooHi, int &numOut, int &denOut)




//
//
//
// Input:
//    n: number of items; we watch for 1 or not-1.
//    cpPlural (default NULL): if non-NULL, use this; else use "s" to form plural.
//    cpSingular (default NULL): if non-NULL, use this; else use "" for singular.
//
//
//
//
// Sample calls:
//    fprint(stdout, "I found %d dog%s.\n", nDogs, fnPlural(nDogs));
//    fprint(stdout, "I found %d fox%s.\n", nFoxes, fnPlural(nFoxes, "es"));
//    fprint(stdout, "I found %d g%s.\n", nGeese, fnPlural(nGeese, "eese", "oose"));
//    fprint(stdout, "I found %d moose%s.\n", nMoose, fnPlural(nMoose, ""));         // of course, we could skip fnPlural()
//
//
//
//
// qxfnPlural()
//

const char *fnPlural(int n, const char *cpPlural, const char *cpSingular) {
   const char *pcRet = NULL;

   if(n == 1) {
      // singular:
      if(cpSingular == NULL) {
         pcRet = "";
      } else {
         pcRet = cpSingular;
      } // !if(cpSingular == NULL)
   } else {
      // plural (0 comes here (is this correct?)):
      if(cpPlural == NULL) {
         pcRet = "s";
      } else {
         pcRet = cpPlural;
      } // !if(cpPlural == NULL)
   } // !if(n == 1)

   return pcRet;
} // end of const char *fnPlural(int n)...






// qxShuffled()

Shuffled::Shuffled(int nEntries, MT64 *pMT, bool bOne, bool bRestr, const std::vector<int> *pvcBitOrder, bool bOnes) : m_nAdvance(0), m_nReshuf(0), m_bOneForAll(bOne), m_bRestrict(bRestr), m_bOnesCompl(bOnes) {
   if(pvcBitOrder == NULL) {
      for(int i=0; i<8; i++)
         m_vcBitOrder.push_back(i);
   } else {
      m_vcBitOrder = *pvcBitOrder;
   } // !if(pvcBitOrder == NULL)

   if(m_bOneForAll) {
      setSize(256, pMT);
   } else {
      setSize(nEntries, pMT);
   } // !if(bOneForAll)
} // Shuffled(int nEntries=0, MT64 *pMT=NULL)

Shuffled::Shuffled(const Shuffled &rs) : m_vcSchuf(rs.m_vcSchuf), m_walk(rs.m_walk), m_nAdvance(rs.m_nAdvance), m_nReshuf(rs.m_nReshuf), m_bOneForAll(rs.m_bOneForAll), m_bRestrict(rs.m_bRestrict), m_bOnesCompl(rs.m_bOnesCompl), m_vcBitOrder(rs.m_vcBitOrder) {}





// qx~Shuffled()

Shuffled::~Shuffled() {}



// qxShuffled::operator=()

const Shuffled &Shuffled::operator=(const Shuffled &rs) {
   if(&rs != this) {
      m_vcSchuf    = rs.m_vcSchuf;
      m_walk       = rs.m_walk;
      m_nAdvance   = rs.m_nAdvance;
      m_nReshuf    = rs.m_nReshuf;
      m_bOneForAll = rs.m_bOneForAll;
      m_bRestrict  = rs.m_bRestrict;
      m_bOnesCompl = rs.m_bOnesCompl;
      m_vcBitOrder = rs.m_vcBitOrder;
   } // if(&rs != this)

   return *this;
} // const Shuffled &operator=(const Shuffled &rs)





// qxsetSize()
int Shuffled::setSize(int nEntries, MT64 *pMT) {
   if(nEntries < 0)
      return NADA;

   m_walk = 0;

   m_vcSchuf.resize(nEntries);
   for(int i=0; i<nEntries; i++)
      m_vcSchuf[i] = i;

   if((nEntries > 1) && (pMT != NULL))
      this->doShuf(pMT);

   // qxtemp:
   if(m_bOneForAll) {
      bool bDbg = false;
      if(bDbg) {
         for(int i=0; i<nEntries; i++) {
            qprintf(fp_dbg, "   %3d  %02X   %3d  %3d  %3d  %3d  %3d  %3d  %3d  %3d\n", i, i, m_vcSchuf[i], (m_vcSchuf[i] & 127),(m_vcSchuf[i] & 63),(m_vcSchuf[i] & 31),(m_vcSchuf[i] & 15),(m_vcSchuf[i] & 7),(m_vcSchuf[i] & 3), (m_vcSchuf[i] & 1));
         } // for(int i=0; i<nEntries; i++)
      } // if(bDbg)
   } // if(m_bOneForAll)

   return nEntries;
} // int setSize(int nEntries=0, MT64 *pMT=NULL)


//
// static function.
//
//
//
// 05-15-25: bTrim (default false).
// 05-15-25: mostestBits (default NADA).
//
// qxmakeVec()

int Shuffled::makeVec(int nNumer, int nDenom, std::vector<int> &vcBitOrderOut, bool bTrim, int mostestBits) {
   vcBitOrderOut.clear();

   if((nNumer <= 0) || (nDenom <= 0) || (nNumer > nDenom))
      return NADA;

   int maxBits(0), nReduce(0), nRemain(0), nUse(0);

   if(!bTrim) {
      // version 1.08: release in haste, repent at leisure:
      maxBits = 6;          // max number of bits we ever expect to embed in one pixel-color byte with bScramble.
      nReduce = (maxBits * nNumer) / nDenom;
      nRemain = (maxBits * nNumer) % nDenom;

      nUse = ((nRemain == 0) ? (nReduce) : (nReduce + 1));
   } else {
      // version 1.09 and later:
      if(mostestBits <= 0) {
         maxBits = 7;          // value which when ratioed down can be real, though 6 is max we'll ever embed.
         nReduce = (maxBits * nNumer) / nDenom;
         nRemain = (maxBits * nNumer) % nDenom;

         nUse = MIN(6, ((nRemain == 0) ? (nReduce) : (nReduce + 1)));
      } else {
         // this comes right out of calcScatter(), definitive.
         nUse = mostestBits;
      } // !if(pBL == NULL)
   } // !if(!bTrim) {

   int highBit = nUse - 1;    // 0-base

   for(int i=highBit; i>=0; i--)
      vcBitOrderOut.push_back(i);

   if(!bTrim) {
      for(int i=highBit+1; i<8; i++)
         vcBitOrderOut.push_back(i);
   } // if(!bTrim)

   qprintf(fp_dbg, "\nvcBitOrder bit order: ");
   for(int i=0; i<(int)vcBitOrderOut.size(); i++)
      qprintf(fp_dbg, "%d ", vcBitOrderOut[i]);
   qprintf(fp_dbg, "\n");

   return 0;
} // int Shuffled::makeVec(int nNumer, int nDenom, std::vector<int> &vcBitOrderOut)



// qxdoShuf()

int Shuffled::doShuf(MT64 *pMT) {
   static bool bDidShow = false;

   // qprintf(fp_dbg, "This is doShuf() with bDidShow %d, non-NULL pMT %d.\n", bDidShow, (pMT != NULL));  // qxtemp!

   if(pMT == NULL)
      return NADA;


   int nElem  = (int)m_vcSchuf.size();
   int nMoved = 0;

   if(!m_bOneForAll) {
      // always, before 05-07-25.  Any number of elements, shuffled like a deck of cards.
      if(nElem < 2)
         return NADA;

      m_nReshuf++;

      for(int iElem=0; iElem<nElem; iElem++) {
         int ioffs = pMT->rand_n_quick(nElem - iElem);
         int jElem = iElem + ioffs;
         if(jElem != iElem) {
            // tswap(pScrambleMe[iElem], pScrambleMe[jElem]);
            int ihold = m_vcSchuf[iElem];
            m_vcSchuf[iElem] = m_vcSchuf[jElem];
            m_vcSchuf[jElem] = ihold;

            nMoved++;
         } // if(jElem != iElem)
      } // for(int iElem=0; iElem<nElem; iElem++)
   } else {
      // bOneForAll: new 05-07-25
      if(nElem != 256)
         return NADA;

      BitSrc aBS;

      std::vector<int> vcBackward;
      vcBackward.resize(256);

      int nCalls0 = MT64::nAllCalls();

      if(m_bOnesCompl) {

// #undef TEST_ONES_COMPL

// #ifdef TEST_ONES_COMPL

         m_vcSchuf.clear();

         Shuffled ashTmp(128, pMT);

         for(int i=0; i<128; i++) {
            int j = ashTmp.getCurrent(pMT);
            int k = 255 - j;                  // 1's complement

            if(aBS.getBit(pMT) == 0) {
               m_vcSchuf.push_back(j);
               m_vcSchuf.push_back(k);
            } else {
               m_vcSchuf.push_back(k);
               m_vcSchuf.push_back(j);
            } // if(aBS.getBit(pMT) == 0)
         } // for(int i=0; i<128; i++)

      } else {
         // Usual: !m_bOnesCompl

// #else // !TEST_ONES_COMPL

#ifdef OUTTT
         std::vector<int> vcOldWay;
         for(int i=0; i<8; i++)
            vcOldWay.push_back(i);

         const std::vector<int> *pVcUse = (((pvcBitOrder == NULL) || (pvcBitOrder->size() == 0)) ? (&vcOldWay) : (pvcBitOrder));
#endif // OUTTT


         for(int i=0; i<256; i++) m_vcSchuf[i] = 0;

         for(int iBit=0; iBit<(int)m_vcBitOrder.size(); iBit++) {
            int kBit = m_vcBitOrder[iBit];
            int kVal = 1 << kBit;

            int halfReach = 1 << iBit;                   // 1, 2, 4,  8, ... 128
            int fullReach = 2 * halfReach;               // 2, 4, 8, 16, ... 256

            int nFull     = 256 / fullReach;             // 128,  64, 32, ... 1
            int nHalf     = 2 * nFull;                   // 256, 128, 64, ... 2

            bool bUseBack = (nFull < 16);

            for(int ii=0; ii<nFull; ii++) {
               int iOff0 = ii * fullReach;
               int iOff1 = iOff0 + halfReach;

               if(bUseBack) {
                  for(int i=0; i<halfReach; i++) {
                     int rghtVal = m_vcSchuf[i + iOff1];
                     vcBackward[rghtVal] = i + iOff1;
                  } // for(int i=0; i<halfReach; i++)
               } // if(bUseBack)

               // walk through the left-hand values; match up to right-hand values for possible swapping:
               int n0(halfReach/2), n1(halfReach/2);

               for(int jj=0; jj<halfReach; jj++) {
                  int jVal = m_vcSchuf[jj + iOff0];

                  bool bLeft1 = false;
                  if(!m_bRestrict || (iBit == 0)) {       // m_bRestrict calcs don't work on lowest row
                     bLeft1 = (aBS.getBit(pMT) == 0);
                  } else {
                     // we are sorting a list of equal 0's and 1's.
                     int itst = pMT->rand_n_quick(n0 + n1);
                     if(itst < n0) {
                        bLeft1 = true;
                        n0--;
                     } else {
                        bLeft1 = false;
                        n1--;
                     } // !if(itst < n0)
                  } // !if(!m_bRestrict)

                  if(bLeft1) {
                     // easy: leave the new 1-bit on the left:
                     m_vcSchuf[jj + iOff0] = jVal + kVal;
                  } else {
                     // put the new 1-bit on the right.
                     if(bUseBack) {
                        int indx = vcBackward[jVal];
                        m_vcSchuf[indx] = jVal + kVal;
                     } else {
                        // harder: must find the same value on the right side:
                        for(int kk=0; kk<halfReach; kk++) {
                           int mVal = m_vcSchuf[kk + iOff1];
                           if(mVal == jVal) {
                              // what we want.
                              m_vcSchuf[kk + iOff1] = jVal + kVal;
                              break;
                           } // if(mVal == jVal)
                        } // for(int kk=0; kk<halfReach; kk++)
                     } // !if(bUseBack)
                  } // !if(bLeft1)

               } // for(int jj=0; jj<halfReach; jj++)

            } // for(int ii=0; ii<nFull; ii++)

         } // for(int iBit=0; iBit<8; iBit++)

#ifdef NOT_USED
         // qxtemp?  Compensate for number of random calls not made:
         if(pMT != NULL) {
            int nNotMade = 2 * (8 - (int)pVcUse->size());
            for(int i=0; i<nNotMade; i++)
               pMT->genrand64_int64();
         } // if(pMT != NULL)
#endif // NOT_USED


      } // !if(m_bOnesCompl)
// #endif // !TEST_ONES_COMPL


      int nCalls1 = MT64::nAllCalls();

      if(!bDidShow) {
         qprintf(fp_dbg, "bOneforAll generation took %d random calls.\n", (nCalls1 - nCalls0));
         bDidShow = true;
      } // if(!bDidShow)

   } // !if(!m_bOneForAll)

   return nMoved;
} // int doShuf(MT64 *pMT)





// qxgetCurrent()
int Shuffled::getCurrent(MT64 *pMT, bool bPeek) {
   if(m_walk >= (int)m_vcSchuf.size()) {
      m_walk = 0;
      if(pMT != NULL)
         this->doShuf(pMT);
   } // if(m_walk >= (int)m_vcSchuf.size())

   int iRet = m_vcSchuf[m_walk];

   if(!bPeek) {
      m_walk++;
      m_nAdvance++;
   } // if(!bPeek)

   return iRet;
} // int getCurrent(MT64 *pMT=NULL, bool bPeek=false)





// from $95/fc.cpp:
const static unsigned char smc[] = {
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
   0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
   0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
   0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
   0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
   0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
   0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
   0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
   0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
   0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
   0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
   0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
   0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
   0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
   0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
   0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};



// qxl: movement in the upper half of ASCII?
const static unsigned char upc[] = {
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
   0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
   0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
   0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
   0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
   0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
   0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
   0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
   0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
   0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
   0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
   0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
   0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
   0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
   0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
   0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};



// qxl: movement in the upper half of ASCII?
const static unsigned char lwc[] = {
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
   0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
   0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
   0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
   0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
   0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
   0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
   0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
   0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
   0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
   0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
   0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
   0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xD7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
   0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
   0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
   0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};



// qxgetUpc()
const unsigned char *getUpc() {return upc;}

// qxgetLwc()
const unsigned char *getLwc() {return lwc;}

// qxgetSmc()
const unsigned char *getSmc() {return smc;}




// doLwc()

int doLwc(std::string &ssInOut) {
   int iLen = (int)ssInOut.size();
   const unsigned char *pLwc = getLwc();

   for(int i=0; i<iLen; i++) {
      unsigned char c = (unsigned char)ssInOut[i];
#ifdef TESTING
      char          cch = ssInOut[i];
      unsigned char uuh = ssInOut[i];
#endif // TESTING
      ssInOut[i] = pLwc[c];
   } // for(int i=0; i<iLen; i++)

   return 0;
} // int doLwc(std::string &ssInOut)





#ifdef _WIN32

//
// new, code taken from main() 02-20-20
//
//
//
// qxgetProccyTime()
//

unsigned long long getProccyTime() {
   static HANDLE mainHandle = NULL;
   if(mainHandle == NULL)
      mainHandle = GetCurrentProcess();

   FILETIME creationTime, exitTime, kernelTime, userTime;
   BOOL bRetTot = GetProcessTimes(mainHandle, &creationTime, &exitTime, &kernelTime, &userTime);
   unsigned long long ullTot = combineFiletime(userTime);

   return ullTot;
} // unsigned long long getProccyTime()...




//
// new 02-20-20
//
//
//
// qxgetProccyTime()
//

unsigned long long getProccyTime(std::vector<unsigned long long> &vcAppend) {
   unsigned long long ullTot = getProccyTime();
   vcAppend.push_back(ullTot);

   return ullTot;
} // unsigned long long getProccyTime(std::vector<unsigned long long> &vcAppend)

#endif // _WIN32


#ifndef _WIN32



#include <stdio.h>
// #include <windows.h>
// #include <conio.h>
// #include <stdlib.h>
// #include <string.h>
#include <string>
// #include <vector>
// #include <set>
// #include <cmath>
//  #include "syscalls.h"    not found
// #include <fcntl.h>
#include <time.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include "jtypes.h"
// #include "op_incFC.h"

#include <unistd.h>




// the following is from:
// http://stackoverflow.com/questions/7469139/what-is-equivalent-to-getch-getche-in-linux

#include <termios.h>
#include <stdio.h>

static struct termios oldd;
static struct termios neww;

/* Initialize new terminal i/o settings */
void initTermios(int echo) 
{
  tcgetattr(0, &oldd); /* grab old terminal i/o settings */
  neww = oldd; /* make new settings same as old settings */
  neww.c_lflag &= ~ICANON; /* disable buffered i/o */
  neww.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
  tcsetattr(0, TCSANOW, &neww); /* use these new terminal i/o settings now */
}

/* Restore old terminal i/o settings */
void resetTermios(void) 
{
  tcsetattr(0, TCSANOW, &oldd);
}

/* Read 1 character - echo defines echo mode */
char getch_(int echo) 
{
  char ch;
  initTermios(echo);
  ch = getchar();
  resetTermios();
  return ch;
}

/* Read 1 character without echo */
char getch(void) 
{
  return getch_(0);
}

/* Read 1 character with echo */
char getche(void) 
{
  return getch_(1);
}






int kbhit() {
    fd_set rfds;
    struct timeval tv;
    int retval;
    int iRet = 0;

    /* Watch stdin (fd 0) to see when it has input. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    /* Wait up to five msec. */
    tv.tv_sec = 0;
    tv.tv_usec = 5000;

    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Don't rely on the value of tv now! */

    if (retval == -1) {
        // perror("select()");
        iRet = 0;
    } else if (retval) {
        // printf("Data is available now.\n");
#ifdef NOT_HERE
        for(int i=0; i<retval; i++) {
           int c = getc(stdin);
           fprintf(stdout, "Character %d  (0x%X) (%c).\n", c, c, c);
        } // for(int i=0; i<retval; i++)
#endif // NOT_HERE
        iRet = retval;
        /* FD_ISSET(0, &rfds) will be true. */
    } else {
        // printf("No data within five msec.\n");
        iRet = 0;
    }

   return iRet;
} // int kbhit()



static struct termios stored_settings;

void set_keypress(void) {
    struct termios new_settings;

    tcgetattr(0,&stored_settings);

    new_settings = stored_settings;

    /* Disable canonical mode, and set buffer size to 1 byte */
    new_settings.c_lflag &= (~ICANON);
    new_settings.c_lflag &= (~ECHO);       // xptl 11-28-23
    new_settings.c_cc[VTIME] = 0;
    new_settings.c_cc[VMIN] = 1;

    tcsetattr(0,TCSANOW,&new_settings);
    return;
}

void reset_keypress(void) {
    tcsetattr(0,TCSANOW,&stored_settings);
    return;
}


#endif // !_WIN32





//
// from $rt/globs.cpp.
//
//
//
//
//
// qxtimeGetTime64()
//

signed long long timeGetTime64() {
   static unsigned long      _prevVal    = 0;
   static unsigned long long _wrapOffset = 0;
   unsigned long newVal = 0;


#ifdef _WIN32
   newVal = (unsigned long)timeGetTime();
#else
   struct timespec tsp;
#endif // !_WIN32

   if (newVal < _prevVal) _wrapOffset += (((unsigned long long)1)<<32);
   _prevVal = newVal;
   signed long long llRet = (signed long long)(_wrapOffset + newVal);

   return llRet;
}








#define MY_BUF_SIZE  (1048576)

#ifdef _WIN32

#define NEWF_FLAGS (_O_WRONLY | _O_CREAT | _O_BINARY)
#define OLDF_FLAGS (_O_RDONLY            | _O_BINARY)

#else

#define NEWF_FLAGS (O_WRONLY | O_CREAT )
#define OLDF_FLAGS (O_RDONLY           )

#endif // !_WIN32



//#ifdef TEMP_OUT


//
//
//
//
// 10-01-20: change return convention.
// 11-20-20: more display.
// 05-17-25: rework #ifdef's.
//
// qxjcpp()

int jcpp(const char *file_from, const char *file_to) {
   int i;
   int n;
   int mode;
   int f1, f2;
   int longestDiag = 0;           // 03-27-00
   long long n_read_tot = 0;
   char cbuf[512];                // 03-27-00
   FILE *fp;



#ifdef _WIN32

   struct _utimbuf utb;   // 10-18-96
   struct _stat64  stbu64;    // for big file sizes (<2^31).
   if(_stat64(file_from, &stbu64) == -1) {
      fprintf(stdout, "%s is NOT found.\n", file_from);
      return NADA;
   }
#else

   struct timeval tvp[2];
   struct stat64  stbu64;     // for big file sizes (<2^31).
   if(stat64(file_from, &stbu64) == -1) {
      fprintf(stdout, "%s is NOT found.\n", file_from);
      return NADA;
   }
#endif // !_WIN32

   long long llSize = stbu64.st_size;

   if((stbu64.st_mode & S_IFMT) != S_IFDIR) {
      // fprintf(stdout, "%s is NOT a directory.\n", file_from);
   }

   if((fp = fopen(file_to, "r")) != NULL) {
      fprintf(stdout, "Attempt to write to existing file: %s.\n", file_to);
      fclose(fp);
      return NADA;
   }


   mode = stbu64.st_mode;

   bool bDbg = false;
   if(bDbg) {
      std::string ssOct = ksprintf("oct 0", (double)mode);
      qprintf(stdout, "Mode: %s\n", ssOct.c_str());
   } // if(bDbg)

   // fprintf(stdout, "mode=%d; compare to %d and %d.\n", mode, _S_IREAD, _S_IWRITE);

   // jdl 01-29-96:
   if((mode & ME_WRITE) != ME_WRITE) {
      mode |= ME_WRITE;
      // fprintf(stdout, "Changed mode from %o to %o.\n", stbuf.st_mode, mode);
   }


#ifdef _WIN32

   if((f2 = open(file_to, NEWF_FLAGS, mode)) == -1) {
      fprintf(stdout, "Can't create dest file: %s.\n", file_to);
      return NADA;
   }

#else

   // seems to do a better job of getting modes right...
   if((f2 = creat(file_to, mode)) == -1) {
      fprintf(stdout, "Can't create dest file: %s.\n", file_to);
      return NADA;
   }

#endif

   if((f1 = open(file_from, OLDF_FLAGS, 0)) == -1) {
      fprintf(stdout, "Can't open source file: %s.\n", file_from);
      close(f2);
      return NADA;
   }

   SMARTA(pcBuf, char, MY_BUF_SIZE, smaBuf);

   int iszPretend = MY_BUF_SIZE;


#ifdef NOT_HERE
   // 03-26-00: watch time as we're going:
   signed long long tgt0 = timeGetTime64();          // never moves
   signed long long tgt1 = tgt0;                     // moves whenever we display.
#endif // NOT_HERE

   while((n = read(f1, pcBuf, iszPretend)) > 0) {
      // fprintf(stdout, "jcpp() reads %d bytes from %s.\n", n, file_from);
      n_read_tot += (long long)n;

      iszPretend = MIN((2 * iszPretend), MY_BUF_SIZE);

      if(write(f2, pcBuf, n) != n) {
         fprintf(stdout, "Wasn't able to write bytes to dest file %s.\n", file_to);
         close(f1);
         close(f2);
         return NADA;
      }

#ifdef NOT_HERE
      bool bTgt = true;

      signed long long tgt2     = 0;
      bool             bDisplay = false;
      if(bTgt) {
         tgt2     = timeGetTime64();
         bDisplay = ((tgt2 - tgt1) > 500);
      } // if(bTgt)

      const int iLenn = 64;
      char cbuLuc1[iLenn], cbuLuc2[iLenn];
      double dRat = 0.0;
      double dSec = 0.0;

      if(bDisplay) {
         tgt1 = tgt2;
         dRat = (double)n_read_tot / (double)llSize;
         dSec = (double)(tgt2 - tgt0) / 1000.0;           // msec -> sec.
         comma_me(n_read_tot, cbuLuc1, iLenn);
         comma_me(llSize,     cbuLuc2, iLenn);
         sprintf(cbuf, "Have written %s bytes of %s (%.2f%%) of %s in %5.1f seconds.", cbuLuc1, cbuLuc2, 100.*dRat, file_to, dSec);
         iLen = strlen(cbuf);
         if(iLen > longestDiag) longestDiag = iLen;
         fprintf(stdout, "%s\r", cbuf);
         fflush(stdout);
      } // if(bDisplay)
#endif // NOT_HERE

   } // while((n = read(f1, pcBuf, iszPretend)) > 0) 

   close(f1);
   close(f2);


   // 03-27-00:
   if(longestDiag > 0) {
      sprintf(cbuf, "%*s", longestDiag, "");
      fprintf(stdout, "%s\r", cbuf);
      fflush(stdout);
   } // if(longestDiag > 0) 


   if(n_read_tot == llSize) {
      // Good!
   } else {
      // BAD!
      fprintf(stderr, "BAD: we should have gotten %lld; only got %lld.\n", llSize, n_read_tot);
   }

#ifdef _WIN32
   // new 10-18-96: use _utime():
   utb.actime  = stbu64.st_atime;
   utb.modtime = stbu64.st_mtime;

   if((i = _utime(file_to, &utb)) != 0) {
      fprintf(stderr, "Bad _utime() return value: %d.\n", i);
      return NADA;
   }
#else
   tvp[0].tv_sec = stbu64.st_atime;
   tvp[0].tv_usec = 0;

   tvp[1].tv_sec = stbu64.st_mtime;
   tvp[1].tv_usec = 0;

   if((i = utimes(file_to, tvp)) != 0) {
      fprintf(stdout, "Bad utimes() return value: %d.\n", i);
      return NADA;
   }

#endif

   // jdl 01-30-96: if source was read-only, set dest that way also:
   if(mode != stbu64.st_mode) {
      mode = stbu64.st_mode;
      chmod(file_to, mode);
   }

   // fprintf(stdout, "Results: %d.\n", i);

   return 0;

} // end of jcpp()...

// #endif // TEMP_OUT








//
//
// Input:
//    pcOldName:      ex "abc.def.wav"
//    pcNewExtension: ex ".txt"
//
// Output: newly formed name: ex "abc.def.txt"
//
//
//
//
// qxReplaceExtension()
//

std::string ReplaceExtension(const char *pcOldName, const char *pcNewExtension) {
   if(pcOldName == NULL)
      return "";

   if(pcNewExtension == NULL)
      return "";

   std::string ssRet;
   std::string ssOrig = pcOldName;
   int ipos = (int)ssOrig.find_last_of('.');
   if(ipos == std::string::npos) {
      ssRet = ssOrig + pcNewExtension;
   } else {
      ssRet = ssOrig.substr(0, ipos) + pcNewExtension;
   } // !if(ipos == std::string::npos)

   return ssRet;
} // std::string ReplaceExtension(const char *pcOldName, const char *pcNewExtension)


#ifndef _WIN32
#define _kbhit kbhit
#define _getch getch
#endif // !_WIN32



//
// new 08-23-11, from randFromPwd()
//
//
//
// Input:
//    fillMe: vector to be filled.
//    [user types password, followed by Enter]
//
//
// Output:
//    fillMe: 10 64-bit integerry guys.
//
//
//
//
//
//
// 06-10-13 (jdl): support back-space.
// 08-20-25 (jdl): move from mt64.cpp to globs.cpp.
//
// qxgetSeedFromPwd()
//

int getSeedFromPwd(std::vector<unsigned long long> &fillMe) {
   int teeRoo = 1;
   int nTyped = 0;
   std::string ssPassword;

#ifndef _WIN32
   set_keypress();
#endif

   while(teeRoo) { // not really
      // if(_kbhit()) {
         int ic = _getch();
         // qprintf(fp_std, "%c %d", ic, ic);  // qxtemp!
#ifdef _WIN32         
         if(ic == 0x0D) break; // enter key
#else         
         if(ic == 0x0A) break; // enter key         
#endif         

         if((ic == 0x08) || (ic == 0x7f)) {      // back-space.
            int nuSiz = MAX(0, ((int)ssPassword.size()-1));
            // qprintf(fp_std, "\nnuSiz %d.\n", nuSiz);
            ssPassword.resize(nuSiz);
            // qprintf(fp_std, "did that\n");
            continue;
         } // if(ic == 0x08)

         // mprintf(fp_std, "Well, got %c (%02X).\n", ic, ic);

         ssPassword += (char)ic;
         nTyped++;
      // } // if(_kbhit()) 
      if(nTyped > 100) break;           // qxl: do we even need this??  Could we trap ctl-c?
   } // while(TRUE)  // not really

   if(file_t("showPwd.txt") > 0)
      qprintf(fp_std, "\nPassword: \"%s\".\n", ssPassword.c_str());

   // qprintf(fp_std, "About to call reset_keypress().\n");  // qxtemp

#ifndef _WIN32
   reset_keypress();
#endif

   // qprintf(fp_std, "Back from reset_keypress() with password \"%s\".\n", ssPassword.c_str());  // qxtemp

   MT64::bytesToVector((const unsigned char *)ssPassword.c_str(), (int)ssPassword.length(), fillMe);
   
   // qprintf(fp_std, "Back from bytesToVector().\n");  // qxtemp   
   zeroMe(ssPassword, ' ');

   return 0;
} // end of getSeedFromPwd()...








//
// new 08-23-11, from randFromPwd()
//
//
//
// Input:
//    fillMe: vector to be filled.
//    fillM2: vector to be filled.  This one takes longer, so restricts brute-force speed.
//    nCycle: number of times to cycle for the slow guy.
//    [user types password, followed by Enter]
//
//
// Output:
//    fillMe: 10 64-bit integerry guys.
//    fillM2: 10 64-bit integerry guys.  Caller can use either or both.
//
//
//
//
//
//
// 06-10-13 (jdl): support back-space.
// 10-25-23 (jdl): nCycle.
// 08-20-25 (jdl): move from mt64.cpp to globs.cpp.
//
// qxgetSeedFromPwd()
//

int getSeedFromPwd(std::vector<unsigned long long> &fillMe, std::vector<unsigned long long> &fillM2, int nCycle) {
   int teeRoo = 1;
   int nTyped = 0;
   std::string ssPassword;

#ifndef _WIN32
   set_keypress();
#endif

   while(teeRoo) { // not really
      if(_kbhit()) {
         int ic = _getch();
#ifdef _WIN32         
         if(ic == 0x0D) break; // enter key
#else         
         if(ic == 0x0A) break; // enter key         
#endif         

         if((ic == 0x08) || (ic == 0x7f)) {      // back-space.
            int nuSiz = MAX(0, ((int)ssPassword.size()-1));
            // qprintf(fp_std, "\nnuSiz %d.\n", nuSiz);
            ssPassword.resize(nuSiz);
            // qprintf(fp_std, "did that\n");
            continue;
         } // if(ic == 0x08)


         // mprintf(fp_std, "Well, got %c (%02X).\n", ic, ic);

         ssPassword += (char)ic;
         nTyped++;
      } // if(_kbhit()) 
      if(nTyped > 100) break;           // qxl: do we even need this??  Could we trap ctl-c?
   } // while(TRUE)  // not really
   
#ifndef _WIN32
   reset_keypress();
#endif

   MT64::bytesToVector((const unsigned char *)ssPassword.c_str(), (int)ssPassword.length(), fillMe, fillM2, nCycle);
   zeroMe(ssPassword, ' ');

   return 0;
} // end of getSeedFromPwd()...



