dSurvey Math Functions

 Note: these are called as newValue=func(arg1,arg2,..) -- they are NOT called as x.func(arg1,arg2,...)

 --- Summary ---
  fix2(aval,dval)             :  return n.2 formatted value of aval. If val='', return dval
  NormalP( x )                 : pdf of a normal distribution
  InvNormalP(x)                : inverse normal (given a critical value)
  sortNum(anArray)             : numeric sort -- returns new array numerically sorted
  indexSort(array,type)        : sort an array, return index into array.
  indexSort2(vec1,vec2,atype)  : returns a 2d index matrix whose elements point to sorted rows of vec1 and vec2
  sortByIndex(theVals,theInds,ifrom,ito)  :   rearrange the "theVals" array using theInds index 

  initArray(nlen,defval)          : initialize an nlen long varray, with each element set to defval 
  seqArray(i1,i2,istep)           : return an array with values starting at i1, ending at i2, by istep (istep=1 if not specified)
  maxArray(varray,amiss)                : returns maximum of a varray, ignoring undefineds and nulls
  minArray(varray,amiss)                : returns minimum of a varray, ignoring undefineds and nulls
  sumArray(varray,amiss)                : returns sum of a varray, ignoring undefineds and nulls, ignoring non-numerics
  meanArray(varray,amiss)               : returns average of a varray, ignoring undefineds and nulls, ignoring non-numerics
  medianArray(varray,amiss)             : returns median value of a varray, ignoring undefineds and nulls, ignoring non-numerics
  varArray(varray,amiss)                : returns variance value of a varray, ignoring undefineds and nulls, ignoring non-numerics
  freqArray(varray,classes,indxM) : return array of frequencies; # of elements of varray that match values specified in the classes array
  uniqueArray(varray,isort)       : vector of unique values founds in this array  
  rnduArray(n,seed)               : return an n element array of uniformly distributed random numbers 
  rndnArray(n,seed)               : return an n element array of normally distributed random numbers 
  choseRandomArray(n,v,repOk)     : choose n values randomly from array v 

  mtxMath(amtx,'mathEqu',defval)       : return an array with values from math operations on the columns of amtx  

  mtxMath2(v1,v2,'mathEqu',defval)       : return an array with values from math operations on v1 and v2 arrays
          This is a shortcut into  mtxMath: define two arrays that are used to construct a 2 column matrix.
          Hence, the mathEqu rules are the same, with $0 refering to the v1 array, and $1 to the v2 array
  

  selectRows(vals,cond,vcomp,asIndex)    :  select rows from vals 
  extractCol(vv,icol)            : extract the icol column from the vv matrix and return as an array. Note that column numbering starts at 0
  extractRows(vv,rlist,useFlag)  : extract rows from a matrix

  appendRows(amtx,bmtx)        :  append rows of matrix bmtx to bottom of amtx
  appendCols(amtx,bmtx)        :  append columns of matrix bmtx to right of amtx

  appendArray(amtx,anarray)    : append the anarray  array as new row of matrix amtx. If only one argument, 
                                 create a new 1 row matrix using anarray


  myRand=new makeURandom(seedValue);            : create a seeded random-number-object
  val1=myRand.next(min,max,idec,iprec)                      : generate a value

   dsvyRandomCreate('varname',seedValue,min,max,idec,prec,nretain)  -- create random-number object
   aval=dsvyRandom('varname',nth,useSeed);               : generate (or retrieve) value from a random-number object


  --- Details ----

fix2(aval,dval)  :  return n.2 formatted value of aval. If val='', return dval. vv=fix2(1512.3412)= 1512.34

NormalP( x )  : pdf of a normal distribution

InvNormalP(x) : inverse normal (given a critical value)

sortNum(anArray)   : numeric sort -- returns new array numerically sorted

indexSort(array,type)    Sort an array, return index.

   type:  'A' = alphabetic
          'N' = numeric 	 	(the default)
          'ND' = descending numeric

indexSort2(vec1,vec2,atype)  :    Returns a 2d index matrix whose elements point to rows of vec1 and vec2

   Each row is a vector that to elements with the same vec2 value;
   within each row the columns point to vec1 values (in sorted order).
   And, the rows are sorted by vec2

   Notes:
      *  the value of the vec2 element, for a given row, is always the same.
      *  the size of each row vector can vary.

   Example:
                1  2  3  4  5   6  7  8                 
         vec1=[11 22 131 51 23  9 35  5 ]
         vec2=[ 1  2   1  1  2  4  1  4 ]
   ind2=indexSort2[vec1,vec1,'N') yields:
         ind2[0]=[ 1 7 4 3]   (vec2[1], vec2[7], etc. equal 1, while vec[1],vec1[7],... are sorted)
         ind2[1]=[ 2 5]       (vec2[2], etc. equal 2)
         ind2[2]=[ 8 6 ]      (vec2[8], etc. equal 4)
  
   Note that atype is used to sort both vectors. 
        N=numeric, ND=descending numeric, A=alphabetic.
        Not specified: numeric


sortByIndex(theVals,theInds,ifrom,ito)  :   rearrange the "theVals" array using theInds index 

    theInd could be an array returned by indexSort; or a row from a matrix returned by indexSort2
    ifrom and ito are optional:; they can be start and end row in theInds (inclusive)
    If not specified, all rows are used.
    If theInds is a 2d matrix (say, as returned by sortIndex2), the rows will be concatenated.
    If ifrom is specified but not ito, then read from ifrom to the end
    To read just one row, ifrom and ito should have the same value
    Note: ifrom and ito are constrained to lie between 0 and theInds.length-1

 initArray(nlen,defval)       
   Onitialize an nlen long array, with each element set to defval.

These following simple statistics functions will ignore undefineds, nulls, and non-numerics. 
If amiss is specified, then amiss is used for missing values. Otherwise, missing values are ignored
If for some reason, there are no valid values in the array (and amiss is not specified), undefined is returned.

  maxArray(array,amiss)               : returns maximum of an array

  minArray(array,amiss)               : returns minimum of an array

  meanArray(array,amiss)              : returns average of an array
                                  vv can be an array or a matrix (a 2d array)
                                  If vv is an array, a scalar is returned
                                  If vv is a matrix, an array is returned (with same number of columns as matrix)

   sumArray(vv,ismiss)            Returns sum of an array or a matrix 
                                  vv can be an array or a matrix (a 2d array)
                                  If vv is an array, a scalar is returned
                                  If vv is a matrix, an array is returned (with same number of columns as matrix)

  medianArray(array,amiss)            : returns median value of an array

  varArray(array,amiss)                : returns variance value of an array

  missArray(array)               : number of non-numeric values in arrray

Alternate syntax for the above (except for missArray):
        funcname(array,missval)
By default, missing values (undefined or non-numeric) are ignored.  If you specify missval, then its value is used for all
"missing values". 


mtxMath(amtx,'mathEqu',defVal)  
    Return an array with values from math operations on the columns of amtx

  Arguments:
      amtx  =  NxK array of values (K<11)
      mathEqu=  A math equation that is used to compute the values.
      defVal  = value to use if unavailable column specified in mathEqu. Optional: 0 is used if not specified

  mathEqu is a string that contains dSurvey math operators and dSurvey global or control variables.
  It also can contain '$n' special codes, that point into the n'th column of amtx.
  Note that $0 is the first column of amtx, and $9 is the largest column pointer allowed.

  Examples:
    If vv is a 4 x 2 matrix=    
        10, 1
        12, 1
        13, 2
         5,  1
    Then 
         newvv=mathEqu(vv,'$0 + $1 ')
     returns
           11
           13
           15
            6

  Notes:
    *    if n is > # of columns in a row of amtx, defval is used (or 0 if defVal not specified)


mtxMath2(v1,v2,'mathEqu',defval)     
  This is basically a shortcut into  mtxMath.
  Instead of defining a matrix, v1 and v2 are arrays.
  These will be combined to form a 2 column matrix, which is then used in a call to mtxMath.

  Thus, the following are identical
        vnew=mtxMath2(v1,v2,'$0 + $1',0);
   and
        v12=appendCols(v1,v2);
        vnew=mtxMath2(v12,'$0 + $1',0);


selectRows(vv,acond,vvcomp,asIndex) 
   Return a subset of a matrix, using a selection equation. Or, return a 0/1 array pointing.

  Arguments:
       vv   =  NxK array of values (a subset of these are returned
      acond =  A logical condition that determines which rows to select. You can refer to the value in a column  by using  $n
     vvcomp =  NxJ matrix used by acond. IF not specified, or if a '', vv is used. 
    asIndex =  If 0 or not specified, return matrix that matches acond.
                If a 1, return an array of 0/1 values (1 means the row passed the test)
               If 2, return an array pointing to which rows pass the test  (0 points to the first row)
 Notes:
    * if vvcomp.length < vv.length, then all rows after vvcomp.length will be discarded
    * basically: if things don't make sense, the row is discarded

 Notes on acond:
    *    the standard dSurvey logical equation synatx is used; hence you can specify general and control variables
    *    the n in a $n  is the column of vvcomp to examine (n can range from 0 to 9). Note that each row is examined seperately, using the nth
         column in the row when needed.
    *    if n is > # of columns in a row of vvcomp, the row (of vv) is dropped (or a 0 is used in the index).
    *    shortcut: $ by itself is a synonym for $0. This is especially useful if vvcomp is a vector
 
  Examples:
    If vv is a 4 x 2 matrix=    
        10, 1
        12, 1
        13, 2
         5,  1
    Then 
         newvv=selectRows(vv,'$1 eq 1 ')
    will return 
          10, 1
          12, 1
          5 , 1
     And,
        newvv=selectRows(vv,'$ eq 1 ','',1)
     will return
         1 1 0 1
     And,
        newvv=selectRows(vv,'$ eq 1 ','',2)
     will return
         1 2 4  

     Alternatively, if 
         vvcomp=[0 0 1 1]
      then
         newvv=selectRows(vv,'$ eq 1 ',vvcomp)
      will return 
          13, 2
          5 , 1

extractCol(vv,icol)
   Extract the icol column from the vv matrix and return as an array.
   Note that column numbering starts at 0 (NOT at 1).
   If one of the rows of vv does NOT have an icol column, then undefined is returned for that row.

extractRows(vv,rowList,useFlag)

   Extract the rows noted in the rowList array; where rowList points to the rows to extract.

   If useFlag not specified, or 0, then rowList contains the row numbers of the rows to extract.
      Note that row numbering starts at 0.
   If useFlag is 1, then rowList contains flags for the corresponding row in vv
      If 0, do NOT use. If 1, use.
      If rowList too short, these "excess" rows in vv are not used.
   If useFlag is 2, then rowList should be a 2 element array:
        rowList[0]  = first row to keep
        rowList[1]  = last row

   Thus, the following are equivalent  (assuming that vv is has a length of  8):
         rowlist=extractRows(vv,[1,4],2) 
         vnew=extractRows(vv,[1,2,3,4]);
         vnew=extractRows(vv,[0,1,1,1,1,0,0,0],1);

appendRows(amtx,bmtx)         
   Append matrix b to end of matrix a 
        This is a vertical append: the number of rows increases.
   The matrices may have different column sizes: thus, each row of the new matrix may NOT have the same number of columns.


 appendArray(amtx,anarray)
      append array anarray to end of matrix amtx. If only one argument, create a matrix using array specified in amtx (first) argument
      This is a vertical append: the number of rows increases
   

appendCols(amtx,bmtx)         
   Append matrix b to right of matrix a 
      This is a horizontal append: the number of columns increases.
   The matrices do not need to have the same number of rows (but typically do).
   If they don't, then the rows that are beyond the smaller matrices row length will just contain the  row from the longer matrix.

replaceCol(amtx,icol,newCol)
  Replace the icol column of amtx with the values in the newCol array.
  The length of amtx and newCol MUST be the same (otherwise an error occurs).
   EXCEPTION: if newCol is a constant, then this "constant" value is used.
   icol can be greater than the length of a row of amtx (leading to a new row being an array with gaps in it).


replaceRows(amtx,rlist,newRows)
   Replace the rlist rows in vv with newRows
   If rlist contains a row number greater than the number of rows in amtx, it is ignored (no replacement occurs).
   Rlist must contain row numbers (it should NOT be a 0/1 "flags" array). 
        I.e.: Rlist could be the output of a call to  selectRows withan asIndex of  2.
   newRows MUST be the same length as rList. If it is not, an error occurs.
   EXCEPTION: if newRows is a constant, then this "constant" value is used.


 freqArray(varray,classes,indxM)      :
        Return array of frequencies.
                varray: array of values to examine
                classes : values to compute sums for -- the number of elements of varray that have this value of classes
                indxM   : 0/1 flag. If 1, varray indices are the values of classes
                               Thus, the length of the array is the maximum value in classes. This means that there may be undefined elements.
                          Otherwise, ith index refers to value in classes[i]
                               Thus, the length of the returned array will be classes.length
      If an element in varray does NOT match an element in classes, it is ignored (it does not contribute to a sum)
      If classes not defined, use the unique values in varray (in order they would be returned by uniqueArray)

  seqArray(i1,i2,istep)          : 
    Return an array with values starting at i1, ending at i2, by istep (istep=1 if not specified)
    Example:  
        vnew=seqArray(1,10,2)
    is equivalent to
         vnew=[1,3,5,7,9]

  uniqueArray(vv,isort) 
     Return an array containing the unique values of the vv array. If isort=1, sort in order.

  choseRandomArray(n,v,repOk) 
     Choose n values randomly from array v.
     If repOk=1, then with replacement. Else, without replacement (in which case n<v.length)


  -- Functions for creating seeded & named random-number-objects

 randObj=makeURandom(aseed)  and 
 randObj.next(amin,amax,idec,iprec)

    makeURandom is the constructor for random-number-objects.
    The, optional but typically specified, aseed is the starting seed of this random-number-object.

    The .next() method of the random-number-object will generate a random number, that 
    is based on the seed (and is modified by the amin, amax, and idec arguments).

    The basic idea:
       The sequence of number created after instantiating a makeURandom object, with a given seed,
       is always the same. Thus,random-number-objects created with the same  seedValue.
       will ALWAYS generate the same sequence of random values!
    
        
    Usage:
     myRand=new makeURandom(seedValue);
     val1=myRand.next(min,max,idec,iprec)
          ...
     val2=myRand.next(min,max,idec,iprec);

    where:
      min: minimum value of random number  (default=0.0)
      max: maximum value (default = 1.0)
      idec: digits after decimal point (0 = return an integer) (-1=the default == about 15 digits?)
     iprec : round to this precision -- value will be multiple of iprec (iprec can be a decimal
              value, such as 0.25). (0=the default= do NOT round)

   
      Thus: if no arguments, return a value between 0.0 and 1.0 (using maximum precision)

   Notes:
      * If seed not specified, use current date (basically, the same as Math.random()
      * To reset the seed, use myRand.seed=avalue;
      * dsvyRandomCreate starts with a makeURandom object, and adds fields


   dsvyRandomCreate('varname',seedValue,min,max,idec,prec,nretain) 

    Create a seeded random-number-object, with a starting seed and bounds.
    Once created, dsvyRandom(..) can be used to generate a random value "from a sequence" 

       This means that different random-number-objects (say, ones created at completely
       different times) will return the same sequence of values -- so long as the seed value 
       (and bounds) are the same!
   
    Syntax:
            dsvyRandomCreate('varname',seedValue,min,max,idec,prec,nretain)
   
     where:
   
         varname : the name of this random-number-object (it is used by dsvyRandom)
                       varname will be capitalized, with leading/trailing spaces stripped
          seed: the seed  (default value is current time (so basically it's a random value))
          amin: minimum value  (default value is 0.0)
          amax: maximum value  (default value is 1.0)
          idec: number of  digits after decimal point (0=integer) )
                (default value is to retain all decimal digits)
          prec: precision. Value will be a multiple of prec. Prec can be a decimal value (i.e.;
                 0.25). (default value is 0.0; which means do not impose this precision)
          nretain: how many prior values to retain. If not specified, 0. If '*', infinite
                     See dsvyRandom for hints on how to use nretain
   
      Note: if a 'varname' random-number-object was previously defined, 
             the above will just reset its seed,  min,max, idec, and nretain: 
             Note that prior retained values are NOT removed, and the "counter" is NOT reset
   
    Technical notes:
       * dsvyRandomCreate (and dsvyRandom) uses a _dsvyRandoms global object 
         (it will be created if it doesn't exist)
      * The makeURandom(seedValue) function is used to create objects,
         with the following additional fields added (with default values as noted):
              minVal      : default=0.0   
              maxVal     : default=1.0 
              decVal      :  default=-1                -1 means "use max precision"
              precVal     : default=0                   0 means do not modify 
              nretain      : default=0                   0 means "do not retain prior values"
         and a few used internally
              nct=0 ;                               tracks how many values have been created 
              retains=[];                           stores "retained" values
   
       *  If an existing 'varname' is reset, future values generated from this object may 
          have different characteristics that previously generate values.
          However:  the "retained" values will not change (which means that a value outside of the
          current bounds can be returned if nth < current_counter is requested)
   
    Example: create a random number with seed of 100, that returns full precision values
                between 0.0 and 1.0
     dsvyRandomCreate('myRand',122,0,1,100,4,0) 
         ...
     v1=dsvyRandom('myRand')
         ...
     v2=dsvyRandom('myRand')
   
    everytime this is run, the same values of v1 and v2 will be created  (v1= 0.8597, v2= 0.0806)
   
    Shortcut:
        dsvyRandomCreate()  will create a random-number-object with the name $DEFAULT, using
        the default values for min=0.0, max=1.0, maximum precision, 'random' starting seed , and no
        retained variables. In other words, pretty much the same thing as using Math.random()


 aval=dsvyRandom('varname',nth,useseed);

    Generate a random value from the "varname" random-number-object
    This uses objects created by dsvyRandomCreate
   
    Syntax:
       aval=dsvyRandom('varname',nth);
    where:
       'varname' : name of the random-number-object (as created by dsvyRandomCreate)
                     if no such object has been created, an error message is displayed
       nth: optional (typically not used). 
                 If not used, returns the next value in this random-number-object's  "sequence"
                 If specified, than return the 'nth' value; where nth=1 means the first value in the sequence
   
             Special values:
                   * nth='N' means "return nct (the number of values generated using this random-number-object)
                   * nth='*' : return a vector containing all the retained values (if any)

        useseed: optional. If 0, generate a pure random number (do NOT generate from the seeded sequence)
                                 
    More on nth
       nth allows you to "look forward" or (if nretain was specified in dsvyRandomCreate) look backward.
       This makes it easier to keep sets of random-number-objects synchronized, so that for any
       given value of nth one can get the save vector of random values (assuming the set of random-
       number-objects are always created with the same seeds)
          Examples:
                  * if 6 values have been generated, and nth=8:
                          a) a value is generated, and stored (as the 7th value)
                          b) another value is generated, stored (as the 8th value), and returned
                  * if 6 values have been generated, and nth=3:
                      a) If retain is greater then 3, then the 3rd value is returned
                      b) if retain ls less than or equal to 3, an error message is shown and 0 is returned
                            (since the oldest value is for nct=4)
   
          Thus: retain should be large enough to accomodate the largest "look back" (the largest
                  difference between the current number of values generated and the nth value
                  requested.
   
     Shortcut: dsvyRandom() will return a value from the '$default' random-number-object
                 (assuming you created it with dsvyRandomCreate()
   


