/* The GRBL2 batch-processor procedure
   Created by Daniel Hellerstein, April 2004. Updated Dec 2005.

Usage:
      include at the top of your main program file (that implements your model):
           #include grbl2_procs.inc;
           #include grbl2_file.inc ;
           #include grbl2_math.inc ;
           #include grbl2_batch.inc ;

      Note that grbl2_procs, grbl2_math, and grbl2_file are both used by grbl2_batch (and should appear before
      the #include grbl2_batch.inc).
      This assumes that these .inc files are in the current directory, or in GAUSS's   path.


Full documentation:
    See GRBL2_BATCH.TXT for a complete description of these procedures.


Quick description:

   G_BATCH, the GRBL batch-processor procedure, supports "GRBL command file" interface to GAUSS
   programs.  A "GRBL command file" contains instructions on  dataset to use, variables to
   include, and modeling options.

   These command files consist of statements, with each statement ending with a semi-colon (;).

   Each statement has the form:
             option   [val1 ... valn] ;
   where
        option: a generic, or model specific, option
        val1 ... valn : optioanl values specific to  the option



********************************************************************************** */

@ the following lines are only executed when the program (that contains this .INC file) is first run @

@ global parameters @

clear  Filename,varlist,defines,definew,ndefines,fixnames ;
clear bstart,bstart1,bstart2,outfile,verbose,prntit,title,i_svlist,savevars,savefils;
clear baux,baux1,baux2;

clear xindex,xname,nx ;
clear X1index,X1name,nX1 ;
clear X2index,X2name,nX2 ;
clear x3index,x3name,nx3 ;
clear zindex,zname,nz ;
clear z1index,z1name,nz1 ;
clear z2index,z2name,nz2 ;
clear z3index,z3name,nz3 ;
clear yindex,yname ;
clear y1index,y1name ;
clear y2index,y2name ;
clear y3index,y3name ;
clear idindex,idname,idtype ;
clear id1index,id1name,id1type ;
clear id2index,id2name,id2type ;
clear id3index,id3name,id3type ;
clear wtindex,wtname,wtnorm,wtrep;
clear wt1index,wt1name,wt1norm,wt1rep ;
clear wt2index,wt2name,wt2norm,wt2rep, wtindex2,wtname2,wtnorm2,wtrep2; @legacy!@

clear aindex,auxname,a2index,aux2name,naux,naux2,a1index,aux1name,naux1;

clear nxnew,nxdummy ;

clear X, X1, X2,X3, Y,Y1, Y2, Y3, Z,Z1, Z2, Z3, ID,ID1, ID2, ID3, XDUMMY, XNEW ;
clear weight,weight2, AUX, AUX2,aux1;

clear varlist;
clear slctlist ;
clear dinfo ;
clear n_gosubs ;
clear work_dir ; work_dir="";

clear run_noest ;

struct minfo_s XNEWinfo ;   @XNEWinfo used for XNEW equations @

XNEWinfo.neq=0 ;     @ start with zero equations @
XNEWinfo.names=zeros(20,1) ; @ max of 20 equations @
XNEWinfo.nterms=XNEWinfo.names ;
XNEWinfo.vtypes=zeros(20,5) ; @ max of 5 terms per equation @
XNEWinfo.vvals=XNEWinfo.vtypes; XNEWinfo.opers=XNEWinfo.vtypes ;

ndefines=6;
let string definew="MODEL" "BASETITLE" "VARLIST" "FILENAME" "DATE" "PROGRAM";
let string defines=" "
                    " "
                    "varlist"
                    "filename "
                    "date"
                    "GRBL2";
defines[5]=datestring(0) $+ " " $+ timestr(0) ;
defines[6]="" $+ grbl2_program;


/*******************************************/
@ PROC g_batch
Process batch commands. Note use of g_batch1 (g_batch1 processes each command)

Arguments:
  commands: a string containing a ; delimited list of parameter and option
           definitions
  reset: if 1, then reset globals, etc.
         Reset should be set to 1 on the first call to g_batch
  got2 : Two forms: a 0/1 flag, or a 2 column set of instructions:
         0/1 Flag: if 1, then call g_batch2 if unknown keyword encountered
         2 column set of instructions:
                first column contains "start keyword".
                last column contains "end keyword"
            Whenever a keyword matches a "start keyword", then all content between (and including) the
            keyphrase containing this keyword and the "End keyword" (specified in column 2) is returned.
            The action is set to 3     

            If you want to call G_BATCH2, just have one of the rows have a "1" and "1" in the first and 2nd columns
   Example:
        {stuff,iact}=g_batch(cmd_string,0,"CREATE"~"RUN");
   will return a "create file" block if CREATE ; .... ; RUN ; is encounterd (which can then be fed to
   GRBL2_CREATE).

2 Arguments are returned:
  COMMANDS -- The string containing the REMAINING commands to be executed.
              Or, if action=3, then a 3x1 String Vector. 
                row 1 : The string containing the REMAINING commands to be executed.    
                row 2:  The block returned
                row 3:  The keyword (basically, the first word in the block)

  ACTION:
        0) End of batch commands, return to user input.
        1) RUN
        3) Block returned

@

proc (2)=g_batch(commands,reset,batch2a);
   local comlen,KEYWURD,aword,acommand,com1a,com1b,ie,foo,status,ok,lookfor ;
   local inststu2,tmp ;
   local tmp1,tmp2,tmp3,foo1,foo2,foo3,batch2,j;
   local batchon,batchstop,batchstuff,batchstart,isrun;

   inststu2=commands ;           @used for GOTOs @

  if cols(batch2a) gt 1 ;               @ check for "1"~"1" flag @
     batch2a=upper(batch2a);
     batch2=0;
     for j (1,cols(batch2a),1);
        if batch2a[j,1]$=="1" and batch2a[j,2]$=="1" ;
                batch2=1;
        else;
           batch2a[j,1]=g_strip(batch2a[j,1]);
           batch2a[j,2]=g_strip(batch2a[j,2]);
        endif;
     endfor;
  else;
     batch2=batch2a;
  endif;

/* initialize the generic parameters */

if reset==1 ;
   call clearem ;

   clearg bstart,bstart1,bstart2,outfile,verbose,prntit,title,i_svlist,savevars,savefils;
   clearg baux,baux1,baux2;

/* NDEFINES, DEFINEW and DEFINES are used to define substitution strings that
   are used in parameter file definitions
   NDEFINES is the number of substitution strings (the # of words in DEFINEW)
   Words, when prefaced by a $, in DEFINEW are replaced by respective
   strings in DEFINES   */

   ndefines=6;
   let string definew="MODEL" "BASETITLE" "VARLIST" "FILENAME" "DATE" "PROGRAM" ;
   let string defines="The  Estimator "
                    " The title "
                    "varlist"
                    "filename "
                     "date"
                     "GRBL2" ;
     defines[5]= datestring(0) $+ " " $+ timestr(0) ;
     defines[6]="" $+ grbl2_program ;
endif;

comlen=strlen(commands);
com1a=1;
com1b=1;
output off ;
batchon=0; batchstuff="";
batchstart="" ;
status=0;

do until com1b==0  ;

 com1b=strindx(commands,";",com1a);

 if com1b==0 ;
           output off ;
           retp("",0);          @ end of command string @
  endif;

  acommand=strsect(commands,com1a,com1b-com1a) ; @drop end ; @


piglet: ;


@ check for blocks @
if cols(batch2a) gt 1 ;
   {keywurd,tmp}=token(acommand); keywurd=g_strip(upper(keywurd));

   if batchon==1 ;
        com1a=com1b+1;
        if keywurd$==batchstop ;
            batchstuff=batchstuff$+acommand $+" ; " ;
            acommand=strsect(commands,com1a,100000000) ; @ get the end (hack-- use large number for 3rd arg @
            tmp=acommand$|batchstuff$|batchstart;
            retp(tmp,3);
        else;
            batchstuff=batchstuff$+acommand  $+" ; ";
            continue;
        endif;
     endif;

     for j (1,rows(batch2a),1) ;
       if batch2a[j,1]$==KEYWURD;               @ found a block start-- return the block @
         batchstart=keywurd;
         batchon=1;
         batchstop=batch2a[j,2];
         batchstuff=acommand $+" ; ";
         com1a=com1b+1;            @ ready for  next acommand @
         continue;
       endif;
     endfor;
    if batchon==1 ; continue ; endif;
endif;

if status==-11 or status==-12 ;

  isrun=g_find_keywurd(acommand,"RUN");
  " status " status isrun ;

  if isrun==1 ;                 @found a RUN @
    if status==-12;             @ skip PAST @
       status=0;
       com1a=com1b+1;            @ ready for  next acommand @
       continue ;
    else;
       status=0;         @ turn of "search for RUN" switch, and process the RUN below @
    endif;
  else ;
    com1a=com1b+1;            @ ready for  next acommand @
    continue ;
 endif;
endif;


{KEYWURD,status}=g_batch1(acommand,batch2);

@ Status flag values:
 -2 = execute command in keywurd
 -1 = skip to KEYWURD:
 -11 and -12 : SKip to, or past, next RUN keywurd
  0 = OK (get next command)
  1 = STOP  (typically, return to user input mode)
  2 = RUN
  3 = No such "generic" Command    @


 if status==0;
                @ keywourd processed successfuly, go get next one @

        elseif status==-2 ;              @ then yields a literal @
           acommand=keywurd ; goto piglet ;


        elseif status==-11 or status==-12       ;       @ skip to RUN, or past RUN @


        elseif status==-1 ;              @ skip command @
            lookfor=upper("" $+ keywurd $+ ":" );
            com1b=strindx(upper(inststu2),lookfor,1);
            if com1b==0 ; call grbl2_error("ERROR: GRBL2_BATCH -- no such label:" $+ acommand,1) ; stop; endif;
            com1b=com1b+strlen(lookfor);

        elseif status==1;        @ stop processing, return to user input @
              output off ;
              retp(strsect(commands,com1b+1,comlen+1-com1b),0);

        elseif status==2 or status>3  ;
               output off ;
               retp(strsect(commands,com1b+1,comlen+1-com1b),1);

        elseif status==3 ;           @unknown, skip it @
           tmp=g_stripc(acommand,chrs(10|32|13|09)) ;
           call grbl2_error(" Error (GRBL2_BATCH): Unknown command: "  $+tmp,1) ;  
           stop;

         endif;              @ check status @


      com1a=com1b+1;            @ ready for  next acommand @

endo;               @ scan through commands @

output off;
retp("",0);    @ signals end of batch file @


endp;


@=====================@
@ clear several global indices @
proc clearem ;
  clearg X, X1,X2, X3, Y,Y1, Y2, Y3, Z,Z1, Z2, Z3, ID,ID1, ID2, ID3, XDUMMY, XNEW ;
  clearg weight,weight2,AUX,AUX2,aux1;
  clearg  Filename,varlist,defines,definew,ndefines,fixnames ;

  clearg xindex,xname,nx ;
  clearg X1index,X1name,nX1 ;
  clearg X2index,X2name,nX2 ;
  clearg x3index,x3name,nx3 ;

  clearg zindex,zname,nz ;
  clearg z1index,z1name,nz1 ;
  clearg z2index,z2name,nz2 ;
  clearg z3index,z3name,nz3 ;
  
  clearg yindex,yname ;
  clearg y1index,y1name ;
  clearg y2index,y2name ;
  clearg y3index,y3name ;

  clearg idindex,idname,idtype ;
  clearg id1index,id1name,id1type ;
  clearg id2index,id2name,id2type ;
  clearg id3index,id3name,id3type ;

 clearg wtindex,wtname,wtnorm,wtrep;
 clearg wt1index,wt1name,wt1norm,wt1rep ;
 clearg wt2index,wt2name,wt2norm,wt2rep, wtindex2,wtname2,wtnorm2,wtrep2; @legacy!@

  clear aindex,auxname,a2index,aux2name,naux,naux2,a1index,aux1name,naux1;

  clearg nxnew,nxdummy ;

  clearg dinfo;
  XNEWinfo.neq=0; 
  clearg slctlist ;  
  ndefines=6;
  let string definew="MODEL" "BASETITLE" "VARLIST" "FILENAME" "DATE"  "PROGRAM" ;
  let string defines=" "
                    " "
                    "varlist"
                    "filename "
                    "date"
                    "GRBL2" ;
     defines[5]=datestring(0) $+ " " $+ timestr(0) ;
     defines[6]="" $+ grbl2_program ;

 retp(1);
 endp;

@=====================@
@ clear several global indices -- do NOT clear DEFINEs (clearem will clear the DEFINEW,DEFINES, and NDEFINE)@
proc clearemf ;

  clearg X, X1,X2, X3, Y,Y1, Y2, Y3, Z,Z1, Z2, Z3, ID,ID1, ID2, ID3, XDUMMY, XNEW ;
  clearg weight,weight2,AUX,AUX2,aux1;
  clearg  Filename,varlist ;

  clearg xindex,xname,nx ;
  clearg X1index,X1name,nX1 ;
  clearg X2index,X2name,nX2 ;
  clearg x3index,x3name,nx3 ;

  clearg zindex,zname,nz ;
  clearg z1index,z1name,nz1 ;
  clearg z2index,z2name,nz2 ;
  clearg z3index,z3name,nz3 ;
  
  clearg yindex,yname ;
  clearg y1index,y1name ;
  clearg y2index,y2name ;
  clearg y3index,y3name ;

  clearg idindex,idname,idtype ;
  clearg id1index,id1name,id1type ;
  clearg id2index,id2name,id2type ;
  clearg id3index,id3name,id3type ;


  clearg wtindex,wtname,wtnorm,wtrep;
  clearg wt1index,wt1name,wt1norm,wt1rep ;
  clearg wt2index,wt2name,wt2norm,wt2rep, wtindex2,wtname2,wtnorm2,wtrep2; @legacy!@

  clear aindex,auxname,a2index,aux2name,naux,naux2,a1index,aux1name,naux1;

  clearg nxnew,nxdummy ;

  clearg dinfo;
  XNEWinfo.neq=0; 
  clearg slctlist ;  

 retp(1);
 endp;


@=====================@
@ clear several global indices -- do NOT clear DEFINEs, and do NOT clear file stuff)@
proc clearemf2 ;

  clearg X, X1,X2, X3, Y,Y1, Y2, Y3, Z,Z1, Z2, Z3, ID,ID1, ID2, ID3, XDUMMY, XNEW ;
  clearg weight,weight2,AUX,AUX2,aux1;
@  clearg  Filename,varlist ;@

  clearg xindex,xname,nx ;
  clearg X1index,X1name,nX1 ;
  clearg X2index,X2name,nX2 ;
  clearg x3index,x3name,nx3 ;

  clearg zindex,zname,nz ;
  clearg z1index,z1name,nz1 ;
  clearg z2index,z2name,nz2 ;
  clearg z3index,z3name,nz3 ;
  
  clearg yindex,yname ;
  clearg y1index,y1name ;
  clearg y2index,y2name ;
  clearg y3index,y3name ;

  clearg idindex,idname,idtype ;
  clearg id1index,id1name,id1type ;
  clearg id2index,id2name,id2type ;
  clearg id3index,id3name,id3type ;


  clearg wtindex,wtname,wtnorm,wtrep;
  clearg wt1index,wt1name,wt1norm,wt1rep ;
  clearg wt2index,wt2name,wt2norm,wt2rep, wtindex2,wtname2,wtnorm2,wtrep2; @legacy!@

  clear aindex,auxname,a2index,aux2name,naux,naux2,a1index,aux1name,naux1;

  clearg nxnew,nxdummy ;

  clearg dinfo;
  XNEWinfo.neq=0; 

@  clearg slctlist ;  @

 retp(1);
 endp;



/**********************************/
@ return 1 if a command starts with the daword0 "keyword". Otherwise return 0. Note use of "substitution of
grbl2 variables @

proc g_find_keywurd(acomm0,daword);

local acommandlc,acommand,comlen,i1,ie,keywurd;


if verbose>0 ; " Command: ";;call BLOCKON; acomm0; call g_pause(1,1) ; call NORMALON ;endif;

daword=g_strip(upper(daword));

acommandlc=g_repstg(acomm0,definew,defines);
acommand=upper(acommandlc);
comlen=strlen(acommand);
i1=1 ;

{KEYWURD,ie}=g_getwrd(acommand,i1) ;

keywurd=g_strip(upper(keywurd));

if keywurd$==daword ;
      retp(1);
else;
      retp(0);
endif;
endp;



/**********************************/
proc (2)=g_batch1(acomm0,batch2);

     local comlen,acommand,KEYWURD,aword,ie,aa,stat2;
     local iee ,mmm,mm,oo,qtext,i1,ien,foo,istat,afoo,fgoo;
     local condname,andornam,exactxyz;
     local tmp,tmpc,aword1,aword2,aword3,aword4,t1,t2,t3,t4,nn,n_ors;
     local skipto,defword,defword2;
     local cond1,term1,term2,condn,isthen,ii1,iik,vv;
     local dindex,dname,dvals,d2index,d2name;
     local newkol,oldkol,tmp1,tmp2,tmp3,foo1,foo2,foo3,fdum,nr,ddta;
     local ietmp,qconst;
     local ndummies,basename,ffname ;
     local j2,j3,aword0,ie0;
     local tword,tvalue,abaux;
     local titlen,aii,ii;
     local awtname,awtindex,awtrep,awtnorm,aidtype,aidname,aidindex;
     let condname="EQ" "NE" "GE" "LE" "GT" "LT" ;
     let andornam="AND" "OR" ;
     local afile,avarlist,filename0,acommandlc;

     exactxyz=1 ;

    if verbose>0 ; " Command: ";;call BLOCKON; acomm0; call g_pause(1,1) ; call NORMALON ;endif;


binok0: ;

     acommandlc=g_repstg(acomm0,definew,defines);
     acommand=upper(acommandlc);

     comlen=strlen(acommand);
     i1=1 ;

binok: ;


    {KEYWURD,ie}=g_getwrd(acommand,i1) ;

    if verbose>1 ; call BOLDON ;"Keyword: ";;keywurd; call normalon; endif;

@ Depending on KEYWURD, do different things @


@ ==== a)Some generic commands (not specific to an estimator) @

          if KEYWURD$=="COMMENT"  ;   @echo to screen,etc@
               output on ;
               term2=strsect(acomm0,ie+1,comlen) ;
               term2=g_repstg(term2,definew,defines);
              " Comment: " $term2 ;
                output off;

           elseif strindx(keywurd,":",1)>0 ;        @ ignore labels @
                  i1=ie+1; goto binok ;

           elseif  KEYWURD$=="*" ;   @echo to screen,etc@
                output on ;
                $strsect(acommand,ie+1,comlen) ;
                output off;


           ELSEIF KEYWURD$=="!" ;        @ to screen only @
              printdos " Remark: " $+ strsect(acommand,ie+1,comlen) ;

           elseif KEYWURD$==" " or KEYWURD$=="" ;        @ do nothing  @
                    ;
           elseif KEYWURD$=="RESET" ;     @ reset parameters @
                call clearem ;
                "GRBL2 parameters have been reset to their defaults.";

           elseif KEYWURD$=="RUN" ;     @ RUN the model (return to g_batch @
              run_noest=0 ;
              aword=get_copt(acommand,"-NOEST","");
              if aword$/="" ; run_noest=1; endif;
              retp(KEYWURD,2);

           elseif KEYWURD$=="STOP" or KEYWURD$=="EXIT" or KEYWURD$=="QUIT";
                  {aword,ie}=g2getwrd(acommand,ie+1);
                  if ie==0;
                      retp(KEYWURD,1);              @ return to user input @
                  elseif aword$=="GAUSS";
                      " Returning to GAUSS " ;
                        stop ;
                  elseif aword$=="DOS" ;
                      " Exiting to system in 4 seconds "; pause(4.0); system ;
                  elseif aword$=="SYSTEM";
                      " Exiting ";  system ;
                  else;
                      retp(KEYWURD,1);              @ return to user input @
                  endif;

         elseif keywurd$=="GOTO" or keywurd$=="SKIP" ;
             {skipto,ie}=g_getwrd(acommand,ie+1);
             skipto=g_Strip(upper(skipto));
             if skipto$=="$RUN";        @ skip to RUN (run the model NOW -- ignoring any more commands@
                retp(skipto,-11);
             endif;
             if skipto$=="$NEXT" or skipto$=="$PAST";   @ Do NOT run the model@
                retp(skipto,-12);
             endif;
             retp(skipto,-1);

         elseif keywurd$=="GOSUB" or keywurd$=="STARTBLOCK" or keywurd$=="ENDBLOCK" ;
             call grbl2_error( "ERROR: " $+ keywurd $+  " found. Your program should first call G_GOSUB0 ",1);
             stop;



        elseif g_abbrev("IF_EXIST"$|"EXIST"$|"IFEXIST",keywurd)==1 ;
               isthen=0;
              {term1,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                 call grbl2_error("ERROR: missing filename in EXIST " ,1); stop ;
               endif;

              {skipto,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                  call grbl2_error("ERROR: missing location/command in EXIST " ,1); stop ;
               endif;
               skipto=g_eatspc(skipto);
               if upper(skipto)$=="GOTO" ;       @ ignore GOTO @
                 {skipto,ie}=g2getwrd(acomm0,ie+1);
                 if ie==0 ;
                     call grbl2_error("ERROR: missing location in EXIST ",1) ; stop ;
                  endif;
              endif;
              if upper(skipto)$=="THEN" ;    @ interpret next statemetn @
                 isthen=-1;
                 skipto=strsect(acomm0,ie+1,10000);
               endif;

@ substitute DEFINEd variables for term1 and   @

           term1=upper(g_stripc(g_repstg(term1,definew,defines),"' "));

           ii1=-1 + isthen ;
           if strindx(".",term1,1)==0 ; term1=term1$+".DAT"; endif;
           tmp=g_exists2(term1,work_dir,"");
           if tmp$/="" ; retp(skipto,ii1); endif;


        elseif keywurd$=="IF" or keywurd$=="#IF";
             isthen=0;
              {term1,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                 call grbl2_error("ERROR: missing first term in IF " ,1); stop ;
               endif;
              {cond1,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                 call grbl2_error("ERROR: missing  condition in IF ",1) ; stop ;
               endif;
              {term2,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                  call grbl2_error("ERROR: missing second term in IF ",1) ; stop ;
               endif;
              {skipto,ie}=g2getwrd(acomm0,ie+1);
              if ie==0 ;
                  call grbl2_error("ERROR: missing location/command in IF " ,1); stop ;
               endif;
               skipto=g_eatspc(skipto);
               if upper(skipto)$=="GOTO" ;       @ ignore GOTO @
                 {skipto,ie}=g2getwrd(acomm0,ie+1);
                 if ie==0 ;
                     call grbl2_error("ERROR: missing location in IF ",1) ; stop ;
                  endif;
              endif;
              if upper(skipto)$=="THEN" ;    @ interpret next statemetn @
                 isthen=-1;
                 skipto=strsect(acomm0,ie+1,10000);
               endif;

@  permitted conditions: "EQ" "NE" "GE" "LE" "GT" "LT" @
            condn=indcv(cond1,condname) ;

            if scalmiss(condn);
                call grbl2_error(" ERROR: bad GRBL2 IF condition: " $+ acomm0,1) ; stop;
            endif;

@ substitute DEFINEd variables for term1 and term2  @

           term1=upper(g_stripc(g_repstg(term1,definew,defines),"' "));
           term2=upper(g_stripc(g_repstg(term2,definew,defines),"' "));
           if keywurd$=="#IF" ;
             if g_isnum(term1)==0 ;
                call grbl2_error( "ERROR: bad #IF 1st term:" $+term1,1); stop;
             endif;
             if g_isnum(term2)==0 ;
                call grbl2_error( "ERROR: bad #IF 2nd term: " $+ term2,1); stop;
             endif;
             term1=stof(term1); term2=stof(term2);
           endif ;

           ii1=-1 + isthen ;
@  permitted conditions: "EQ" "NE" "GE" "LE" "GT" "LT" @
           if keywurd$=="IF" ;
              if condn==1 ;
                    if term1 $== term2 ; retp(skipto,ii1) ; endif;
              endif;
               if condn==2 ;
                  if term1 $/= term2 ; retp(skipto,ii1) ; endif;
              endif;
              if condn==3 ;
                  if term1 $>= term2 ; retp(skipto,ii1) ; endif;
              endif;
              if condn==4 ;
                    if term1 $<= term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==5 ;
                    if term1 $> term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==6 ;
                    if term1 $< term2 ; retp(skipto,ii1) ; endif;
               endif;
            else   ;        @ #IF @
               if condn==1 ;
                    if term1 == term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==2 ;
                   if term1 /= term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==3 ;
                  if term1 >= term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==4 ;
                    if term1 <= term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==5 ;
                    if term1 > term2 ; retp(skipto,ii1) ; endif;
               endif;
               if condn==6 ;
                    if term1 < term2 ; retp(skipto,ii1) ; endif;
               endif;
            endif;

        elseif keywurd$=="FIXNAMES" ;
             fixnames=miss(1,1)~miss(1,1);
             {aword1,ie}=g_getwrd(acommand,ie+1);
             ien=0;
             do until ie==0;
                {aword2,ie}=g_getwrd(acommand,ie+1);
                if ie >0 ;                 @ got two words @
                  fixnames=fixnames|(aword1~aword2);
                  {aword1,ie}=g_getwrd(acommand,ie+1);
                endif;
             endo ;
             fixnames=packr(fixnames);

@ controls verbosity status reporting @
           elseif keywurd$=="VERBOSE" ;
               verbose=1 ;
           elseif keywurd$=="VERBOSE2" ;
               verbose=2 ;

        elseif keywurd$=="QUERY";
             {defword,ie}=g_getwrd(acomm0,ie+1);
             if defword$=="";
                call grbl2_error( "ERROR: bad QUERY ",1);
                stop ;
             endif;
             defword=g_strip(upper(defword)); @ read in this definition @
             qtext=g_strip(strsect(acomm0,ie+1,comlen)) ;
             qtext=g_repstg(qtext,definew,defines);
             if g_strip(qtext)$==""; qtext="Value for "$+defword;endif;
             call normalon;call boldon; qtext " ? ";; call normalon ;
             defword2=cons ; ?;
             defword2=g_strip(defword2);
             if strsect(defword2,1,2)$=="$=";  @ specify a batch command @
                  retp(strsect(defword2,3,1111),-2);
             endif;                         @ otherwise, just a variable @
             defword2=g_repstg(defword2,definew,defines);
             iik=0;

             do until iik==ndefines;
               iik=iik+1;
               if definew[iik]$==defword ;
                  defines[iik]=defword2; retp(keywurd,0);
               endif;
             endo;

             ndefines=ndefines+1;
             definew=definew $| defword ;
             defines=defines $| defword2;

         elseif keywurd$=="DEFINE" ;
                {defword,ie}=g_getwrd(acomm0,ie+1);
                if defword$=="";  retp(KEYWURD,0); endif;

                if g_strip(upper(defword)) $=="-DROP" ;  @ drop user-defined grbl vars @

                    ndefines=6;
                    let string definew="MODEL" "BASETITLE" "VARLIST" "FILENAME" "DATE" "PROGRAM";
                    let string defines=" "
                       " "
                        "varlist"
                       "filename "
                       "date"
                       "GRBL2"  ;
                    defines[5]= datestring(0) $+ " " $+ timestr(0) ;
                    defines[6]="" $+ grbl2_program ;
    
                else; 

                   defword=g_strip(upper(defword)); @ read in this definition @
                   {aword,iik}=g_getwrd(acommand,ie+1);
                   if aword$=="=" ; ie=iik ; endif;  @ remove = sign @
                   defword2=strsect(acomm0,ie+1,comlen) ;
                   iik=0;
                   defword2=g_repstg(defword2,definew,defines);
                   if defword2$=="''"; defword2=""; endif;
                   do until iik==ndefines;
                     iik=iik+1;
                     if definew[iik]$==defword ;
                        defines[iik]=defword2; 
                       retp(keywurd,0);
                     endif;
                   endo;          @else, this is a new substitution @
                   ndefines=ndefines+1;
                   definew=definew $| defword ;
                   defines=defines $| defword2;
                 endif;



@ Pause until told to continue @
         elseif keywurd$=="PAUSE" or keywurd$=="WAIT";
             {aword,ie}=g_getwrd(acommand,ie+1);
             aword=g_strip(upper(aword));
             if aword$==""  or g_isnum(aword)==0;
                  call g_pause(100000,-1);
             else ;
                t3=g_strip(strsect(acomm0,ie+1,11111));
                if t3$==""; t3=" hit a key to continue ... "; endif;
                aword=g_pause2(t3,stof(aword));
                if missrv(aword,0)>32 ; aword=chrs(aword); " " aword ;
                else ; ? ; aword=""; endif;
                retp("DEFINE PAUSE "$+aword,-2);
             endif;


@ ==== b) Some often used, parameters @

@ -- The name of the GAUSS dataset. Its value is stored in the FILENAME global @
        elseif KEYWURD$=="FILE" or KEYWURD$=="DATASET" or KEYWURD$=="DATAFILE"
            or keywurd$=="FILENAME"  or keywurd$=="INPUT";

             call clearemf ;    @ clear all file specific variables @
             ndefines=maxc(6|ndefines);
             if ndefines==6 ;
                   let string definew="MODEL" "BASETITLE" "VARLIST" "FILENAME" "DATE" "PROGRAM" ;
                   let string defines="The  Estimator "
                      " The title "
                      "varlist"
                      "filename "
                      "date" 
                      "GRBL2" ;
                  defines[5]=datestring(0) $+ " " $+ timestr(0) ;
                  defines[6]="" $+ grbl2_program ;
                  
            endif;              @ else, these were defiined previously @
            filename=get_copt(acommand,"","You must specify the name of an input file");
            if strindx(upper(filename),".",1) == 0 ;
                    filename=filename $+ ".DAT" ;
            endif;
            filename0=filename;

            filename=g_exists2(filename0,work_dir,"");
            if filename$/="" ;
                       "Input dataset: " $filename;
             else;
                        " ERROR: Not found dataset: ";; filename0;
                        "  -- not in ";;$work_dir;; " or " ;;  g_replacestring(getpath(""),"/","\\",1);
                        call g_pause(20,0);
                       retp(KEYWURD,1);              @ return to user input @
             endif;

@ would be nice if there was some way of non-fatally testing if filename is a legit gauss dataset.
  But there doesnt seem to be @
            varlist=getname(filename);
            aa="" ;


             mmm=0;
             do until mmm==rows(varlist);
                     mmm=mmm+1;
                      aa=aa $+ " " $+ varlist[mmm];
             endo;
             defines[3]=aa;    @ one of the default DEFINES substitutions @
             closeall ;
             open fgoo=^FILEname;
             if fgoo<0 ;
                 "Error,no such dataset: " $filename ;
                     retp(keywurd,1);
              endif;        
              call close(fgoo);
              defines[4]=filename;
              defines[5]=datestring(0) $+ " " $+ timestr(0) ;


        elseif KEYWURD$=="DELETE" ;
                {filename,ie}=g_getwrd(acommand,ie+1);
                if strindx(upper(filename),".",1) == 0 ;
                    filename=filename $+ ".DAT" ;
                endif;
                filename=g_exists2(filename,work_dir,"");
                if filename$/="" ;
                       output on ; ? ;
                       foo=deletefile(filename);
                       if foo ne 0 ;
                         "Unable to delete file (errorcode= ";;format /ld 8,0 ;foo ;;"):";; format /ld 1,8; $filename;
                       else;
                              "File deleted: " $filename;
                      endif;
                      output off ;
                else;
                          " ERROR: Not found file: " ;; filename0;
                        call g_pause(10,0);
                       retp(KEYWURD,1);              @ return to user input @
                endif;

                 

@ -- Uncomment if you want to use SAVE_VC (for saving results). Also, add
     CLEARG RESFILE to g_batch 
                   resfile=filename  ;   @



      elseif KEYWURD$=="BSTART1"  or KEYWURD$=="B1" or KEYWURD$=="BETA1"   or
            KEYWURD$=="BSTART" or KEYWURD$=="B" or KEYWURD$=="BETA" or
            KEYWURD$=="BSTART2" or KEYWURD$=="B2" or KEYWURD$=="BETA2" ;

                tword="BSTART";
                if strindx(keywurd,"1",1) > 0 ; tword="BSTART1" ; endif;
                if strindx(keywurd,"2",1) > 0 ; tword="BSTART2" ; endif;
                tvalue={} ;
                {aword,ie0}=g_getwrd(acommand,ie+1);  @"" YES or NO @
                if ie0==0;             @ nothing after BSTART @
                       $tword ;; " is set to missing ";
                       tvalue=miss(1,1);
                elseif aword$=="NO" ;           @program determines @
                       $tword ;; " is set to missing ";
                       tvalue=miss(1,1);

                elseif aword$=="FILE" ;           @program determines @
                        ie=ie0;    tvalue=miss(1,1);
                        {aword,ie}=g_getwrd(acommand,ie+1);
                         if ie==0;
                           " A GAUSS .FMT file was not specified: " ;; $tword ;; " is set to missing ... ";
                            tvalue=miss(1,1);
                         else;
                              aword=g_strip(aword);
                              oo=g_exists(aword,"");
                              if oo$=="" ;
                                 oo=g_exists(aword $+ ".FMT","");
                              endif;
                              if oo$=="";
                                  " The file ";;$aword;; " does not exists. " ;; $tword ;; " is set to missing ... ";
                                  tvalue=miss(1,1);
                              else;
                                 $aword;
                                 load tmp=^aword;
                                 "Reading " ;;format /ld 6,0; rows(tmp);" starting values from " ;; $aword;
                                 format /le 10,5 ;"      ";; tmp';
                                 tvalue=tmp;
                              endif;
                         endif;

                        if tword$=="BSTART1" ; bstart1=tvalue;
                        elseif tword$=="BSTART2" ; bstart2=tvalue;
                        else ; bstart=tvalue ;
                        endif;
                        retp(KEYWURD,0);


                else ;    @enter new B @
                       if aword$=="YES" or aword$=="ALL"; ie=ie0; endif;
                       {aword,ie}=g_getwrd(acommand,ie+1);
                       if ie==0;
                           " Values were not specified: " ;; $tword ;; " is set to missing ... ";
                           tvalue=miss(1,1);
                        else ;
                          do until ie==0;
                            if g_isnum(aword)==0;
                                 " Bad value, " ;; $tword ;;  " is set to missing";
                                 tvalue=miss(1,1);
                                 break ;
                             endif;
                             tvalue=tvalue|stof(aword) ;
                             {aword,ie}=g_getwrd(acommand,ie+1);
                          endo;
                        endif    ;
                        if not(scalmiss(tvalue));
                            output on;
                            format 11,4;" Values: " tvalue';
                            output off;
                        endif;
                        if tword$=="BSTART1" ; bstart1=tvalue;
                        elseif tword$=="BSTART2" ; bstart2=tvalue;
                        else ; bstart=tvalue ;
                        endif;
                        retp(KEYWURD,0);
                    endif;


          elseif KEYWURD$=="BAUX";
                    baux=miss(1,1);
                    {aword,ie0}=g_getwrd(acommand,ie+1);  @"" YES or NO @
                    if ie0==0;             @ nothing after BAUX @
                       "BAUX is set to missing ";
                       baux=miss(1,1);
                       retp(KEYWURD,0);
                    elseif aword$=="NO" ;           @program determines @
                       "BAUX is set to missing ";
                        baux=miss(1,1);
                        retp(KEYWURD,0);
                    else  ;     @enter new B @
                       if aword$=="YES" or aword$=="ALL"; ie=ie0; endif;
                       {aword,ie}=g_getwrd(acommand,ie+1);
                       if ie==0;
                     " Values were not specified: BAUX is set to missing ... ";
                            baux=miss(1,1);
                            retp(KEYWURD,0);
                        endif    ;
                        do until ie==0;
                          if g_isnum(aword)==0;
                                " Bad value, BAUX is set to missing";
                               baux=miss(1,1);
                               retp(KEYWURD,0);
                           endif;
                           baux=baux|stof(aword) ;
                           {aword,ie}=g_getwrd(acommand,ie+1);
                        endo;
                        baux=packr(baux);
                        if scalmiss(baux);
                             " Error. No  values ";
                        else;
                            output on;
                            format 11,4;" Values: " baux';
                            output off;
                        endif;
                        retp(KEYWURD,0);
                    endif;



          elseif KEYWURD$=="BAUX2" or keywurd$=="BAUX1" ;
                    aBAUX={};


                    {aword,ie}=g_getwrd(acommand,ie+1);  @"" YES or NO @

                    if ie==0;             @ nothing after BAUX2 @
                       $keywurd;; " is set to missing ";
                       aBAUX=miss(1,1);
                    elseif aword$=="NO" ;           @program determines @
                       $keywurd;; " is set to missing ";
                       aBAUX=miss(1,1);

                    elseif aword$=="YES" or aword$=="ALL";     @enter new B @
                       {aword,ie}=g_getwrd(acommand,ie+1);
                       if ie==0;
                            " Values were not specified: ";; $keywurd ;; " is set to missing ... ";
                            aBAUX=miss(1,1);
                        else ;
                          do until ie==0;
                            if g_isnum(aword)==0;
                                output on ; ?;
                                 " Bad value, ";; $keywurd ;; " is set to missing"; ?;
                                output off;
                                 abAUX=miss(1,1);
                                 break ;
                           endif;
                           aBAUX=abAUX|stof(aword) ;
                           {aword,ie}=g_getwrd(acommand,ie+1);
                          endo;

                       endif;

                       if keywurd$=="BAUX2" ;
                                baux2=abaux ;
                        else;
                                baux1=abaux;

                        endif;

                        output on;
                        format 11,4;" Values for " ;; $keywurd ;; "= " ;; abaux';
                        output off;

                       retp(KEYWURD,0);
 

                    endif;

         elseif keywurd$=="WORKDIR" or keywurd$=="WORK_DIR" ;
             foo1=g_strip(strsect(acommand,ie+1,11111)) ;
             if foo1 $=="0" or foo1$=="NO";
                 work_dir="";
                 "Using default directory as working directory ";
             else;
               foo=g_as_dir(foo1,0);
               foo=g_exists(foo,"");       @ relative names are relative to gauss default directory @
              if foo$=="" ;
                         foo=cdir("");
                            call grbl2_error(" ERROR: No such working directory: " $+ foo1 $+ " (current dir= " $+ foo,1);
                           stop;
               else;
                 work_dir=foo;
               endif;   
               " Using working directory: ";; work_dir ; 
            endif;



           elseif KEYWURD$=="OUTPUT" or keywurd$=="OUTFILE" ;  format 1,8;
                   aword=get_copt(acommand,"FILE"|"","You must specify the name of a file to write OUTPUT to");
            
                    outfile=g_strip(aword) ;
                    if strindx(outfile,".",1)==0 ; outfile=outfile $+".OUT"; endif;              
                    tmp=work_dir ;
                    if filename $/=""  ; tmp=g_fspec("DP",filename); endif;        
                    OUTFILE=G_FSPEC2("A",OUTFILE,tmp) ;
                   "OUTPUT FILE IS: " $outfile ;
                    output file=^outfile  ; outwidth 128 ;

                    aword=get_copt(acommand,"-OVERWRITE"|"-RESET","");

@ --- NOTE: 11/05 change.. No longer support OUTPUT filename reset ;
                 Instead: output filename -reset
                        or output file="a file name" -reset ;  
-------- @
                
                    if aword$/="";
                           output off ;
                           output reset ; output off ;
                           $outfile " will be overwritten." ; 
                    endif;
                    output off;



@ controls how many intermediate results are displayed @
           elseif KEYWURD$=="PRNTIT" or keywurd$=="PRINT_IT"
                  or keywurd$=="VERBOSE" ;  format 1,8;
                   {aword,ie}=g_getwrd(acommand,ie+1);
                   if aword$=="NO" or  Aword$=="0";
                        prntit=0 ;
                   else;
                        prntit=1 ;

                   endif;

@ display basic info on dataset @
           elseif KEYWURD$=="INFO" ;
                afile=get_copt(acommand,"","");
                if afile$/="";    @ use this filename @
                   afile=g_strip(afile);
                   if strindx(upper(afile),".",1) == 0 ;
                     afile=afile $+ ".DAT" ;
                   endif;
                  open fgoo=^afile ;
                   if fgoo<1;
                         "ERROR: no such gauss data file: " ;; $afile ;
                        call g_pause(10,0);
                       retp(KEYWURD,1);              @ return to user input @
                  endif;
                  avarlist=getname(afile);
                  output on ;
                  ?;"=== Info === ";

                  {tmp,tmp1}=fileinfo(afile);
                  tmp=g_addcomma(tmp1[8],0);
                  tmp2=utctodt(tmp1[10]);
                  tmp2=dttostr(tmp2,"YYYY-MO-DD HH:SS");
                  "Dataset: ";; $afile; format /ld 1,8;
                  "   Bytes=";;$tmp;;" ; Last modified on ";;$tmp2;

                  "# of rows in dataset: ";; format /ld 9,0 ; rowsf(fgoo);
                  fgoo=close(fgoo);
                  "Variables: ";           
                  call g_showvec(avarlist,6,3," ");
                  ?;
                  output off;

                else;           @ use currently specifield filename @

                  if varlist==0; 
                      call grbl2_error("ERROR :: INFO requested before FILE specified ",1);
                      stop ;
                  endif;
                  open fgoo=^filename ;

                  output on;
                 ?;"=== Info === ";

                  {tmp,tmp1}=fileinfo(filename);
                  tmp=g_addcomma(tmp1[8],0);
                  tmp2=utctodt(tmp1[10]);
                  tmp2=dttostr(tmp2,"YYYY-MO-DD HH:SS");
                  "Dataset: ";; $filename; format /ld 1,8;
                  "   Bytes=";;$tmp;;" ; Last modified on ";;$tmp2;

                "# of rows in dataset: ";; format /ld 9,0 ; rowsf(fgoo);
                 fgoo=close(fgoo);
                  "Variables: ";           
                  call g_showvec(varlist,6,3," ");
                 ?;
                 output off;
              endif;             


@ A title to use when displaying results @
           elseif KEYWURD$=="TITLE";  format 1,8;
                 title=strsect(acomm0,ie+1,comlen) ;
                 print "Title: ";;$title;

           elseif strsect(KEYWURD,1,5)$=="TITLE";  format 1,8;     @ TITLEn? @

                titlen=strsect(acomm0,ie+1,comlen) ;

                aii=g_strip(strsect(keywurd,6,10000)); 
                ii=stof(aii);
                if ii le 0 or ii> 10; call grbl2_error(" ERROR: bad n in TITLEn -- " $+ keywurd,1) ; stop; endif;

                foo=g_parse_title(title,"");
                if rows(foo)< ii;
                   for j (rows(foo)+1,ii,1);
                      foo=foo$|" " ;
                   endfor;
                endif;
                foo[ii]=titlen;

                title=foo[1];
                for j (2,maxc(rows(foo)|ii),1);
                   title=title $+ chrs(13) $+ chrs(10)  $+ foo[j];
                endfor;


@ Create a list of matrices to save to gauss .FMT files
  Returns I_SVLIST (# of matrices to save),
          SAVEVARS (name of matrix to save) and 
          SAVEFILS (.FMT file to save to) @

         elseif KEYWURD$=="SAVE"  ;  format 1,8;
            i_svlist=0;
            let string savefils=" ";
            let string savevars=" ";
            do until 1==2 ;
               {aword,ie}=g_getwrd(acommand,ie+1);
               aword=g_strip(upper(aword));
               if aword$=="" ; break ; endif;
               if aword$=="NO" ; break ; endif;
               iee=strindx(aword,"=",1);
                if iee==0;
                  aword1=aword;
                  aword2=aword ;
                else;
                   aword2=strsect(aword,1,iee-1);
                   aword1=strsect(aword,iee+1,1000);
                endif;
                i_svlist=i_svlist+1;
                savevars=savevars|aword1 ;
                savefils=savefils|aword2 ;
                if ie eq 0 ; break ; endif;
             endo;

@ Set the value of a GAUSS variable @
        elseif keywurd$=="SET" ;
            do until 1==2 ;
               {aword,ie}=g_getwrd(acommand,ie+1);
               aword=g_strip(upper(aword));
               if aword$=="" ; break ; endif;
               iee=strindx(aword,"=",1);
               if iee==0;
                  call grbl2_error(" ERROR in SET statement: " $+ acomm0,1) ;
                  stop;
               endif;
               aword1=strsect(aword,1,iee-1);
               aword2=strsect(aword,iee+1,1000);
               if g_isnum(aword2) ;aword2=stof(aword2) ;endif;
               oo=varput(aword2,aword1);
               if oo==0;
                   call grbl2_error("ERROR attempting SET : " $+ aword1 $+ " = " $+ aword2,1) ;
                   stop;
               endif;
             endo;


@ Read in a vector of variable names (the "X" vector). Results saved to
  XINDEX (indices to variable locations), XNAME (the actual variable names),
  and NX (the # of variables) @
       elseif KEYWURD$=="X"  ;  
          {aword,ietmp}=g2getwrd(acommand,ie+1); 
          qconst=0;
          if aword$=="-CONST" ;
              ie=ietmp;
              qconst=1;
          endif;
          if get_copt(acommand," *","")$/="" ;  @ use all? @
               xname=varlist;
               xindex=indcv(xname,varlist);
               nx=rows(xname);
          else;
             {xindex,xname,nx,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
          endif;
          if missrv(xindex,0)==0 ; 
             if qconst==1 ;
                xname="CONSTANT" ; nx=1 ; xindex=0 ;
            else;
              xname=miss(1,1); nx=0; 
            endif;
         else;
           xname=varlist[xindex];   @ just to be sure @
           if qconst==1 ;
              nx=nx+1 ;
              xindex=0|xindex ;
              xname="CONSTANT"|xname ;
           endif;
        endif;


@ Read in a vector of variable names (the "X1" vector). Results saved to
  X1INDEX (indices to variable locations), X1NAME (the actual variable names),
  and NX1 (the # of variables) @
          elseif KEYWURD$=="X1";
                {aword,ietmp}=g2getwrd(acommand,ie+1); 
                qconst=0;
                if aword$=="-CONST" ;
                    ie=ietmp;
                    qconst=1;
                 endif;
                {X1index,X1name,nX1,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(X1index,0)==0 ; 
                      if qconst==1 ;
                         X1name="CONSTANT" ; nX1=1 ; X1index=0 ;
                      else;
                         X1name=miss(1,1); nX1=0; 
                      endif;
                   else;
                       X1name=varlist[X1index];   @ just to be sure @
                       if qconst==1 ;
                          nX1=nX1+1 ;
                          X1index=0|X1index ;
                          X1name="CONSTANT"|x1name ;
                       endif;
                   endif;



@ Read in a vector of variable names (the "X2" vector). Results saved to
  X2INDEX (indices to variable locations), X2NAME (the actual variable names),
  and NX2 (the # of variables) @
          elseif KEYWURD$=="X2";
                {aword,ietmp}=g2getwrd(acommand,ie+1); 
                qconst=0;
                if aword$=="-CONST" ;
                    ie=ietmp;
                    qconst=1;
                 endif;
                {X2index,X2name,nX2,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(X2index,0)==0 ; 
                      if qconst==1 ;
                         x2name="CONSTANT" ; nx2=1 ; x2index=0 ;
                      else;
                         x2name=miss(1,1); nx2=0; 
                      endif;
                   else;
                       X2name=varlist[X2index];   @ just to be sure @
                       if qconst==1 ;
                          nx2=nx2+1 ;
                          x2index=0|x2index ;
                          x2name="CONSTANT"|x2name ;
                       endif;
                   endif;


@ Read in a vector of variable names (the "X3" vector). Results saved to
  X3INDEX (indices to variable locations), X3NAME (the actual variable names),
  and NX3 (the # of variables) @
          elseif KEYWURD$=="X3";
                {aword,ietmp}=g2getwrd(acommand,ie+1); 
                qconst=0;
                if aword$=="-CONST" ;
                    ie=ietmp;
                    qconst=1;
                 endif;
                {x3index,x3name,nx3,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(x3index,0)==0 ; 
                      if qconst==1 ;
                         x3name="CONSTANT" ; nx3=1 ; x3index=0 ;
                      else;
                          x3name=miss(1,1); nx3=0; 
                      endif;
                   else;
                       x3name=varlist[x3index];   @ just to be sure @
                       if qconst==1 ;
                          nx3=nx3+1 ;
                          x3index=0|x3index ;
                          x3name="CONSTANT"|x3name ;
                       endif;
                   endif;


@ Read in a vector of variable names (the "Z" vector). Results saved to
  ZINDEX (indices to variable locations), ZNAME (the actual variable names),
  and NZ (the # of variables) @
          elseif KEYWURD$=="Z"  ;
                {zindex,zname,nz,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(zindex,0)==0 ; zname=miss(1,1); nz=0; else;
                         zname=varlist[zindex];   @ just to be sure @
                   endif;

@ Read in a vector of variable names (the "Z1" vector). Results saved to
  Z1INDEX (indices to variable locations), Z2NAME (the actual variable names),
  and NZ1 (the # of variables) @
          elseif KEYWURD$=="Z1" ;
                {z1index,z1name,nz1,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(z1index,0)==0 ; z1name=miss(1,1); nz1=0; else;
                         z1name=varlist[z1index];   @ just to be sure @
                   endif;

 
@ Read in a vector of variable names (the "Z2" vector). Results saved to
  Z2INDEX (indices to variable locations), Z2NAME (the actual variable names),
  and NZ2 (the # of variables) @
          elseif KEYWURD$=="Z2" ;
                {z2index,z2name,nz2,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(z2index,0)==0 ; z2name=miss(1,1); nz2=0; else;
                         z2name=varlist[z2index];   @ just to be sure @
                   endif;

@ Read in a vector of variable names (the "Z3" vector). Results saved to
  Z3INDEX (indices to variable locations), Z3NAME (the actual variable names),
  and NZ3 (the # of variables) @
          elseif KEYWURD$=="Z3" ;
                {z3index,z3name,nz3,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(z3index,0)==0 ; z3name=miss(1,1); nz3=0; else;
                         z3name=varlist[z3index];   @ just to be sure @
                   endif;



@ Read in a dependent variable @
           elseif KEYWURD$=="DEP" or KEYWURD$=="Y" or KEYWURD$=="DEPENDENT" ;
                 if varlist==0; 
                     call grbl2_error("ERROR :: Y variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {yindex,yname,foo,ie}=g_1opt("DEPENDENT Variable",
                                  acommand,ie,exactxyz,varlist,0,miss(1,1)) ;
                 if foo==0;
                        " WARNING :: NO DEPENDENT VARIABLE ";
                 endif;

@ Read in a 1std dependent variable @
           elseif KEYWURD$=="Y1";
                 if varlist==0; 
                     call grbl2_error("ERROR :: Y1 variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {y1index,y1name,foo,ie}=g_1opt("Y1 variable ",
                                  acommand,ie,exactxyz,varlist,0,miss(1,1)) ;


@ Read in a 2nd dependent variable @
           elseif KEYWURD$=="Y2";
                 if varlist==0; 
                     call grbl2_error("ERROR :: Y2 variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {y2index,y2name,foo,ie}=g_1opt("Y2 variable ",
                                  acommand,ie,exactxyz,varlist,0,miss(1,1)) ;

@ Read in a 3rd dependent variable @
           elseif KEYWURD$=="Y3";
                 if varlist==0; 
                     call grbl2_error("ERROR :: Y3 variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {y3index,y3name,foo,ie}=g_1opt("Y3 variable ",
                                  acommand,ie,exactxyz,varlist,0,miss(1,1)) ;

@ Read in a vector of variable names (the "AUX" vector). Results saved to
  AINDEX (indices to variable locations), AUXNAME (the actual variable names),
  and NAUX (the # of variables) @
          elseif KEYWURD$=="AUX"  ;
                {aindex,auxname,naux,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(aindex,0)==0 ; auxname=miss(1,1); naux=0; else;
                         auxname=varlist[aindex];   @ just to be sure @
                   endif;

@ Read in a vector of variable names (the "AUX1" vector). Results saved to
  A1INDEX (indices to variable locations), AUX1NAME (the actual variable names),
  and NAUX1 (the # of variables) @

          elseif KEYWURD$=="AUX1"  ;
                {a1index,aux1name,naux1,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(a1index,0)==0 ; aux1name=miss(1,1); naux1=0; else;
                         aux1name=varlist[a1index];   @ just to be sure @
                   endif;


@ Read in a vector of variable names (the "AUX2" vector). Results saved to
  A2INDEX (indices to variable locations), AUX2NAME (the actual variable names),
  and NAUX2 (the # of variables) @
          elseif KEYWURD$=="AUX2"  ;
                {a2index,aux2name,naux2,ie}=g_Nopts("" $+ keywurd $+ " variables",
                                acommand,ie,exactxyz,varlist,"0"|"NO");
                   if missrv(a2index,0)==0 ; aux2name=miss(1,1); naux2=0; else;
                         aux2name=varlist[a2index];   @ just to be sure @
                  endif;


@ Read in a id variable @
           elseif keywurd$=="ID" or keywurd$=="ID1" or keywurd$=="ID2" or keywurd$=="ID3";
                 if varlist==0 ; 
                     call grbl2_error("ERROR :: " $+ keywurd $+" variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {aidindex,aidname,foo,ie}=g_1opt(keywurd $+" variable ",
                                  acommand,ie,exactxyz,varlist,1,"0"|"NO"|"*") ;  @ if number, idname=#, index=numeric_value @

                 aidtype=get_copt(acommand,"TYPE"|"-ID"|"-COUNT"|"-BAL","");
                 if aidtype$=="-COUNT"; aidtype="COUNT"; endif;
                 if aidtype$=="-BAL"; aidtype="BAL" ; endif;
                 if aidtype$=="-ID" ; aidtype="ID" ; endif;
                 if scalmiss(indcv(aidtype,"BAL"|"COUNT"|"ID")); aidtype="" ; endif;


                 if aidname$=="*"; 
                        aidname="#" ; aidindex=-1 ; aidtype="BAL" ;             @ 0 means "use all" @ 
                 elseif foo==3 ;                @ ID 0 or ID NO @
                    aidindex=0 ; aidname="" ; aidtype="" ;
                 elseif foo==2 ;                @ # entered; must be balanced @
                    aidindex=aidname ; aidname="#" ; aidtype="BAL";  @ balanced id default .. (note that idname is in numeric format already @
                 elseif scalmiss(idindex); 
                     stop ;
                 endif;

                 if keywurd$=="ID" ;
                    idname=aidname; idindex=aidindex; idtype=aidtype;
                 elseif keywurd$=="ID1" ;
                    id1name=aidname; id1index=aidindex; id1type=aidtype;
                 elseif keywurd$=="ID2" ;
                    id2name=aidname; id2index=aidindex; id2type=aidtype;
                 elseif keywurd$=="ID3" ;
                    id3name=aidname; id3index=aidindex; id3type=aidtype;
                 endif;
               


@ Read in a weight variable (WEIGHT, WEIGHT1, WEIGHT2) @
           elseif strsect(KEYWURD,1,6)$=="WEIGHT";
                 if varlist==0; 
                     call grbl2_error("ERROR :: " $+keywurd $+ " variable defined before FILE specified ",1);
                     stop ;
                 endif;
                 {awtindex,awtname,foo,ie}=g_1opt(keywurd$+" variable ",
                                  acommand,ie,exactxyz,varlist,0,"0"|"NO") ;
                 if foo==3 ; awtindex=0 ; endif;
                 awtnorm=0; awtrep=0;
                 if get_copt(acommand,"-NORM"$|"-NORMALIZE","")$/="" ; awtnorm=1 ; endif;
                 if get_copt(acommand,"-REP"$|"-REPLICATION","")$/="" ; awtrep=1 ; endif;
                 if keywurd$=="WEIGHT";
                    wtindex=awtindex ; wtname=awtname;wtnorm=awtnorm;wtrep=awtrep;
                 elseif keywurd$=="WEIGHT1";
                    wt1index=awtindex ; wt1name=awtname;wt1norm=awtnorm;wt1rep=awtrep;
                 elseif keywurd$=="WEIGHT2";
                    wtindex2=awtindex ; wtname2=awtname;wtnorm2=awtnorm;wtrep2=awtrep;
                    wt2index=awtindex ; wt2name=awtname;wt2norm=awtnorm;wt2rep=awtrep;
                 endif;



@ create an index of observations to retain @
        elseif keywurd$=="SELECT";
           if missrv(varlist,0)==0 ;
               call grbl2_error("ERROR:: Specify a FILE before using SELECT ",1);
                stop ;
            endif;
             gosub dosel1 ;

        elseif keywurd$=="DUMMY";
           if missrv(varlist,0)==0 ;
               call grbl2_error("ERROR:: Specify a FILE before using DUMMY  ",1);
                stop ;
            endif;
            gosub dodum1 ;
        

        elseif keywurd$=="XNEW";
           if missrv(varlist,0)==0 ;
               call grbl2_error("ERROR:: Specify a FILE before using XNEW ",1);
                stop ;
            endif;
            gosub doxnew ;

                
@ View a file @
         elseif keywurd$=="VIEW" or keywurd$=="VU";
             {aword,ie}=g_getwrd(acommand,ie+1); aword=upper(aword);
             if g_strip(aword)$=="" or aword$=="."; aword=outfile; endif;
             t3="Viewing current output file: ";
             if ie> 0 ;
                t3=strsect(acomm0,ie+1,1111);
                t3=g_repstg(t3,definew,defines);
             endif;
             call g_vufile(aword,t3);


@ ==== c) End of options. Possibly check g_Batch2.
          If not a known option, a WARNING is issued @
         else;                 @ unknown keywurd -- perhaps custom? @
            stat2=0;

            if batch2==1 ;
               {ie,stat2}=g_batch2(keywurd,acommandlc,ie) ;
            endif;
            if stat2==3;
                output on ;
                " WARNING: Unknown keyword: " $keywurd;
                 output off;
                retp(KEYWURD,3);
            endif;

         endif;

         retp(KEYWURD,0);              @ known KEYWURD @


@ ********************************************* @
@ end of main portion of this procedure.  @


@-------- GOSUB'able routine:  ------------- @


@create slctlist (observation selection criteria) @
@ Process observation selection instructions. Returns "slctlist" matrix, which
  contains instructions on what observations to remove.
  slctlist is used by g_dropob and g_dspcrt

If missrv(slctlist,0)==0, use all observations.
Otherwise;
 each row of slctlist is a selection criteria
  col1: AND (1) or OR (2)
  col2: Varindex (into the VARLIST variable)
  col3: Condition (1=EQ  2=NE  3=GE  4=LE  5=GT  6=LT ;
  col4: A numeric value

@

dosel1: ;
slctlist=miss(1,1); tmpc=0 ; n_ors=0;

do until ie==0 ;
  {aword1,ie}=g2getwrd(acommand,ie+1);  @ or or and   @
  if ie>0;            @ end of list ? @
     if aword1$=="NONE" or aword1$=="NO" or aword1$=="0"
                      OR AWORD1$=="ALL";
       " All observations will be used ";
        slctlist=miss(1,1);
        retp(KEYWURD,0);
     endif;
     if aword1 $/= "OR" and aword1 $/= "AND" ;
             call grbl2_error("ERROR: select clause must begin with either OR or AND ( " $+ acommand,1) ;
              stop;
     endif;
     {aword2,ie}=g2getwrd(acommand,ie+1);  @ Variable    @
     {aword3,ie}=g2getwrd(acommand,ie+1);  @ condition   @
     {aword4,ie}=g2getwrd(acommand,ie+1);  @ value       @


     t1=indcv(aword1,andornam);
     if t1==2 ; n_ors=n_ors+1 ; endif;
     if exactxyz==1 ;
                t2=indcv(aword2,varlist);
      else;
              t2=g_indcv2(aword2,varlist);
      endif;
      if ismiss(t2);
                call grbl2_error("Bad selection variable: " $+ aword2,1);
                 stop;
       endif;
       t3=indcv(aword3,condname);
       t4=g_isnum(aword4);
       if ismiss(t1~t2~t3) or t4==0 ;
              call grbl2_error(" Bad element in SELECT: " $+ acommand,1);
            stop ;
        else; 
            if tmpc==0 ;
               tmpc=andornam[t1]~t2~condname[t3]~stof(aword4);
            else ;              
               tmpc=tmpc|andornam[t1]~t2~condname[t3]~stof(aword4);
            endif;
        endif;
      endif;                    @ processing a SELECT clause @
endo;                   @ processing SELECT command (possibly many clauses) @
if n_ors==0 ;
      call grbl2_error("ERROR: no OR clause in: " $+ acommand,1) ;
     stop;
endif;
slctlist=tmpc;
format /ld ; rows(slctlist);; " selection criteria will be used. ";

return;

@ ********************************************* @

@ This will process commands of the form:
  DUMMY ORDINAL_VAR CARDINAL_VAR_OR_1 -DROP_or_-KEEP , def2 ...  , ;

  Examples: DUMMY REGION 1 -DROP  ; if 5 different regions, makes a vector of four 0/1 dummies (first level is dropped)
            DUMMY RACE INCOME -KEEP ;  if 4 races, makes a 4x1 vector zero vector, with one of the zeros replaced by INCOME

or
  DUMMY NO
to not have any dummies

Creates Dinfo
Each row in Dinfo refers constains instructions for creating a dummy variables. The structure of the row is:
   ORDINAL_VAR_INDEX~CARDINAL_VAR_INDEX~basename~NLEVELS~Level1~...~LevelN
Use CARDINAL_VAR_INDEX=0 (or missing) to signal create 0/1 dummies (no cardinal variable used)
if more then one DUMMY being created, the rows will be of unequal length (use missings to pad the matrix size to the length
of the longest row, where the longest row is belongs to the ordinal variable with the most levels)

if DUMMY NO, dinfo is set to miss(1,1)
 @


dodum1:

newkol=0; oldkol=0;
nxdummy =0 ;
ndummies=0;

dinfo=miss(1,1);
open fdum=^filename ;

do until 1==2 ;

if ie==0 ; foo=close(fdum) ; return ;endif ;

{aword,foo}=g2getwrd(acommand,ie+1);

if foo==0 ; foo=close(fdum) ;  return ; endif ;
if aword$==" " ; foo=close(fdum) ; return ; endif ;

foo=seekr(fdum,1);

@ first, find dummy id variable @
{dindex,dname,foo,ie}=g_1opt("DUMMY ordinal variable ",
                                  acommand,ie,exactxyz,varlist,0,"0"|"NO") ;

if foo>2 or (foo==1 and dindex$=="NO") ;    @0 or NO mean "no dummies" @
 dinfo=0 ;  call close(fdum) ; return ;
endif ;    

if foo eq 0 ;
    call grbl2_error(" ERROR, no such ordinal variable in : " $+acommand,1) ;
     stop;
endif;


"Finding levels for dummy var " $dname ;; " ... ";;

dvals=miss(1,1);
nr=minc(1000|rowsf(fdum));
do until eof(fdum);
    ddta=readr(fdum,nr);
    ddta=ddta[.,dindex];
    dVALS=UNIQUE(dVALS|dDTA,1);
endo;
dvals=packr(dvals);
if rows(dvals) gt 50 ;  @ max of 50 levels @
   ?; 
   call grbl2_error("DUMMY error: max of 50 levels, found " $+ g_ftos2(rows(dvals),1) $+  " : " $+ acommand,1);
   stop;
endif;
format /ld 5,0 ; rows(dvals);; " levels detected ";

if scalmiss(dvals) ;
  format /ld 1,8; 
  call grbl2_error("ERROR: no non-missing values for ordinal variable: "  $+dname ,1);
  stop;
endif;
if rows(dvals)==1 ;
  call grbl2_error("ERROR: ordinal variable" $+ dname  $+ " has only one value (" $+ g_ftos2(dvals[1],1),1) ; 
  stop;
endif;

if nxnew>0 ;
  vv=varlist|xnewinfo.names[1:nxnew] ;
else ;
  vv=varlist;
endif;
 
{d2index,d2name,foo,ie}=g_1opt("   (DUMMY cardinal variable ",
                                  acommand,ie,exactxyz,vv,0,"1");
if foo==0; 
    call grbl2_error(" ERROR, no such cardinal variable in : " $+acommand,1) ;
     stop;
endif;
 

if foo>2 ;
   d2name="0/1"; d2index=0 ;
endif ;
print "   (using cardinal variable: " $d2name;;"  " ;;

@ look for -DROP/-KEEP and -BASE- options @
ndummies=ndummies+1;
basename="D" $+ ftos(ndummies,"%0*.*lf",2,0) $+ "_";

do while 1==1 ;
    {aword,ie}=g2getwrd(acommand,ie+1);

    if aword$=="" or ie==0 ; break ; endif;
    if aword$=="~" or aword$=="|" ; break; endif;
       
     if aword$=="-DROP" ;
         dvals=dvals[2:rows(dvals)]; 
         print "   (dropping first level)" ;
     endif;
     if strindx(aword,"-BASE=",1) eq 1 ;
        basename=g_strip(upper(strsect(aword,7,5))) ;
     endif;
endo;   


@   ORDINAL_VAR_INDEX~CARDINAL_VAR_INDEX~BASENAME~NLEVELS~Level1~...~LevelN@

if scalmiss(dinfo);
   nxdummy=rows(dvals);
   dinfo=dindex~d2index~basename~rows(dvals)~(dvals') ;
   continue ;
else ;
  nxdummy=nxdummy+rows(dvals);
  tmp=dindex~d2index~basename~rows(dvals)~(dvals') ;
  newkol=cols(tmp);
  oldkol=cols(dinfo);
  if newkol eq oldkol ;
    dinfo=dinfo|tmp ;
    continue; 
  endif;
  if oldkol > newkol ;
     tmp=tmp~zeros(1,oldkol-newkol) ;
     dinfo=dinfo|tmp ;
    continue;
 endif;

@else, this one is longer @
  tmp2=zeros(rows(dinfo),newkol);
  tmp2[1:rows(dinfo),1:oldkol]=dinfo;
  dinfo=tmp2|tmp;
  continue ;
endif;


endo;   @ loop forever @


@ -------------------------------- @
@ make Xnew variables 
Sets the XNEWinfo global structure
   scalar neq ;   # of equations, max of 20 
   matrix names ;   neq x 1 vector of new varnames (up to 20, neq+1 to 20 are ignored)
   matrix nterms ;  neq x 1 vector of # of terms in each equation 
   matrix vtypes ;  matrix of  VAR/NUM/FUNC indicators, 1 row per equation ( max of 5 columns) 
   matrix vvals ;   matrix of indices/values, 1 row per equation 
   matrix opers ;  matrix of +-* / operators, 1 row per equation  
@

doxnew:
local ithm,acomm2,ac,iac;
local topers,topers2;
local nxt_var,minfo1,imat;
local funcflag ;
local ee,newee,i2,ee0;

nxnew=0 ;

topers= "("|"^"|"+"|"-"|"*"|"/"|"="|"%"|"\\"|"<"|">";
topers2="("|"^"|"+"|"-"|"*"|"/"|"\\"|"%"|"="|"<"|">";

@ 1: fix up equations to adhere to polish notation @
newee="";
{ee0,ee}=token(acommand);
 
do while 1==1 ;
 if ee $=="" ; break ; endif;
  i1=strindx(ee,"~",1);
  i2=strindx(ee,"|",1);
  if i1==0 and i2==0 ;    @ just this equation @
    if newee$/="" ; newee=newee $+ " ~  " ; endif;
    newee=newee$+ g_mkpolish(ee);
    break ;
  elseif i1==0 and i2>0 ;               @ found | @
    ee0=strsect(ee,1,i2-1);
    ee=strsect(ee,i2+1,10000000);
    if newee$/="" ; newee=newee $+ " ~  " ; endif;
    newee=newee$+ g_mkpolish(ee0);
  elseif i1>0 and i2==0 ;               @ found ~ @
    ee0=strsect(ee,1,i1-1);
    ee=strsect(ee,i1+1,10000000);
    if newee$/="" ; newee=newee $+ " ~  " ; endif;
    newee=newee$+ g_mkpolish(ee0);
 else;
    i1=minc(i1|i2);
    ee0=strsect(ee,1,i1-1);
    ee=strsect(ee,i1+1,10000000);
    if newee$/="" ; newee=newee $+ " ~  " ; endif;
    newee=newee$+ g_mkpolish(ee0);
 endif;
endo;    
 
acommand=newee ;


@ 2: add spaces before/after operators @ 
acomm2="" ;
for iac (1,strlen(acommand),1) ;
  ac=strsect(acommand,iac,1);
  if not(scalmiss(indcv(ac,topers)));
     acomm2=acomm2$+" "$+ac$+" ";
  else;
     acomm2=acomm2$+ac;
  endif;
endfor;

XNEWinfo.neq=0 ;
XNEWinfo.names=zeros(20,1);

ithm=0; 
ie=1;
do until 1==2 ;
  {aword,ie}=g2getwrd(acomm2,ie+1);
  if aword$=="" ; break ; endif;
  ithm=ithm+1;
  if ithm> 20 ;
     call grbl2_error("Error: too many equations (max of 20) : "  $+acommand ,1); 
    stop;
  endif;
@ first word is Dep variable @
  XNEWinfo.names[ithm]=aword ;
  {aword,ie}=g2getwrd(acomm2,ie+1);  aword=g_strip(aword);
  if aword$=="=" ;   @ drop the = siqn @
     {aword,ie}=g2getwrd(acomm2,ie+1);  
  endif;
  if aword$=="|" or aword$=="~" or aword $=="" ;  @ end of defs, or end of this equation @
     call grbl2_error( "Error: no terms in equation: " $+acommand,1) ;
     stop;
   endif;   
  if g_isnum(aword)==1 ;  @ number @
        XNEWinfo.vtypes[ithm,1]="NUM";
        XNEWinfo.vvals[ithm,1]=stof(aword) ;
   else ;              @ else, variable @
        tmp=indcv(aword,varlist);
        if scalmiss(tmp)==1 ;
             tmp=indcv(aword,XNEWinfo.names) ;
             if scalmiss(tmp)==1 ;
                call grbl2_error( "XNEW error: no such variable, " $+ aword $+  ", in "  $+acommand,1) ;
                stop ;
             endif;
             XNEWinfo.vtypes[ithm,1]="NEWVAR";
             XNEWinfo.vvals[ithm,1]=tmp ;
         else ;
            XNEWinfo.vtypes[ithm,1]="VAR";
            XNEWinfo.vvals[ithm,1]=tmp ;
         endif;
   endif;
   XNEWinfo.opers[ithm,1]="=";
   XNEWinfo.nterms[ithm]=1;

  nxt_var=0; ;imat=2;
  funcflag=0;
    
  do until 1==2 ;       @ now do 2 through 5  * VAR/VALUE terms @
     {aword,ie}=g2getwrd(acomm2,ie+1);   


     if aword$==" " or aword$=="|" or aword$=="~" or aword $=="" ;  @ end of defs, or end of this equation @
         break ;
     endif;   
     if imat>5 ;
        call grbl2_error( "Error: too many terms in equation (max of 5) : " $+acommand,1) ; 
        stop;
     endif;

     if nxt_var==1 ;   @ expecting var or number  (VAR or  NUM) @
        if g_isnum(aword)==1 ;  @ number @
           XNEWinfo.vtypes[ithm,imat]="NUM";
           XNEWinfo.vvals[ithm,imat]=stof(aword) ;
        elseif funcflag==1 ;
           XNEWinfo.vtypes[ithm,imat]="FUNC";
           XNEWinfo.vvals[ithm,imat]= aword ;

        else ;              @ else, variable @
          tmp=indcv(aword,varlist);
          if scalmiss(tmp)==1 ;
               tmp=indcv(aword,XNEWinfo.names) ;
               if scalmiss(tmp)==1 ;
                  call grbl2_error("XNEW error: no such variable " $+ aword $+ ": "  $+acommand,1) ;
                  stop ;
               endif;
               XNEWinfo.vtypes[ithm,imat]="NEWVAR";
               XNEWinfo.vvals[ithm,imat]=tmp ;
           else ;
              XNEWinfo.vtypes[ithm,imat]="VAR";
              XNEWinfo.vvals[ithm,imat]=tmp ;
           endif;
        endif;
        XNEWinfo.nterms[ithm]=imat ;
        imat=imat+1 ; 
        nxt_var=0  ;        @ signal that next word should be an operator @

     else ;             @  the operator ... @
        if aword$==" " ;      
            call grbl2_error("Error: there is no such operator (" $+aword $+ ") in XNEW list: "  $+acommand ,1);
            stop ;
        endif;
        tmp=indcv(aword,topers2);
        if scalmiss(tmp)==1 ;
            call grbl2_error("Error: no such operator (" $+ aword $+ ") in XNEW list: "  $+ acommand ,1);
            stop ;
        endif;
        XNEWinfo.opers[ithm,imat]=aword;
        if aword$=="(" ; funcflag=1 ; else ; funcflag=0 ; endif;
        nxt_var=1 ;
     endif;
  endo;

  if nxt_var==1 ;
     call grbl2_error( "Error: expecting a value after operator "$+acommand,1) ;
     stop;
  endif;
  format /ld 10,8 ; "New variable: ";; $XNEWinfo.names[ithm];;
  " has ";; 
  format /ld 3,0 ; XNEWinfo.nterms[ithm] ;; " terms. ";

  XNEWinfo.neq=XNEWinfo.neq+1;

  if ie==0 ;   break ;  endif ;

endo;

nxnew=xnewinfo.neq ;

return;

endp;


/****************************************************/
/* Create global  variables, given indices set by g_batch.
Uses:
   xindex, X1index ,X2index, x3ndex, yindex,y1index, y2index, y3index,
   zindex ,z1index, z2index, z3index, idindex, id1index, id2index, id3index 
   wtindex,wt2index,aindex,a2index
  (and nx,nX2,nx3,nz,nz2,nz3)
   dinfo,XNEWinfo
to create the global matrices (or vectors):
   x, X1, X2, x3, y,  y1,  y2,  y3
   z, Z1, z2, z3, id,id1, id2, id3
   weight, weight2,aux,aux2,aux1
   XDUM, and XNEW
Syntax:
   astat=g_mkdata(which,dta,selects,remove)
 where:
   dta: the original (from data file) data to extract these variables from
        Or, missing, in which case the entire current dataset is read
  which:
    ALL - make all defined variables 
    or a vector containing any of the following:
      X,X1,X2,X3,Y,Y1,Y2,Y3,Z,Z1,Z2,Z3,ID,ID1,ID2,ID3,DUMMY,XNEW,WEIGHT,WEIGHT2,AUX,AUX1,AUX2 :
    to make the corresponding variables
  selects: observations to start with (i.e.; the output of G_DOSLCT).  ONLY USED if
           DTA=miss(1,1).  A value of 0 means "do NOT do observation selection "
  removes: 0= keep all rows, 1=drop rows with missings, 2= 1 & treat nans and infs as missing

  okrows: index, into dta, of retained rows -- retained rows are based on selects, and missing/bad
          values in variables that are created

Note: there will be rows(dta) (or rowsf(filename) rows in all matrices created.

        OBSERVATION SELECTION SHOULD BE PERFORMED ON DTA BEFORE CALLING THIS PROCEDURE

*/
proc g_mkdata(which,dta,selects,removes);

local which2,fdum,foo,nr,ncols;
local oindex,cindex,lvls,nl,adummy,t1,jj,jj2,ntrms,ntrms2;
local atype,aval,aoper,tid;
local xn1,xn2;
local okindx,no_nan,gots ,i_Id;
local funcname,tmp ;
local ddo;

which=upper(which);


if scalmiss(dta);       @  read all observations in current dataset @
  open fdum=^filename ;
  if fdum<0 ; 
        call grbl2_error("G_MKDATA error: no such filename: " $+filename,1) ;
        stop ;
  endif;
  nr=rowsf(fdum);
  dta=readr(fdum,nr);
  if scalmiss(miss(selects,0)) ne 1 ;  @ valid selects vector was supplied @
    dta=dta[selects,.];
  endif;
  foo=close(fdum);
else;
  nr=rows(dta);
endif ;

if which$=="ALL" ;
   let which2=X X1 X2 X3 Y Y1 Y2 Y3 Z Z1 Z2 Z3 ID ID1 ID2 ID3 WEIGHT WEIGHT2 AUX AUX1 AUX2 DUMMY XNEW  ;
else;
  if type(which)==13 ;
     which2=0 $+ which ;
  else ;
     which2=which;
  endif;
endif;
ncols=cols(dta);

okindx=seqa(1,1,rows(dta)) ;
no_nan=0 ; if removes==2 ; no_nan=1 ; endif;

gots=0 ;

/* for each possible matrix to create.... */

@ X? @

if not(scalmiss(indcv("X",which2))) ;
  clearg x; 
  if missrv(nx,0) gt 0 ;        
     if ismiss(xindex) ;
          call grbl2_error( "G_MKDATA error: missing in XINDEX " ,1);
          stop;
      endif;
      if maxc(xindex) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ " in XINDEX ",1);
          stop;
      endif;
      if xindex[1]==0 ;
         if rows(xindex)==1 ;
            x=ones(Rows(dta),1) ;
         else;
            x=ones(Rows(dta),1)~dta[.,xindex[2:rows(xindex)]];
          endif;
      else ;
         x=dta[.,xindex];
      endif;
      gots=gots|"X" ;
      if removes>0 ; okindx=g_okindx(x,okindx,0,no_nan) ; endif;
  endif;

endif;

@ X1? @

if not(scalmiss(indcv("X1",which2)))  ;
  clearg x1; 
  if missrv(nX1,0) gt 0 ;
     if ismiss(X1index) ;
          call grbl2_error("G_MKDATA error: missing in X1INDEX ",1);
          stop;
      endif;
      if maxc(X1index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ " in X1INDEX ",1);
          stop;
      endif;
      if x1index[1]==0 ;
         x1=ones(Rows(dta),1)~dta[.,x1index[2:rows(x1index)]];
      else ;
         x1=dta[.,x1index];
      endif;
      gots=gots|"X1" ;
      if removes>0 ; okindx=g_okindx(x1,okindx,0,no_nan) ; endif;
  endif;
endif;


@ X2? @

if not(scalmiss(indcv("X2",which2)))  ;
  clearg x2; 
  if missrv(nX2,0) gt 0 ;
     if ismiss(X2index) ;
          call grbl2_error("G_MKDATA error: missing in X2INDEX " ,1);
          stop;
      endif;
      if maxc(X2index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in X2INDEX ",1);
          stop;
      endif;
      if x2index[1]==0 ;
         x2=ones(Rows(dta),1)~dta[.,x2index[2:rows(x2index)]];
      else ;
         x2=dta[.,x2index];
      endif;
      gots=gots|"X2" ;
      if removes>0 ; okindx=g_okindx(x2,okindx,0,no_nan) ; endif;
  endif;

endif;

@ X3? @
if not(scalmiss(indcv("X3",which2)))  ;
  clearg x3; 
  if missrv(nX3,0) gt 0 ;       
     if ismiss(X3index) ;
          call grbl2_error( "G_MKDATA error: missing in X3INDEX ",1);
          stop;
      endif;
      if maxc(X3index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+  g_ftos2(ncols,1) $+") in X3INDEX ",1);
          stop;
      endif;
      if x3index[1]==0 ;
         x3=ones(Rows(dta),1)~dta[.,x3index[2:rows(x3index)]];
      else ;
         x3=dta[.,x3index];
      endif;
      gots=gots|"X3" ;
      if removes>0 ; okindx=g_okindx(x3,okindx,0,no_nan) ; endif;
  endif;

endif;


@ AUX? @

if not(scalmiss(indcv("AUX",which2)))  ;
  clearg aux; 
  if missrv(naux,0) gt 0 ;      
     if ismiss(aindex) ;
          call grbl2_error("G_MKDATA error: missing in AINDEX ",1);
          stop;
      endif;
      if maxc(aindex) gt ncols ;
          call grbl2_error("G_MKDATAr: index gt cols (" $+ g_ftos2(ncols) $+ ") in AINDEX ",1);
          stop;
      endif;
      aux=dta[.,aindex];
        gots=gots|"AUX" ;
      if removes>0 ; okindx=g_okindx(aux,okindx,0,no_nan) ; endif;
  endif;
endif;


@ AUX1? @

if not(scalmiss(indcv("AUX1",which2)))  ;
  clearg aux1; 
  if missrv(naux1,0) gt 0 ;     
     if ismiss(a1index) ;
          call grbl2_error("G_MKDATA error: missing in A1INDEX " ,1);
          stop;
      endif;
      if maxc(a1index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+") in A1INDEX ",1); 
          stop;
      endif;
      aux1=dta[.,a1index];
        gots=gots|"AUX1" ;
      if removes>0 ; okindx=g_okindx(aux1,okindx,0,no_nan) ; endif;
  endif;
endif;


@ AUX2? @

if not(scalmiss(indcv("AUX2",which2)))  ;
  clearg aux2; 
  if missrv(naux2,0) gt 0 ;     
     if ismiss(a2index) ;
          call grbl2_error("G_MKDATA error: missing in A2INDEX " ,1);
          stop;
      endif;
      if maxc(a2index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in A2INDEX ",1);
          stop;
      endif;
      aux2=dta[.,a2index];
        gots=gots|"AUX2" ;
      if removes>0 ; okindx=g_okindx(aux2,okindx,0,no_nan) ; endif;
  endif;
endif;



@ Z? @

if not(scalmiss(indcv("Z",which2)))  ;
  clearg z; 
  if missrv(nZ,0) gt 0 ;        
     if ismiss(Zindex) ;
          call grbl2_error("G_MKDATA error: missing in ZINDEX ",1);
          stop;
      endif;
      if maxc(zindex) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in ZINDEX ",1);
          stop;
      endif;
      z=dta[.,zindex];
        gots=gots|"Z" ;
      if removes>0 ; okindx=g_okindx(z,okindx,0,no_nan) ; endif;
  endif;
endif;


@ z1? @

if not(scalmiss(indcv("Z1",which2)))  ;
  clearg z1; 
  if missrv(nz1,0) gt 0 ;       
     if ismiss(z1index) ;
          call grbl2_error("G_MKDATA error: missing in z1INDEX ",1);
          stop;
      endif;
      if maxc(z1index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Z1INDEX ",1);
          stop;
      endif;
      z1=dta[.,z1index];
        gots=gots|"Z1" ;
      if removes>0 ; okindx=g_okindx(z1,okindx,0,no_nan) ; endif;
  endif;
endif;



@ z2? @

if not(scalmiss(indcv("Z2",which2)))  ;
  clearg z2; 
  if missrv(nz2,0) gt 0 ;       
     if ismiss(z2index) ;
          call grbl2_error("G_MKDATA error: missing in z2INDEX " ,1);
          stop;
      endif;
      if maxc(z2index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Z2INDEX ",1);
          stop;
      endif;
      z2=dta[.,z2index];
        gots=gots|"Z2" ;
      if removes>0 ; okindx=g_okindx(z2,okindx,0,no_nan) ; endif;
  endif;
endif;

@ z3? @
if not(scalmiss(indcv("Z3",which2)))  ;
  clearg z3; 
  if missrv(nz3,0) gt 0 ;       
     if ismiss(z3index) ;
          call grbl2_error("G_MKDATA error: missing in Z3INDEX " ,1)';
          stop;
      endif;
      if maxc(z3index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Z3INDEX ",1);
          stop;
      endif;
      gots=gots|"Z3" ;
      z3=dta[.,z3index];
      if removes>0 ; okindx=g_okindx(z3,okindx,0,no_nan) ; endif;
  endif;
endif;

@ Y? @

if not(scalmiss(indcv("Y",which2)))  ;
   clearg y; 
    if missrv(yindex,0)>0 ;
      if maxc(yindex) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in YINDEX ",1);
          stop;
      endif;
      y=dta[.,yindex];
      gots=gots|"Y" ;
      if removes>0 ; okindx=g_okindx(y,okindx,0,no_nan) ; endif;
  endif;

endif;

@ Y1? @

if not(scalmiss(indcv("Y1",which2)))  ;
   clearg y1; 
    if missrv(y1index,0)>0 ;
      if maxc(y1index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Y1INDEX ",1);
          stop;
      endif;
      y1=dta[.,y1index];
      gots=gots|"Y1" ;
      if removes>0 ; okindx=g_okindx(y1,okindx,0,no_nan) ; endif;
   endif;
endif;



@ Y2? @

if not(scalmiss(indcv("Y2",which2)))  ;
   clearg y2; 
    if missrv(y2index,0)>0 ;
      if maxc(y2index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Y2INDEX ",1);
          stop;
      endif;
      y2=dta[.,y2index];
      gots=gots|"Y2" ;
      if removes>0 ; okindx=g_okindx(y2,okindx,0,no_nan) ; endif;
   endif;
endif;

@ Y3? @

if not(scalmiss(indcv("Y3",which2)))  ;
   clearg y3; 
    if missrv(y3index,0)>0 ;
      if maxc(y3index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in Y3INDEX ",1);
          stop;
      endif;
      y3=dta[.,y3index];
      gots=gots|"Y3" ;
      if removes>0 ; okindx=g_okindx(y3,okindx,0,no_nan) ; endif;
  endif;
endif;


@ ID? @

if not(scalmiss(indcv("ID",which2)))  ;
   clearg id; 
   if idname$=="#" ;            @ balanced ... @
       i_Id=0;
       if idindex==-1 ; idindex=rows(dta) ; endif; ddo=idindex;
       for i (1,rows(dta),ddo) ;
         i_id=i_id+1 ;
         if i_id==1 ;
            id=ones(ddo,1);
          else ;
            id=id|(ones(ddo,1)*i_id) ;
          endif;
       endfor;
       if rows(id) ne rows(dta); format /ld 5,0;
          call grbl2_error("G_MKDATA error: balanced ID (" $+ g_Ftos2(idindex,1) $+ " does not evenly divide # of rows (" $+ g_ftos2(rows(dta),1),1) ;
          stop;
       endif;
       gots=gots|"ID" ;  
   else ;
      if missrv(idindex,0)>0 ;
        if maxc(idindex) gt ncols ;
           call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in IDINDEX ",1);
            stop;
        endif;
        id=dta[.,idindex];
        gots=gots|"ID" ;
        if removes>0 ; okindx=g_okindx(id,okindx,0,no_nan) ; endif;
      endif;
   endif;
endif;



@ ID1? @

if not(scalmiss(indcv("ID1",which2)))  ;
   clearg id1; 
   if id1name$=="#" or id1name$=="*" ;          @ balanced ... @
       i_Id=0;
       if id1index==-1 ; id1index=rows(dta) ; endif; ddo=id1index;
       for i (1,rows(dta),ddo) ;
         i_id=i_id+1 ;
         if i_id==1 ;
            id1=ones(ddo,1);
          else ;
            id1=id1|(ones(ddo,1)*i_id) ;
          endif;
       endfor;
       if rows(id1) ne rows(dta);
          call grbl2_error("G_MKDATA error: balanced ID1 (" $+ g_Ftos2(id1index,1) $+ " does not evenly divide # of rows (" $+ g_ftos2(rows(dta),1),1) ;
          stop;
       endif;
       gots=gots|"ID1" ;  
   else ;
      if missrv(id1index,0)>0 ;
        if maxc(id1index) gt ncols ;
            call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in ID1INDEX ",1);
            stop;
        endif;
        id1=dta[.,id1index];
        gots=gots|"ID1" ;
        if removes>0 ; okindx=g_okindx(id1,okindx,0,no_nan) ; endif;
      endif;
   endif;
endif;



@ id2? @

if not(scalmiss(indcv("ID2",which2)))  ;
   clearg id2; 
   if ID2name$=="#" or  id2name$=="*" ;         @ balanced ... @
       i_Id=0;
       if id2index==-1 ; id2index=rows(dta) ; endif; ddo=id2index;
       for i (1,rows(dta),ddo) ;
         i_id=i_id+1 ;
         if i_id==1 ;
            ID2=ones(ddo,1);
          else ;
            ID2=id2|(ones(ddo,1)*i_id) ;
          endif;
       endfor;
       if rows(ID2) ne rows(dta);
          call grbl2_error("G_MKDATA error: balanced ID2 (" $+ g_Ftos2(id2index,1) $+ " does not evenly divide # of rows (" $+ g_ftos2(rows(dta),1),1) ;
          stop;
       endif;
       gots=gots|"ID2" ;  
   else ;
      if missrv(ID2index,0)>0 ;
        if maxc(ID2index) gt ncols ;
            call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in ID2INDEX ",1);
            stop;
        endif;
        ID2=dta[.,ID2index];
        gots=gots|"ID2" ;
        if removes>0 ; okindx=g_okindx(id2,okindx,0,no_nan) ; endif;
      endif;
   endif;
endif;

@ id3? @

if not(scalmiss(indcv("ID3",which2)))  ;
   clearg id3; 
   if ID3name$=="#" or  id3name$=="*" ;         @ balanced ... @
       i_Id=0;
       if id3index==-1 ; id3index=rows(dta) ; endif; ddo=id3index;
       for i (1,rows(dta),ddo) ;
         i_id=i_id+1 ;
         if i_id==1 ;
            ID3=ones(ddo,1);
          else ;
            ID3=id3|(ones(ddo,1)*i_id) ;
          endif;
       endfor;
       if rows(ID3) ne rows(dta);
          call grbl2_error("G_MKDATA error: balanced ID3 (" $+ g_Ftos2(id3index,1) $+ " does not evenly divide # of rows (" $+ g_ftos2(rows(dta),1),1) ;
          stop;
       endif;
       gots=gots|"ID3" ;  
   else ;
      if missrv(Id3index,0)>0 ;
        if maxc(ID3index) gt ncols ;
            call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in ID3INDEX ",1);
            stop;
        endif;
        ID3=dta[.,ID3index];
        gots=gots|"ID3" ;
        if removes>0 ; okindx=g_okindx(id3,okindx,0,no_nan) ; endif;
      endif;
   endif;
endif;




@ WEIGHT? @

if not(scalmiss(indcv("WEIGHT",which2)))  ;
   clearg weight ; 
   if missrv(wtindex,0)>0 ;
      if maxc(wtindex) gt ncols ;
            call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in WTINDEX ",1);
            stop;
      endif;
      weight=dta[.,wtindex];
      gots=gots|"WEIGHT" ;
      if removes>0 ; okindx=g_okindx(weight,okindx,0,no_nan) ; endif;
  endif;

  if WTNORM==1;         @ normalize to sum to # rows? @
        weight=rows(weight)*(weight/(sumc(packr(weight))));
   endif;

endif;


if not(scalmiss(indcv("WEIGHT1",which2)))  ;
   clearg weight1 ; 
   if missrv(wt1index,0)>0 ;
      if maxc(wt1index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in WT1INDEX ",1);
          stop;
      endif;
      weight1=dta[.,wt1index];
      gots=gots|"WEIGHT1" ;
      if removes>0 ; okindx=g_okindx(weight1,okindx,0,no_nan) ; endif;
    endif;
    if WT1NORM==1;              @ normalize to sum to # rows? @
          weight1=rows(weight1)*(weight1/(sumc(packr(weight1))));
    endif;
 
endif;


@ WEIGHT2? @

if not(scalmiss(indcv("WEIGHT2",which2)))  ;
   clearg weight2 ; 
   if missrv(wt2index,0)>0 ;
      if maxc(wt2index) gt ncols ;
          call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in WT2INDEX ",1);
          stop;
      endif;
      weight2=dta[.,wt2index];
      gots=gots|"WEIGHT2" ;
      if removes>0 ; okindx=g_okindx(weight2,okindx,0,no_nan) ; endif;
    endif;
    if WT2NORM==1;              @ normalize to sum to # rows? @
          weight2=rows(weight2)*(weight2/(sumc(packr(weight2))));
    endif;
 
endif;


format /ld 4,0 ;
@ xnew @ 
if not(scalmiss(indcv("DUMMY",which2))) and missrv(nxnew,0)>0  ;
  clearg xnew ;
  for jj (1,missrv(xnewinfo.neq,0),1);
     xn1=zeros(rows(dta),1) ;
     ntrms=xnewinfo.nterms[jj] ;

     for jj2 (1,ntrms,1) ;
        atype=xnewinfo.vtypes[jj,jj2];
        tid=xnewinfo.vvals[jj,jj2];

        if atype$=="NUM" ;
           xn2=tid;
        elseif atype$=="FUNC" ;
          funcname=g_strip(Upper(tid)) ;
        elseif atype$=="NEWVAR";
           if tid ge jj ;
                call grbl2_error("G_MKDATA error. New var refers to later new variable ",1);
                stop;
           endif;
           xn2=xnew[.,tid];
        else;
           if tid>ncols;
             call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in XNEW ",1);
             stop ;
           endif;
           xn2=dta[.,tid];                  
        endif;
        aoper=xnewinfo.opers[jj,jj2];
        if aoper$=="+" or jj2==1 ;
            xn1=xn1+xn2 ;
        elseif aoper$=="-";
           xn1=xn1-xn2 ;
        elseif aoper$=="*" ;
           xn1=xn1.*xn2 ;
        elseif aoper$=="/" ;
           if sumc(xn2.==0)>0  ;  
               call grbl2_error("G_MKDATA ERROR: divide by zero when creating new variable ",1);
                stop;
           endif;
           xn1=xn1./xn2 ;
        elseif aoper$=="^";
           xn1=xn1.^xn2 ;
        elseif aoper$==">";
           xn1=xn1.> xn2 ;

        elseif aoper$=="<";
           xn1=xn1 .< xn2 ;

        elseif aoper$=="=";
           xn1=xn1.== xn2 ;

        elseif aoper$=="%" ;
           xn1= ((xn1./xn2)-(trunc(xn1./xn2))).*xn2; 
        elseif aoper$=="\\" ;
           xn1=trunc(xn1./xn2) ;

        elseif aoper$=="(" ;            @ function @
           if tid$=="LN" ;
             if minc(xn1) le 0  ; format /ld 1,8 ; 
                call grbl2_error("G_MKDATA ERROR: ln of negative or 0 number when creating new variable " $+ xnewinfo.names[jj],1);
                 stop;
              endif;
              xn1=ln(xn1);
            elseif tid$=="EXP" ;
              xn1=exp(xn1) ;
            elseif tid$=="SIN" ;
              xn1=sin(xn1);
            elseif tid$=="COS" ;
              xn1=cos(xn1);
            elseif tid$=="TAN" ;
              xn1=tan(xn1);
            elseif strindx(tid,"LN_",1)==1 ;
                tmp=strindx(tid,4,100); tmp=stof(tmp);
                tmp=10^(-tmp) ;
                xn1=dolog(xn1,tmp);
            else ;
               call grbl2_error("G_MKDATA error. Unknown function " $+tid,1);
               stop;
            endif;

        else ;
            call grbl2_error("G_MKDATA error. Unknown operation " $+aoper,1);
            stop;
        endif;

    endfor;

    if jj==1 ;
        xnew=xn1;
    else;
        xnew=xnew~xn1;
    endif;
  endfor;

  if missrv(xnewinfo.neq,0)>0;
     if removes>0 ; okindx=g_okindx(xnew,okindx,0,no_nan) ; endif;
     gots=gots|"XNEW" ;
  endif;

endif;


@ dummies @   
if not(scalmiss(indcv("DUMMY",which2)))  ;
  
  if missrv(nxdummy,0) > 0 ;
    clearg xdummy ;
    for jj (1,rows(dinfo),1);
      oindex=dinfo[jj,1] ; cindex=dinfo[jj,2] ; nl=dinfo[jj,4];

      lvls=dinfo[jj,5:4+nl];
      if missrv(oindex,0)==0 ;
         call grbl2_error("G_MKDATA error. Missing ordinal variable in DUMMY",1);
         stop;
       endif;
       if oindex>ncols;
          print "G_MKDATA error: index gt cols (";;format /ld 5,0 ; ncols;;") in DUMMY ordinal" oindex;
       endif;
       t1=dta[.,oindex];

       adummy= t1.==lvls;

       if missrv(cindex,0) ne 0 ;  @ else dummy okay as is @
          if cindex>ncols;
             if cindex>(ncols+nxnew) ; 
                call grbl2_error("G_MKDATA error: index gt cols (" $+ g_ftos2(ncols,1) $+ ") in DUMMY cardinal",1);
                stop ;
             else ;
                adummy=adummy.*(xnew[.,cindex-ncols]) ;
             endif;
         else ;
             adummy=adummy.*dta[.,cindex] ;
          endif;

       endif;   
       if jj==1 ;
           xdummy=adummy;
        else;
           xdummy=xdummy~adummy;
        endif;
     endfor ;
     gots=gots|"DUMMY" ;
     if removes>0 ; okindx=g_okindx(xdummy,okindx,0,no_nan) ; endif;
   endif;
endif;       




if removes>0; 
   if scalmiss(okindx);
       x=miss(1,1) ;x1=x ;x2=x; x3=x; 
       y=x; y1=x; y2=x; y3=x;
       z=x;z1=x; z2=x;z3=x; 
       id=x ; id1=x; id2=x ; id3=x ; 
       weight=x ; weight2=x ; aux=x ; aux2=x; aux1=x;
       xdummy=x ; xnew=x ;
   else;
      if not(scalmiss(indcv("X",gots))) ;  x= x[okindx,.]; endif;
      if not(scalmiss(indcv("X1",gots))) ;      x1=x1[okindx,.]; endif;
      if not(scalmiss(indcv("X2",gots))) ;      x2=x2[okindx,.]; endif;
      if not(scalmiss(indcv("X3",gots))) ;      x3=x3[okindx,.]; endif;
      if not(scalmiss(indcv("Y",gots))) ;           y= y[okindx,.]; endif;
      if not(scalmiss(indcv("Y1",gots))) ;          y1=y1[okindx,.]; endif;
      if not(scalmiss(indcv("Y2",gots))) ;          y2=y2[okindx,.]; endif;
      if not(scalmiss(indcv("Y3",gots))) ;          y3=y3[okindx,.]; endif;
      if not(scalmiss(indcv("Z",gots))) ;           z= z[okindx,.]; endif;
      if not(scalmiss(indcv("Z1",gots))) ;          z1=z1[okindx,.]; endif;
      if not(scalmiss(indcv("Z2",gots))) ;          z2=z2[okindx,.]; endif;
      if not(scalmiss(indcv("Z3",gots))) ;          z3=z3[okindx,.]; endif;
      if not(scalmiss(indcv("ID",gots))) ;        id = id[okindx,.]; endif;
      if not(scalmiss(indcv("ID1",gots))) ;        id1=id1[okindx,.]; endif;
      if not(scalmiss(indcv("ID2",gots))) ;        id2=id2[okindx,.]; endif;
      if not(scalmiss(indcv("ID3",gots))) ;        id3=id3[okindx,.]; endif;
      if not(scalmiss(indcv("WEIGHT",gots))) ;        weight=weight[okindx,.];  endif;
      if not(scalmiss(indcv("WEIGHT2",gots))) ;        weight2=weight2[okindx,.]; endif;

      if not(scalmiss(indcv("AUX",gots))) ;        aux=aux[okindx,.]; endif;
      if not(scalmiss(indcv("AUX1",gots))) ;       aux1=aux1[okindx,.]; endif;
      if not(scalmiss(indcv("AUX2",gots))) ;       aux2=aux2[okindx,.]; endif;


      if not(scalmiss(indcv("XNEW",gots))) ;        xnew=xnew[okindx,.]; endif;
      if not(scalmiss(indcv("DUMMY",gots))) ;        xdummy=xdummy[okindx,.]; endif;
    endif;
endif ;

retp(okindx);
endp;



/***************************************/
/* make an XUSE global from X, XNEW, and DUMMY info (as set by G_BATCH processing of a grbl2 command file
Uses a number of globals set by g_batch.

   retp(vector_of_normalizers, variable_name_vector) 

The "Normalizers" are the denominators used when "normalizing" the alues (only done if donorm=1)

*/

proc (2)=g_mkxuse(donorm) ;
local dname,parnames,nxuse,norms,goo,i;
clearg xuse ;
parnames=miss(1,1);

if nx>0 ;  xuse=x ; nxuse=cols(xuse); parnames=xname; endif;
if nxdummy>0 ;
   dname=g_vudum(dinfo,filename,1,0,xnewinfo.names);
   if nxuse>0 ; 
       xuse=xuse~xdummy ; 
       parnames=parnames|dname ;
    else ; 
      xuse=xdummy ; 
      parnames=dname;
   endif;
   nxuse=cols(xuse);
endif;


if nxnew>0 ;
   if nxuse>0 ; 
        xuse=xuse~xnew ; 
        parnames=parnames|xnewinfo.names[1:nxnew] ;
   else ;
         xuse=xnew ; 
         parnames=xnewinfo.names[1:nxnew];
   endif;
   nxuse=cols(xuse);
endif;


norms=ones(cols(xuse),1);
if donorm==1 ;
  for i (1,cols(xuse),1) ;
     goo=packr(miss(xuse[.,i],0));
     if scalmiss(goo); continue ; endif;
     if goo[1]==goo ; continue; endif;   @ this is 0/V dummy? @
     norms[i]=stdc(xuse[.,i]) ;
  endfor; 
  xuse=xuse./(norms') ;
endif;

retp(norms,parnames);
endp;


/*************************************/
/* Convert simple equation to "polish notation" -- basically, change fun( ... ) to  ... ( fun  */
proc g_mkpolish(aeq);

local opers,astr,m,achar,neweq,word1,word2,dafunc,iword;
opers="+"|"-"|"*"|"/"|"\\"|"%"|"^"|">"|"<"|"=" ;   @ \ IS division with truncation 8/3=2.6666, 8\3=2, 11%3=2 @
  
astr="" ;

if strindx(aeq," (",1)>0 ;
   call grbl2_error("G_MKDATA ERROR: there can be no spaces between a function name and a ( ",1) ;
   stop;
endif;
for m (1,strlen(aeq),1);
  achar=strsect(aeq,m,1);
  if not(scalmiss(indcv(achar,opers|")")));
     astr=astr $+ " " $+ achar $+ " " ;
  elseif achar$=="(" ;
     astr=astr$+ "( " ;
  else ; 
     astr=astr$+ achar;
  endif;
endfor;
aeq=astr;

neweq="" ;
iword=0;
do until aeq$=="" ;
{word1,aeq}=token(aeq); word1=g_strip(word1); iword=iword+1;
if strsect(word1,strlen(word1),1)$=="(" ;       @ this is a function @
  if iword ne 3;
     call grbl2_error("G_MKDATA Error: functions can only be used at start of equation ("  $+ astr,1);
     stop;
  endif;
  dafunc=strsect(word1,1,strlen(word1)-1);
  do while 1==1 ;
     {word2,aeq}=token(aeq); word2=g_strip(word2);
     if word2$==")" or aeq $== "" ;
        neweq=neweq $+ " ( " $+ dafunc $+ " ";
        break ;
     endif;
     neweq=neweq $+ " " $+ word2 ;
  endo;
else ;
  neweq=neweq $+ " " $+ word1 ;
endif;
endo;
retp(neweq);

endp;


/*****************************************/
/**********************************/
@ procedure to ask user for batch commands @
proc g_askbatch(helpfile) ;

local foo,astring,cnststrg,helpfile1;

cnststrg="";

oy: ;

if helpfile$=="" ; helpfile="GRBL2_BATCH.TXT"; endif;

?;
" Enter batch commands.
  Each command should end with a ;
  Commands can span multiple lines, or several commands can be on a line.
  Blank line signals you are done.
  For help, enter a ? on a line.
 ";

 cnststrg=" ";
 do until 2==1 ;             @ exit via retp @
    call cyanon ;call boldon ;" ? (ENTER when done)> ";;call normalon ;
    foo=upper(cons) ; ?;
   if foo$=="";      @ blank line@
       retp(cnststrg);

   elseif type(foo)==6 ;           @ ignore arrow keys, etc @
        continue ;

  elseif foo$=="?";
      helpfile1=g_exists(helpfile,"");
      if helpfile1$=="";
        " Sorry, documentation file (";;$helpfile;;") not available ";
        if helpfile$/="GRBL2_BATCH.TXT";
           helpfile="GRBL2_BATCH.TXT";
           helpfile1=g_exists(helpfile,"");
           if helpfile1$=="" ;
           "   Sorry, generic documentation file (";;$helpfile;;") was also not found ";
               continue;
           else;
           "    Displaying the generic GRBL2 batch-commands help file ";
           endif;
        endif;
      else; 
         continue ;     
      endif;

      astring=getf(helpfile,0);
      cls ;
      locate 1,1 ; call boldon ;
               " Documentation " ;
           call normalon ;
      call g_dsptxt(2,20,astring,&boldon,&yellowon) ;
      ?;
  else ;
        cnststrg=cnststrg $+ " " $+ foo ;
  endif;

 endo ;
endp ;



