// Created 11/26/14 12:30:54 by "/usr/site/scripts/loadfiles nqs.hoc"
//================================================================
// INSERTED nqs.hoc
// =Id=  nqs.hoc,v 1.689 2012/10/04 01:41:57 billl Exp 

// primarily edited in nrniv/place
if (!name_declared("VECST_INSTALLED")) {
    printf("NQS ERROR: Need vecst.mod nmodl package compiled in special.\n")
    quit()
  }
if (!VECST_INSTALLED) install_vecst()
if (! name_declared("datestr")) load_file("setup.hoc")
//================================================================
// INSERTED /usr/site/nrniv/local/hoc/decvec.hoc
// =Id=  decvec.hoc,v 1.426 2011/12/21 17:12:04 billl Exp 

proc decvec() {}

//* Declarations
objref ind, tvec, vec, vec0, vec1, tmpvec, vrtmp, veclist, veccollect, pwm
objref tmpobj, XO, YO, rdm, dir
strdef filename,dblform,tabform,xtmp
{dblform="%.4g" tabform=" "}

dir = new List()
tmpfile = new File()
if (! name_declared("datestr")) load_file("setup.hoc")
//================================================================
// INSERTED /usr/site/nrniv/local/hoc/declist.hoc
// =Id=  declist.hoc,v 1.157 2010/12/07 14:19:28 billl Exp 

print "Loading declist.hoc..."

//* Declarations
strdef mchnms
objref tmplist,tmplist2,tmpobj,aa,XO,YO
if (! name_declared("datestr")) {load_file("setup.hoc")}
tmplist = new List()
proc declist() {}

//* Templates
//** template String2
begintemplate String2
public s,t,x,append,prepend,exec,tinit
strdef s,t
proc init() {
    if (numarg() == 1) { s=$s1 }
    if (numarg() == 2) { s=$s1 t=$s2 }
    if (numarg() == 3) { s=$s1 t=$s2 x=$3 }
  }
proc append () { local i
    if (argtype(1)==2) sprint(t,"%s%s",s,$s1)
    for i=2,numarg() sprint(t,"%s%s",t,$si)
  }
proc prepend () { local i
    if (argtype(1)==2) sprint(t,"%s%s",$s1,s)
    for i=2,numarg() sprint(t,"%s%s",$si,t)
  }
proc exec () {
    if (numarg()==1) sprint(t,"%s%s",$s1,s)
    execute(t)
  }
proc tinit() { t=$s1 }

endtemplate String2

//** template DBL
begintemplate DBL
  public x
  proc init () { if (numarg()==1) x=$1 else x=0 }
endtemplate DBL

//** template Union
// USAGE: XO=new Union(val) where val can be a number string or object
//        XO=new Union(list,index) will pick up a string or object from list
//        XO=new Union(vec,index)  will pick up a number from vector
//        XO=new Union(&dbl,index) will pick up a number from double array
// Union allows storage of a double, string or object
// 2 ways of storing strings: directly in XO.s (t,u,v) OR 
//   using XO.set("NAME","STR") which is returned by XO.get("NAME").s
// It is useful as a localobj in an obfunc since it will permit returning anything
// It also makes it easy to pick up any kind of value out of a list, vector or array
// declared() not yet declared
if (!name_declared("isobj")) execute1("func isobj(){return 0}") // place holder
if (!name_declared("isassigned")) execute1("func isassigned(){return 0}") // place holder
if (!name_declared("llist")) execute1("proc llist(){}") // place holder
begintemplate Union
public ty,x,s,t,u,v,o,xptr,xg,xs,optr,og,os,get,set,xl,ol,fi,inc,list,err,pr,sz
strdef s,t,u,v,tstr
objref o[10],xl,ol,this,xo
double x[10],sz[1],err[1]
external sfunc,isobj,isassigned,nil,DBL,llist
// 1arg -- store a num, string or obj
// 2arg -- pick up type out of list
proc init () { local ii,i,ox,xx,sx,flag
    ox=xx=sx=0 ty=argtype(1) // 'type' of Union defined by 1st arg on creation
    sz=10 // 10 is default size of arrays; for ii=0,sz-1 
    for ii=0,sz-1 x[ii]=ERR
    // special case of number in a vector or something in a list -- 2 args: list or vec,index
    err=1 flag=0
    if (numarg()/2==int(numarg()/2)) for (i=1;i<numarg();i+=2) {
        if (argtype(i)!=2 || argtype(i+1)==2) break
        flag=1
      }
    if (flag) { // "name",val,etc
        for (i=1;i<numarg();i+=1) {
            tstr=$si i+=1 
            if (argtype(i)==0) set(tstr,$i) else set(tstr,$oi)
          }
        s="" // used as temp storage only
      } else if (argtype(1)==1 && argtype(2)==0 && argtype(3)==-1) {
        ii=$2
        // printf("Union WARNING: taking single element out of a Vector or List\n")
        if (argtype(1)==1) {
            if (isobj($o1,"Vector")) { 
                if (ii<0 || ii>=$o1.size) {
                    printf("Union ERR: vec index (%d) out of bounds for %s\n",ii,$o1)
                    return }
                ty=0 x=$o1.x[ii] o=$o1
              } else if (isobj($o1,"List")) {
                if (ii<0 || ii>=$o1.count) {
                    printf("Union ERR: list index (%d) out of bounds for %s\n",ii,$o1)
                    return }
                if (isobj($o1.object(ii),"String")) { // will handle String2
                    ty=2 s=$o1.object(ii).s o=$o1.object(ii) x=ii
                  } else { ty=1 o=$o1.object(ii) x=ii }
              }
          } else if (argtype(1)==3) {
            ty=0 x=$&1[ii] // could check - but no + bound checking possible
          }
      } else for i=1,numarg() {
        ii=argtype(i)
        if (ii==0) { 
            if (xx<sz) { 
                x[xx]=$i xx+=1 
              } else printf("Only have %d doubles available so can't store %g\n",sz,$i)
          } else if (ii==2) { 
            if (sx==0) s=$si else if (sx==1) t=$si else if (sx==2) u=$si else if (sx==3) v=$si else {
                printf("Only have 4 strings available so can't store %s\n",$si) }
            sx+=1
          } else if (ii==1) { 
            if (ox<sz) { o[ox]=$oi ox+=1
              } else printf("Only have %d objects available so can't store %s\n",sz,$oi)
          } else printf("Union ERR: argtype not recognized numarg %d %d\n",i,ii)
      }
    err=0
  }

proc pr () { print "Use Union[].list" }

proc list () { local flag
    flag=0
    if (ol!=nil) {
        for ii=0,sz-1 if ((! isojt(ol,ol.o(ii)))||o[ii]!=nil) {
            if (isojt(ol,ol.o(ii))) printf("%10s\t%s\n","NO NAME", o[ii]) else {
                                      printf("%10s\t%s\n",ol.o(ii).s,o[ii]) }
          }
        printf("%s\n","================================") 
        flag=1
      }
    if (xl!=nil) {
        for ii=0,sz-1 if ((! isojt(xl,xl.o(ii)))||x[ii]!=ERR) {
            if (isojt(xl,xl.o(ii))) printf("%10s\t%g\n","NO NAME", x[ii]) else {
                                      printf("%10s\t%g\n",xl.o(ii).s,x[ii]) }
          }
        printf("%s\n","================================") 
        flag=1
      }
    if (!flag) {
        printf("%s\nSCALARS: ","================================") 
        for ii=0,sz-1 if (x[ii]!=ERR) printf("%d:%g ",ii,x[ii])
        printf("\n%s\nOBJECTS: ","================================") 
        for ii=0,sz-1 if (o[ii]!=nil) printf("%d:%s ",ii,o[ii])
        printf("\n%s\n","================================") 
      }
    if (sfunc.len(s)>0) printf("%10s\t%s\n","s",s)
    if (sfunc.len(t)>0) printf("%10s\t%s\n","t",t)
    if (sfunc.len(u)>0) printf("%10s\t%s\n","u",u)
    if (sfunc.len(v)>0) printf("%10s\t%s\n","v",v)
  }

// full List
obfunc newl () { localobj o
    o=new List() 
    for ii=0,sz-1 o.append(o)
    return o
  } 
proc repl () { 
    if (isobj($o1.o($2),"String")) { $o1.o($2).s=$s3
      } else { $o1.remove($2) $o1.insrt($2,new String($s3)) }
  }

func fi () { local ii
    fif=-1
    for ii=0,sz-1 {
        if ($o1.o(ii)==$o1) {
            if (fif==-1) fif=ii // first empty slot if any
          } else if (strcmp($o1.o(ii).s,$s2)==0) return ii
      }
    return ERR
  }
// put in pointers for the x values
func xptr () { local i,ii
    if (numarg()>sz) {printf("More names (%d) than x items (%d) in %s:xptr\n",numarg(),sz,this) 
        error()}
    if (!isassigned(xl)) xl=newl()
    for i=1,numarg() repl(xl,i-1,$si)
    return xl.count
  }
func optr () { local i,ii
    if (numarg()>sz) {printf("More names %d than o items (%d) in %s:optr\n",numarg(),sz,this) error()}
    if (!isassigned(ol)) ol=newl()
    for i=1,numarg()  repl(ol,i-1,$si)
    return ol.count
  }
// xg() associative get of a num
func xg () { local ii
    ii=fi(xl,$s1)
    if (ii==ERR) {printf("Union:og (num get) failed -- \"%s\" not found\n",$s1) return ERR}
    return x[ii] 
  }
// xg() associative get of an obj
obfunc og () { local ii
    ii=fi(ol,$s1)
    if (ii==ERR) {printf("Union:og (obj get) failed -- \"%s\" not found\n",$s1) return nil}
    return o[ii] 
  }
// xs(index,"label",num) set a specific location using a string -- see set()
// xs("label",num) set an existing one only
func xs () { local ii,i
    if (!isassigned(xl)) xl=newl()
    if (argtype(1)==0) {
        i=3 ii=$1 // an index
        repl(xl,ii,$s2)
      } else {
        i=2 ii=fi(xl,$s1)
        if (ii==ERR) {printf("Union:xs (num set) failed -- no %s found\n",$s1) return ERR}
      }
    if (argtype(i)!=0) {printf("Union:xs err -- arg %d must be object\n",i) return ERR}
    x[ii]=$i
    return $i
  }
// os(index,"label",obj) set a specific location using a string -- see set()
// os("label",obj) set an existing one only
obfunc os () { local ii,i
    if (!isassigned(ol)) ol=newl()
    if (argtype(1)==0) {
        i=3 ii=$1 // $1 is an index
        repl(ol,ii,$s2)
      } else {
        i=2 ii=fi(ol,$s1)
        if (ii==ERR) {printf("Union:os (obj set) failed -- \"%s\" not found\n",$s1) return nil}
      }
    if (argtype(i)!=1) {printf("Union:os err -- arg %d must be object\n",i) return nil}
    o[ii]=$oi
    return $oi
  }

// get() get any type
obfunc get () { local ii
    if        ((ii=fi(ol,$s1))!=ERR) { 
        return o[ii] 
      } else if ((ii=fi(xl,$s1))!=ERR) { 
        if (argtype(2)==3) { $&2=x[ii] return this } else { return new DBL(x[ii]) }
      } else {
        // printf("Union:get() \"%s\" not found\n",$s1) 
        return nil // make returning nil the unique error
      }
  }

// increment or with neg decrement something
func inc () { local n
    if (numarg()==2) n=$2 else n=1
    if ((ii=fi(xl,$s1))==ERR) {printf("Union:inc ERR: %s %s\n",this,$s1) return ERR}
    return x[ii]+=n
  }

// set() set any type
func set () { local ii,i,ty,fo,fx
    if (xl==nil) xl=newl() if (ol==nil) ol=newl()
    for (i=1;i<numarg();i+=1) {
        tstr=$si i+=1 
        if ((ty=argtype(i))==0) xo=xl else if (ty==1 || ty==2) xo=ol // use String() for strings
        ii=fi(xo,tstr)
        if (ii==ERR) {
            if (fif==-1) {printf("UNION:set() ERRB: %s full for type %d\n",this,ty)  return ERR}
            repl(xo,(ii=fif),tstr)
          }
        if (ty==0) x[ii]=$i else if (ty==1) o[ii]=$oi else if (ty==2) { // don't use s,t,u,v
            o[ii]=new String($si)
          }
      }
    return ii
  }
endtemplate Union

//* Iterators
//** list iterator ltr
// usage 'for ltr(tmplist) { print XO }' :: 1 arg, assumes XO as dummy
// usage 'for ltr(YO, tmplist) { print YO }' 2 arg, specify dummy
// usage 'for ltr(XO, tmplist, &x) { print XO,x }' :: x is counter (else use global i1)
// usage 'for ltr(XO, tmplist, 5) { print XO,x }' :: max; only print out 0-5
// usage 'for ltr(XO, tmplist, 5, 7) { print XO,x }' :: print 5-7
// usage 'for ltr(XO, tmplist, &x, 5) { print XO,x }' :: counter with max
// usage 'for ltr(XO, tmplist, &x, 5, 7) { print XO,x }' :: counter,min,max
iterator ltr () { local i,min,max,ifl
    min=0 max=1e9 ifl=0 // iterator flag
    if (numarg()==3) {
        if (argtype(3)==3) {ifl=1 $&3=0}
        if (argtype(3)==0) max=$3
      } else if (numarg()==4) { 
        if (argtype(3)==0) i1=min=$3 else {ifl=1 $&3=0}
        max=$4 
      } else if (numarg()==5) { 
        $&3=min=$4 max=$5 ifl=1 
      }
    if (! ifl) i1=0
    if (numarg()==1) {
        for i = 0, $o1.count-1 {
            XO = $o1.object(i)
            iterator_statement
            i1+=1
          }
        tmpobj=$o1
        XO=nil
      } else {
        if (max==1e9) max=$o2.count()-1 else if (max<0) { min=$o2.count+max max=$o2.count-1 
          } else if (max>$o2.count-1) max=$o2.count-1
        for i = min, max {
            $o1 = $o2.object(i)
            iterator_statement
            if (ifl) { $&3+=1 } else { i1+=1 }
          }
        tmpobj=$o2
        $o1 = nil
      }
  }

//** list iterator ltrb -- backwards list iterator
// usage 'for ltrb(tmplist) { print XO }' :: 1 arg, assumes XO as dummy
// usage 'for ltrb(YO, tmplist) { print YO }' 2 arg, specify dummy
// usage 'for ltrb(XO, tmplist, &x) { print XO,x }' :: 3 args, define counter (else i1 default)
//                                                 :: note that x, i1 must be defined
iterator ltrb () { local i
    if (numarg()==1) {
        i1=$o1.count-1
        for (i=$o1.count()-1;i>=0;i-=1) {
            XO = $o1.object(i)
            iterator_statement
            i1-=1
          }
        tmpobj=$o1 XO=nil
      } else {
        if (numarg()==3) $&3=$o2.count-1 else i1=$o2.count-1
        for (i=$o2.count()-1;i>=0;i-=1) {
            $o1 = $o2.object(i)
            iterator_statement
            if (numarg()==3) { $&3-=1 } else { i1-=1 }
          }
        tmpobj=$o2
        $o1 = nil
      }
  }

//** list iterator ltr2
// usage 'for ltr2(XO, YO, list1, list2) { print XO,YO }'
iterator ltr2() { local i,cnt
    if (numarg()==5) $&5=0 else i1=0
    cnt=$o4.count
    if ($o3.count != $o4.count) { print "ltr2 WARNING: lists have different lengths" 
        if ($o3.count<$o4.count) cnt=$o3.count }
    for i = 0, cnt-1 {
        $o1 = $o3.object(i)
        $o2 = $o4.object(i)
        iterator_statement
        if (numarg()==5) { $&5+=1 } else { i1+=1 }
      }
    $o1=nil $o2=nil
  }

//** list pairwise iterator ltrp
// usage 'for ltrp(XO, YO, list) { print XO,YO }' takes them pairwise
iterator ltrp() { local i
    if (numarg()==4) {$&4=0} else {i1 = 0}
    for (i=0;i<$o3.count()-1;i+=2) {
        $o1 = $o3.object(i) $o2 = $o3.object(i+1)
        iterator_statement
        if (numarg()==4) { $&4+=1 } else { i1+=1 }
      }
    $o1=nil $o2=nil
  }

//** list iterator sltr
// usage 'for sltr(XO, string) { print XO }'
iterator sltr() { local i
    tmplist = new List($s2)
    if (numarg()==3) {$&3=0} else {i1=0}
    for i = 0, tmplist.count() - 1 {
        $o1 = tmplist.object(i)
        iterator_statement
        if (numarg()==3) { $&3+=1 } else { i1+=1 }
      }
    $o1 = nil
  }

//* Procedures
//** append(LIST,o1,o2,...) -- like revec() makes a list of objects
proc append () { local i,max
    if (!isassigned($o1)) $o1=new List()
    max=numarg()
    if (argtype(max)==0) {max-=1 $o1.remove_all}
    for i=2,max $o1.append($oi)
  }

//** rcshead([flag])
// with flag does head from archive
func rcshead () { local x,flag localobj st
    x=-1 flag=0
    st=new String2()
    if (numarg()==2) {
        st=dirname($s1)
        sprint(st.t,"%sRCS/%s,v",st.s,st.t)
        flag=1
      } else st.t=$s1
    sprint(st.s,"head -1 %s",st.t)
    system(st.s,st.s)
    if (flag) sscanf(st.s,"head\t1.%d",&x) else sscanf(st.s,"%*[^,],v 1.%d",&x)
    return x
  }

//** getenv()
obfunc getenv () { localobj st
    st=new String()
    sprint(st.s,"echo $%s",$s1)
    system(st.s,st.s)
    sfunc.left(st.s,sfunc.len(st.s)-1)
    if (numarg()==2) $s2=st.s
    return st
  }

//** rcsopen(file,vers[,flag])  -- version open, flag to do load_file
// rcsopen(file,vers,proc_name)  -- just load the single proc or func from that vers
//  rcsopen("nqsnet.hoc,43,network.hoc,17,params.hoc,842,run.hoc,241") will open several files
func rcsopen () { local a,vers,ii,x,ret localobj st,fn,v1,o,xo
    st=new String2()   ret=1
    if (argtype(1)==2) st.s=$s1 else st.s=$o1.s
    // open network.hoc,params.hoc,run.hoc files for "#.#.#" 
    if (strm(st.s,"^[^,]+,[^,]+,[^,]+,[^,]+")) {
        a=allocvecs(v1)  o=new List()
        split(st.s,o)
        for (ii=1;ii<o.count;ii+=2) v1.append(str2num(o.o(ii).s))
        if (o.count!=v1.size*2) { printf("rcsopen() discrepency: ") vlk(v1) llist(o) dealloc(a) 
            return 0 }
        for ltr(xo,o,&x) if (x%2==0) { // every other
            printf("Opening %s%d\n",xo.s,v1.x[x/2])
            if (!rcsopen(xo.s,v1.x[x/2])) { dealloc(a) ret=0 }
          }
        dealloc(a)
        return ret
      }
    if (numarg()>1) vers=$2 else vers=0
    if (! vers) vers=rcshead(st.s,1) // vers=0 means get latest version
    // do checkout
    fn=filname(st.s)
    sprint(st.t,"RCS/%s,v",st.s)
    if (sfunc.len(fn.t)>0) {
        sprint(st.t,"%sRCS/%s,v",fn.t,fn.s)
        print st.s,"::",st.t,"::",fn.s,"::",fn.t
      } else if (fexists(st.t)) {
        if (rcshead(st.s)!=vers) {
            sprint(st.t,"cp -f %s %s.OLD",st.s,st.s)   system(st.t)
            sprint(st.t,"co -f -r1.%d %s",vers,st.s)   system(st.t)
          }
      } else { // look for file in remote location
        getenv("SITE",st.t)
        sprint(st.t,"%s/nrniv/local/hoc/%s",st.t,st.s)
        if (! fexists(st.t)) {printf("Can't find %s\n",st.t) return 0}
        sprint(st.s,"%s%d",st.s,vers)
        printf("Found %s at %s -> %s\n",$s1,st.t,st.s)
        sprint(st.t,"co -f -p1.%d %s > %s",vers,st.t,st.s) system(st.t)
      }
    if (numarg()>2) {
        if (argtype(3)==0) {  // 3rd arg 1 means do load_file statt xopen
            if ($3) { 
                load_file(st.s)
                return 1
              } else return 1 // 3rd arg 0 does co but no open
          } else { // 3rd arg string means locate a single proc, iter etc.
            sprint(st.s,"hocproc %s %s %s",st.s,$s3,".tmp")
            system(st.s,st.t)
            if (strm(st.t,"ERROR")) return 0 else {
                printf("Loading '%s()' from %s%d (via .tmp)\n",$s3,$s1,vers)
                xopen(".tmp")
                return 1
              } 
          }
      }
    xopen(st.s)
    return 1
  }

//** simopen()
proc simopen () { local x localobj o,xo
    o=new List()
    if (numarg()==1) split(allfiles,o) else split(simfiles,o)
    for ltr(xo,o,&x) xopen(xo.s)
  }

//** pxopen(FILE) prints out the version before opening a file
proc pxopen () { local flag localobj o
    flag=1
    o=new List("ParallelContext")
    if (o.count>0) if (o.o(0).id!=0) flag=0
    if (flag) printf("%s version %d\n",$s1,rcshead($s1))
    xopen($s1)
  }

//** lrm(LIST,STR) will remove item with string from LIST
proc lrm () { local cnt
    cnt=0
    if (argtype(2)==2) {
        for ltrb(XO,$o1) if (strm(XO.s,$s2)) { $o1.remove(i1) cnt+=1 }
        printf("%s found %d time\n",$s2,cnt)
      } else {
        $o1.remove($o1.index($o2))
      }
  }

//** lcnt(LIST,STR) will count item with string in LIST
func lcnt () { local cnt localobj xo
    cnt=0
    for ltr(xo,$o1) if (strm(xo.s,$s2)) cnt+=1
    return cnt
  }

//** lrepl(LIST,#,OBJ) will replace item at location # with OBJ
proc lrepl () { $o1.remove($2) $o1.insrt($2,$o3) }

//** lswap(list,#1,#2) swap items on a list
proc lswap () { local a,b
    if ($2<$3) {a=$2 b=$3} else {a=$3 b=$2}
    $o1.insrt(a,$o1.object(b))
    $o1.remove(b+1)
    $o1.insrt(b+1,$o1.object(a+1))
    $o1.remove(a+1)
  }

//** proc shl() show a list
proc shl () { 
    if (numarg()==1) tmpobj=$o1 else tmpobj=tmplist 
    if (tmpobj.count==0) return
    if (isstring(tmpobj.object(0),tstr)) {
        for ltr(XO,tmpobj) print XO.s
      } else for ltr(XO,tmpobj) print XO
  }

//** lfu() = ltr follow-up, pick out a single item from the last ltr request
// lfu(list,num[,obj])
proc lfu () { 
    if (numarg()==1) { 
        if (argtype(1)==0) XO=tmpobj.object($1)
        if (argtype(1)==1) tmpobj=$o1
      }
    if (numarg()==2) {
        if (argtype(1)==1 && argtype(2)==0) {tmpobj=$o1 XO=$o1.object($2)}
        if (argtype(1)==0 && argtype(2)==1) {$o2=tmpobj.object($1)}
      }
    if (numarg()==3) {
        if (argtype(1)==1 && argtype(2)==0 && argtype(3)==1) { tmpobj=$o1 $o3=$o1.object($2) }
      }
    if (numarg()==4) {
        $o2=tmpobj.object($1)  $o4=tmpobj.object($3) 
      }
  }

//** listedit() allows you to remove things by clicking
proc listedit () {
    if (numarg()==0) { print "listedit(list,str) gives browser(list,str) for removing items" return}
    if (numarg()==1) {
      if (! isstring($o1.object(0),temp_string_)) {print "Give name for string of object?" return }
        sprint(temp_string_,"proc ledt1 () {sprint(temp_string_,%s,hoc_ac_,%s.object(hoc_ac_).%s)}","\"%d:%s\"",$o1,"s")
      } else {
        sprint(temp_string_,"proc ledt1 () {sprint(temp_string_,%s,hoc_ac_,%s.object(hoc_ac_).%s)}","\"%d:%s\"",$o1,$s2)
      }
    execute1(temp_string_)
    $o1.browser("Double click to remove",temp_string_,"ledt1()")
    sprint(temp_string_,"%s.remove(hoc_ac_)",$o1)
    $o1.accept_action(temp_string_)
  }

//** crac() create and access
proc crac () {
    execute("create acell_home_")
    execute("access acell_home_")
  }

//** listXO() connects stuff to XO from a list
proc listXO () {
    if (numarg()==1) {
        $o1.browser("Double click")
        sprint(temp_string_,"print hoc_ac_,\":XO -> \",%s.object(hoc_ac_) XO = %s.object(hoc_ac_)",$o1,$o1)
        $o1.accept_action(temp_string_)
      } else if (numarg()==2) {
        $o1.browser($s2)
        sprint(temp_string_,"XO = %s.object(hoc_ac_) print %s.object(hoc_ac_).%s",$o1,$o1,$s2)
        $o1.accept_action(temp_string_)
      } else if (numarg()==3) {
        $o1.browser($s2)
        sprint(temp_string_,"XO = %s.object(hoc_ac_) print %s.object(hoc_ac_).%s,%s.object(hoc_ac_).%s",$o1,$o1,$s2,$o1,$s3)
        $o1.accept_action(temp_string_)
      }
  }

//** lcatstr(list,s1,s2,...) make new List("s1") new List("s2") ... in one list
proc lcatstr() { local i
    if (numarg()<3) { print "lcatstr(l1,s1,s2,...) puts new Lists into l1" return }
    $o1 = new List($s2)
    for i=3,numarg() {
        tmplist2 = new List($si)
        for ltr(XO,tmplist2) { $o1.append(XO) }  
      }
  }

//** sublist() places a sublist in LIST0 from LIST1 index BEGIN to END inclusive
proc sublist () { local ii
    $o1.remove_all
    for ii=$3,$4 {
        $o1.append($o2.object(ii))
      }
  }

//** catlist() concats LIST2...LISTN on end of LIST1
proc catlist () { local i
    for i = 2, numarg() {
        for ltr(YO,$oi) {
            $o1.append(YO)
          }
      }
  }  

//** mechlist() creates a LIST of all this CELL type's TEMPLATE type
// list, cell, template
// make a list of mechanisms belonging to a certain template
proc mechlist () { local num,ii
  //  mchnms = ""  // not a good storage since runs out of room
    if (numarg()==0) { print "mechlist(list, cell, template)" return}
    $o1 = new List($s2)
    num = $o1.count
    for ii=0,num-1 {
        sprint(temp_string_,"%s.append(%s.%s)",$o1,$o1.object(ii),$s3)
        execute(temp_string_)
        sprint(mchnms,"%s/%d/%s.%s",mchnms,ii,$o1.object(ii),$s3)
      }
    for (ii=num-1;ii>=0;ii=ii-1) { $o1.remove(ii) }
  }

//** lp() loop through a list running command in object's context
// assumes list in tmplist
// with 1 args run $o1.object().obj_elem
// with 2 args run comm($o1.object().obj_elem)
proc lp () {
    for ii=0,tmplist.count-1 {
        printf("%s ",tmplist.object(ii))
        if (numarg()==2) {
            sprint(temp_string_,"%s(%s.%s)",$s2,tmplist.object(ii),$s1)
          } else {
            sprint(temp_string_,"%s.%s",tmplist.object(ii),$s1)
          }
        execute(temp_string_)
      }
  }

//** prlp() loop through a list printing object name and result of command
proc prlp () {
    tmpobj=tmplist
    if (numarg()>0) if (argtype(1)==1) tmpobj=$o1
    for ltr(XO,tmpobj) {
        printf("%d %s ",i1,XO)
        if (numarg()>1) {
            sprint(temp_string_,"print %s.%s",XO,$s2)
            execute(temp_string_)
          } else { print "" }
      }
  }

//** secl2l(SecList[,List]) convert a SectionList to set of secrefs on a regular List
obfunc secl2l () { localobj sref,ll,secl
    secl=$o1
    if (numarg()>1) ll=$o2 else ll=new List()
    ll.remove_all()
    forsec secl {sref = new SectionRef() ll.append(sref)}
    return ll
  }

//* String functions
//** repl_str(str,stra,strb): replace stra with strb in string
// will only replace first string match
proc repl_str() { localobj scr
    scr=new String()
    if (sfunc.head($s1,$s2,scr.s) == -1) { print $s2," not in ",$s1  return }
    sfunc.tail($s1,$s2,scr.s)
    sprint(scr.s,"%s%s",$s3,scr.s)
    sfunc.head($s1,$s2,$s1)
    sprint($s1,"%s%s",$s1,scr.s)
  }

//** find_str(str,left,right): pull out dest flanked by left and right
obfunc find_str() { localobj st
    st=new String2()
    if (sfunc.tail($s1,$s2,st.t) == -1) { print $s2," not in ",$s1  return }
    sfunc.head(st.t,$s3,st.s)
    return st
  }

//** find_num(str,left,right): pull out number flanked by left and right
func find_num() { local x localobj st
    st=new String()
    if (sfunc.tail($s1,$s2,st.s) == -1) { print $s2," not in ",$s1  return }
    sfunc.head(st.s,$s3,st.s)
    sscanf(st.s,"%g",&x)
    return x
  }

//** repl_mstr(str,stra,strb): replace stra with strb in string
// multiple replace
proc repl_mstr () { localobj scr
    scr=new String()
    while (sfunc.head($s1,$s2,scr.s) != -1) {
        sfunc.tail($s1,$s2,scr.s)
        sprint(scr.s,"%s%s",$s3,scr.s)
        sfunc.head($s1,$s2,$s1)
        sprint($s1,"%s%s",$s1,scr.s)
      }
  }

//** extract(str,stra,strb[,dest]): pull out piece of string surrounded by stra,strb
obfunc str_extract() { local b,e,err localobj scr
    scr=new String2() err=0
    b=sfunc.tail($s1,$s2,scr.s) // scr.s contains after s2
    if (b==-1){printf("%s not found in %s\n",$s2,$s1) err=1}
    e=sfunc.head(scr.s,$s3,scr.t) // beg of s3
    if (e==-1){printf("%s not found in %s\n",$s3,scr.s) err=1}
    if (err) scr.s="" else sfunc.left(scr.s,e)
    if (numarg()==4) $s4=scr.s
    return scr
  }

//** clean_str(str,scratch,s1,s2,s3,...)
// remove serial $si from string
proc clean_str () { local i
    for i=3,numarg() {
        while (sfunc.head($s1,$si,$s2) != -1) { 
            sfunc.tail($s1,$si,$s2)
            sfunc.head($s1,$si,$s1)
            sprint($s1,"%s%s",$s1,$s2)      
          }
      }
  }

//** aaaa() (or $o2) becomes a list of strings from file $s1
proc aaaa () { local flag
    if (numarg()==2) { tmpfile.ropen($s1) aa=$o2 flag=0
      } else if (numarg()==1) { tmpfile.ropen($s1) flag=1
      } else { tmpfile.ropen("aa") flag=1 }
    if (flag==1) if (isobj(aa,"List")) { aa.remove_all() } else { aa=new List() }
    while (tmpfile.gets(temp_string_)>0) {
        chop(temp_string_)
        tmpobj=new String(temp_string_)
        aa.append(tmpobj)
      }
    tmpobj=nil
  }

//* Object identification
//** objid() find information about object -- replaces var2obj, canobj, objnum
obfunc objid () { local flag localobj xo
    xo=new Union()
    if (argtype(1)==1) sprint(xo.s,"tmpobj=%s",$o1) else sprint(xo.s,"tmpobj=%s",$s1)
    execute(xo.s) // change variable name to object name
    xo.o=tmpobj
    sprint(xo.s,"%s",xo.o)
    sscanf(xo.s,"%*[^[][%d]",&xo.x)
    return xo
  }

//** var2obj() and canobj() -- find true object names
// var2obj("tstr"[,"objvar"]) replaces variable name with actual name of the object
// default into XO; optional second arg allows to place somewhere else
// eg tstr="TC[0].ampa" var2obj(tstr) -> AMPA[0]
proc var2obj () { local flag
    if (numarg()==1) flag=1 else flag=0
    if (flag) sprint($s1,"XO=%s",$s1) else sprint($s1,"%s=%s",$s2,$s1)
    execute($s1) // change variable name to object name
    if (flag) sprint($s1,"%s",XO) else sprint($s1,"%s",$s2)
    printf("var2obj() PLEASE REPLACE WITH objid()\n") 
  }

//** objnum(OBJ) -- find object number
func objnum () { local x localobj st
    st=new String()
    if (argtype(1)==1) sprint(st.s,"%s",$o1) else st.s=$s1
    if (sscanf(st.s,"%*[^[][%d]",&x) != 1) x=-1
    return x
  }

proc allobjs () {
    hoc_stdout($s1)
    allobjects()
    hoc_stdout()
  }

//** strnum(str,"PRE") -- pull number out of a string
func strnum () { local x localobj st
    st=new String2($s1)
    sfunc.tail(st.s,$s2,st.t)
    if (sscanf(st.t,"%d",&x) != 1) x=-99e99
    return x
  }

//** canobj(obj[,"OBJVAR"]) -- default will assign to XO
// canonical object -- return canonical identity for an object
// eg canobj(tc,"YO") -- figure out what tc is and assign it to YO
proc canobj () { local flag
    if (numarg()==1) flag=1 else flag=0
    if (flag) sprint(tstr,"XO=%s",$o1) else sprint(tstr,"%s=%s",$s2,$o1)
    execute(tstr) // change variable name to object name
    sprint(tstr,"%s",$o1)
    printf("canobj() PLEASE REPLACE WITH objid()\n") 
  }

//* push() and pop() for objects -- returns
proc push () { local i
    for i=2,numarg() $o1.append($oi)
  }

//** pop()
obfunc pop () { local i,cnt localobj o
    cnt=$o1.count-1
    if (cnt==-1) { print "ERR: unable to pop" return } 
    o=$o1.object(cnt)
    $o1.remove(cnt)
    return o
  }

//* time()
strdef tmstr
tmstr="run()"
func time () { local tti,uintmax,cps
    uintmax=4294967295 // UINT_MAX/CLOCKS_PER_SEC
    cps=1e6 // CLOCKS_PER_SEC
    tti=prtime()
    system("date")
    if (numarg()==1) execute1($s1) else execute1(tmstr)
    tti=prtime()-tti
    if (tti<0) tti+=uintmax
    if (tti<cps) {           print tti/1e3,       "kilocycles" 
      } else if (tti<60*cps) { print tti/cps,   "s" 
      } else                   print tti/cps/60,"m"
    system("date")
    return tti/1e3
  }

func ftime () { local tti
    prtime()
    if (numarg()==1) execute($s1) else execute(tmstr)
    tti=prtime()
    return tti
  }

proc setdate () {
    system("date +%y%b%d",datestr)
    chop(datestr)
    downcase(datestr)
  }

proc sortlist () { local a,err,ii,x,rev,na localobj v1,v2,xo,nl,il
    il=$o1
    err=rev=0
    na=numarg()
    for ii=2,3 if (na>=ii) if (argtype(ii)==0) rev=1
    a=allocvecs(v1,v2,il.count)
    nl=new List()
    if (!name_declared("oform")) err=1
    if (!err) if (oform(v1)==NOP) err=1
    if (err) {printf("sortlist uses oform()\n") error()}
    for ltr(xo,il) v1.append(oform(xo))
    v1.sortindex(v2)
    if (rev) v2.reverse
    for ii=0,v2.size-1 nl.append(il.o(v2.x[ii]))
    if ((na==2 && !rev) || na==3) $o2=nl else {
        il.remove_all
        for ltr(xo,nl) il.append(xo)
      }
    dealloc(a)
  }

func assoc () { local i,ty,ii localobj xo
    // object_push(xo) ty=name_declared("s",1) object_pop() // strdef:4, dbl:5
    for ltr(xo,$o2,&ii) {
        if (argtype(1)==2) {
            if (strcmp(xo.s,$s1)==0) break
          } else if (argtype(1)==0) {
            if (xo.x==$1) break
          }
      }
    if ($o2.count==ii) return -1 else return ii
  }

// assoco() like assoc but returns the object instead of the index
obfunc assoco () { local i,ty,ii localobj xo
    // object_push(xo) ty=name_declared("s",1) object_pop() // strdef:4, dbl:5
    for ltr(xo,$o2,&ii) {
        if (argtype(1)==2) {
            if (strcmp(xo.s,$s1)==0) break
          } else if (argtype(1)==0) {
            if (xo.x==$1) break
          }
      }
    if ($o2.count==ii) return nil else return xo
  }

// excu() does the associative search and executes the resultant string 
// eg excu("key",list) excu("key",list,"arg1"[,"arg2","arg3"...]) 
func excu () { local i,ty,ii localobj xo,o,st
    o=$o2
    // object_push(xo) ty=name_declared("s",1) object_pop() // strdef:4, dbl:5
    for ltr(xo,o,&ii) {
        if (argtype(1)==2) {
            if (strcmp(xo.s,$s1)==0) break
          } else if (argtype(1)==0) {
            if (xo.x==$1) break
          }
      }
    if (o.count!=ii) {
        if (sfunc.len(xo.t)==0) return 0 // empty string
        st=strcat(st,"hoc_ac_=",xo.t,"(")
        if (numarg()>2) { 
            for i=3,numarg() {
                if (argtype(i)==2) { sprint(st.s,"%s%s,",st.s,$si)
                  } else if (argtype(i)==0) { sprint(st.s,"%s%g,",st.s,$i)
                  } else if (argtype(i)==1) { sprint(st.s,"%s%s,",st.s,$oi) }
              }
            chop(st.s)
          }
        strcat(st,")")
        execute(st.s)    // print st.s
        return hoc_ac_
      } else return 0
  }

// LAR: list_array is list used as an array
begintemplate LAR
public l,set,x,size,nil,resize,get,lt
objref l,nil
double size[1]

proc init () { local ii
    size=$1
    l=new List()
    for ii=0,size-1 l.append(l) // place holder
  }

func set () { local ix
    ix=$1
    if (ix>=size) {printf("LAR set OOR %d>=%d\n",ix,size) return -1}
    l.remove(ix) l.insrt(ix,$o2) 
    return ix
  }

obfunc x ()   { if (eqojt(l,l.o($1))) return nil else return l.o($1) }
obfunc get () { if (eqojt(l,l.o($1))) return nil else return l.o($1) }

proc resize () { local newsz,ii
    newsz=$1
    if (newsz<size) {
        for (ii=size-1;ii>=newsz;ii-=1) l.remove(ii)
      } else if (newsz>size) {
        for ii=size,newsz-1 l.append(l)
      }
    size=newsz
  }

iterator lt () { local ii
    for ii=0,size-1 if (!eqojt(l,l.o(ii))) { 
        $o1=l.o(ii)
        iterator_statement
      }
  }
endtemplate LAR
// END /usr/site/nrniv/local/hoc/declist.hoc
//================================================================
print "Loading decvec"

fnum=verbose=0
{symnum = 7 colnum = 10}
func cg () { return $1%(colnum-1)+1 } // skip white color
objref clrsym[colnum+1]
for ii=0,colnum { clrsym[ii]=new Union() clrsym[ii].x=ii%colnum }
// black->red->blue->green->orange->brown->violet->yellow->grey
{clrsym[0].s="white" clrsym[1].s="black" clrsym[2].s="red" clrsym[3].s="blue"
  clrsym[4].s="green" clrsym[5].s="orange" clrsym[6].s="brown" clrsym[7].s="violet" 
  clrsym[8].s="yellow" clrsym[9].s="grey"} 
clrsym[0].o=new List()
{symmax=20 symmin=2}
obfunc sg () { local ii localobj o
    ii=$1
    o=clrsym[ii%(colnum-1)+1]
    o.x=ii%(colnum-1)+1
    o.t=clrsym[0].o.o(ii%symnum).s
    o.x[1]=(3-ii)%4+1 // descending line types
    o.x[2]=(symmax-symmin-2*ii)%(symmax-symmin+1)+symmin
    o.x[3]=(4-ii)%(symmax-symmin+1)+symmin
    o.x[4]=(int((ii+1)/5)%2+1)*4-(ii+1)%4+1 // another line type sequence
    return o
  }

{ MSONUM=100 MSOSIZ=100 msomax=0 msoptr=0 objref mso[MSONUM] }
double x[4],y[4]
xx=0 // declare a scalar 
ind = new Vector(100)
tvec = new Vector(100)
vec = new Vector(100)
vec0 = new Vector(10)
vec1 = new Vector(10)
vrtmp = new Vector(10)
veclist = new List()
veccollect = new List()
rdm = new Random()
rdm.MCellRan4()

if (!(xwindows && name_declared("xwindows"))) {
    xwindows=0
    objref graphItem
    strdef temp_string_, temp_string2_
  }

//* stuff that doesn't belong here
//** dired([list,]file) - put together list of files matching 'file', calls 'ls -1 file'
//   dired([list,]file,1) file name to read for list of files
//   dired([list,]file,2) clear dir first; if list isn't present assume 'dir'
//   dired([list,]file,3) do recursive search
func dired () { local f,i,f1 localobj st,o
    f1=f=0
    st=new String2()
    if (numarg()==0) { print "dired([list,]filename[,flag])\t\
        adds the filename to list (use wildcards) (flag:1 read file;flag:2 clear list)"
        return 0 }
    if (argtype(1)==2) {o=dir st.s=$s1 i=2} else {o=$o1 st.s=$s2 i=3}
    while (i<=numarg()) { 
        if (argtype(i)==2) st.t=$si else f=$i
        i+=1
      }
    if (f==2 || f==3) o.remove_all
    if (f==1) {
        tmpfile.ropen(st.s) 
      } else { // f!=1
        rmxtmp()
        if (f==3) {
            printf("Search starting in ") system("pwd")
            sprint(st.t,"sh -fc \"find . -name %s > %s\"",st.s,xtmp)
          } else {
            if (strm(st.s,"[*?]")) f1=0 else f1=1 // is this a wildcard or a directory
            if (f1) {  
                if (sfunc.len(st.t)>0) {
                    sprint(st.t,"find %s -name %s >> %s",st.s,st.t,xtmp)
                  } else  sprint(st.t,"find %s >> %s",st.s,xtmp)
              } else     sprint(st.t,"ls -1R %s >> %s",st.s,xtmp)
          }
        system(st.t)
        tmpfile.ropen(xtmp)
      }
    while (tmpfile.scanstr(st.t) != -1) {
        if (f1) { // a directory
            if ((x=ftype(st.t))!=2) {print "Ignoring ",st.t,x  continue}
          }
        o.append(new String(st.t))
        tmpfile.gets(st.t)  // get rid of the rest of the line
      }
    printf("%d files in dir\n",o.count)
    return o.count
  }

// lsdir([dir])
proc lsdir () { 
    if (numarg()==1) {
        for ltr($o1) {sprint(tstr,"ls -l %s",XO.s) system(tstr)}
      } else for ltr(dir) {sprint(tstr,"ls -l %s",XO.s) system(tstr)}
  }

//** lbrw(list,action) is used to put up a browser
// note action given without '()'
proc lbrw () {
    $o1.browser($s2,"s")
    sprint($s2,"%s()",$s2)
    $o1.accept_action($s2)
  }

//** l2v(S1,S2) makes a list(S1) and puts all the XO.S2 into vec
// eg l2v("IClamp","amp")
proc l2v () {
    tmpobj=new List($s1)
    if (numarg()==3) YO=$o3 else YO=vec
    YO.resize(tmpobj.count) YO.resize(0)
    for ltr(tmpobj) {
        sprint(tstr,"YO.append(%s.%s)",XO,$s2)
        execute(tstr)
      }
  }

//* vector iterator vtr
// for vtr(vec) { print x }
// for vtr(&x, vec) { print x }
// for vtr(&x, vec, &y) { print x,y }
// for vtr(&x, vec, max_ind) { print x }
// for vtr(&x, vec, min_ind, max_ind) { print x }
// for vtr(&x, vec, &y, min_ind, max_ind) { print x,y }
iterator vtr () { local i,j,pf,cf,b,e localobj o
    cf=pf=0
    if (argtype(1)==1) { // 1st arg a vector or the pointer
        o=$o1 i=2
      } else if (argtype(1)==3) {
        pf=1 o=$o2 i=3 // pointer alway in position 1
      } 
    b=0 e=o.size-1 // default: do whole vec
    // now can take counter or limits
    if (argtype(i)==3) {cf=i i+=1} // cf gives counter location
    if (argtype(i)==0) {
        if (argtype(i+1)==0) {b=$i i+=1 e=$i} else e=$i
      }
    if (!cf) i1=0 else {i=cf $&i=0} // default counter
    for j=b,e {
        if (pf) $&1=o.x[j] else x=o.x[j]  
        iterator_statement 
        if (cf) $&i+=1 else i1+=1
      }
  }

//* vector iterator vtr2, treat two vectors as pairs
// usage 'for vtr2(&x, &y, vec1, vec2) { print x,y }'
iterator vtr2 () { local i,pairwise,noi1
    noi1=pairwise=0
    if (numarg()==3) { pairwise=1 i1=0 }
    if (numarg()==4) if (argtype(4)==3) { pairwise=1 $&4=0 noi1=1}
    if (pairwise) if ($o3.size%2!=0) { print "vtr2 ERROR: vec not even sized." return }
    if (! pairwise) {
        if ($o3.size != $o4.size) { print "vtr2 ERROR: sizes differ." return }
        if (numarg()==5) {$&5=0 noi1=1} else {i1 = 0}
      }
    for i = 0,$o3.size()-1 {
        $&1 = $o3.x[i]
        if (pairwise) $&2=$o3.x[i+=1] else $&2=$o4.x[i]
        iterator_statement
        if (noi1) { if (pairwise) $&4+=1 else $&5+=1 } else i1+=1
      }
  }

//** viconv(TARG,OLD_INDS,NEW_INDS)
proc viconv () { local a,b
    if (numarg()==0) { printf("viconv(TARG,OLD_INDS,NEW_INDS)\n") return }
    a=b=allocvecs(2) b+=1
    if ($o2.size!=$o3.size) {printf("OLD_INDS %d != NEW_INDS %d\n",$o2.size,$o3.size) return}
    mso[b].resize($o1.size)
    for vtr2(&x,&y,$o2,$o3) { // x -> y
        mso[a].indvwhere($o1,"==",x)
        mso[b].indset(mso[a],y)
      }
    $o1.copy(mso[b])
    dealloc(a)
  }

//* iterator lvtr, step through a list and a vector together
// usage 'for lvtr(XO, &x, list, vec) { print XO,x }'
iterator lvtr () { local i
    if ($o3.count <  $o4.size) { printf("lvtr ERROR: vecsize > listsize: list %d,vec %d.\n",$o3.count,$o4.size) return }
    if ($o3.count != $o4.size) { printf("lvtr WARNING: sizes differ: list %d,vec %d.\n",$o3.count,$o4.size) }
    if (numarg()==5) {$&5=0} else {i1 = 0}
    for i = 0, $o4.size()-1 {
        $o1 = $o3.object(i)
        $&2 = $o4.x[i]
        iterator_statement
        if (numarg()==5) { $&5+=1 } else { i1+=1 }
      }
  }

//* other iterators: case, scase, ocase
iterator case () { local i,j,max,flag
    if (argtype(numarg())==3) {flag=1 max=numarg()-1} else {flag=0 max=numarg()}
    if (flag) {i=max+1 $&i=0} else i1 = 0
    for i = 2, max {
        $&1 = $i
        iterator_statement
        if (flag) {j=i i=max+1 $&i+=1 i=j} else i1+=1
      }
  }

iterator scase () { local i,j,min,max,flag
    if (argtype(numarg())==3) {flag=1 max=numarg()-1} else {flag=0 max=numarg()}
    if (flag) {i=max+1 $&i=0} else i1=0
    if (argtype(1)==1) {
        if (! isobj($o1,"String")) $o1=new String() // will accept String or String2
        min=2 
      } else min=1
    for i = min,max {
        if (min==1) temp_string_=$si else $o1.s=$si
        iterator_statement
        if (flag) {j=i i=max+1 $&i+=1 i=j} else i1+=1
      }
  }

// eg for scase2("a","b","c","d","e","f") print tmpobj.s,tmpobj.t
iterator scase2 () { local i,min,flag,na,newstr localobj o
    flag=i1=0 newstr=min=1 
    na=numarg()
    if (argtype(na)==0) {i=na newstr=$i na-=1}
    if (argtype(1)==1) {flag=1 min=2}
    for i=min,na {
        if (i==min || newstr) o=new String2()
        o.s=$si i+=1  o.t=$si
        if (flag) $o1=o else tmpobj=o
        iterator_statement
        i1+=1
      }
  }

iterator ocase () { local i
    i1 = 0
    if (isassigned($o1)) {
        for i = 1, numarg() {
            XO = $oi
            iterator_statement
            i1+=1
          }
        XO=nil
     } else {
        for i = 2, numarg() {
            $o1 = $oi
            iterator_statement
            i1+=1
          }
      }
    XO=nil
  }

//* strm(STR,REGEXP) == regexp string match 
func strm () { return sfunc.head($s1,$s2,"")!=-1 }
func strc () { return strcmp($s1,$s2)==0 }

//** count_substr(str,sub): count occurences of substring in str
func count_substr () { local cnt localobj st
    st=new String($s1)
    cnt = 0
    while (sfunc.tail(st.s,$s2,st.s) != -1) { cnt += 1}
    return cnt
  }

//* nind(targ,data,ind) fill the target vector with data NOT index by ind (opposite of v.index)
proc nind () { 
    if (! eqobj($o1,$o2)) $o1.copy($o2)
    $o1.indset($o3,-1e20)
    $o1.where($o1,">",-1e20)
  }

//* vlk(vec)
// vlk(vec,max)
// vlk(vec,min,max)
// vlk(vec,min,max,"INDEX") -- give index of each entry
// prints out a segment of a vector
vlk_width=80
proc vlkomitoff () { execute1("func vlkomit () {return NOP}") }
proc vlkomit0(){execute1("func vlkomit(){if(vcnt($o1,0)==$o1.size) return 1 else return 0}")}
vlkomitoff()
proc vlk () { local ii,i,j,ami,min,max,wdh,nl,na,width,tablen,omfl,ixfl localobj st,v1,vi
    st=new String2()  v1=new Vector(numarg()) vi=new Vector()
    nl=1 wdh=vlk_width j=0 omfl=ixfl=0 ami=2
    na=numarg() min=0 max=$o1.size-1
    if (vlkomit(v1)!=NOP) omfl=1 // omfl to omit printing some
    if (argtype(na)==2) {i=na 
        if (strm($si,"I")) {ixfl=1 vrsz($o1,vi) vi.indgen ami=1}
        na-=1
      }
    if (argtype(na)==0) {i=na 
        if ($i<0) min=$i else max=$i 
        na-=1
      }
    if (argtype(na)==0) {i=na min=$i na-=1}
    if (max<0) max+=$o1.size
    if (min<0) min+=$o1.size
    if (max>$o1.size-1) { max=$o1.size-1 printf("vlk: max beyond $o1 size\n") }
    sprint(st.t,"%%s:%s",dblform)
    width=0
    if (strm(tabform,"\t")) tablen=6 else if (strm(tabform,"\n")) tablen=-1e5 else {
        tablen=sfunc.len(tabform) }
    for ii=min,max { 
        if (omfl) { v1.resize(0)
            for i=1,na v1.append($oi.x[ii])
            if (vlkomit(v1)) continue
          }
        if (ixfl) sprint(st.s,dblform,vi.x[ii]) else sprint(st.s,dblform,$o1.x[ii])
        for i=ami,na sprint(st.s,st.t,st.s,$oi.x[ii]) 
        width+=(sfunc.len(st.s)+tablen)
        if (width>vlk_width && nl) {printf("\n") width=0}
        printf("%s%s",st.s,tabform)
      }
    if (nl) print ""
  }

// vpr2(v1,v2) to print out 2 vecs in parallel
proc vpr2 () { local i,fl2,max,min,newline localobj v1,v2
    newline=80
    v1=$o1 v2=$o2 min=0 max=v1.size if (v2.size!=max) {print "vpr2 diff szs" return}
    for ({i=min fl2=0}; i<max; i+=1) {
        if (v1.x[i]==BVBASE || v2.x[i]==BVBASE) {
            if (!fl2) printf(" ")  
            fl2=1  
          } else { 
            fl2=0
            printf("\n%5.1g %5.1g [%d]",v1.x[i],v2.x[i],i)
          }
      }
    print ""
  }

//** vlkp(SRC,PVEC) uses indices in PVEC to print out values in SRC
proc vlkp () { local i,j,wdh
    j=0 nl=1 wdh=vlk_width
    if (numarg()==2) {
        for vtr(&x,$o1) {
            printf("%g%s",$o2.x[x],tabform)
            if ((j=j+1)%vlk_width==0 && nl && strcmp(tabform," ")==0) { print "" }
          }
      } else {
        for vtr(&x,$o1) { for i=2,numarg() printf("%g%s",$oi.x[x],tabform) 
            print "" }
      }
    if (nl) print ""
  }

//* vprf() prints 1,2 or 3 vectors in parallel to output file
proc vprf () { local x2
    if (! tmpfile.isopen()) {
        print "Writing to temp file 'temp'"
        tmpfile.wopen("temp")
      }
    if (numarg()==1) { 
        for vtr(&x,$o1) { tmpfile.printf("%g\n",x) }
      } else if (numarg()==2) { 
        for vtr2(&x,&y,$o1,$o2) { tmpfile.printf("%g %g\n",x,y) }
      } else if (numarg()==3) { 
        for vtr2(&x,&y,$o1,$o2,&ii) { x2=$o3.x[ii] tmpfile.printf("%g %g %g\n",x,y,x2) }
      }
    tmpfile.close
  }

//* vpr() prints 1,2 or 3 vectors in parallel to STDOUT
proc vpr () { local x2
    if (numarg()==1) { 
        for vtr(&x,$o1) { printf("%g",x) }
      } else if (numarg()==2) { 
        for vtr2(&x,&y,$o1,$o2) { printf("%g:%g ",x,y) }
      } else if (numarg()==3) { 
        for vtr2(&x,&y,$o1,$o2,&ii) { x2=$o3.x[ii] printf("%g:%g:%g ",x,y,x2) }
      }
    print ""
  }

//* readvec(vec) read from line
proc readvec () { 
    $o1.resize(0)
    while (read(xx)) $o1.append(xx)
    vlk($o1)
  }
  
//* popvec(), savenums, readnums, vecsprint, savevec, savestr
//  vrsz(), vcp(), zvec(), resize, copy, empty
proc pushvec () { local i // same as .append, retained for compatability
    for i=2, numarg() $o1.append($i)
  }

//** insvec(VEC,IND,VAL1[,VAL2,...]) insert values into the vector
proc insvec () { local ix,i,a // insert values into a vector
    a=allocvecs(1)  ix=$2
    for i=3, numarg() mso[a].append($i)
    $o1.insrt(ix,mso[a])
    dealloc(a)
  }

//** revec() clear vector then append
proc revec () { local i,x localobj o,st // clear vector then append
    if (! isobj($o1,"Vector")) $o1=new Vector(100)
    if (argtype(2)==2) {
        o=$o1 o.resize(0)
        st=new String2($s2)
        if (strm(st.s,",")) { // use split
            split(st.s,o)
          } else if (strm(st.s," ")) {
            split(st.s,o," ")
          } else while (sfunc.len(st.s)>0) { // assume binary vector, could generalize for hex and base64
            sscanf(st.s,"%1d",&x)
            o.append(x)
            sfunc.tail(st.s, ".", st.t)
            st.s=st.t
          }
      } else for (i=1;i<=numarg();i+=1) { 
        ty=argtype(i)
        if (ty==1) {        o=$oi o.resize(0)
          } else if (ty==0) { o.append($i)
          } else if (ty==3) { o.append($&i) }
      }
  }

//** unvec(VEC,&a,&b,...) put values from vector back into doubles (via pointers)
proc unvec () { local i
    if ($o1.size!=numarg()-1) { printf("unvec WARNING resizing %s to %d\n",$o1,numarg()-1) 
        $o1.resize(numarg()-1) }
    for i=2,numarg() $&i=$o1.x[i-2] 
  }

//** wevec(VEC,wt0,wt1,...) returned weighted sum
func wevec () { local i,sum
    if ($o1.size!=numarg()-1) { printf("wevec SIZE ERR %d %d\n",$o1.size,numarg()-1) return }
    sum=0
    for i=2,numarg() sum+=$o1.x[i-2]*$i
    return sum
  }

//** vrsz(VEC or NUM,VEC1,VEC2...,VECn or NUM)  -- vector resize -- to size of first arg
//   vrsz(VEC or NUM,VEC1,NUM,VEC2,etc)  -- vector resize -- to size of first arg (vec or num)
// or prior NUM
// optional final number is fill
func vrsz () { local i,sz,max,fill,flag,rsz0
    max=numarg()
    flag=rsz0=0
    if (argtype(1)==1) { 
        if (isobj($o1,"Vector")) sz=$o1.size else if (isobj($o1,"List")) sz=$o1.count
        if (argtype(2)==0) {printf("vrsz(vec,num) backwards ERR\n") return -1}
      } else sz=$1
    if (argtype(max)==0) {i=max max-=1 fill=$i flag=1}
    if (argtype(max)==2) {max-=1 rsz0=1} // string means resize(0)
    if (sz<0) sz+=$o2.size // reduce size
    if (sz<0) {printf("vrsz ERR: can't resize %s to %d\n",$o2,sz) return sz}
    for i=2, max { 
        if (argtype(i)==0) sz=$i else {
            $oi.resize(sz)  
            if (rsz0) $oi.resize(0) else if (flag) $oi.fill(fill) 
          }
      }
    return sz
  }

//** vcp() -- copy vector segment with resizing
proc vcp () { local i,sz
    $o1.resize($4-$3+1) $o1.copy($o2,$3,$4)
  }

//** veccut(VEC,min,max) just keep a piece of the vector
// veccut(VEC,min,max,tstep) generate indices from times using tstep
proc veccut () { local a localobj v1
    a=allocvecs(v1)
    if (numarg()==4) { min=round($2/$4) max=round($3/$4)
      } else { min=$2 max=$3 }
    v1.copy($o1,min,max) 
    $o1.copy(v1) 
    dealloc(a)
  }

//** zvec()
proc zvec () { local i // make vectors zero size
    for i=1, numarg() $oi.resize(0)
  }

//* save and read series
//** savenums(x[,y,...]) save numbers to tmpfile via a vector
proc savenums () { local i,vv
    vv=allocvecs(1)
    if (argtype(1)==3) {
        mso[vv].from_double($2,&$&1)  
      } else for i=1, numarg() mso[vv].append($i)
    mso[vv].vwrite(tmpfile)
    dealloc(vv)
  }

//** readnums(&x[,&y...]) recover nums from tmpfile via a vector
func readnums () { local a,i,cnt localobj v1
    if (tmpfile.eof()) return 0
    a=allocvecs(v1)
    v1.vread(tmpfile)
    cnt=0
    if (numarg()==1 && v1.size>1) {
        cnt=v1.size
        if (verbose) printf("readnums WARNING: reading %d vals into presumed double array\n",cnt)
        v1.v2d(&$&1)
      } else {
        if (numarg()>v1.size && verbose) {
            printf("readnums() WARNING: too many args %d>%d\n",numarg(),v1.size) }
        for (i=1; i<=numarg() && i<=v1.size; i+=1) $&i = v1.x[i-1]
        cnt=i-1
      }
    dealloc(a)
    return cnt
  }

//** wrvstr(str) save string to a file by converting to ascii
proc wrvstr () { local vv,i
    vv=allocvecs(1)
    str2v($s1,mso[vv])
    mso[vv].vwrite(tmpfile,1)
    dealloc(vv)
  }

//** rdvstr(str) read string from a file via vread and conversion
func rdvstr () { local vv,i,flag
    flag=1
    vv=allocvecs(1)
    if (mso[vv].vread(tmpfile)) {
        if (numarg()==1) v2str(mso[vv],$s1) else v2str(mso[vv],tstr)
      } else flag=0
    dealloc(vv)
    return flag
  }

//** str2v()
proc str2v () { localobj lo
    lo=new String()
    $o2.resize(0)
    lo.s=$s1
    while (sfunc.len(lo.s)>0) {
        sscanf(lo.s,"%c%*s",&x)
        sfunc.right(lo.s,1)
        $o2.append(x)
      }
  }
    
//** v2str() translates from vector to string
proc v2str () { local ii,x
    $s2=""
    round($o1)
    for ii=0,$o1.size-1 { x=$o1.x[ii] sprint($s2,"%s%c",$s2,x) }
  }

//* popvec() remove last entry
func popvec () { local sz, ret
    sz = $o1.size-1
    if (sz<0) return 1e9
    ret = $o1.x[sz]
    $o1.resize[sz]
    return ret
  }

//* chkvec (look at last entry)
func chkvec () { if ($o1.size>0) return $o1.x[$o1.size-1] else return -1e10 }

// vecsprint(strdef,vec)
proc vecsprint () { local ii
    if ($o2.size>100) { return }
    for ii=0,$o2.size-1 { sprint($s1,"%s %g ",$s1,$o2.x[ii]) }
  }

// savevec([list,]vec1[,vec2,...]) add vector onto veclist or other list if given as 1st arg
// don't throw out vectors
func savevec () { local i,flag,beg localobj v1
    if (numarg()==0) { savevec(hoc_obj_[0],hoc_obj_[1]) return }
    if (isobj($o1,"List")) beg=2 else beg=1
    for i=beg, numarg() { 
        if (veccollect.count>0) { // grab a vector from garbage collection
            v1=veccollect.object(veccollect.count-1)
            veccollect.remove(veccollect.count-1)
          } else v1 = new Vector($oi.size)
        v1.copy($oi)
        v1.label($oi.label)
        if (beg==2) $o1.append(v1) else veclist.append(v1)
      }
    if (beg==2) return $o1.count-1 else return veclist.count-1
  }

// prveclist(filename[,list])
proc prveclist () { localobj xo
    if (!batch_flag && tmpfile.ropen($s1)) {
        printf("%s exists; save anyway? (y/n) ",$s1)
        getstr(temp_string_) chop(temp_string_)
        if (strcmp(temp_string_,"y")!=0) return
      }
    if (! tmpfile.wopen($s1)) { print "Can't open ",$s1  return }
    if (numarg()==2) {
        for ltr(xo,$o2) xo.vwrite(tmpfile)
      } else {
        for ltr(xo,veclist) xo.vwrite(tmpfile)
      }
    tmpfile.close()
  }

// prvl2(filename[,list]) --- save using a more standard fwrite
proc prvl2 () { localobj xo,o
    if (!batch_flag && tmpfile.ropen($s1)) {
        printf("%s exists; save anyway? (y/n) ",$s1)
        getstr(temp_string_) chop(temp_string_)
        if (strcmp(temp_string_,"y")!=0) return
      }
    if (! tmpfile.wopen($s1)) { print "Can't open ",$s1  return }
    if (numarg()==2) o=$o2 else o=veclist
    for ltr(xo,o) {tmpfile.printf("%d\n",xo.size) xo.fwrite(tmpfile)}
    tmpfile.close()
  }

// rdveclist("FILENAME"[,list])
// rdveclist("FILENAME"[,NOERASE])
proc rdveclist () { local flag,a
    flag=0
    a=allocvecs(1)
    if (numarg()==1) { flag=1 clrveclist() } else if (argtype(2)==1) $o2.remove_all else flag=1
    if (! tmpfile.ropen($s1)) { print "Can't open ",$s1  return }
    while (mso[a].vread(tmpfile)) {
        if (flag) savevec(mso[a]) else savevec($o2,mso[a])
      }
    tmpfile.close()
    tmpobj=veclist
    dealloc(a)
  }
  
// rdvecs("FILENAME",vec1,vec2,...)
proc rdvecs () { local i
    if (! tmpfile.ropen($s1)) { print "Can't open ",$s1  return }
    for i=2,numarg() {
        if ($oi==nil) $oi=new Vector()
        if ($oi.vread(tmpfile)==0) printf("WARNING nothing to read into %s\n",$oi)
      }
    tmpfile.close()
  }
  
// svvecs("FILENAME",vec1,vec2,...)
proc svvecs () { local i
    clrveclist()
    for i=2,numarg() savevec($oi)
    prveclist($s1)
    clrveclist()
  }
  
// vpad(vec,howmany,val[,right])
proc vpad () { local a
    a=allocvecs(1)
    mso[a].resize($2) mso[a].fill($3)
    if (numarg()==4) $o1.append(mso[a]) else {
        mso[a].append($o1) $o1.copy(mso[a])     }
    dealloc(a)
  }

// vtrunc(vec,howmany[,right])
proc vtrunc () { local a
    if (numarg()==3) $o1.resize($o1.size-$2) else {
        $o1.reverse $o1.resize($o1.size-$2) $o1.reverse
      }
  }

proc rdxy () { local a,flag
    a = allocvecs(1)
    revec(ind,vec)
    if (numarg()>=1) tmpfile.ropen($s1) else tmpfile.ropen("aa")
    if (numarg()>=2) flag=$2 else flag=2
    mso[a].scanf(tmpfile)
    if (flag==2) {
        for vtr2(&x,&y,mso[a]) {ind.append(x) vec.append(y)}
      } else {
        ind.copy(mso[a])
      }
    print ind.size," points read"
    dealloc(a)
  }

// closest(vec,num) -- return ind for vec member closest to num
func closest () { local a,ret
    a=allocvecs(1)
    mso[a].copy($o1) mso[a].sub($2) mso[a].abs
    ret=mso[a].min_ind
    dealloc(a)
    return ret
  }

// memb(TEST#,#1,#2,...) -- true if the TEST# is in the list
func memb () { local na,i
    for i=2,numarg() if ($1==$i) return 1
    return 0
  }

proc clrveclist () { localobj o,xo
    if (numarg()==1) o=$o1 else o=veclist
    for ltr(xo,o) { xo.resize(0) veccollect.append(xo) }
    o.remove_all()
  }

// savestr(str1...) add string obj onto tmplist
proc savestr () { local i
    if (argtype(1)==1) for i=2, numarg() $o1.append(new String($si)) else {
        for i=1, numarg() tmplist.append(new String($si))
      }
  }

// redund with v.count in vecst.mod
func vcount () { local val,sum
    val=$2 sum=0
    for vtr(&x,$o1) if (x==val) sum+=1
    return sum
  }

// tvecl(inlist[,outlist]) -- transpose veclist
obfunc tvecl () { local x,cnt,sz,err,ii,p localobj xo,il,ol
    il=$o1 
    if (numarg()>1) ol=$o2
    if (!isassigned(ol)) {ol=veclist clrveclist()} else ol.remove_all
    err=0   cnt=il.count sz=il.o(0).size
    for ltr(xo,il,&x) if (xo.size!=sz) err=x
    if (err) { print "Wrong size vector is #",x return ol }
    p = allocvecs(1,cnt)  mso[p].resize(cnt)
    for ii=0,sz-1 {
        for jj=0,cnt-1 mso[p].x[jj] = il.o(jj).x[ii]
        savevec(ol,mso[p])
      }
    dealloc(p)
    return ol
  }  

//* vinsect(v1,v2,v3) -- v1 gets intersection (common members) of v2,v3
//  replaced by v.insct() in vecst.mod
proc vinsect () {
    $o1.resize(0)
    for vtr(&x,$o2) for vtr(&y,$o3) if (x==y) $o1.append(x)
  }

//* vecsplit(vec,vec1,vec2[,vec3,...])
// splits vec into other vecs given
proc vecsplit () { local num,ii,i
    num = numarg()-1 // how many
    for i=2,numarg() $oi.resize(0)
    for (ii=0;ii<$o1.size;ii+=num) {
        for i=2,numarg() if (ii+i-2<$o1.size) $oi.append($o1.x[ii+i-2])
      }
  }

//* vecsort(vec,vec1,vec2[,vec3,...])
// sorts n vecs including first vec by first one
proc vecsort () { local i,inv,scr,narg
    narg=numarg()
    if (narg<2 || narg>10) {print "Wrong #args in decvec.hoc:vecsort" return}
    scr=inv=allocvecs(2) scr+=1
    $o1.sortindex(mso[inv])
    mso[scr].resize(mso[inv].size)
    sprint(temp_string_,"%s.fewind(%s,%s,%s",mso[scr],mso[inv],$o1,$o2)
    for i=3,narg sprint(temp_string_,"%s,%s",temp_string_,$oi)
    sprint(temp_string_,"%s)",temp_string_)
    execute(temp_string_)
    dealloc(inv)
  }

//** order(&x,&y,...) put values in order
proc order () { local a,i,na
    na=numarg()
    a=allocvecs(1)
    for i=1,na mso[a].append($&i)
    mso[a].sort
    for i=1,na $&i=mso[a].x[i-1]
    dealloc(a)  
  }

//** vdelind()  -- delete a single index
proc vdelind () { local i,iin
    iin = $2
    if (iin<0) iin=$o1.size+iin
    if (iin>$o1.size-1 || iin<0) {
        printf("vdelind Error: index %d doesn't exist.\n",iin) return }
    if (iin<$o1.size-1) $o1.copy($o1,iin,iin+1,$o1.size-1)
    $o1.resize($o1.size-1)
  }

//* mkveclist(num[,sz]) recreate veclist to have NUM vecs each of size SZ (or MSOSIZ)
proc mkveclist () { local ii,num,sz,diff localobj xo
    num=$1 
    diff = num-veclist.count
    if (numarg()==2) { sz=$2 } else { sz = MSOSIZ }
    if (diff>0) {
        for ii=0,diff-1 {
          tmpvec = new Vector(sz)
          veclist.append(tmpvec)
          }
      } else if (diff<0) {
        for (ii=veclist.count-1;ii>=num;ii=ii-1) { veclist.remove(ii) }
      }
    for ltr(xo,veclist) { xo.resize(sz) }
  }

//* allocvecs
// create temp set of vectors on mso
// returns starting point on mso 
// eg p = allocvecs(3)
// p = allocvecs(v1,v2,v3) // where v1..v3 are localobj or objref
// p = allocvecs(v1,v2,v3,...,size) // set all to size
// p = allocvecs(num,list) // append num vecs to list
// p = allocvecs(num,size) // num vecs of size size
// p = allocvecs(num,size,list) // append num vecs of size to list
// p = allocvecs(num,list,size) // allow args to be given in reverse order
// access these vectors by mso[p+0] ... [p+2]
func allocvecs () { local i,ii,llen,sz,newv,aflg,lflg,na localobj o
    if (numarg()==0) { 
        print "p=allocvecs(#) or p=allocvecs(v1,v2,...), access with mso[p], mso[p+1]..." return 0 }
    sz=MSOSIZ  na=numarg()
    lflg=0
    if (argtype(1)==0) {
        aflg=0 newv=$1
        if (na>=2) if (argtype(2)==0) sz=$2 else {lflg=1 o=$o2} // append to list in arg2
        if (na>=3) if (argtype(3)==0) sz=$3 else {lflg=1 o=$o3} 
        if (lflg) o.remove_all
      } else {
        aflg=1
        if (argtype(na)==0) {
            i=na sz=$i newv=i-1
          } else newv=na 
      }
    llen = msoptr
    for ii=msomax,msoptr+newv-1 { // may need new vectors
        if (ii>=MSONUM) { print "alloc ERROR: MSONUM exceeded." return 0 }
        mso[ii] = new Vector(sz)
      }
    for ii=0,newv-1 {
        mso[msoptr].resize(sz) 
        mso[msoptr].resize(0) 
        msoptr = msoptr+1
      }
    if (msomax<msoptr) msomax = msoptr
    if (aflg) for i=1,newv $oi=mso[i-1+llen]
    if (lflg) for i=0,newv-1 o.append(mso[i+llen])
    return llen
  }

//** dealloc(start)
// remove temp set of vectors from mso
proc dealloc () { local ii,min
    if (numarg()==0) { min = 0 } else { min = $1 }
    msoptr = min
  }

//* indvwhere family
//** vwh(VEC,VAL) returns index where VEC.x[i]==VAL
func vwh () { 
    if (argtype(2)==0) return $o1.indwhere("==",$2) 
    if (numarg()==3) return $o1.indwhere($s2,$3)
    return $o1.indwhere($s2,$3,$4)
  }

//** vval(VEC,STR,NUM) uses indwhere to return first value that qualifies
func vval () {
    if (argtype(2)==0) return $o1.x[$o1.indwhere("==",$2)]
    if (numarg()==3) return $o1.x[$o1.indwhere($s2,$3)] 
    return $o1.x[$o1.indwhere($s2,$3,$4)] 
  }

//** vcnt(VEC,STR,x[,y]) uses indvwhere and returns # of values that qualify
func vcnt () { local a,ret
    a=allocvecs(1) 
    if (numarg()==2) mso[a].indvwhere($o1,"==",$2)
    if (numarg()==3) mso[a].indvwhere($o1,$s2,$3)
    if (numarg()==4) mso[a].indvwhere($o1,$s2,$3,$4)
    ret = mso[a].size
    // if ($o1.size>0) printf("%d/%d (%g)\n",ret,$o1.size,ret/$o1.size*100)
    dealloc(a)
    return ret
  }
//** civw(DEST,SRC1,STR1,x1[,y1]...) does compound indvwhere
// overwrites tstr; DEST should be size 0 unless to be compounded
// civw(DEST,0,...) will resize DEST to 0
func civw () { local i,a,b,c,f2,x,y,sz,min
    a=b=c=allocvecs(3) b+=1 c+=2
    min=2
    // if ($o1.size>0) print "Starting with previously set index vector"
    if (argtype(2)==0) {
        if ($2==0) { $o1.resize(0) min=3
            if (argtype(3)==1) sz=$o3.size else {
                printf("ERR0: arg 3 should be obj when $2==0\n",i) return -1 }
          } else {
            printf("ERR0a: arg 2 should be 0 if a number -- zero sizes ind vector\n") 
            return -1
          }
      } else if (argtype(2)==1) sz=$o2.size else {
        printf("ERR0b: arg 2 should be obj\n",i) return -1 }
    for (i=min;i<=numarg();) {
        mso[c].copy($o1) 
        if (argtype(i)!=1) { printf("ERR1: arg %d should be obj\n",i) return -1}
        if ($oi.size!=sz)  { printf("ERR1a: all vecs should be size %d\n",sz) return -1}
        mso[a].copy($oi)  i+=1   // look in a
        if (argtype(i)!=2) { printf("ERR2: arg %d should be str\n",i) return -1}
        tstr=$si          i+=1
        if (strm(tstr,"[[(]")) f2=1 else f2=0 // opstring2 needs 2 args
        if (argtype(i)!=0) { printf("ERR3: arg %d should be dbl\n",i) return -1}
        x=$i              i+=1 
        if (f2) {
            if (argtype(i)!=0) { printf("ERR4: arg %d should be dbl\n",i) return -1}
            y=$i            i+=1
          }
        if (f2) mso[b].indvwhere(mso[a],tstr,x,y) else { // the engine
                  mso[b].indvwhere(mso[a],tstr,x)   }
        $o1.resize(sz) // make sure it's big enough for insct -- shouldn't need
        if (mso[c].size>0) $o1.insct(mso[b],mso[c]) else $o1.copy(mso[b])
        if ($o1.size==0) break
      }
    dealloc(a)
    return $o1.size
  }

//* vecconcat(vec1,vec2,...)
// vecconcat(vec1,list) puts concat all vecs from list
// destructive: concatenates all vecs onto vec1
// performs a list2vec() functionality
proc vecconcat () { local i,max localobj xo
    max=numarg()
    if (numarg()<2) { 
        print "vecconcat(v1,v2,...) puts all into v1" return 
      }
    if (argtype(max)==0) {max-=1 $o1.resize(0)}
    if (isobj($o2,"List")) {
        $o1.resize(0)
        for ltr(xo,$o2) $o1.append(xo)
      } else for i=2,max $o1.append($oi)
  }
  
//** vecelim(v1,v2)  eliminates items in v1 given by index vec v2
proc vecelim () {
    for vtr(&x,$o2) { $o1.x[x]= -1e20 }
    $o1.where($o1,"!=",-1e20)
  }

//** redundout(vec) eliminates sequential redundent entries
// destructive
func redundout () { local x,ii,p1
    p1=allocvecs(1)
    $o1.sort
    mso[p1].resize($o1.size)
    mso[p1].redundout($o1)
    $o1.copy(mso[p1])
    dealloc(p1)
    return $o1.size
  }

//** uniq(src,dest[,cnt]) uses redundout to return random values of a vector
// like redundout except nondestructive
obfunc uniq () { local a localobj v1,vret
    a=allocvecs(v1)
    v1.copy($o1) v1.sort 
    if (numarg()==3) {          $o2.redundout(v1,0,$o3) 
      } else if (numarg()==2) {   $o2.redundout(v1) 
      } else {
        vret=new Vector(v1.size) vret.redundout(v1)
      }
    dealloc(a)
    if (numarg()==1) return vret else return $o2
  }

//** complement(ind,max) for indices -- return values from 0..max that are not in ind
proc complement () { local a,b,max
    a=b=allocvecs(2) b+=1
    max=$2
    mso[a].indgen(0,max,1)
    mso[b].resize(mso[a].size)
    mso[b].cull(mso[a],$o1)
    $o1.copy(mso[b])
    dealloc(a)
  }

//** albetname() generate sequential 3 char alphabetical file names (make filenames)
obfunc albetname () { local na,sret localobj st
    na=numarg() sret=0
    st=new String2()
    if (na==0) { fnum+=1 st.t=".id"
      } else if (na>=1) {
        if (argtype(1)==2) {st.t=$s1 fnum+=1} else fnum=$1
      }
    if (na==2) st.t=$s2  // partially back compatible, doesn't handle albetname(tstr,".id")
    if (na==3) {st.t=$s3 sret=1}
    if (fnum>17575) printf("albetname WARN: out of names: %d > %d\n",fnum,26^3-1)
    sprint(st.s,"%c%c%c",  fnum/26/26%26+97,fnum/26%26+97,fnum%26+97)
    if (sfunc.len(st.t)>0) sprint(st.s,"%s%s",st.s,st.t)
    if (sret) $s2=st.s 
    return st
  }

// save_idraw([filename,save_all,PS]) -- save idraw to file
// default is to generate albetname, save selected to idraw
proc save_idraw () { local sv,ps localobj st,li
    st=new String()
    find_pwm()
    ps=sv=1
    if (argtype(1)==2) {
        st.s=$s1 
        if (argtype(2)==0) sv=$2
        if (argtype(3)==0) ps=$3
      } else if (argtype(1)==0) {
        sv=$1
        if (argtype(2)==0) ps=$2  
      }
    if (sfunc.len(st.s)==0) st.s=albetname().s
    if (verbose) printf("Saving to %s\n",st.s)
    pwm.printfile(st.s, ps, sv)
  }

proc find_pwm () { localobj li
    if (isassigned(pwm)) return
    li=new List("PWManager")
    if (li.count==0) pwm=new PWManager() else pwm=li.o(0)
  }

// vecconv() convert $o1 by replacing instances in $o2 by corresponding instances in $o3
proc vecconv () { local a,x,y localobj v1,v2,v3,v4
    a=allocvecs(v1,v2,v3,v4)
    if (argtype(2)==0) v2.append($2) else v2=$o2
    if (argtype(3)==0) v3.append($3) else v3=$o3
    vrsz($o1,v4)
    for vtr2(&x,&y,v2,v3) { // x -> y
        v1.indvwhere($o1,"==",x)
        $o1.indset(v1,y)
      }
  }

//** veceq()  like vec.eq but don't have to be same size and shows discrepency
func veceq () { local sz1,sz2,eq,beg,ii,jj,kk
    sz1=$o1.size sz2=$o2.size
    if (numarg()==3) beg=$3 else beg=0
    if (sz1!=sz2) printf("%s %d; %s %d\n",$o1,sz1,$o2,sz2)
    ii=0 jj=beg
    while (ii<sz1 && jj<sz2) {
        if ($o1.x[ii]!=$o2.x[jj]) { 
            eq=0
            printf("Differ at %d %d\n",ii,jj) 
            for kk=-10,10 if ((ii+kk)>=0 && (ii+kk)<sz1 && (jj+kk)>=0 && (jj+kk)<sz2) {
                printf("(%d)%g:(%d)%g ",(ii+kk),$o1.x[ii+kk],(jj+kk),$o2.x[jj+kk])  }
            print ""
            break 
          } else eq=1
        ii+=1 jj=ii+beg
      }
    return eq
  }

//* isstring() determine if object $o1 is of type string, if so return the string in [$s2]
func isstring () {
    sprint(tstr,"%s",$o1)
    if (sfunc.substr(tstr,"String")==0) {
        if (numarg()==2) sprint($s2,"%s",$o1.s)
        return 1
      } else {
        if (numarg()==2) sprint($s2,"%s",$o1)
        return 0
      }
  }

//** isassigned() checks whether an object is Null
if (name_declared("VECST_INSTALLED")) {
    sprint(tstr,"func isassigned () { return !isojt($o1,nil) }")
  } else {
    sprint(tstr,"func isassigned () { return !isobt($o1,nil) }")
  }
execute1(tstr)

//** declare goes through a list of scalars and assigns them if they don't already exist
// eg declare("adj",nil,"vd",nil,"vc",nil,"allcells",0,"div","d[1][1]")
func declare () { local i,nd,min,verbose,ty,tye,ret localobj st,ut
    if (argtype(1)==0) {verbose=0 min=2} else {min=verbose=1}
    if (numarg()==0) {
        printf("eg declare(\"obj\",new Vector(),\"num\",0,\"arr\",\"d[2][2]\",\"vec\",\"o[5]\",\"str\",\"\")\nfirst arg num means quiet")
        return 0 }
    st=new String2() ut=new String2() ret=0
    ut.s="^[do]\\["  ut.t="^[A-Z][A-Za-z]+[(][^)]*[)]$" // for declaring arrays and objects
    for i=min,numarg() {
        st.s=$si     i=i+1
        ty=argtype(i)
        nd=name_declared(st.s)
        if (nd==1) { nd=0
            if (verbose) {
                printf("declare WARN defining '%s' despite prior existence in symbol table\n",st.s) }}
        if ((nd==5 && ty==0) || (nd==4 && ty==2) || (nd==2 && ty==1)) { // OK -- DO NOTHING
            if (i==min+1) ret=1
            if (verbose) printf("'%s' predeclared\n",st.s)
          } else if (nd != 0) { // different type codes from name_declared() bzw argtype()
            if ((nd==5 && ty==2) || (nd==2 && ty==2)) {
                if (strm($si,ut.s)) { // name_declared gives 2 for either objref and objarray
                    if (nd==5) st.t="DOUBLE" else st.t="OBJ"
                    if (verbose) {
                        printf("'%s' predeclared as %s array: WARN: no check on type or dims\n",st.s,st.t)}
                    continue  // declaration for a double or obj array 
                  } else  if (strm($si,ut.t)) { 
                    if (verbose) printf("'%s' predeclared\n",st.s)
                    continue  // declaration for an object
                  }
              }
            if (nd==5) tye=0 else if (nd==4) tye=2 else if (nd==2) tye=1 
            if (verbose) printf("Declared ERR %s predeclared as a %d statt %d\n",st.s,tye,ty)
          } else { // declare it
            if (ty==0) { 
                sprint(st.t,"%s=%g",st.s,$i)
              } else if (ty==1) { 
                if ($oi==nil) {  sprint(st.t,"objref %s\n%s=nil",st.s,st.s)
                  } else           sprint(st.t,"objref %s\n%s=%s", st.s,st.s,$oi)
              } else if (ty==2) { // key strings are eg d[5][5] or o[5][5] for double or obj array
                if (strm($si,ut.s)) {
                    if (strm($si,"^d")) ty=0 else ty=1
                    sfunc.tail($si,"[do]",st.t) 
                    if (ty==0) sprint(st.t,"double %s%s",st.s,st.t) else \
                               sprint(st.t,"objref %s%s",st.s,st.t) 
                  } else if (strm($si,ut.t)) {
                    sprint(st.t,"objref %s\n%s = new %s",st.s,st.s,$si) 
                  } else sprint(st.t,"strdef %s\n%s=\"%s\"",st.s,st.s,$si)
              }
            execute1(st.t)
          }
      }
    return ret
  }

//** declared goes through a list of function names and makes sure they exist
// useful to check a bunch of names before entering a template that calls them as external
proc declared () { local i,nd,min,flag
    if (argtype(1)==0) {flag=$1 min=2} else {flag=0 min=1}
    for i=min,numarg() {
        nd=name_declared($si)
        if (nd==0 || (flag==1 && nd==1)) { // declare it or clear it
            // sprint(tstr,"func %s () {printf(\"\\tEMPTY FUNCTION %s()\\n\") return 0}",$si,$si)
            sprint(tstr,"func %s () {return 0}",$si)
            execute1(tstr)
          } else if (nd!=1) { // 2=obj,3=sec,4=str,5=dbl
            printf("NAME CONFLICT: %s can't be a func since was declared as a %d\n",$si,nd)
          }
      }
  }

//** isit() like isassigned() but can take a string instead
obfunc isit () { local ret localobj sv,o,st
    o=new Union()  st=new String()
    if (argtype(1)==2) {
        sv=XO
        sprint(st.s,"XO=%s",$s1)
        execute(st.s) // XO points to the thing
        ret=isassigned(XO)
        o.x=ret
        o.s=$s1
        o.o=XO
        XO=sv
        return o
      } else return isassigned($o1)
  }

//** isob(s1,s2) like isobj but takes string statt obj
func isob () { localobj st
    st=new String()
    sprint(st.s,"XO=%s",$s1)
    execute(st.s) // XO points to the thing
    sprint(st.s,"%s",XO)
    if (sfunc.substr(st.s,$s2)==0) {
        return 1
      } else {
        return 0
      }
  }

//** eqobj(o1,o2) checks whether 2 objects are the same
func eqobj () {	return object_id($o1) == object_id($o2) }
//** ocnt(STR) counts number of objects named string
func ocnt () { local ret
    tmpobj=new List($s1) ret=tmpobj.count
    tmpobj=nil
    return ret
  }

//** isobj(o1,s2) checks whether object $o1 is of type $s2
func isobj () { localobj st
    st=new String()
    sprint(st.s,"%s",$o1)
    if (sfunc.substr(st.s,$s2)==0) {
        return 1
      } else {
        return 0
      }
  }

//** isobt(o1,o2) checks whether object $o1 is of type $o2
func isobt () { localobj s
    s=new String2()
    sprint(s.s,"%s",$o1)  sprint(s.t,"%s",$o2)
    sfunc.head(s.s,"\\[",s.s) sfunc.head(s.t,"\\[",s.t)
    if (strc(s.s,s.t)) return 1 else return 0
  }

// destructive of $s1
func str2num () { local x localobj o,st
    x=1e9
    if (sscanf($s1,"%d",&x)!=1) {
        o=new Union() st=new String()
        sprint(st.s,"%s.x=%s",o,$s1)  // o has temp existance in global namespace
        if (!execute1(st.s,0)) printf("Can't find '%s'\n",$s1)
        x=o.x
      }
    return x
  }

func isnum () { return strm($s1,"^[-+0-9.][-+0-9.eE]*$") }

// fln(num,STR_OBJ) move forward line n lines, chop string at end, remove quotes if present
// fln("rxp",STR_OBJ) move forward to regexp rxp; with arg3 use exact search
// fln("rxp OR n",STR_OBJ,obj) return union in obj with more data
// uses tmpfile; assumes that file is open for reading
fln_lnum=0 // use to count lines; must be rest by calling routine
func fln () { local n,nn,x,y localobj o,st,un
    n=nn=0 y=OK o=$o2
    if (argtype(1)==0) nn=$1 // if need to back up
    if (argtype(3)==1 || argtype(4)==1) { un=new Union(tmpfile.tell())
        if (argtype(3)==1) $o3=un else $o4=un 
      }
    if (nn) {
        while(n<nn) { tmpfile.gets(o.s) n+=1 }
      } else { // look for a string
        st=new String2()
        if (argtype(3)!=0) { // default regexp search
            if (strc(o.s,"A")) o.s="B" else o.s="A" // start with something different
            while (! strm(o.s,$s1)) {tmpfile.gets(o.s) n+=1}
          } else {  // exact string search
            sprint(st.s,"%s\n",$s1) // look for this string
            o.s=$s1 // make sure not somehow starting with exactly the same thing
            while (strcmp(o.s,st.s)!=0) {tmpfile.gets(o.s) n+=1} // might want to return line cnt
          }
      }
    sfunc.left(o.s,sfunc.len(o.s)-1) // get rid of newline
    if (sfunc.head(o.s,"\"","")==0) { // find quote at beginning
        sfunc.left(o.s,sfunc.len(o.s)-1) // assume quote at end
        sfunc.right(o.s,1)
      } else if (sscanf(o.s,"%g,%g",&x,&y)==2) { // specifically used by NQS.rddif
      } else if (sscanf(o.s,"%g",&y)==1) { }
    if (un!=nil) {un.x[1]=tmpfile.tell() un.x[2]=n un.x[3]=x un.x[4]=y un.s=o.s}
    fln_lnum+=n
    return y
  }

// like perl chop -- removes the last character
// chop(STR[,TERM]); TERM chop only TERM
// note that no + means must repeat char eg "))*" statt ")+"
func chop () { local ln1,match localobj st
    ln1=sfunc.len($s1)  st=new String()
    if (numarg()==2) { 
        if (!strc($s2,"\n")) sprint($s2,"%s$",$s2) // just look for terminal character
        if ((match=sfunc.head($s1,$s2,st.s))==-1) {
            return 0 
          } else {
            sfunc.left($s1,match) 
            return match
          }
      } else if (sfunc.len($s1)>=1) {
        sfunc.left($s1,ln1-1) 
        return 1
      } else {
        print "ERR: chop called on empty string" }
      return 0
  }

// lchop(STR[,BEGIN]) -- chop from the left
func lchop () { local ln1,match localobj st
    ln1=sfunc.len($s1)  st=new String()
    if (numarg()==2) { 
        sprint($s2,"^%s",$s2) // just look for initial chars
        if ((match=sfunc.tail($s1,$s2,st.s))==-1) {
            return 0 
          } else {
            sfunc.right($s1,match) 
            return match
          }
      } else if (sfunc.len($s1)>=1) {
        sfunc.right($s1,1) 
        return 1
      } else {
        print "ERR: chop called on empty string" }
      return 0
  }

// strcat(tstr,"stra","strb",...) tstr=stra+strb+...
// 1st arg can be a strdef or String obj
// other args can be strdef, String obj, literal string, or number handled as %g
// returns String obj
obfunc strcat () { local i,na localobj out
    na=numarg() 
    if (argtype(1)==0) { out=new String() 
      } else if (argtype(1)==1) { 
        if (isassigned($o1)) out=$o1 else { out=new String() $o1=out }
      } else { out=new String() out.s=$s1 } // print "AA:",$s1," ",sfunc.len($s1)
    if (argtype(na)==0) { out.s="" na-=1 } // clear string
    for i=2,na {
        if (argtype(i)==1) {        sprint(out.s,"%s%s",out.s,$oi.s) 
          } else if (argtype(i)==2) { sprint(out.s,"%s%s",out.s,$si) 
          } else                      sprint(out.s,"%s%g",out.s,$i) 
      }
    if (argtype(1)==2) $s1=out.s
    return out
  }

// split(string,vec) // split comma sep string into numbers
// split(string,list) // split comma sep string into list of strings
// split(string,list,regexp) // split regexp sep string into list of strings
// split(string,vec,regexp,1) // split but don't interpret: eg "5*3"->15 or "x"->val
// split(string,list,num) // split every num chars
// eg split("534,  43 , 2, 1.4, 34",vec[,"/"])
// split("13, 3*PI/2*tau/2, 32+7, 6, 9.2, 42/3",vec) 
//    optional 3rd str is what to split on; default is comma
split_interp=1
func split () { local i,vf,x,done,num localobj s,st,o
    if (numarg()==0) {
        printf("eg split(\"534 43 2 1.4 34\",vec,\" \") split(\"a,b,c,d,e\",tmplist)")
        return -1 }
    s=new String2() st=new String2()
    s.t=$s1 
    if (argtype(2)!=1) {
        o=new Vector() vf=2 i=2
      } else { 
        o=$o2 i=3
        if (isobj(o,"Vector")) {
            vf=1 
          } else {
            vf=0
            if (!isassigned(o)) { o=new List() $o2=o }
          }
      }
    if (vf) revec(o) else o.remove_all
    if (argtype(i)==2) {       st.s=$si   i+=1
      } else if (argtype(i)==1) {st.s=$oi.s i+=1
      } else                     st.s="[, ]+"
    if (argtype(i)==0) { // length split into a list eg c2 c2 c2 etc
        if (vf) {printf("Split err: attempting to do length split into vector: %s",o) return 0}
        num=$i
        sprint(st.s,"%%*%ds%%s",num) // eg "%2*s%s" to get all but first 2 chars
        sprint(st.t,"%%%ds",num)     // eg "%2s"    to get first 2 chars
        if (num>sfunc.len(s.t)){
            printf("split() ERRA %s of length %d in pieces of %d?\n",s.t,sfunc.len(s.t),num) return 0}
      } else num=0
    while (sfunc.len(s.t)>0) {
        done=0
        if (vf) {
            if (split_interp) if (strm(s.t,"^[^,]+[+*/-]")) {
                sfunc.head(s.t,",",s.s)
                if (sfunc.len(s.s)==0) s.s=s.t
                sprint(s.s,"%s.append(%s)",o,s.s) execute(s.s)
                done=1
              } 
            if (!done) if (sscanf(s.t,"%lf",&x)) { 
                o.append(x)
                done=1
              } 
            if (!done && split_interp) { // try to interpret as a variable
                hoc_ac_=ERR // global hoc_ac_ for execute1()
                sfunc.head(s.t,st.s,s.s)
                if (sfunc.len(s.s)==0) s.s=s.t // the end
                sprint(st.t,"hoc_ac_=%s",s.s)
                execute1(st.t,0) // no error
                if (hoc_ac_==ERR) {
                    printf("split WARNING skipping non-parsable value: %s\n",s.s)
                  } else {
                    o.append(hoc_ac_) 
                  }
                done=1
              }
            if (vf==2) if (o.size==0) return ERR else return o.x[0]
          } else { // split into a list
            if (num) {
                sscanf(s.t,st.t,s.s)
                o.append(new String2(s.s,s.s))
              } else {
                sfunc.head(s.t,st.s,s.s)
                if (sfunc.len(s.s)==0) s.s=s.t // the end
                o.append(new String2(s.s,s.s))
              }
            done=1
          }
        if (num) { // splitting equal length strings
            if (sfunc.len(s.t)<=num) s.t="" else sscanf(s.t,st.s,s.t)
          } else {
            sfunc.tail(s.t,st.s,s.t)
          }
      }
    if (vf) return o.size else return o.count
  }

//** splitmatch(str,strlist) returns which string in strlist matches the str
func splitmatch () { local ii,x,ret localobj ll,xo
    ll=$o2 ret=-1
    for ltr(xo,ll,&x) {
        if (strm($s1,xo.s)) {ret=x
            break }
      }
    return ret
  }

// parsenums(STR[,VEC]) find first or all the numbers in a string
func parsenums () { print "Use split(\"str\") instead" }

// intervals(TRAIN,OUTPUT)
func intervals () { local a
    if ($o1.size<=1) { printf("%s size <2 in intervals()\n",$o1) return 0}
    $o2.deriv($o1,1,1)
    return $o2.size
  }

// invl(train,stats[,thresh])
func invl () { local a,x,sz localobj v1,v2
    a=allocvecs(v1,v2)
    if ($o1.size<=1) { printf("%s size <2 in invl()\n",$o1) 
        $o2.resize(5) $o2.fill(-1)
        dealloc(a)
        return 0
      }
    if (!$o1.ismono(2)) printf("decvec::invl() WARN: %s not sorted\n",$o1)
    v1.deriv($o1,1,1)
    if (numarg()==3) {
        if ((x=v1.w("<",$3,-1))>0) { // tag and remove all below threshold
            v1.sort v1.reverse
            v1.resize(v1.size-x)
          }
      }
    stat(v1,$o2)
    sz=v1.size
    dealloc(a)
    return sz
  }

// freql(train,stats)
// doesn't make much sense to take the mean of inverses 
func freql () { local a localobj v1
    a=allocvecs(v1)
    if ($o1.size<=1) { printf("%s size <2 in intervals()\n",$o1) return 0}
    v1.deriv($o1,1,1)
    v1.inv(1e3)
    stat(v1,$o2)
    return v1.size
  }

// downcase(tstr[,UPCASE]) 
obfunc downcase () { local len,ii,let,diff,min,max localobj st
    diff=32 min=65 max=90 st=new String2()
    if (argtype(1)==2) st.s=$s1 else st.s=$o1.s
    if (numarg()==2) { diff=-diff min=97 max=122 } // if flag -> upcase
    len = sfunc.len(st.s)
    for ii=1,len {
        sscanf(st.s,"%c%*s",&x)
        sfunc.right(st.s,1)
        if (x>=min&&x<=max) {
            sprint(st.s,"%s%c",st.s,x+diff)
          } else sprint(st.s,"%s%c",st.s,x) // just rotate the letter
      }
    if (argtype(1)==2) $s1=st.s
    return st
  }

// newlst() puts a newline in the middle of a string
proc newlst () { local l
    if (numarg()>1) l=$2 else l=int(sfunc.len($s1)/2)
    temp_string_=$s1
    temp_string2_=$s1
    sfunc.left(temp_string_,l)
    sfunc.right(temp_string2_,l)
    sprint($s1,"%s\n%s",temp_string_,temp_string2_)
  }

//* rdcol(file,vec,col#,cols): read multicolumn file
func rdcol () { local col,cols,length
    if (numarg()==0) { print "\trdcol(\"file\",vec,col#,cols) // col#=1..." return 0}
    col=$3 cols=$4 length=0
    if (! tmpfile.ropen($s1)) { printf("\tERROR: can't open file \"%s\"\n",$s1) return 0}
    while (tmpfile.gets(temp_string_) != -1) length+=1 // count lines
    print length
    tmpfile.seek()
    $o2.scanf(tmpfile,length,col,cols)
    if ($o2.size!=length) printf("rdcol ERR: only read %d statt %d\n",$o2.size,length)
    return length
  }

//* rdmuniq(vec,n,rdm) -- augment vec by n unique vals from rdm
//  rdmuniq(vec,n,max) -- augment vec by n unique vals 0-max
//  rdmuniq(vec,n,min,max) -- augment vec by n unique vals min-max
// draw n numbers without replacement, only makes sense with discrete distribution
// could do something like 
// mso[a].setrand($o3) mso[d].copy(mso[a]) 
//     mso[b].indsort(mso[a]) mso[a].sort() mso[c].redundout(mso[a],1)
// to get indices of unique values but then have to back index to original
// rdmuniqm4=1 to use setrnd() statt setrand()
rdmuniqm4=0
proc rdmuniq () { local i,max,min,n,a3,a,see localobj ro,v1,v2,vin,l
    if (numarg()==0) {
        printf("rdmuniq(vec,n,rdm) - augment vec by n unique vals from rdm\
      rdmuniq(vec,n) - augment vec by n unique vals 0-99\
      rdmuniq(vec,n,max) - augment vec by n unique vals 0-max\
      rdmuniq(vec,n,min,max) - augment vec by n unique vals min-max\
      rdmuniq(vec,n,min,max,seed) - augment vec by n unique vals min-max using seed\n") 
      return }
    if (rdmuniqm4 && argtype(5)!=0) {
        printf("flag set: rdmuniqm4==1: should use all 5 args: rdmuniq(vec,n,min,max,seed)\n") return }
    vin=$o1 min=max=0 see=0 n=int($2) // round down
    a3=argtype(3)
    if (a3==1) {
        ro=$o3  // random previously setup, min,max unknown
      } else if (a3==-1) {
          min=0 max=99
      } else {
         max=$3
        if (argtype(4)==0){min=$3 max=$4}
        if (argtype(5)==0) see=$5
      }
    if (max>0) { // else rdm was an arg with unknown min,max
        if (max-min+1==n) {
            vin.indgen(min,max,1)
            return
          } else if (n>max-min+1) {
            printf("rdmuniq ERR incompatible: min=%d max=%d n=%d (%g vs %d)\n",min,max,n,$2,max-min+1) 
            return 
          }
      }
    if (!rdmuniqm4) {
        if (ro==nil) ro=new Random()
        if (see) ro.ACG($5) // seed it
        ro.discunif(min,max)
      }
    vin.resize(0)
    a=allocvecs(v1,v2) l=new List() l.append(v2) l.append(vin)
    for (ii=1;vin.size<n && ii<5;ii+=1) {
        v1.resize(n*4*ii)
        if (rdmuniqm4) { 
            v1.setrnd(5,max-min+1,see) 
            v1.add(min)
          } else v1.setrand(ro)
        v1.uniq(l,1) // preserve order
      }
    if (vin.size<n) {
        printf("rdmuniq ERR unable to find %d uniq values [%d,%d] -- only \n",n,min,max,vin.size)
      }
    vin.resize(n)
    dealloc(a)
  }

// rdmord (vec,n) randomly ordered numbers 0->n-1 in vec
// eg rdmord(ind,ind.size); check: for ii=0,ind.size-1 if (ind.count(ii)!=1) print ii
proc rdmord () { local n,a localobj v1
    a=allocvecs(v1)  n=$2
    rdm.uniform(0,100)
    v1.resize(n)
    v1.setrand(rdm)
    v1.sortindex($o1)
    dealloc(a)
  }

// shuffle(VSRC[,VDEST]) randomly rearrange elements of vec
obfunc shuffle () { local a  localobj v1,v2,oi,oo
    oi=$o1
    if (numarg()==2) {oo=$o2 oo.copy(oi)} else oo=oi
    oo.shuffle()
    return oo
  }

// colshuf(ind,veclist) shuffle the 'transpose' of indexed vecs -- ie not the vecs themselves
// shuffle items from list of vectors across the list rather than across the vectors
// all vecs should be same size, eg colshuf(nq.ind,nq.fcdo) after a select
func colshuf () { local a,x,ii,cols,rows  localobj v1,v2,oi,ol
    oi=$o1 ol=$o2 cols=ol.o(oi.x[0]).size rows=oi.size
    if (rows==0 || cols==0) {printf("ERRA:a 0") return -1}
    a=allocvecs(v1,v2)
    vrsz(rows*cols,v1,"O") 
    for vtr(&x,oi) v1.append(ol.o(x))
    v1.transpose(cols) v1.mshuffle() v1.transpose() // shuffle in the other direction
    for vtr(&x,oi,&ii) ol.o(x).copy(v1,ii*cols,(ii+1)*cols-1)
    dealloc(a)
    return cols
  }

// sample(vec,beg,end,vals) pick out n integer values from given range
// sample(vec,end,vals) -- assumes 0
proc sample () { local min,max,vals
    min=0
    if (numarg()==4) {min=$2 max=$3 vals=$4} else {max=$2 vals=$3}
    $o1.indgen(min,max,1)
    shuffle($o1)
    $o1.resize(vals)
  }

// round() round off to nearest integer
func round () { local ii
    if (argtype(1)==1) {
        if ($o1.size==0) return 1e9
        for ii=0,$o1.size-1 {
            if ($o1.x[ii]>0) $o1.x[ii]=int($o1.x[ii]+0.5) else $o1.x[ii]=int($o1.x[ii]-0.5) 
          }
        return($o1.x[0])
      } else {
        if ($1>0) return int($1+0.5) else return int($1-0.5) 
      }
  } 

// filevers() pulls out version of file from first line
func filevers () { localobj f1,s1,lx1
    f1=new File() s1=new String() lx1=new Union()
    if (! f1.ropen($s1)) { printf("filevers ERR, can't open %s\n",$s1) 
        return 0 }
    f1.gets(s1.s)
    if (sscanf(s1.s,"%*s $Id: %*s %*d.%d",&lx1.x)!=1) {
        printf("filevers ERR, sscanf failed %s: %s",$s1,s1.s) }
    f1.close
    return lx1.x
  }

//* hocfind(FILENAME) searches through HOC_LIBRARY_PATH and locates file
obfunc hocfind () { local done localobj f1,s1,s2
    f1=new File() s1=new String() s2=new String()
    done=0
    system("echo -n $HOC_LIBRARY_PATH",s1.s)
    sprint(s1.s,"%s ",s1.s) // to look at last item
    while (sfunc.len(s1.s)>2) {
        sfunc.head(s1.s,"[ :]",s2.s)
        sprint(s2.s,"%s/%s",s2.s,$s1)
        if (f1.ropen(s2.s)) {done=1 break}
        sfunc.tail(s1.s,"[ :]",s1.s)
      }
    if (!done) if (f1.ropen($s1)) {sprint(s2.s,"./%s",$s1) done=1}
    if (!done) s2.s="NOT FOUND"
    return s2
  }

//* usefiles(F1[,F2,...]) list of files returns string with list of files and versions
obfunc usefiles () { local i localobj s1,s2
    s2=new String()
    s2.s="Using "
    for i=1,numarg() {
        s1=hocfind($si)
        sprint(s2.s,"%s %s%d",s2.s,$si,filevers(s1.s))
      }
    return s2
  }

//* ttest(v1,v2)  student t-test
// nrniv/sync/notebook.dol:16230
// checked against http://www.physics.csbsju.edu/stats/t-test_bulk_form.html
func ttest () {  local prob,df
    df=$o1.size+$o2.size-2
    t_val=($o1.mean-$o2.mean)/sqrt($o1.var/$o1.size + $o2.var/$o2.size)
    prob=betai_stats(0.5*df,0.5,df/(df+t_val*t_val))
    return prob
  }

// pttest() paired t-test
func pttest () {  local prob,sd,df,cov,j,ave1,ave2,var1,var2
    n=$o1.size ave1=$o1.mean ave2=$o2.mean var1=$o1.var var2=$o2.var cov=0
    if (n!=$o2.size) {printf("pttest ERR: != sizes\n",n,$o2.size) return -1}
    for (j=0;j<n;j+=1) cov+=($o1.x[j]-ave1)*($o2.x[j]-ave2)
    cov /= (df=n-1)
    sd=sqrt((var1+var2-2.0*cov)/n)
    t_val=(ave1-ave2)/sd
    prob=betai_stats(0.5*df,0.5,df/(df+t_val*t_val))
    // printf("%g ",t_val)
    return prob
  }

func howfull () { local a,min,max,bins,ret localobj v1
    min=0 bins=1e3
    if (numarg()==4){min=$2 max=$3 bins=$4} else if (numarg()==3){max=$2 bins=$3} else max=$2
    a=allocvecs(v1,bins)
    $o1.bin(v1,(max-min)/bins,min,max)
    ret=1-v1.count(0)/bins
    dealloc(a)
    return ret
  }

//* Matrix things
// msize() print size of a matrix
proc msize () { printf("%d x %d\n",$o1.nrow,$o1.ncol) }

//** l2m() turn a vector list into a matrix
obfunc l2m () { local ii,m,n localobj mat,l,xo
    l=$o1
    n=l.o(0).size m=l.count // rows x cols (data in cols)
    mat=new Matrix(n,m)
    for ltr(xo,l,&ii) mat.setcol(ii,xo)
    return mat
  }

//** m2l() turn a matrix into a vector list
obfunc m2l () { local ii,m,n localobj mat,l,xo
    l=new List() mat=$o1 m=mat.ncol n=mat.nrow
    for ii=0,m-1 l.append(mat.getcol(ii))
    return l
  }

//** reind(ind,filter0[,ifilter1,...]) goes through a series of vectors or values 
// to translate an index if filter is a vec then get vec.x[x] else get x+val
func reind () { local i,x
    x=$1 
    for i=2,numarg() if (argtype(i)==0) x+=$i else x=$oi.x[x]
    return x
  }

// rmxtmp() sets xtmp string
proc rmxtmp () { localobj st
    if (wopen("xtmp")) { 
        xtmp = "xtmp" 
      } else if (wopen("/tmp/xtmp")) {
        xtmp="/tmp/xtmp" 
      } else {  
        xtmp=strcat(1,getenv("HOME"),"/.xtmp").s 
        if (!wopen(xtmp)) printf("Can't open an xtmp file anywhere: . /tmp ~/ \n")
      }
    wopen() // close the file
  }
rmxtmp()

//* matrix functions using matrix.mod stuff
//** getdiag(matvec,destvec) 
proc getdiag () { local a localobj v1
    a=allocvecs(v1)
    v1.indgen(0,ROWS*COLS,COLS+1)
    $o2.index($o1,v1)
    dealloc(a)
  }

//** setdiag(matvec,value) 
proc setdiag () { local a localobj v1
    a=allocvecs(v1)
    v1.indgen(0,ROWS*COLS,COLS+1)
    $o1.indset(v1,$2)
    dealloc(a)
  }

func msetrc () { local x
    if ((x=sqrt($o1.size))==int(x)) ROWS=COLS=x else printf("Unable to set ROWS,COLS\n")
    return x
  }

for scase(XO,"o","t","s","O","T","S","+") clrsym.o.append(new String(XO.s))
// END /usr/site/nrniv/local/hoc/decvec.hoc
//================================================================

objref g[10]
gvmarkflag=0
declared("file_len")
strdef execstr,strform
strform="%s "

//* stubs for ancillary programs
double sops[21]  // AUGMENT TO ADD NEW OPSYM
declared("whvarg","whkey","varstr","nqsdel","chsel2","grsel2","oform")
proc oformoff () { execute1("func oform(){return NOP}") } // default no operation
proc oform64(){execute1("func oform(){{$o1.vpr(64,1)}return OK}")} // vec.vpr(64)
oformoff()
nqsselcp=1

//* NQS template
// potential to overwrite XO,tmpfile,i1
begintemplate NQS
public cob,out,up // operate on this or out 
public s,comment,file,v,m,x,ind,scr,fcd,fcds,fcdo,fcdv,fcdl,sstr // strings and vecs
public objl,verbose,tmplist,vlist,vl,vlc,nval,sval,oval,selcp,rxpstr,slorflag,stub,chunk,rdpiece
public sv,rd,append,pr,pri,prs,zvec,resize,size,fi,sets,set,setcol,setcols,gets,get,fetch,tog
public cp,copy,mo,aind,it,qt,ot,ut,vt,appi,eq,fcdseq,fcdoeq,sort,select,stat,map,apply,applf
public calc,pad,unpad,delect,fill,uniq,gr,clear,strdec,coddec,odec,fdec,vdec
public join,fillin,fillv,otl,selall
public unuselist,useslist,delrow,elimrepeats,grow,shuffle,fewind,listvecs,loose,rdcols,rdcols2
public percl,psel,svsetting,getrow,getcol,resize2,find,family,delcol,keepcols,selone
public sethdrs,gethdrs,version,svvers,i2,ay,svcols,svR,scpflag,unref,refs,qtset,keylook,cpout
public deriv,interval,renamecol,renamecols,info,getsel,tomat,frmat,marksym,vsort,hash,unnan
public cindx,mkind,cindcol,mset,mget,covarc,tomatc,noheader,csel,newval,rddif,rdatf,transpose
objref v[1],s[1],is[4],x,nil,ind,scr[3],fcd,fcds,fcdo,fcdv,fcdl,this,objl
objref cob,out,up,Xo,Yo,oval,tmplist,otl,vlist,vl,vlc,info,cindx

strdef comment,file,sstr,sstr2,sstr3,sstr4,tstr,sval,nqsvers,marksym
double m[1],refs[1]
external readnums,savenums,rdvstr,wrvstr,sfunc,repl_str,repl_mstr,isobj,rdmord,count_substr
external vlk,Union,String,tmpfile,strm,XO,execstr,i1,allocvecs,dealloc,mso,strform,dblform,tabform
external eqobj,isnum,chop,isassigned,whvarg,whkey,sops,batch_flag,g,varstr,gvmarkflag,split
external file_len,nqsdel,chsel2,grsel2,oform,nqsselcp,tmpobj,fln,fln_lnum

//** init()
proc init () { local i,ii,flag,ofl,scnt,na,fl,rdflag
    refs=-1 CODTY=10 FUNTY=11 VECTY=12 verbose=1
    noheader=nval=fl=scnt=ofl=flag=rdflag=ni=0 // flag set if creating the internal NQS
    selcp=nqsselcp
    svsetting=4 loose=1e-6
    for ii=2,3 is[ii]=new String()
    is[3].s="INDEX"  is[2].s="SCRATCH"
    na=numarg()
    for i=1,na scnt+=(argtype(i)==2) // string count
    if (na==0) scnt=-1
    if (na==1) if (argtype(1)==2) rdflag=1 else if (argtype(1)==1) rdflag=2
    if (na>=1) if (argtype(1)==0) {
        fl=1 // 1 arg taken care of
        if ($1<=-10) {
            flag=1 up=$o2 fl=2 ofl=-($1+10)
            m=up.m 
            if (ofl) {
                objref v[m]
                for ii=0,m-1 v[ii]=new Vector()
              } else if (m>0) {
                objref v[m],s[m]
                for ii=0,m-1 {v[ii]=new Vector() s[ii]=up.s[ii]}
              }
            fcd=up.fcd
            fcds=up.fcds fcdl=up.fcdl fcdo=up.fcdo fcdv=up.fcdv // finish creation of .out here
          } else if ($1<0) { // flag to create a large set of vectors with no labels and no .out
            fl=2 noheader=ofl=-$1 
            if (argtype(2)==0) m=$2 else m=$o2.count()
            objref v[m]
            for ii=0,m-1 v[ii]=new Vector() // no s[ii] strings
            if (argtype(2)==1) for ii=0,m-1 v[ii].copy($o2.o(ii))
          } else {
            m=$1
            objref v[m],s[m]
            for ii=0,m-1 { v[ii]=new Vector() s[ii]=new String2() }
          }
      }
    if (fl!=1 && na==scnt) { // all strings
        fl=2 // all args taken care of
        m=na
        objref v[m],s[m]
        for ii=0,m-1 {i=ii+1 v[ii]=new Vector() s[ii]=new String2($si) }
      }
    if (fl!=2 && na>=2) if (argtype(2)==0) { 
        fl==2  // all args taken care of
        for ii=0,m-1 v[ii].resize($2) 
      }
    if (fl!=2) { // if first arg is not a string these other can be
        if (na>=2) file=$s2
        if (na>=3) comment=$s3
        if (na>=4) x.x[0]=$4
      }
    if (!flag) { 
        // fcd gives field codes according to values used for argtype()
        fcds=new List() fcd=new Vector(m) tmplist=new List() vlist=new List()
        fcd.resize(m) fcd.fill(0) // field codes to have a field that's string based
      }
    x=new Vector(m) ind=x.c for ii=0,2 scr[ii]=x.c
    scr.resize(0) ind.resize(0)
    objl=new List() cob=this
    v0sz=slorflag=0
    qtset=0
    chunk=100
    info=new Union()
  nqsvers="=Id=  nqs.hoc,v 1.689 2012/10/04 01:41:57 billl Exp " svvers=-1
    if (!flag && ofl!=2) { 
        out=new NQS(-10-ofl,this)
        if (rdflag==1) rd($s1)
        if (rdflag==2) copy($o1)
      } 
    chk()
  }

// deallocate the attached nqs if destroyed
// after build NQS should have external pointer and out.up pointer +/- cob
// NB: 'this' is created and destroyed as needed
proc unref () {
    return
    // if (isassigned(out)) printf("AA:%d ",$1) else printf("BB:%d ",$1)
    if ($1<=refs) { // don't bother if have more than 2 refs or if currently building
        if (m>=0 && isassigned(out)) { // only do it on a live master nqs
            if ($1<2 || eqobj(cob,out.up)) { // means that only up are left
                m=-7  // indicate have started the process so don't reenter here
                printf("Entering destructor for %s: %d %d %s %s %s\n",out.up,$1,refs,cob,out,out.up)
                out.unref(-1) // take care of out first
              }
          }
      }
    if ($1==-1) { // for .out
        cob=nil
        up=nil
      } else if (m==-7) { // should only be done once
        m= -8
        // printf("Removal of %s on call %d\n",out.up,$1)
        if (isassigned(fcdo)) fcdo.remove_all
        if (isassigned(fcds)) fcds.remove_all
        if (isassigned(fcdv)) fcdv.resize(0)
        cob=nil
        up=nil
      }
  }


//** make sure there are no inconsistencies -- also set vl
func chk () { local ii,jj,ret
    ret=1
    if (out!=nil && !noheader) {
        for ii=0,m-2 for jj=ii+1,m-1 {
            if (sfunc.len(s[ii].s)>0 && strcmp(s[ii].s,s[jj].s)==0) {
                printf("NQS:chk ERRA: %s col: %s(%d) %s(%d) with same name\n",this,s[ii].s,ii,s[jj].s,jj)
                ret=0
              }
          }
      }
    listvecs(vl)
    return ret
  }

//** tog() toggle flag that determines whether actions are on out or this
func tog () { local ret
    if (eqobj(cob,out)) ret=20 else ret=10 // report old value
    if (numarg()==0) {
        if (eqobj(cob,out)) { cob=this if (verbose) print "Operate on full db" 
          } else {              cob=out  if (verbose) print "Operate on output of select" 
          }
      } else if (numarg()==1) {
        if (argtype(1)==0) {
            if ($1>=10) { // set 
                if ($1==10) cob=this else if ($1==20) cob=out else printf("tog ERRA:%d\n",$1)
              } else {  // just give information
                if (eqobj(cob,out)) { print "Using output db" 
                  } else {              print "Using full db"   }
              }
          } else if (argtype(1)==2) { // out,output,selected to choose these
            if (strm($s1,"[Oo][Uu][Tt]") || strm($s1,"[Ss][Ee][Ll]")) {
                cob=out 
              } else {
                cob=this
              }
          }
      }
    return ret
  }

//** sethdrs() set the column names to given args
// sethdrs(#,"NAME") sethdrs("NAME1","NAME2",...)  sethdrs(nq) -- copy from nq
proc sethdrs () { local i,nm
    nm=numarg()
    // out.s should always be a pointer to s but early on was keeping different copies:
    if (! eqobj(s,out.s)) printf("sets INTERRA\n")
    if (nm==2 && argtype(1)==0) { 
        s[$1].s=$s2 
      } else if (nm==1) {
        if ($o1.m!=m) resize($o1.m)
        for i=0,m-1 s[i].s=$o1.s[i].s
      } else {
        if (nm>m) { 
            if (batch_flag) {
                printf("NQS sets WARNING resized table from %d to %d\n",m,nm)
              } else if (! boolean_dialog("Resize TABLE?","YES","NO")) return
            printf("resizing TABLE: %d -> %d\n",m,nm) resize(nm) 
          }
        for i=1,nm { s[i-1].s=$si }
      }
  }

// gethdrs() print the strings
proc gets () { printf("gets() changed to gethdrs()\n") } 
proc gethdrs () { local ii,jj,kk,mm localobj o
    if (numarg()==1) {
        if ($1==-1) { // set the strings
            if (!batch_flag && sfunc.len(s[0].s)!=0) {
                printf("Overwrite headers for %s? (y/n) ",this)
                getstr(tstr) chop(tstr)
                if (strcmp(tstr,"y")!=0) return
              }
            o=new String("%s%c")
            for ii=0,m-1 {
                jj=ii%26 kk=int(ii/26)+1
                for mm=1,kk sprint(s[ii].s,o.s,s[ii].s,65+jj)
              }
          } else if ($1==1) { // show the types of fields
            for ii=0,m-1 printf("%s(%d) ",s[ii].s,fcd.x[ii])
          } else if ($1==2) { // just the names
            for ii=0,m-1 printf("%s ",s[ii].s)
          }
      } else {
        for ii=0,m-1 printf("%s(%d) ",s[ii].s,ii) // field numbers
      }
  }

//* selone(COL,VAL[,FLAG]) -- uses vec.slone when just working with one col and one value
func selone () { local val,niflag
    if (numarg()==3) niflag=$3 else niflag=0 // use if searching repeatedly through same vec
    tog("DB") // start at full db
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return
    val=$2
    // if (!v[fl].ismono) {printf("NQS selone: must sort on %s before using\n",s[fl].s) return -1}
    if (niflag) ni=ind.slone(v[fl],val,ni) else ni=ind.slone(v[fl],val)
    if (selcp) {
        if (ind.size==0) { 
            if (verbose) printf("None selected\n") 
          } else {
            out.ind.copy(ind) 
            aind()
            cob=out
          }
      } else cob=this
    return ind.size
  }
  
//* ay() is an n-dim associative array with p return values
// emulate a high-dim sparse array with optional string args, eg
// nq.ay("SU","IN",7,2,12).x // like nq_array["SU"]["IN"][7][2][12]
// an associative array could do same with non-numeric indices
// here if we have m cols can use any n of them as indices and rest are return values up to
// what a Union() can hold (2 strings, 2 objs, 2 doubles)
// if want to ignore one index can use OK as a globbing value
// for SET use an explicit set aa[5][4][7][2][12]=17 -> nq.ay(5,4,7,2,12,SET,17)
// select based on first n cols and always using EQU or SEQ
// gives more feel of an array -- assumes columns are IND0,IND1,...,VAL
// keys are SET to begin setting values, and OK to leave a value as is
// noninteger must be a set value
// eg XO=ncq.ay(SU,SU,AM,INC,SET,OK,List[35])
// alternate use with specific labels for things to be set eg
// eg XO=ncq.ay(SU,SU,AM,SET,"del",2.2,"wt")
obfunc ay () { local a,b,i,j,jo,k,na,flag,done,ix,nx,sx,ox,fl localobj key,arg,o
    if (numarg()==0) {
        printf("ay(I0,I1 ...[SET,V1,V2 ...])\n") return o }
    tog("DB") // start at full db
    a=allocvecs(key,arg)
    o=new Union() o.err=1 // assume .err set to return errors
    na=numarg()
    vlist.remove_all
    ind.resize(v.size)
    if (argtype(1)==1) for ii=0,$o1.size-1 {
        key.append(EQU) arg.append($o1.x[ii],0) vlist.append(v[ii])
        flag=0 j=b=$o1.size 
        if (numarg()>1) if ($2==SET) {i=3 flag=1}
      } else {
        for ({i=1 flag=0 done=0} ; i<=na && !flag; {i+=1 done=0}) {
            if (argtype(i)==2) {
                if (fi($si,"NOERR")!=-1) break else if (fcd.x[i-1]!=2) {
                    printf("ay ERRA: %d %d\n",fcd.x[i-1],argtype(i)) dealloc(a) return o
                  }
                for (j=0;j<fcds.count && !done;j+=1) if (strcmp(fcds.o(j).s,$si)==0) {
                    key.append(EQU) arg.append(j,0) vlist.append(v[i-1]) done=1
                  }
                if (!done) {printf("%s ay ERRE: %s not found in col %s\n",this,$si,s[i-1].s) 
                    dealloc(a) return o }
              } else if (argtype(i)==0) {
                if ($i==GET) { flag=2 // this is a value to start retrieval
                  } else if ($i==SET) { flag=1 // this is a value to set
                  } else if ($i!=OK) {  // ignore an OK
                    key.append(EQU) arg.append($i,0) vlist.append(v[i-1])
                  }
              }
          }
        b=i-1 // will begin again here -- these are v[] indices hence 0- statt 1-offset
        j=i-2 // have also had a SET arg to go behind
      }
    ind.slct(key,arg,vlist)
    if (ind.size>1) printf("%s ay WARNING: mult rows %d (using %d)\n",this,ind.size,ind.x[0])
    if (ind.size==0) { // printf("%s ay ERRBB: none selected\n",this)
        dealloc(a) return o }
    ix=ind.x[0] // just getting the first row of this
    if (i==na && argtype(i)==2) if ((fl=fi($si,"NOERR"))!=-1) { // return just 1 with col label
        j=getval(fl,v[fl].x[ix],ix)
        if (j==0) o.x=nval else if (j==1) o.o=oval else if (j==2) o.s=sval
        dealloc(a)
        o.err=0
        return o
      }
    for (;i<=na && flag==1;{i+=1 j+=1 jo=0}) { // set using the rest of the args
        if (argtype(i)==0) if ($i==OK) continue // don't set this one
        if (argtype(i)==2) {
            if (strcmp($si,"")==0) continue // don't set this one
            if ((k=fi($si,"NOERR"))!=-1) {jo=j j=k i+=1}
          }
        if (argtype(i)!=fcd.x[j]) {
            printf("%s ay ERRC: %d %d %d\n",this,i,fcd.x[j],argtype(i)) dealloc(a) return o }
        if (argtype(i)==0) { v[j].x[ix]=$i 
          } else if (argtype(i)==1) { set(j,ix,$oi) print $oi,ix
          } else if (argtype(i)==2) { set(j,ix,$si)
          } else {printf("%s ay ERRD: set %d not implemented\n",this,argtype(i)) dealloc(a) return o}
        if (jo) j-=1 // back up: perhaps can but would be a bad idea to mix setting stuff
      }
    nx=sx=ox=-1
    if (flag==2) {i=b if ((b=fi($si,"NOERR"))==-1) {
            printf("nqs:ay() GET ERR %s not found\n",$si) return o }}
    for (i=b;i<m;i+=1) { // return values -- get 10 of each
        j=getval(i,v[i].x[ix],ix)
        if (j==0) { 
            nx+=1 
            if (nx>=10) continue
            o.set(s[i].s,nval)
          } else if (j==1 || j==2) { 
            ox+=1 // string is handled as String obj
            if (ox>=10) continue
            if (j==2) o.set(s[i].s,sval) else o.set(s[i].s,oval)
          }
      }
    dealloc(a)
    o.err=0
    return o
  }

//* select() -- based loosely on SQL select
func select () { local ii,i,tmp,tmp1,ret,isv,key,arg,vc,selcpsav,savind,union,not,rxpflg localobj o
    if (numarg()==0) { out.cp(this,2) cob=out return v.size }
    tog("DB") // start at full db
    if (size(1)==-1) { printf("%s:select ERR0: cols not all same size\n",this) return -1 }
    // key holds OPs; arg holds ARGs; vc holds COL NAMEs
    key=arg=vc=allocvecs(3) arg+=1 vc+=2 // key is an operator, arg is args, vc is col#
    selcpsav=selcp i=1 not=rxpflg=union=savind=0
    tmplist.remove_all vlist.remove_all
    if (argtype(i)==0) if ($1==-1) {selcp=0  i+=1} // else is a number identifying a vector
    if (argtype(i)==2) { // check first string for &&, ||, !
        if (strcmp($si,"&&")==0) {        savind=1 union=0 i+=1 
          } else if (strcmp($si,"||")==0) { savind=1 union=1 i+=1 
          } else if (strcmp($si,"!")==0)  { savind=0  not=1 i+=1
          } else if (strcmp($si,"&&!")==0)  {savind=1 not=1 i+=1
          } else if (strcmp($si,"||!")==0)  {savind=1 union=1 not=1 i+=1 }
      } else if (argtype(i)==1) { i+=1
        if (argtype(i)==1 && argtype(i+1)==1) { // 3 vectors in a row are preset info for slct()
            if (numarg()!=3) { printf("%s:select ERR0: 3 vecs should be mso[key],mso[arg],cols\n",this) 
                dealloc(key) return -1 }
            if ($o1.size!=$o3.size || $o1.size*2!=$o2.size) {
                printf("%s:select ERR0c: size problem %d %d %d\n",this,$o1.size,$o2.size,$o3.size) 
                dealloc(key) return -1 }
            i=4 // have sucked up all the args
            mso[key].copy($o1) mso[arg].copy($o2)
            for ii=0,$o3.size-1 vlist.append(v[$o3.x[ii]])
          } else if (isobj($o1,"Vector")) { ind.copy($o1) savind=1 union=0 // assume &&
          } else { 
            printf("%s:select ERR0a: first vec obj should be ind vector\n",this) dealloc(key) return -1 }
      }
    if (savind) scr.copy(ind) else scr.resize(0)
  
    while (i<=numarg()) {
        if (argtype(i)==2) { 
            if (strcmp($si,"IND_")==0) {
                if ((vn=fi($si,"NOERR"))!=-3) { 
                    printf("NQS:select() WARNING: IND_ is a reserved word: ?%s\n",s[vn].s) }
                vn=-1e9  scr[1].indgen(0,v.size-1,1) tmplist.prepend(scr[1])
              } else if ((vn=fi($si))<0) { dealloc(key) return -1 }
            sstr=$si  // save for join: use with "NAME",EQW,OTHER_NQS
          } else if (argtype(i)==0) { vn=$i // can avoid repeated string search
            if (vn<0 || vn>=m) {
                printf("%s:select ERR0b: can't ident arg %d: %d\n",this,i,vn) dealloc(key) return -1}
            if (s[vn]!=nil) sstr=s[vn].s else sstr="UNDEFINED"
          } else {printf("%s:select ERR1: arg %d should be col name or num\n",this,i) dealloc(key) return -1}
        if (vn>=0) if (fcd.x[vn]==1) { 
            if (oform(fcdo.o(v[vn].x[0]))!=NOP) { // look at obj list
                scr[1].resize(0)      
                for ii=0,v[vn].size-1 scr[1].append(oform(fcdo.o(v[vn].x[ii])))
                vn=-1e9   tmplist.prepend(scr[1])
              } else {
                printf("NQS:select WARNING selecting on indices in an obj column: %d (?oform)\n",vn)
              }
          }
        mso[vc].append(vn)                        i+=1 
        if (argtype(i)==0) {
            if ((isv=isvarg($i))!=-1) { 
                lk=$i 
              } else { // arg2 is a regular number use "~"
                  mso[key].append(IBI) // approximately equal -- generate a range
                  tmp=$i-loose tmp1=$i+loose
                  if (tmp<tmp1) mso[arg].append(tmp,tmp1) else mso[arg].append(tmp1,tmp)
                  i+=1
                  continue
              }
          } else if (argtype(i)==2) { isv=isvarg(lk=whvarg($si))
            if (isv==-1) { 
                if (strcmp($si,"==")==0 || strcmp($si,"~")==0) {
                    mso[key].append(IBI) // approximately equal -- generate a range
                    i+=1
                    tmp=$i-loose tmp1=$i+loose
                    if (tmp<tmp1) mso[arg].append(tmp,tmp1) else mso[arg].append(tmp1,tmp)
                    i+=1
                    continue
                  } else {
                    printf("%s:select ERR1a: operator %s not recognized\n",this,$si) dealloc(key) return -1 
                  }
              }
          } else {
            printf("%s:select ERR2: arg %d/%d should be symbolic (eg GTE, EQU ...) or string (eg '[)','<=') op: %d\n",this,i,numarg(),argtype(i))
            dealloc(key) return -1 
          }
        mso[key].append(lk)                       i+=1
        // pick up ARGS
        for ii=0,isv-1   {  
            if (argtype(i)==0) { 
                if (lk==EQV) {
                    if ($i<0 || $i>=m) printf("ERRQ\n") else {
                        mso[arg].append(0)
                        mso[vc].append($i)
                      }
                  } else mso[arg].append($i)
                i+=1 
              } else if (argtype(i)==2) {
                if (lk==EQV) { // look for a column id
                    vn=fi($si) // OPSYM exception
                    if (vn==-1) { printf("%s:select ERR2a EQV but what col?\n",this) dealloc(key) return -1 }
                    mso[arg].append(0)
                    mso[vc].append(vn)                    i+=1
                  } else if (lk==SEQ) {
                    mso[key].x[mso[key].size-1]=EQU
                    if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
                    mso[arg].append(ret=finval(vn,argtype(i),lk))  i+=1
                  } else if (lk==RXP) {
                    mso[key].x[mso[key].size-1]=EQW
                    mso[arg].append(0)
                    if (argtype(i)!=2) {printf("%s:select ERR2a1\n",this) dealloc(key) return -1}
                    if (rxpflg==1) {printf("%s:select ERR2a2: RXP twice\n",this) dealloc(key) return -1}
                    ret=tmplist.prepend(scr[2])
                    if (rxpstr(vn,$si,scr[2])==0) {
                        printf("%s:select WARNING: No RXP matches for %s\n",this,$si) }
                    mso[vc].append(-1e9)                  i+=1
                  } else {printf("%s:select ERR2b string arg needs EQV,SEQ or RXP?\n",this)
                    dealloc(key) return -1}
              } else if (argtype(i)==1) { 
                if (lk>=EQW && lk<=EQY) { // pick up a vector
                    if (isobj($oi,"Vector")) {
                        if ($oi.size==0) {printf("%s:select ERR2a3: EQ[WXY] with empty vec %s\n",this,$oi) 
                            dealloc(key) return -1}
                        mso[arg].append(0)
                        mso[vc].append(-i)                    i+=1
                      } else if (isobj($oi,"NQS")) {
                        mso[arg].append(0)
                        if ((tmp=$oi.fi(sstr,"NOERR"))!=-1) { // JOIN with output from other nqs
                            tmplist.prepend($oi.out.v[tmp])
                          } else {
                            o=$oi                               i+=1
                            if ((tmp=o.fi($si))==-1){printf("%s:select ERR2c: can't find %s in %s?\n",this,$si,o)
                                dealloc(key) return -1 }
                            tmplist.prepend(o.out.v[tmp])
                          }
                        mso[vc].append(-1e9)                  i+=1
                      } else { printf("%s:select ERR2c1: EQ[WXY] needs Vec or NQS not %s?\n",this,$oi)
                        dealloc(key) return -1
                      }
                  } else { printf("%s:select ERR2d only EQ[WXY] takes obj arg: %d:%d?\n",this,i,argtype(i))
                    dealloc(key) return -1}
              } else {
                whkey(lk,sstr) printf("%s:select ERR3 arg %d should be arg for %s",this,i,sstr) 
                dealloc(key) return -1
              }
          }
        // args in wrong order - swap
        if (isv==2) if (mso[arg].x[mso[arg].size-2]>mso[arg].x[mso[arg].size-1]) {
            tmp=mso[arg].x[mso[arg].size-2]
            mso[arg].x[mso[arg].size-2]=mso[arg].x[mso[arg].size-1]
            mso[arg].x[mso[arg].size-1]=tmp
          }
        // pad so every OP sees 2 ARGS
        for ii=0,2-isv-1 { mso[arg].append(0) } 
      }
    ind.resize(v.size)
  
    for ii=0,mso[vc].size-1 { vn=mso[vc].x[ii] 
        if (vn==-1e9) { // code for EQW case with NQS arg
            vlist.append(tmplist.object(tmplist.count-1)) 
            tmplist.remove(tmplist.count-1) // pop
          } else if (vn<0) { i=-vn // code for EQV case where vector is in the arg list
            vlist.append($oi) 
          } else vlist.append(v[vn]) 
      }
    if (tmplist.count!=0) { printf("NQS:select ERR5 %s.tmplist not empty\n",this) return -1 }
  
    if (slorflag) { ind.slor(mso[key],mso[arg],vlist)
      } else        { ind.slct(mso[key],mso[arg],vlist) }
    if (verbose==2) keylook(key) // look at the keys
    if (not==1) complement() // ind->!ind
    if (savind) { 
        if (union==1) {
            scr.append(ind) scr.sort ind.resize(scr.size+ind.size)
            ind.redundout(scr)
          } else {
            mso[key].resize(scr.size+ind.size)
            mso[key].insct(scr,ind) ind.copy(mso[key]) }
      }
    ret=ind.size
    if (selcp) {
        out.ind.copy(ind) 
        if (ind.size==0) { 
            if (verbose) printf("None selected\n") 
          } else {
            aind()
            cob=out
          }
      } else cob=this
    dealloc(key)
    selcp=selcpsav
    slorflag=0
    return ret
  }

//** keylook()
proc keylook () { local key,arg,vc,ii
    if (numarg()==0) key=0 else key=$1
    arg=key+1 vc=key+2
    printf("slct(keys,args,cols)\n")
    for ii=0,mso[key].size-1 {
        whkey(mso[key].x[ii],tstr)
        for jj=0,m-1 if (eqobj(v[jj],vlist.o(ii))) break
        if (jj==m) jj=-1
        printf("KEY: %s; ARGS: %g %g; COL: %d (%s)\n",\
               tstr,mso[arg].x[2*ii],mso[arg].x[2*ii+1],jj,vlist.o(ii))
      } // vlk(mso[key]) vlk(mso[arg])
  }

//** selall()
proc selall () { local ii
    if (numarg()==2) {
        for ii=0,m-1 out.v[ii].where(v[ii],$s1,$2)
      } else {
        for ii=0,m-1 out.v[ii].where(v[ii],$s1,$2,$3)
      }
    tog("SEL")
  }

// csel() puts col#s in ind
// csel(colname) select by column header
// csel("OP",arg1[,arg2]) runs oform() on each col then runs OP() on result
// eg with oform(){return $o1.count(0)} then csel("==",3) finds cols with 3 zeros
func csel () { local a,ii localobj v1
    a=allocvecs(v1,m)
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    if (numarg()==1) {
        for ii=0,m-1 if (strm(s[ii].s,$s1)) v1.append(ii)
        ind.copy(v1)
      } else {
        for ii=0,m-1 v1.append(oform(cob.v[ii]))
        if (numarg()==2) { ind.indvwhere(v1,$s1,$2)
          } else           { ind.indvwhere(v1,$s1,$2,$3) }
      }
    dealloc(a)
    return ind.size
  }

//** complement() ind -> !ind
proc complement () { local a,b
    a=b=allocvecs(2) b+=1
    mso[a].indgen(0,size(1)-1,1)
    mso[b].resize(mso[a].size)
    mso[b].cull(mso[a],ind)
    ind.copy(mso[b])
    dealloc(a)
  }

//** delect([NQS])
// move the selected rows from the out db [or other] back to the main db
// the assumption is that you have operated on some of the fields and now want to
//      put the rows back
// ind must not have been altered since it will be used to replace the items
func delect () { local beg,ii,flag
    scr.resize(v.size)
    if (numarg()==1) flag=1 else flag=0
    if (flag) { 
        if (m!=$o1.m){ 
            printf("NQS:delect ERRa m mismatch: %s:%d vs %s:%d\n",this,m,$o1,$o1.m) return -1 }
        ind.copy($o1.ind)
      } else if (out.ind.size==0) { return 0
      } else if (!out.ind.eq(ind) || ind.size!=out.v.size) {
        printf("NQS:delect ERR ind size mismatch\n") 
        return -1 
      }
    for (beg=0;beg<m;beg+=50) { // sindx() can only handle vecst.mod:VRRY vecs at a time
        tmplist.remove_all vlist.remove_all
        for ii=beg,beg+49 if (ii<m) tmplist.append(v[ii])
        for ii=beg,beg+49 if (ii<m) if (flag) { 
            vlist.append($o1.v[ii]) 
          } else {
            vlist.append(out.v[ii]) 
          }
        ind.sindx(tmplist,vlist)
      }      
    cob=this
    return ind.size
  }  
  
//** isvarg() returns number of args an op takes or -1 if not symbolic OP
func isvarg () { local m,op // ADD NEW OPSYM CHECK
    op=$1
    for m=0,5 if (op<=EBE*(m+1) && op>=ALL*(m+1)) { op/=(m+1) break } // m is is field key 1-5
    if (op<ALL) return -1 else if (op<GTH) return 0 else if (op<IBE) { return 1 
      } else if (op<=EBE) return 2 else return -1 
  }

//** fi(STR[,XO]) find the index for a particular string, can set a objref
//  fi(STR,INDEX) return INDEXed value from that vector
//  fi(STR,"NOERR")  suppress error message
//  fi(STR,"EXACT")  string match
//  fi(STR,"ALL") return vector of all indices that match regexp
func fi () { local num,flag,ii,ret,err,ext
    if (refs==-1) if (isassigned(out)) { // calculate refs
        // refs=sfunc.references(this,1)-1 // make sure 'this' is turned on
        // printf("%d refs\n",refs)
      }
    ext=noerr=err=num=flag=all=0
    if (numarg()>=2) if (argtype(2)==2) {
        if (strcmp($s2,"NOERR")==0) noerr=1 // use "NOERR" string
        if (strcmp($s2,"EXACT")==0) ext=1 // string match statt regexp
        if (strcmp($s2,"ALL")==0) {all=1 $o3.resize(0)} // all regexp matches
      }
    for ii=0,m-1 if (strcmp(s[ii].s,$s1)==0) {flag=1 ret=ii break} // exact match
    if (ext) if (flag) return ret else return -1
    if (strcmp($s1,"SCR_")==0) {flag=1 ret=-2}
    if (strcmp($s1,"IND_")==0) {flag=1 ret=-3}
    if (!flag) for ii=0,m-1 { // make sure $s1 could be a regexp to avoid regexp error
        if (sfunc.len($s1)<sfunc.len(s[ii].s) && !strm($s1,"[()]")) if (strm(s[ii].s,$s1)) { 
            if (num>=1) {
                if (all) $o3.append(ii) else {
                    err=1 
                    printf("%s fi ERR: regexp matches more than once: %d %s\n",this,ii,s[ii].s)
                  }
              } else {
                if (all) $o3.append(ii)
                num+=1 ret=ii flag=1
              }
          }
      }
    if (err) printf("NQS WARNING; ambiguous regexp; fi() returning pointer for: %d %s\n",ret,s[ret].s)
    if (flag) {
        if (numarg()==2 && noerr==0) { 
            if        (argtype(2)==1) { 
                if (ret==-2) $o2=scr else if (ret==-3) {printf("%s:fi ERRa copy what?\n",this) return ret
                  } else $o2=v[ret] 
              } else if (argtype(2)==0) { 
                if ($2<0 || $2>=v[ret].size) { 
                    printf("%s:fi ERR index out of bounds: %d %d\n",this,$2,v[ret].size) 
                    return -1
                  }
                if (ret==-2) ret=scr.x[$2] else if (ret==-3) {printf("NQS:fi ERRb what?\n") return ret
                  } else ret=v[ret].x[$2]
              } else                    { printf("%s:fi WARNING 2nd arg ignored\n",this) }
          }
        return ret
      } else {
        if (!noerr) printf("%s.fi() ERR '%s' not found\n",this,$s1)
        return -1
      }
  }

//** find(STR) find the vector associated with a COL label
obfunc find () { local fl
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    fl=fi($s1)
    if (fl==-2) { return scr 
      } else if (fl==-3) { return ind
      } else return cob.v[fl]
  }

//** mkind(COL) sort by COL and then use mkind to put index of a single col in cindx vector
obfunc mkind () { local fl
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    if (cindx==nil) cindx=new Vector(1e3)
    if (argtype(1)==0) fl=$1 else fl=fi($s1)
    cindcol=fl
    sort(fl)
    v[fl].mkind(cindx)
    if (argtype(2)==1) $o2.copy(cindx)
    return cindx
  }

//** set("name",IND,VAL)
proc set () { local fl,ix,sel
    sel=0
    if (eqojt(cob,out)) { sel=1
        if (verbose) printf("NQS set() WARNING: setting value in Selected db\n") }
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    ix=$2 
    if (fl==-1) return
    if (ix<0) ix=cob.v[fl].size+ix
    // 2 LINE 'SET' MACRO
    if        (ix> cob.v[fl].size) {  // nonexistent row
        printf("%s set ERRA: col %s size %d<%d\n",this,s[fl].s,v[fl].size,ix) return
      } else if (ix==cob.v[fl].size) {  // single col expansion
        if (sel) {printf("%s set() ERR: can't expand Selected db\n",this) return}
        cob.v[fl].resize(ix+1)
      }
    if (argtype(3)==0) { cob.v[fl].x[ix]=$3
      } else {
        if (argtype(3)==1) oval=$o3 else if (argtype(3)==2) sval=$s3
        cob.v[fl].x[ix]=newval(argtype(3),fl)
      }
  }

//** sets(IND,COLA,VAL[,COLB,VAL,...])
proc sets () { local fl,ix,i,sel,sz
    sel=0 ix=$1
    if (eqojt(cob,out)) { sel=1
        if (verbose) printf("NQS set() WARNING: setting value in Selected db\n") }
    if (ix>=cob.v.size){
        printf("NQS sets ERRA: OOB %s: %d (size %d)\n",this,v[0].size,cob.v.size) return }
    if (ix<0) ix=cob.v.size+ix
    for i=2,numarg() {
        if (argtype(i)==2) fl=fi($si) else fl=$i
        if (fl==-1) return
        i+=1 
        if (argtype(i)==0) { cob.v[fl].x[ix]=$i // shortcut
          } else { 
            if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
            cob.v[fl].x[ix]=newval(argtype(i),fl)
          }
      }
  }

//** setcol("name",VEC)
//   setcol(num,VEC)
//   setcol(num,"name",VEC)
//   setcol(num,"name",VEC,flag) // with flag==1 use pointer to vec instead of copying
scpflag=0
proc setcol () { local fl,flag localobj vo
    if (eqobj(cob,out) && verbose) { 
        printf("%s setcol() ERR: attempting to set column in Selected db\n",this)
        return  }
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return
    if (v[fl].size!=0) {
        sprint(sstr,"WARNING %s col not empty (size %d)",s[fl].s,v[fl].size)
        if (boolean_dialog(sstr,"Clear col","Cancel")) { v[fl].resize(0) } else {
            printf("%s (%s) setcol() canceled\n",this,s[fl].s) return  
          }}
    if (argtype(2)==2) { s[fl].s=$s2 
      } else if (argtype(2)==1) { 
        if (scpflag) v[fl].copy($o2) else v[fl]=$o2
        return 
      } else { sprint(tstr,"%d",$2) s[fl].s=tstr }
    if (numarg()>=3) vo=$o3
    if (numarg()>=4) flag=$4 else flag=0
    if (!flag || scpflag) v[fl].copy(vo) else v[fl]=vo
    chk()
  }

//** setcols(VEC1,VEC2,...) -- does either pointer or copy depending on scpflag
// setcols(LIST) -- does either pointer or copy depending on scpflag
// see also resize("NAME",vec, ...) for similar functionality
proc setcols () { local i,na,flag,sz
    sz=na=numarg() flag=0
    if (na==0) { scpflag=1-scpflag
        if (scpflag) printf("setcols() will copy vecs\n") else {
            printf("setcols() will use vec pointers\n") }
        return
      }
    if (na==1 && isobj($o1,"List")) {flag=1 sz=$o1.count}
    if (m==0) resize(sz)
    if (eqobj(cob,out) && verbose) { 
        printf("%s setcols() ERR: attempting to set column in Selected db\n",this)
        return  }
    if (!flag && na!=m) {
        printf("%s setcols() ERR: need %d not %d args\n",this,m,na)
        return  }
    if (flag) {
             if (scpflag) for i=0,m-1 v[i].copy($o1.o(i)) else for i=0,m-1 v[i]=$o1.o(i)
      } else if (scpflag) for i=1,m v[i-1].copy($oi)      else for i=1,m v[i-1]=$oi
    chk()
  }

//** newval(typ,col#) -- check if a value is already on the list and if not put it there
// usuall preceded by eg:
// if (argtype(i)==0) nval=$i else if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
// NB: makes a copy of a Vector or an NQS so DON'T do eg nq.append(new Vector())
func newval () { local ret,typ,ty,fl,ii localobj o
    typ=$1 fl=$2 ty=fcd.x[fl] // arg type may not be same as field type
    if (ty==typ||ty==-1||(ty==FUNTY&&typ==2)||(ty==VECTY&&typ==1)) { // OK
      } else { printf("nqs::newval() ERRa %d statt %d\n",typ,ty) return ERR }
    if (typ==0 || ty==-1) { 
        return nval
      } else if (ty==1) { // object handling
        if (! isassigned(oval)) return -1
        if (isojt(oval,v)) { o=new Vector(oval.size) o.copy(oval) 
          } else if (isojt(oval,this)) { o=new NQS() o.cp(oval) 
          } else {
            for (ii=0;ii<fcdo.count;ii+=1) if (eqojt(fcdo.object(ii),oval)) { // already on list?
                printf("nqs %s WARNING: 2nd pointer to same %s on fcdo\n",this,oval)
                return ii
              }
            o=oval      
          }
        return fcdo.append(o)-1
      } else if (ty==FUNTY) { // method handling
        for ({ii=0 ret=-1};ii<fcds.count;ii+=1) { 
            if (strcmp(fcds.object(ii).s,sval)==0) {ret=ii break}
          }
        if (ret==-1) ret=fcds.append(new String(sval))-1
        o=new Union(ret) // .x is pointer to the string
        return fcdo.append(o)-1
      } else if (ty==VECTY) { // append to a vector
        if (!isojt(oval,v)) { printf("nqs:newval ERRB -- put vectors in vdec col\n") return ERR }
        ret=fcdv.size()
        fcdv.append(oval)
        return ret
      } else if (ty==2) { // string handling
        for (ii=0;ii<fcds.count;ii+=1) { 
            Xo=fcds.object(ii)
            if (strcmp(Xo.s,sval)==0) return ii
          }
        return fcds.append(new String(sval))-1
      }
  }

//*** finval(col#,type,OP) find the location on list for an object or string
func finval () { local fl,typ,op,ii,ret
    fl=$1 typ=$2 op=$3 ret=-1
    if (fcd.x[fl]!=typ) { // doesn't handle fcd.x[]==-1
        printf("nqs::finval ERRa type mismatch; %d %d\n",fcd.x[fl],typ) return ERR }
    for ii=0,fcds.count-1 { Xo=fcds.object(ii)
        if (typ==2) { 
            if (strcmp(Xo.s,sval)==0) return ii
          } else {}
      }
    // if (ret==-1) printf("nqs::finval WARNING %s not found in string or object list\n",sval)
    return ret
  }

//*** rxpstr(col#,vec) find the location on list for an object
func rxpstr () { local fl
    fl=$1 $o3.resize(0)
    if (fcd.x[fl]!=2) {
        printf("nqs::rxpstr ERRa type mismatch; %d %d\n",fcd.x[fl],2) return -1 }
    for ii=0,fcds.count-1 if (strm(fcds.object(ii).s,$s2)) $o3.append(ii)
    return $o3.size
  }

//*** getval(col#,index) return type and value in nval,oval,sval as appropriate
// usually followed by eg
// if (typ==0) ... nval else if (typ==1) ... oval else if (typ==2) ... sval
func getval () { local typ,n,flag,fl,ix,ii,ed,nacc
    fl=$1 ix=$2 flag=0
    typ=fcd.x[fl] // argtype
    if (typ==0) { 
        nval=ix
      } else if (typ==CODTY) { 
        if (numarg()==3) nval=uncodf($3,ix) else {scr.resize(5) scr.uncode(ix)}
      } else if (typ==VECTY) { 
        if (ix>fcdv.size-1) { printf("nqs::getval() ERR fcdv index OOB %d, %d\n",ix,fcdv.size) 
            return ERR 
          } else if (ix<0) { printf("nqs::getval() WARNING empty VECTY ptr\n\t") sval="nil" typ=2
          } else {
            nacc=$3 // direct index only needed here
            if (nacc+1<cob.v[fl].size) ed=cob.v[fl].x[nacc+1]-1 else ed=fcdv.size-1
            oval = fcdv.c(ix,ed)
          }
      } else if (typ==1) { // object handling
        if (ix>fcdo.count-1) {
            printf("nqs::getval() ERR fcdo index OOB %d, %d\n",ix,fcdo.count) return ERR 
          } else if (ix<0) {
            // printf("nqs::getval() WARNING empty obj ptr\n\t")
            sval="nil"
            typ=2
          } else oval = fcdo.object(ix)
      } else if (typ==2) { // string handling
        if (ix==-1) {
            sval="NULL"
          } else if (ix<0 || ix>fcds.count-1) {
            printf("nqs::getval() ERR index OOB %d, %d\n",ix,fcds.count) return ERR 
          } else sval=fcds.object(ix).s
      } else if (typ==-1) { // string from external list
        if (fcdl.count<=fl) {printf("%s getval ERRa\n",this) return -1}
        if (! isobj(fcdl.object(fl),"List")) {printf("%s getval ERRb\n",this) return -1}
        if (fcdl.object(fl).count<=ix) {printf("%s getval ERRc\n",this) return -1}
        if (ix==-1) sval="XX" else {
            if (!isobj(fcdl.object(fl).object(ix),"String")){printf("%s getval ERRd\n",this) return -1}
            sval=fcdl.object(fl).object(ix).s
          }
      }
    return typ
  }

//*** useslist() connects a list of strings to fcdl to use when printing
// fcdl: list of lists to make it easy to attach lists from outside
proc useslist () { local fl,ii
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return
    if (! isobj(fcdl,"List")) {fcdl=new List() out.fcdl=fcdl}
    if (fcdl.count!=m) for ii=fcdl.count,m-1 fcdl.append(fcdl) // use fcdl as placeholder
    fcdl.remove(fl) fcdl.insrt(fl,$o2)  // replace:fcdl.object(fl)=$o2
    fcd.x[fl]=-1
  }

//*** unuselist() connects a list of strings to fcdl to use when printing
// fcdl: list of lists to make it easy to attach lists from outside
proc unuselist () { local fl,ii
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return
    fcd.x[fl]=0
  }

//*** listvecs([LIST]) put the vecs in the list for use with eg uncode()
obfunc listvecs () { local ii,i,b localobj ol
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    if (argtype(1)==1) {ol=$o1 b=2} else b=1
    if (!isassigned(ol)) { 
        ol=new List()
        if (argtype(1)==1) $o1=ol
      }
    ol.remove_all
    if (numarg()>=b) {
        for i=b,numarg() {
            if (argtype(i)==2) ii=fi($si) else ii=$i
            if (ii==-1) return
            ol.append(cob.v[ii])
          }
      } else {
        for ii=0,m-1 ol.append(cob.v[ii])
      }
    return ol
  }

//*** hash([COLA],[COLB] etc.) put the vecs in the list for use with eg uncode()
proc hash () { local ii,i,fl localobj o
    if (eqobj(cob,out) && verbose) {
        printf("hash() ERR: can't create hash col in 'Selected'\n") return }
    o=new List()
    if (numarg()==0) { // do all columns
        resize("hashall")
        for ii=0,m-2 o.append(v[ii])
      } else {
        tstr="hash"
        for i=1,numarg() {
            if (argtype(i)==0) fl=$i else fl=fi($si)
            if (fl==-1) return
            sprint(tstr,"%s_%s",tstr,s[fl].s)
            o.append(v[fl])
          }
        resize(tstr)
      }
    pad()
    v[m-1].hash(o)
  }

//*** mat=tomat([MAT]) put the cols in cols of matrix
// mat=tomat(MAT,1) or mat=tomat(1) puts the cols in rows of matrix
obfunc tomat () { local ii,transpose,fo,sz localobj mat
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    fo=transpose=0 sz=size(1)
    if (numarg()>=1) {
        if (argtype(1)==0) transpose=$1 else {mat=$o1 fo=1}
      } 
    if (numarg()>=2) transpose=$2
    if (!isassigned(mat)) { 
        if (transpose) mat=new Matrix(m,sz) else mat=new Matrix(sz,m)
        if (fo) $o1=mat
      } else {
        if (transpose) mat.resize(m,sz) else mat.resize(sz,m)
      }
    if (transpose) {for ii=0,m-1 mat.setrow(ii,cob.v[ii])
      } else          for ii=0,m-1 mat.setcol(ii,cob.v[ii])
    return mat
  }

//*** frmat(MAT) gets cols from cols of matrix
// frmat(MAT,1)  gets the cols from rows of matrix
proc frmat () { local ii,transpose,rows,cols localobj mat
    if (eqobj(cob,out)) {printf("frmat() ERR cannot reset 'Selected' to matrix\n") return}
    fo=transpose=0
    mat=$o1
    if (numarg()>=2) transpose=$2
    rows=mat.nrow cols=mat.ncol
    if (transpose) {
        if (cols!=size(1)) pad(cols)
        if (rows!=m) resize(rows)
        for ii=0,m-1 mat.getrow(ii,cob.v[ii])
      } else {
        if (rows!=size(1)) pad(rows)
        if (cols!=m) resize(cols)
        for ii=0,m-1 mat.getcol(ii,cob.v[ii])
      }
  }

//*** newnqs=transpose() create a new nqs that is the transpose of this one
obfunc transpose () { localobj mat,m2,oq
    mat=tomat()
    oq=new NQS()
    oq.frmat(mat,1)
    return oq
  }

//*** prtval() use %g or %s to print values
proc prtval () { local i,typ,flag,otmp localobj f1
    if (argtype(1)==0) {typ=$1 flag=0 i=2} else {f1=$o1 flag=1 typ=$2 i=3}
    // oform() returns a double for printing an object
    if ((typ==1 || typ==VECTY) && isassigned(oval)) {
        otmp=oform(oval)
        if (otmp==NOP) {       typ=1
          } else if (otmp==OK) { return // print nothing since being printed as side effect
          } else {               typ=0 nval=otmp // print a single numeric value
          }
      }
    if (typ==0) sstr=dblform else sstr=strform
    if (numarg()==i) { 
        sprint(sstr,"%s%s",sstr,$si)
      } else if (numarg()==i+1) {
        sprint(sstr,"%s%s",$si,sstr) i+=1 sprint(sstr,"%s%s",sstr,$si)
      }
    if (flag) {
             if (typ==0) { f1.printf(sstr,nval)
        } else if (typ==1) { f1.printf(sstr,oval) 
        } else if (typ==2) { f1.printf(sstr,sval) 
        } else if (typ==10) { for ii=0,4 f1.printf("%d ",scr.x[ii])
        } else if (typ==-1) { f1.printf(sstr,sval) } // special code for externally provided list
      } else {
             if (typ==0) { printf(sstr,nval)
        } else if (typ==1) { printf(sstr,oval) 
        } else if (typ==2) { printf(sstr,sval) 
        } else if (typ==10) { for ii=0,4 printf("%d ",scr.x[ii])
        } else if (typ==-1) { printf(sstr,sval) } // special code for externally provided list
      }
  }

//** get("name",[IND]]) if omit IND take ind from first ind.x[0]
obfunc get () { local ty,fl,ix,outf,be,en localobj lo
    outf=0
    if (argtype(1)==0) { fl=$1 sstr2=s[fl].s
      } else if (argtype(1)==2) { fl=fi($s1) sstr2=$s1 }
    if (fl==-1) { return lo }
    if (eqobj(cob,out)) { outf=1 
        if (verbose) printf(" *Selected* ") }
    if (numarg()==1) {
        if (outf) ix=0 else ix=ind.x[0]
      } else ix=$2
    if (ix<0 || ix>=cob.v[fl].size) {
        printf("%s::get ERR ix %d out of range for %s (%s)\n",this,ix,sstr2,cob) return lo }
    ty=fcd.x[fl]
    if (ty==0) {lo=new Union(cob.v[fl].x[ix]) if (numarg()==3) $&3=lo.x
      } else if (ty==1) {lo=new Union(fcdo,cob.v[fl].x[ix]) if (numarg()==3) $o3=lo.o
      } else if (ty==FUNTY) {lo=methget(fl,cob.v[fl].x[ix])  if (numarg()==3) $o3=lo.o
      } else if (ty==VECTY) { be=cob.v[fl].x[ix] 
        if (ix+1<cob.v[fl].size) en=cob.v[fl].x[ix+1]-1 else en=cob.v[fl].size-1
        lo=new Union(fcdv.c(be,en)) if (numarg()==3) $o3=lo.o
      } else if (ty==2) {lo=new Union(fcds,cob.v[fl].x[ix])  if (numarg()==3) $s3=lo.s
      } else if (ty==-1){lo=new Union(fcdl.object(fl),cob.v[fl].x[ix]) if (numarg()==3) $o3=lo.o}
    return lo
  }

//** mget(row,col) 
obfunc mget () { local ty,fl,ix,outf localobj lo
    outf=0
    ix=$1 fl=$2 
    if (ix<0 || ix>=cob.v[fl].size) {printf("%s::get ERR no %d,%d\n",this,fl,ix) return lo}
    ty=fcd.x[fl]
    if (ty==0) {lo=new Union(cob.v[fl].x[ix]) if (numarg()==3) $&3=lo.x
      } else if (ty==1) {lo=new Union(fcdo,cob.v[fl].x[ix]) if (numarg()==3) $o3=lo.o
      } else if (ty==2) {lo=new Union(fcds,cob.v[fl].x[ix])  if (numarg()==3) $s3=lo.s
      } else if (ty==FUNTY) {lo=methget(fl,cob.v[fl].x[ix])  if (numarg()==3) $o3=lo.o
      } else if (ty==-1){lo=new Union(fcdl.object(fl),cob.v[fl].x[ix]) if (numarg()==3) $o3=lo.o}
    return lo
  }

//** mset(row,col) 
proc mset () { local ty,fl,ix,outf localobj lo
    outf=0
    ix=$1 fl=$2 
    if (ix<0 || ix>=cob.v[fl].size) {printf("%s::get ERR no %d,%d\n",this,fl,ix) return}
    ty=fcd.x[fl]
    if (ty==0) cob.v[fl].x[ix]=$3 else \
    if (ty==1) fcds.o(cob.v[fl].x[ix])=$o3 else \
    if (ty==2) fcds.o(cob.v[fl].x[ix]).s=$s3 else \
    if (ty==-1)fcdl.object(fl).o(cob.v[fl].x[ix])=$o3
  }

//** fetch(COLA,VAL,COLB) does fast select where COLA is VAL and returns value in COLB
// fetch(COLA,VAL) return the row number
// fetch(COLA,VAL,COLB,XO) places COLB object in XO
// ambiguity -- if 1,3 args can fetch from full db or selected else must do select first
func fetch () { local fl1,fl2,max,i localobj st,v0
    if (eqobj(cob,out)) if (verbose) printf(" *Selected* ")
    if (numarg()==1) {
        if (argtype(1)==2) fl1=fi($s1) else fl1=$1
        return cob.v[fl1].x[0]
      } else if (numarg()==2) { // return the index
        if (argtype(1)==2) fl1=fi($s1) else fl1=$1
        if ((i=cob.v[fl1].indwhere("==",$2))<0){
            printf("fetch ERR %d not found in %s\n",$2,s[fl1].s) return -1 }
        return i
      } else if (numarg()==3 || argtype(4)==1) {
        if (argtype(1)==2) fl1=fi($s1) else fl1=$1
        if (argtype(3)==2) fl2=fi($s3) else if (argtype(3)==0) fl2=$3 else {v0=$o3 fl2=-1}
        if ((i=cob.v[fl1].indwhere("==",$2))<0) {
            printf("fetch ERR %d not found in %s\n",$2,s[fl1].s) return -1 }
        if (fl2>=0) {
            if (numarg()==4) $o4=fcdo.o(cob.v[fl2].x[i])
            return cob.v[fl2].x[i] 
          } else { // return row as a vector
            v0.resize(0) for (ii=0;ii<m;ii+=1) v0.append(cob.v[ii].x[i])
            return cob.v[0].x[i]
          }
      } else {
        st=new String("select(-1,")
        for i=1,numarg()-1 {
            if (argtype(i)==0) sprint(st.s,"%s%g,",st.s,$i) else sprint(st.s,"%s\"%s\",",st.s,$si)
          }
        chop(st.s) sprint(st.s,"%s)",st.s)
        execute(st.s,this)
        i=numarg()
        if (argtype(i)==0) fl2=$i else fl2=fi($si)
        if (ind.size!=1 && verbose) printf("NQS fetch WARNING -- %d lines found\n",ind.size)
        if (ind.size>0) return v[fl2].x[ind.x[0]] else return ERR
      }
  }

//** stat("COL","operation")
//   stat("COL",VEC) // save into a vector: max,min,mean,sdev
//   stat(NQS) // save all of them into another NQS
proc stat () { local i,vn
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (numarg()==0) {
        for i=0,m-1 { 
            if (noheader) printf("%d:\t",i) else printf("%s:\t",s[i].s) 
            stat(i) // recursive call
          }
        return
      }
    if (argtype(1)==1) {
        $o1.resize(5)
        $o1.sethdrs("NAME","MAX","MIN","MEAN","SDEV") 
        $o1.strdec("NAME")
        $o1.clear
        for i=0,m-1 { 
            stat(i,scr[1]) // recursive call
            $o1.append(scr[1],1)
            $o1.set(0,i,s[i].s)
          }
        return
      }
    if (argtype(1)==0) vn=$1 else vn=fi($s1)
    i=2
    if (cob.size(1)<2) { printf("NQS:stat small NQS: %d\n",cob.size(1)) ok=0 } else ok=1
    if (vn==-2) {
        sprint(sstr2,"%s",cob.scr) 
      } else if (vn<0||vn>=m) {
        return
      } else if (fcd.x[vn]==CODTY) { 
        scr[1].resize(cob.v.size)
        field=$i i+=1
        cob.v[vn].uncode(scr[1],field)
        sprint(sstr2,"%s",scr[1]) 
      } else if (fcd.x[vn]==1) { // looking at a list of vectors
    //if (oform()==NOP){printf("%s:stat ERR: set oform() to do stats on vectors or nqs\n",this)return}
        scr[1].resize(0)
        for ii=0,cob.v[vn].size-1 scr[1].append(oform(fcdo.o(cob.v[vn].x[ii])))
        sprint(sstr2,"%s",scr[1]) 
      } else {
        sprint(sstr2,"%s",cob.v[vn])
      }
    if (numarg()<i) {
        sprint(sstr,   "printf(\"max=%%g; \",%s.max) ",sstr2)
        sprint(sstr,"%s printf(\"min=%%g; \",%s.min) ",sstr,sstr2)
        if (ok) sprint(sstr,"%s printf(\"mean=%%g; \",%s.mean) ",sstr,sstr2)
        if (ok) sprint(sstr,"%s printf(\"stdev=%%g; \",%s.stdev) ",sstr,sstr2)    
        execute(sstr)
        print ""
      } else if (argtype(i)==1) { // a vector
        $oi.resize(0)
        if (ok) {$oi.append(cob.v[vn].max,cob.v[vn].min,cob.v[vn].mean,cob.v[vn].stdev)
          } else  {$oi.append(cob.v[vn].max,cob.v[vn].min,cob.v[vn].min,0) }
      } else if (!ok) { return
      } else for (;i<=numarg();i+=1) {
        if (strm($si,"[(][)]$")) {
            sfunc.left($si,sfunc.len($si)-2)
            sprint(sstr,"printf(\"%s()=%%g; \",%s(%s))",$si,$si,sstr2)
          } else sprint(sstr,"printf(\"%s=%%g; \",%s.%s)",$si,sstr2,$si)
        execute(sstr)
        print ""
      }
  }

//** iterator it() iterates over columns
// set's global tstr and XO to string bzw vec
iterator it () { local ii,nostr localobj st
    nostr=i2=0
    st=new String()
    if (argtype(1)==2) st.s=$s1 else nostr=1 // regexp
    for ii=0,m-1 {
        if (nostr || strm(s[ii].s,st.s)) {
            XO=cob.v[ii] execstr=s[ii].s i1=ii
            iterator_statement
            i2+=1
          }
      }
  }

//** iterator ot() creates names for each col (same as col header) and goes through them all
iterator ot () { local i,na,val
    if (! isobj(otl,"List")) { // create list to execute
        otl=new List()
        for i=0,m-1 { 
            Xo=new String(s[i].s)
            if (fcd.x[i]==2) {
                varstr(Xo.s,1) 
                sprint(Xo.s,"%s=%s.fcds.object(%s.cob.v[%d].x[i2]).s",Xo.s,this,this,i)
              } else {
                varstr(Xo.s) 
                sprint(Xo.s,"%s=%s.cob.v[%d].x[i2]",Xo.s,this,i)
              }
            otl.append(Xo)
          }
        Xo=nil
      }
    for (i2=0;i2<cob.v[0].size;i2+=1) {
        for i=0,m-1 execute(otl.object(i).s)
        iterator_statement
      }
  }

//** iterator qt(&x1,NAME1,&x2,NAME2,...) 
// qt(&x1,NAME1,&x2,NAME2,...,&x)  
// qt(&x1,NAME1,&x2,NAME2,...,5,7,&x) // just from 5 to 7
// qt(&x1,NAME1,&x2,NAME2,...,8) // ending at 8
// note &x arg location is opposite to that for ltr
// eg for sp.qt(&x,"PRID",&y,"POID",&z,"NC1",&ii,"WID1",&jj,"WT1") print x,y,z,ii,jj
// NB set qtset if resetting values
iterator qt () { local a,i,j,ii,na,val,noset,min,max,cntr localobj cols,cd
    na=numarg()
    cols=new Vector() cd=new Vector()
    min=0 max=size(1)-1
    if (argtype(na)==3) {i=cntr=na $&i=0 na-=1} else cntr=-1
    if (argtype(na-1)==0) { // 2 values for min and max
          i=na-1 min=$i i=na max=$i na-=2
      }
    if (na/2!=int(na/2)) { // odd number
        if (argtype(na)==0) { i=na max=$i na-=1 
          } else {printf("%s::qt() needs even # of args\n",this) return }
      }
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
  //*** pick up column numbers
    for (i=2;i<=na;i+=2) { 
        if (argtype(i)!=2) val=$i else val=fi($si)
        cols.append(val) // cols has col #s
        if (val<0 || val>=m) { if (argtype(i)==2) printf("(%s)",$si)
            printf("%s::qt() ERR %d not found\n",this,val) return }
      }
    cd.copy(fcd)          // cd gives codes for col's
  //*** pick up the arguments
    for (i=1;i<=na;i+=2) { 
        // can't do iteration over externally defined strings (eg -1) see useslist()
        if (cols.x[int(i/2)]<0) continue
        if (cd.x[cols.x[int(i/2)]]!=0) { 
            if (argtype(i)==3) {
                printf("NQS::qt() WARNING using list index statt str for col %s\n",s[cols.x[int(i/2)]].s) 
                cd.set(cols.x[int(i/2)],0)
              }
          }
        if (cd.x[cols.x[int(i/2)]]==2 && argtype(i)!=2) {
            printf("%s::qt() ERR %s is strdec but arg %d not string\n",this,s[cols.x[int(i/2)]].s,i) return }
        if (cd.x[cols.x[int(i/2)]]==1 && argtype(i)!=1) {
            printf("%s::qt() ERR %s is odec but arg %d not obj\n",this,s[cols.x[int(i/2)]].s,i) return }
      }
  //*** iterate through setting local variables to values in NQS
    for (ii=min;ii<=max;ii+=1) {
        for (i=1;i<=na;i+=2) { j=cols.x[int(i/2)]
            if (cd.x[j]==0) {
                $&i=cob.v[j].x[ii]
              } else if (cd.x[j]==2) {
                $si=fcds.object(cob.v[j].x[ii]).s
              } else if (cd.x[j]==1) {
                $oi=fcdo.object(cob.v[j].x[ii])
              } else {
                printf("%s qt ERRA: %d %d\n",this,i,j)
                continue
              }
          }
        // for i=0,m-1 qtv.x[i]=v[i].x[ii] // if want these values need a separate vector
        iterator_statement
    //*** if qtset -> iterate through resetting NQS according to changed variables
        if (qtset) for (i=1;i<=na;i+=2) { j=cols.x[int(i/2)]
            if (cd.x[j]==0) {
                cob.v[j].x[ii]=$&i
              } else if (cd.x[j]==2) {
                fcds.object(cob.v[j].x[ii]).s=$si
              } else if (cd.x[j]==1) {
                if (!eqojt(fcdo.object(cob.v[j].x[ii]),$oi)) {
                    printf("%s qt ERRB: can't reassign obj: %d %s %s\n",this,\
                           i,fcdo.object(cob.v[j].x[ii]),$oi)
                  }
              }
          }
        if (cntr>-1) {i=cntr $&i+=1}
      }
    qtset=0 // turn back off
  }


//** iterator ut(&x1,NAME1,&x2,NAME2,...) 
// like qt but iterates over selected items without a copy (ie with selcp=0)
// ut(&x1,NAME1,&x2,NAME2,...,&x)  
// ut(&x1,NAME1,&x2,NAME2,...,5,7,&x) // just from 5 to 7
// ut(&x1,NAME1,&x2,NAME2,...,8) // ending at 8
// note &x arg location is opposite to that for ltr
// eg for sp.ut(&x,"PRID",&y,"POID",&z,"NC1",&ii,"WID1",&jj,"WT1") print x,y,z,ii,jj
// NB set qtset if resetting values
iterator ut () { local a,i,j,ii,jj,na,val,noset,min,max,cntr localobj cols,cd
    na=numarg()
    a=allocvecs(cols,cd)
    min=0 max=ind.size-1
    if (argtype(na)==3) {i=cntr=na $&i=0 na-=1} else cntr=-1
    if (argtype(na-1)==0) { // 2 values for min and max
          i=na-1 min=$i i=na max=$i na-=2
      }
    if (na/2!=int(na/2)) { // odd number
        if (argtype(na)==0) { i=na max=$i na-=1 
          } else {printf("%s::ut() needs even # of args\n",this) return }
      }
    if (eqobj(cob,out)) {printf("NQS WARNING: ut() called after full select(); switching\n") 
        cob=this }
  //*** pick up column numbers
    for (i=2;i<=na;i+=2) { 
        if (argtype(i)!=2) val=$i else val=fi($si)
        cols.append(val) // cols has col #s
        if (val<0 || val>=m) { if (argtype(i)==2) printf("(%s)",$si)
            printf("%s::ut() ERR %d not found\n",this,val) return }
      }
    cd.copy(fcd)          // cd gives codes for col's
  //*** pick up the arguments
    for (i=1;i<=na;i+=2) { 
        // can't do iteration over externally defined strings (eg -1) see useslist()
        if (cols.x[int(i/2)]<0) continue
        if (cd.x[cols.x[int(i/2)]]!=0) { 
            if (argtype(i)==3) {
                printf("NQS::ut() WARNING using list index statt str for col %s\n",s[cols.x[int(i/2)]].s) 
                cd.set(cols.x[int(i/2)],0)
              }
          }
        if (cd.x[cols.x[int(i/2)]]==2 && argtype(i)!=2) {
            printf("%s::ut() ERR %s is strdec but arg %d not string\n",this,s[cols.x[int(i/2)]].s,i) return }
        if (cd.x[cols.x[int(i/2)]]==1 && argtype(i)!=1) {
            printf("%s::ut() ERR %s is odec but arg %d not obj\n",this,s[cols.x[int(i/2)]].s,i) return }
      }
  //*** iterate through setting local variables to values in NQS
    for (jj=min;jj<=max;jj+=1) { ii=ind.x[jj]
        for (i=1;i<=na;i+=2) { j=cols.x[int(i/2)]
            if (cd.x[j]==0) {
                $&i=cob.v[j].x[ii]
              } else if (cd.x[j]==2) {
                $si=fcds.object(cob.v[j].x[ii]).s
              } else if (cd.x[j]==1) {
                $oi=fcdo.object(cob.v[j].x[ii])
              } else {
                printf("%s ut ERRA: %d %d\n",this,i,j)
                continue
              }
          }
        iterator_statement
    //*** if qtset -> iterate through resetting NQS according to changed variables
        if (qtset) for (i=1;i<=na;i+=2) { j=cols.x[int(i/2)]
            if (cd.x[j]==0) {
                cob.v[j].x[ii]=$&i
              } else if (cd.x[j]==2) {
                fcds.object(cob.v[j].x[ii]).s=$si
              } else if (cd.x[j]==1) {
                if (!eqojt(fcdo.object(cob.v[j].x[ii]),$oi)) {
                    printf("%s ut ERRB: can't reassign obj: %d %s %s\n",this,\
                           i,fcdo.object(cob.v[j].x[ii]),$oi)
                  }
              }
          }
        if (cntr>-1) {i=cntr $&i+=1}
      }
    dealloc(a)
    qtset=0 // turn back off
  }

//** iterator vt("proc",&x1,NAME1,&x2,NAME2,...) 
proc vt () { local a,i,j,ii,na,val,noset,min,max,cntr localobj cols,cd,at
    na=numarg()
    a=allocvecs(cols,cd)
    min=0 max=size(1)-1
    if (argtype(na)==3) {i=cntr=na $&i=0 na-=1} else cntr=-1
    if (argtype(na-1)==0) { // 2 values for min and max
          i=na-1 min=$i i=na max=$i na-=2
      }
    if (na/2==int(na/2)) { // odd number
        if (argtype(na)==0) { i=na max=$i na-=1 
          } else {printf("%s::vt() needs odd # of args\n",this) return }
      }
    if (eqobj(cob,out)) { printf(" vt() err: run .vt() on full set") return }
  //*** pick up column numbers
    for i=2,na { 
        if (argtype(i)!=2) val=$i else val=fi($si)
        cols.append(val) // cols has col #s
        if (val<0 || val>=m) { if (argtype(i)==2) printf("(%s)",$si)
            printf("%s::vt() ERR %d not found\n",this,val) return }
        if (fcd.x[val]!=0 && fcd.x[val]!=1) {
            printf("%s::vt() ERR %d not handled (%d)\n",this,fcd.x[va],val) return }
      }
  //*** iterate through setting local variables to values in NQS
    sprint(tstr,"%s.nqsvt(\"%s\",%s,%s,%s,%s)",cols,$s1,fcdo,fcd,vl,ind)
    execute(tstr)
    dealloc(a)
  }

//** calc() spread-sheet functionality using vector functions
// takes a compound expression utilizing column names in slant brackets <>
// anything quoted can use either ' or \"
// eg sp.calc("<DIST>.c.mul(DELD).add(DEL)")
proc calc () { local ii,vn,exec
    if (numarg()==0) { 
        printf("eg calc(\"<SCR>.copy(<COL1>.c.mul(<COL2>).add(5))\") \ntakes a compound expression utilizing column names in slant brackets <>\nanything quoted can use either ' slash quote.\n")
        return 
      } else sstr=$s1
    exec=1
    if (argtype(2)==2) if (strcmp($s2,"NOEXEC")==0) exec=0
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    while (sfunc.tail(sstr,"<",sstr2)!=-1) {
        sfunc.head(sstr2,">",sstr2)
        if (strcmp(sstr2,"SCR")==0) { 
            sprint(sstr3,"%s",cob.scr)
          } else if (isnum(sstr2)) {
            sscanf(sstr2,"%d",&vn)
            sprint(sstr3,"%s",cob.v[vn])
          } else if ((vn=fi(sstr2))==-1) {
            return // error 
          } else {
            sprint(sstr3,"%s",cob.v[vn])      
          }
        sprint(sstr2,"<%s>",sstr2)
        repl_mstr(sstr,sstr2,sstr3,sstr4)
      }
    repl_mstr(sstr,"'","\"",sstr4)
    if (exec) execute(sstr)
  }

//** sort () sort according to one index
func sort () { local beg,ii,vn
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (argtype(1)==0) vn=$1 else vn=fi($s1)
    if (vn<0||vn>=m) return -1
    if (fcd.x[vn]==1) {
        cob.scr.resize(0)
        for ii=0,cob.v[vn].size-1 cob.scr.append(oform(fcdo.o(cob.v[vn].x[ii])))
        if (cob.scr.x[0]==NOP) { // looking at a list of vectors
            printf("NQS:sort ERR define oform to sort obj field %s\n",s[vn].s) return 0 }
        cob.scr.sortindex(cob.ind)    
      } else {
        if (cob.v[vn].ismono() && numarg()==1) {
            printf("NQS sort %s already ordered\n",cob.v[vn]) return vn }
        cob.v[vn].sortindex(cob.ind)
      }
    if (numarg()==2) if ($2==-1) cob.ind.reverse
    fewind()
    return vn
  }  

//** vsort(vec) sort according to vec which may have redundancy
func vsort () { local beg,ii,x localobj o
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    o=$o1
    if (numarg()==2) if ($2==-1) o.reverse
    cob.scr.resize(cob.v.size)
    for (beg=0;beg<m;beg+=50) { // fewind() can only handle vecst.mod:VRRY vecs at a time
        tmplist.remove_all
        for (ii=beg;ii<beg+50 && ii<m;ii+=1) tmplist.append(cob.v[ii])
        x=cob.scr.fewind(o,tmplist,1)
      }      
    return x
  }  

//** percl ("FIELD",n %ile) check whether the first n% of the vector are in the top n %ile
// usage -- reverse sort according to 1 field and then ask if other field is in top n %ile
func percl () { local beg,ii,vn,nile,sz
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (argtype(1)==0) vn=$1 else vn=fi($s1)
    if (vn<0||vn>=m) return -1
    sz=cob.v[vn].size
    nile=int($2*sz/100)
    scr[1].copy(cob.v[vn]) scr[1].sort() scr.resize(0) scr.copy(scr[1],sz-nile-1,sz-1)
    scr.reverse() // the top n-ile percentile
    scr[2].resize(0) scr[2].copy(cob.v[vn],0,nile) // the first set of values from NQS
    // scr[1].insct(scr,scr[2])    // find all common values
    for ii=0,scr[2].size-1 if (scr.contains(scr[2].x[ii])) return ii // which one is top of scr[2]
    return -1
  }  

// family("COLA",val,"COLB","COLC")
// pick out rows that have are same as row with "COLA" val except that COLB COLC
//  etc. can be anything
func family () { local a,i,ii,vn,va,nile,sz,om localobj key,arg
    tog("DB") // start at full db
    a=allocvecs(key,arg)
    sz=size(1)
    tmplist.remove_all arg.resize(0) key.resize(0)
    if (select(-1,$s1,$2)!=1) printf("WARNING: NQS family found more than 1 %s=%g\n",$s1,$2)
    va=fi($s1)
    scr.resize(0) scr.append(va)
    for i=3,numarg() scr.append(fi($si))
    for i=0,m-1 if (! scr.contains(i)) {
        tmplist.append(v[i])
        key.append(EQU) 
        arg.append(v[i].x[ind.x[0]],0)
      }
    ind.resize(v.size)
    ind.slct(key,arg,tmplist) // run select function
    if (ind.size>0) {
        out.ind.copy(ind) 
        aind()
        cob=out
      } else if (verbose) printf("None selected\n") 
    dealloc(a)
    return ind.size
  }  

// psel(%ile,"COLA","COLB","COLC")
// psel("COLA",%ileA,"COLB",%ileB,"COLC",%ileC)
// neg %ile means bottom -- eg 10 is largest 10% and -10 is smallest 10%
// return top percentile in these columns
func psel () { local a,i,ii,vn,nile,sz,om localobj key,arg
    tog("DB") // start at full db
    a=allocvecs(key,arg)
    om=numarg()-1
    sz=size(1)
    tmplist.remove_all arg.resize(0) key.resize(0)
    if (argtype(1)==0) {
        if (int($1*sz/100)==0) { printf("%s pselERR: unable %d%% of %d\n",this,$1,sz)
            return -1 }
        key.resize(om)
        if ($1>0) { 
            nile=sz-int($1*sz/100) 
            key.fill(GTE) 
          } else { 
            nile=-int($1*sz/100)
            key.fill(LTE) 
          }
        for i=2,numarg() { 
            if (argtype(i)==0) vn=$i else vn=fi($si)
            if (vn<0||vn>=m) return -1
            tmplist.append(v[vn])
            scr[1].copy(v[vn])
            scr[1].sort()
            arg.append(scr[1].x[nile],0) // 2nd arg for GTE ignored
            if (verbose) printf("%s:%g ",$si,scr[1].x[nile])
          }
      } else for i=1,numarg() { 
        tstr=$si
        vn=fi($si)      i+=1
        if (int($i*sz/100)==0) { printf("NQS psel(): WARNING: ignoring %d%% of %d\n",$i,sz)
            continue }
        if (vn<0||vn>=m) return -1
        tmplist.append(v[vn])
        scr[1].copy(v[vn])
        scr[1].sort()
        if ($i>0) { 
            nile=sz-int($i*sz/100) 
            key.append(GTE) 
          } else { 
            nile=-int($i*sz/100)
            key.append(LTE) 
          }
        arg.append(scr[1].x[nile],0) // 2nd arg for GTE ignored
        if (verbose) printf("%s:%g ",tstr,scr[1].x[nile])
      }
    ind.resize(v.size)
    ind.slct(key,arg,tmplist) // run select function
    if (ind.size>0) {
        out.ind.copy(ind) 
        aind()
        cob=out
      } else if (verbose) printf("None selected\n") 
    if (verbose) print ""
    dealloc(a)
    return ind.size
  }  

//** uniq(COLNAME) will pick out unique row (1st) for the chosen column
func uniq () { local vn
    if (! eqobj(cob,out)) {printf("Only run NQS:uniq() on prior selected set.\n") return -1}
    vn=sort($s1)
    cob.ind.resize(cob.v.size)
    cob.ind.redundout(cob.v[vn],1)
    fewind()
    return cob.ind.size
  }

//** elimrepeats(COLA[,COLB,...])
func elimrepeats () { local a,b,i,ii,indflag localobj sl,tl,v1
    if (eqobj(cob,out)) {printf("%s ERR: run elimrepeats on full db\n",this)  return 0.}
    if (size(1)==0) { printf("%s:elimirepeats Empty NQS\n",this) return 0.}
    a=allocvecs(v1) b=1 indflag=0
    sl=new List() tl=new List()
    if (numarg()==0) {
        for ii=0,m-1 { sl.append(v[ii]) v1.append(ii) }
        b=numarg()+1
      } else if (argtype(1)==0) if ($1==-1) {b=2 indflag=1}
    for i=b,numarg() {
        if (argtype(i)==0) ii=$i else if ((ii=fi($si))==-1) return 0
        sl.append(v[ii])
        v1.append(ii)
      }
    for ii=0,m-1 if (!v1.contains(ii)) tl.append(v[ii])
    for (ii=v1.size-1;ii>=0;ii-=1) sort(v1.x[ii]) // sort them in the reverse order of calling
    ii=ind.mredundout(sl,indflag,tl)
    dealloc(a)
    return ii
  }

//** covarc("vCol") -- generate covariance matrix from vectors in a single column
obfunc covarc () { local a,fl,n,m localobj mat,xo,v1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if ((fl=fi($s1))==-1) return -1
    if (fcd.x[fl]!=1) {printf("Must be an obj column\n") return -1}
    xo=get(fl,0).o
    if (!isojt(xo,ind))  {printf("Must be an vec column\n") return -1}
    n=size(1) // number of data vectors
    m=xo.size    // size of vector 
    a=allocvecs(v1)
    mat=new Matrix(m,m)
    cob.v[fl].covar(fcdo,v1)
    mat.from_vector(v1)
    dealloc(a)
    return mat
  }

//** tomatc("vCol") -- return matrix of vectors from a single column; load into rows of mat
obfunc tomatc () { local a,fl,n,m,ii localobj mat,xo,v1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if ((fl=fi($s1))==-1) return -1
    if (fcd.x[fl]!=1) {printf("Must be an obj column\n") return -1}
    xo=get(fl,0).o
    if (!isojt(xo,ind))  {printf("Must be an vec column\n") return -1}
    n=size(1) // number of data vectors
    m=xo.size    // size of vector 
    mat=new Matrix(n,m)
    for qt(xo,fl,&ii) mat.setrow(ii,xo)
    return mat
  }

//** shuffle()
proc shuffle () { local a,b,i,ii,indflag localobj sl,tl,v1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    if (size(1)==0) { printf("%s:shuffle Empty NQS\n",this) return}
    if (eqobj(cob,out)) out.ind.copy(ind)
    rdmord(cob.ind,cob.v.size)
    fewind()
  }

//** fewind () -- index all of the vecs in place using vecst.mod::fewind()
proc fewind () {
    cob.scr.resize(cob.v.size)
    for (beg=0;beg<m;beg+=50) { // fewind() can only handle vecst.mod:VRRY vecs at a time
        tmplist.remove_all
        for (ii=beg;ii<beg+50 && ii<m;ii+=1) tmplist.append(cob.v[ii])
        cob.scr.fewind(cob.ind,tmplist)
      }      
    cob.ind.resize(0) // prevents reusing it
  }

//** aind () -- index all of the vecs into out list
proc aind () { local beg,ii
    for (beg=0;beg<m;beg+=10) {
        tmplist.remove_all vlist.remove_all
        for ii=beg,beg+10 if (ii<m) {
            out.v[ii].resize(ind.size)
            tmplist.append(v[ii])
            vlist.append(out.v[ii])
          }
        ind.findx(tmplist,vlist)
      }      
  }  

//** append(VEC[,begin]) appends to ends of given vectors
// append(x1,x2,...); append(NQS)
proc append () { local ii,jj,i,flag,begin,o1fcd localobj xo
    cob=this
    if (m==0) { printf("%s append ERR0, attempted append to uninitialized NQS\n",this) return }
    if (argtype(1)==1 && fcd.x[0]!=1) {
        if (isobj($o1,"Vector")) { // a vector of values
            if (numarg()>1) begin=$2 else begin=0
            if (begin+$o1.size>m) { 
                printf("%s append ERR1: vec %s too large; doing nothing: %d>%d",this,$o1,begin+$o1.size,m)
              } else {
                for i=begin,begin+$o1.size-1 v[i].append($o1.x[i-begin])
              }
          } else if (isobj($o1,"NQS")) { // another NQS to add onto end
            if ($o1.m != m) { 
                printf("%s append ERR1a, %s size %d!= %s size %d?\n",this,this,m,$o1,$o1.m)
                return }
            for ii=0,m-1 { 
                o1fcd=$o1.fcd.x[ii]
                if (o1fcd==0) { v[ii].append($o1.cob.v[ii])
                  } else for jj=0,$o1.size(1)-1 { 
                    xo=$o1.get(ii,jj)
                    if (o1fcd==1) oval=xo.o else sval=xo.s
                    v[ii].append(newval(o1fcd,ii))
                  }
              }
          } else if (isobj($o1,"List")) { // list of vectors to add on
            if ($o1.count() != m) { 
                printf("%s append ERR1b, %s size %d!= %s size %d?\n",this,this,m,$o1,$o1.count())
                return }
            for ii=0,m-1 v[ii].append($o1.o(ii))
          } else { printf("%s append ERR1c, what is %s?\n",this,$o1) }
        return
      }
    if (argtype(1)==2) if ((ii=fi($s1,"NOERR"))!=-1) { // a field name
        for i=1,numarg() { 
            if ((ii=fi($si))==-1) return  
            i+=1 
            if (argtype(i)==0) {
                v[ii].append($i)
              } else {
                if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
                v[ii].append(newval(argtype(i),ii))
              }
          }
        return
      }
    if (numarg()>m) { printf("%s append ERR2: args>m; doing nothing\n",this) return }
    if (numarg()<=m) {
        if (numarg()<m) printf("NQS::append() WARNING only filling %d/%d cols for %s\n\tuse NQS.pad",numarg(),m,this)
        for ii=0,numarg()-1 {
            i=ii+1
            if (argtype(i)==0) {
                v[ii].append($i)
              } else { 
                if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
                v[ii].append(newval(argtype(i),ii))
              }
          }
        return
      }
  }

//** appi(index,VEC) or append(index,x1,x2,...) appends to ends of vectors starting at index
proc appi () { local i,ix,n
    cob=this 
    if (argtype(1)==2) ix=fi($s1) else ix=$1
    if (numarg()==2 && argtype(2)==1 && fcd.x[ix]==0) { // a vector
        if ($o2.size>m-ix) { 
            printf("%s appi ERR1: vec too large; doing nothing %d %d %d %d\n",this,m,$o2.size,ix,m-ix)
          } else {
            n=-1
            for i=ix,ix+$o2.size-1 v[i].append($o2.x[n+=1])
          }
      } else {
        if (numarg()-1>m-ix) {
            printf("%s appi ERR2: args>m; doing nothing",this)
            return
          } 
        for i=2,numarg() {
            if (argtype(i)==0) { 
                v[ix+i-2].append($i)
              } else {
                if (argtype(i)==1) oval=$oi else if (argtype(i)==2) sval=$si
                v[ix+i-2].append(newval(argtype(i),ix+i-2))
              }
          }
      }
  }

//** map(FUNC,arg1,...) map $s1 command to other args, replacing strings with vectors as found
// eg nqs.map("gg",0,"volt","cai",2)
proc map () { local i,agt,wf
    if (numarg()==0) { 
        printf("map(FUNC,arg1,...) apply function to args using names for columns.\n")
        return }
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    sprint(sstr,"%s(",$s1) // the command
    wf=0
    for i=2,numarg() { // the args
        agt=argtype(i)
        if (agt==0) {
            sprint(sstr,"%s%g,",sstr,$i)
          } else if (agt==1) {
            sprint(sstr,"%s%s,",sstr,$oi)
          } else if (agt==2) {
            if ((vn=fi($si))==-1) {
                sprint(sstr,"%s\"%s\",",sstr,$si) 
                printf("NQS.map WARNING: including raw string: %s\n",$si) wf=1
              } else if (fcd.x[vn]==1) { // look at a list of obj's
                cob.scr.resize(0)
                for ii=0,cob.v[vn].size-1 cob.scr.append(oform(fcdo.o(cob.v[vn].x[ii])))
                if (cob.scr.x[0]==NOP) {
                    printf("map WARNING: oform not set for %s interp; replacing with fcdo indices\n",$si)
                    cob.scr.copy(cob.v[vn])
                  }
                sprint(sstr,"%s%s,",sstr,cob.scr) 
              } else if (vn==-2) { // code for scr vector
                sprint(sstr,"%s%s,",sstr,cob.scr) 
              } else {
                sprint(sstr,"%s%s,",sstr,cob.v[vn]) 
              }
          } else { printf("argtype %d for arg %d not implemented for NQS:map\n",agt,i) return }
      }
    chop(sstr) sprint(sstr,"%s)",sstr)
    if (wf && !batch_flag) if (boolean_dialog(sstr,"CANCEL","EXECUTE")) return
    // print sstr
    execute(sstr)
  }

//*** gr() use map to graph
// need to assign .crosshair_action so can do visual select procedure
proc gr () { local i,nm,gn,col,lne,f3d,y,x,done localobj symb,st
    nm=numarg() gn=0 f3d=-1 st=new String2()
    col=2 lne=4 stone=0 
    done=0
    if (sfunc.len(marksym)==0) marksym="o"
    sprint(tstr,"symb=\"%s\"",marksym) execute(tstr) // set global symb
    if (argtype(1)==2) st.s=$s1 else if (argtype(1)==0) st.s=s[$1].s
    if (argtype(2)==2) st.t=$s2 else if (argtype(2)==0) if ($2>=0) st.t=s[$2].s else {
        stone=1 } // use -1 as place
    if (nm==0) { 
        print "gr(\"Y\",\"X\"[,\"Z\",g#,col,line]); Y or X can be col# or col#,-1; 'Z' must be str"
        return
      } else if (nm==1) {  map("gg",0,st.s,1,col,lne)               stone=done=1
      } else if (nm==2) {  map("gg",0,st.s,st.t,col,lne)            done=1 }
    i=3
    if (! done)  {
        if (argtype(i)==2) {f3d=fi($si) i+=1}
        if (argtype(i)==0) {gn=$i} else if (argtype(i)==1) {gn=ojtnum($oi) g[gn]=$oi}
        i+=1 if (i<=nm) col=$i 
        i+=1 if (i<=nm) lne=$i 
        if (f3d!=-1) {
            if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
            if (!gvmarkflag) {printf("%s gr ERR: 3D and gvmarkflag=0\n",this) return}
            y=fi(st.s) x=fi(st.t)
            if (lne==1) lne=2 else lne-=2 // will augment below
            if (x==-1 || y==-1) {printf("%s gr ERR: %s,%s not fi()\n",this,st.s,st.t) return}
            for i=0,cob.v.size-1 {
                if (i%9==0) lne+=2
                g[gn].mark(cob.v[x].x[i],cob.v[y].x[i],marksym,lne,cob.v[f3d].x[i]%9+1,4)
              }
          } else if (stone) {
            map("gg",gn,st.s,1,col,lne)
          } else { // print gn,col,lne
            map("gg",gn,st.s,st.t,col,lne)
          }
      }
    g[gn].color(col)
  //  g[gn].label(0.05,0.95,st.s)
  //  if (nm>=2) g[gn].label(0.85,0.05,st.t)
    g[gn].color(1)
    if (!stone) {
        setgrsel(g[gn],fi(st.s),fi(st.t))
        setchsel(g[gn],fi(st.s),fi(st.t))
      }
  }

//** grsel(): CTL-hit == Crosshair
//            SHT-hit == resize
//            hit-drag-release == show in square
//            SHT-hit-drag-release == show new thing there
// Type: press (2), drag (1), release (3)
// Keystate: META-SHT-CTL eg 101=5 is META-CTL
proc setgrsel () {
    $o1.menu_remove("Selector")
    sprint(tstr,"proc p(){grsel($1,$2,$3,$4,%d,%d)}",$2,$3)
    execute1(tstr,this)
    $o1.menu_tool("Selector", "p")
  }

proc setchsel () {
    sprint(tstr,"proc q(){chsel($1,$2,$3,%d,%d)}",$2,$3)
    execute1(tstr,this)
    $o1.crosshair_action("q")
  }

grsbegx=grsbegy=1e9
proc grsel () { local type, x0, y0, keystate, fl1,fl2,sel
    type=$1 x0=$2 y0=$3 keystate=$4 fl1=$5 fl2=$6
    if (type==3) {
        if (grsbegx==1e9) { // no drag was done
            if ((sel=select(fl2,"~",x0,fl1,"~",y0))!=0) {
                pr()
              } else print "Can't find ",s[fl2].s,"~ ",x0,s[fl1].s,"~ ",y0
          } else { // consider a rectangle
            order(&grsbegx,&x0) order(&grsbegy,&y0)
            if ((sel=select(fl2,"[]",grsbegx,x0,fl1,"[]",grsbegy,y0))!=0) {
                if (keystate==0) { grsel2() 
                  } else if (keystate==3) {  // CTL or SHIFT alone are being used by fvwm2
                    pr() // print grsbegx,x0,grsbegy,y0
                  }
              } else printf("Can't find %s %g-%g; %s %g-%g\n",s[fl2].s,grsbegx,x0,s[fl1].s,grsbegy,y0)
            grsbegx=grsbegy=1e9
          }
      } else if (type==1 && grsbegx==1e9) {grsbegx=x0 grsbegy=y0 }
  }

// order(&x,&y) returns the values in order
proc order () { local tmp
    if ($&2<$&1) { tmp=$&2 $&2=$&1 $&1=tmp }
  }

proc chsel () { local ascii, x0, y0,  fl1,fl2,sel
    x0=$1 y0=$2 ascii=$3 fl1=$4 fl2=$5
    if ((sel=select(fl2,"~",x0,fl1,"~",y0))!=0) {
        if (ascii==32) { chsel2()
          } else pr()
      } else print "Can't find ",s[fl2].s,"~ ",x0,s[fl1].s,"~ ",y0
  }

//** apply function or .op to every selected vector -- ignore return val, see applf
proc apply () { local i,fl
    if (numarg()==0) { 
        printf("apply(FUNC,COL1,...) apply function or .op to every selected vector.\n")
        printf("must be function, not proc, since will return value.\n")
        return }
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (numarg()==1) for i=0,m-1 { // apply to all vectors
        if (strm($s1,"^\\.")) sprint(sstr,"%s%s"  ,cob.v[i],$s1) else {
                                sprint(sstr,"%s(%s)",$s1,cob.v[i]) }
        execute(sstr)
      } else for i=2,numarg() {
        if (argtype(i)==0) fl=$i else if ((fl=fi($si))==-1) return
        if (fl==-2) sprint(sstr2,"%s",cob.scr) else sprint(sstr2,"%s",cob.v[fl]) 
        if (strm($s1,"^\\.")) sprint(sstr,"%s%s"  ,sstr2,$s1) else {
                                sprint(sstr,"%s(%s)",$s1,sstr2) }
        execute(sstr)
      }
  }

//** applf(FUNC,COL) function or .op which returns a value
func applf () { local min,max,a,i,fl,ret,na,flag localobj v1,v2
    na=numarg()
    if (na==0) { 
        printf("applf(FUNC,'COLA'[,...,vec]) apply function or .op to selected cols.\n")
        printf("applf(FUNC,vec) apply function or .op to all cols.\n")
        printf("with more than one column need a final vector to copy results to\n")
        printf("must use function, not proc, since will keep return value.\n")
        return -1 }
    a=allocvecs(v1,v2)
    v1.resize(1)
    if (eqobj(cob,out) && verbose) printf(" *Selected* ")
    if (argtype(na)==1) {flag=na na-=1} else flag=0 // flag to copy onto an output vector
    if (na==1) { min=0 max=m-1 } else { min=2 max=na } 
    for i=min,max {
        if (na==1) { fl=i
          } else {if (argtype(i)==0) fl=$i else if ((fl=fi($si))==-1) return -1}
        if (fl==-2) sprint(sstr2,"%s",cob.scr) else sprint(sstr2,"%s",cob.v[fl]) 
        if (strm($s1,"^\\.")) sprint(sstr,"%s.x[0]=%s%s"  ,v1,sstr2,$s1) else {
                                sprint(sstr,"%s.x[0]=%s(%s)",v1,$s1,sstr2) }
        execute(sstr)
        v2.append(v1.x[0])
      }
    if (flag) {i=flag $oi.copy(v2)}
    ret=v2.x[0]
    dealloc(a)
    return ret
  }

//** fill(NAME,val[,NAME1,val1 ...])
// fill each selected vector with next arg
func fill () { local i,fl,fl2,x
    if (numarg()==0) { 
        printf("fill(NAME,val[,NAME1,val1 ...])\n\tfill each selected vector with val\nval can be num, vector, or other col name\n")
        return -1}
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    for i=1,numarg() { 
        fl=fi($si) i+=1
        if (fl==-1) return -1
        field=0
        if (fcd.x[fl]==CODTY) { // code field
            field=$i i+=1
          }
        if (argtype(i)==0) {
            if (field>0) cob.v[fl].uncode(field,$i) else cob.v[fl].fill($i)
          } else if (argtype(i)==1) {
            if (!isobj($oi,"Vector")){
                printf("%s:fill() ERRa: only fill with vector: %s\n",this,$oi) return -1}
            if ($oi.size!=cob.v.size){
                printf("%s:fill() ERRb: wrong vec size: %d!=%s:%d\n",this,cob.v.size,$oi,$oi.size) return -1}
            if (field>0) cob.v[fl].uncode(field,$oi) else cob.v[fl].copy($oi)
          } else if (argtype(i)==2) {
            fl2=fi($si,"NOERR")
            if (fl2== -1) { // add this string to this field?
                if (fcd.x[fl]==2) {
                    sval=$si
                    x=newval(2,fl)
                    cob.v[fl].fill(x)
                  } else {
                    printf("%s:fill() ERRc: trying to fill field %s with string %s\n",this,s[fl].s,$si)
                    return -1
                  }
              } else if (field>0) {
                     cob.v[fl].uncode(field,cob.v[fl2]) 
              } else cob.v[fl].copy(cob.v[fl2])
            i+=1
          }
      }
    return cob.v[fl].size
  }

//** fillin(NAME,val[,NAME1,val1 ...])
// fill in place according to indices in ind -- use with selcp==0
// can also do after a regular select() ie selcp==1 and avoid needing a delect
proc fillin () { local i,fl
    if (numarg()==0) { 
        printf("fillin(NAME,val[,NAME1,val1 ...])\n\tfill selected vectors in place\n")
        printf("\tuse after select(-1,...) eg selcp==0\n")
        return 
      }
    scr.resize(0)
    for (i=2;i<=numarg();i+=2) scr.append($i)
    tmplist.remove_all
    for (i=1;i<=numarg();i+=2) {
        if (argtype(i)==2) { 
            if ((fl=fi($si))==-1) return
          } else fl=$i
        tmplist.append(v[fl])
      }
    ind.sindv(tmplist,scr)
  }

//** fillv(NAME,v1[,NAME1,v2 ...])
// fill from vectors v1,v2,..., places in ind -- use with selcp=0
proc fillv () { local i,fl
    if (numarg()==0) { 
        printf("fillv(NAME,vec1[,NAME1,vec2 ...])\n\tfill selected vectors from vectors\n")
        printf("\tuse after select() with selcp==0\n")
        return 
      }
    tmplist.remove_all vlist.remove_all
    for (i=1;i<=numarg();i+=2) {
        if (argtype(i)==2) { 
            if ((fl=fi($si))==-1) return
          } else fl=$i
        tmplist.append(v[fl])
      }
    for (i=2;i<=numarg();i+=2) vlist.append($oi)
    ind.sindx(tmplist,vlist)
  }

//** pr() print out vectors
// eg pr("COLA","COLB",3,7)
// last args are max or min-max rows
// first set of args can be any of: col_names, file object, file name (not same as col name),
//      vector giving col numbers
func pr () { local ii,i,min,max,na,flag,jj,e,fout,sz,nohdr
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (m==0) {printf("%s EMPTY",this) return 0}
    scr[1].resize(0)
    nohdr=fout=flag=min=0 max=cob.v.size-1 na=numarg()
    nohdr=noheader
    if (argtype(na)==0 && argtype(na-1)==0) {  // pick up the limits first
        i=na na-=2
        max=$i i-=1 min=$i 
      } else if (argtype(na)==0) {
        i=na na-=1
        if ($i>=0) max=$i else min=max+$i // allow printing the end
      } 
    for i=1,na {
        if (argtype(i)==1) {  
            if (isobj($oi,"Vector")) {      flag=1 // column indices in a vector
                scr[1].copy($oi) 
              } else if (isobj($oi,"File")) { fout=1
              } else { printf("NQS::pr() Unknown object: %s\n",$oi) return 0 }
          } else if (argtype(i)==2) {
            if (fi($si,"NOERR")!=-1) { // column identifier to pick up
                scr[1].append(fi($si)) flag=1 
              } else if (strcmp($si,"NOHEADER")==0) { nohdr=1
              } else if (strm($si,"[<>]")) { flag=2  // something to calculate
              } else { // assume it's a file name
                if (fout) { fi($si) return 0 } // already picked up a filename
                if (!tmpfile.wopen($si)) printf("%s:Can't open %s for printing\n",this,$si)
                fout=1 
                printf("==== WARNING: printing ascii to file %s ====\n",$si)
              }
          }
      }
    if (flag==0) scr[1].indgen(0,m-1,1) // didn't pick up any columns
    if (scr[1].min<0 || scr[1].max>m-1) { printf("NQS:pr() bad col values: ") vlk(scr[1]) return 0 }
    if (max>size(1)){ max=size(1)-1
        printf("NQS:pr WARNING: %d rows requested but %s size=%d\n",max,this,size(1)) }
    if (min>size(1)){printf("NQS:pr ERROR: %s size=%d < min %d\n",this,size(1),min) return 0}
    if (!fout && flag!=2) print ""
    if (flag==2) {
        calc($s1,"NOEXEC")
        sprint(sstr,"XO=%s",sstr)
        execute(sstr)
        vlk(XO,min,max)
      } else {
        sz=scr[1].size-1
        if (!nohdr) { 
            printf("%s",tabform)
            for i=0,sz {
                ii=scr[1].x[i]
                if (ii==-1) return -1
                if (fout) {
                    if (ii<0) tmpfile.printf("%s%s",is[-ii].s,tabform) else {
                        tmpfile.printf("%s(%d)%s",s[ii].s,ii,tabform) }
                  } else {
                    if (ii<0) printf("%s%s",is[-ii].s,tabform) else printf("%s(%d)%s",s[ii].s,ii,tabform)
                  }
              } 
            if (fout) tmpfile.printf("\n") else printf("\n") 
          }
        for jj=min,max {
            printf("%s",tabform)
            for i=0,sz { ii=scr[1].x[i]
                if (ii==-2) { printf(dblform,cob.scr.x[jj]) 
                  } else if (ii==-3) { 
                    if (fout) tmpfile.printf(dblform,cob.ind.x[jj]) else printf(dblform,cob.ind.x[jj])
                  } else {
                    if (fout) { prtval(tmpfile,(e=getval(ii,cob.v[ii].x[jj],jj)),tabform) 
                      } else if (sz) { prtval(   (e=getval(ii,cob.v[ii].x[jj],jj)),tabform) 
                      } else {         prtval(   (e=getval(ii,cob.v[ii].x[jj],jj)),tabform)  }
                    if (e==ERR) return ERR
                  }
              }
            if (fout) tmpfile.printf("\n") else if (sz) print "" else printf(" ")
          }
      }
    if (fout) tmpfile.close
    return 0
  }

//** prs() print out vectors
// eg prs("COLA","COLB",3,7)
func prs () { local ii,i,min,max,na,flag,jj,kk,e,fout,sz,nohdr
    if (eqobj(cob,out)) printf("NQS WARNING: prs() called after full select(); switching\n")
    cob=this
    if (m==0) {printf("%s EMPTY",this) return 0}
    nohdr=flag=min=0 max=ind.size-1 na=numarg()
    if (na>=2) { 
        if (argtype(na-1)==0) { 
            i=na na-=2
            max=$i i-=1 min=$i 
            flag=1 // took care of numbers
          }} 
    if (!flag && na>=1) { 
        if (argtype(na)==0) { 
            i=na na-=1
            if ($i>=0) max=$i else min=max+$i // allow printing the end
          }
      } 
    // reuse flag -- means printing only certain cols
    fout=flag=0 i=1 // fout==1 means print out to file
    if (na>=1) if (argtype(1)==2) if (fi($s1,"NOERR")==-1) { // call it a file name
        if (!tmpfile.wopen($s1)) printf("%s:Can't open %s for printing\n",this,$s1)
        fout=1 i+=1
        printf("==== WARNING: printing ascii to file %s ====\n",$s1)
        if (numarg()==2) if (argtype(2)==2) if (strcmp($s2,"NOHEADER")==0) {nohdr=1 i+=1}
      }
    if (na>=i) if (argtype(i)==2 || argtype(i)==1) flag=1 // column names
    if (max>ind.size()){ max=ind.size()-1
        printf("NQS:prs WARNING: %d rows requested but %s size=%d\n",max,this,ind.size()) }
    if (min>size(1)){printf("NQS:prs ERROR: %s size=%d < min %d\n",this,size(1),min) return 0}
    if (!fout) print ""
    if (flag) {
        scr[1].resize(0)
        if (argtype(i)==1) scr[1].copy($oi) else for (;i<=na;i+=1) scr[1].append(fi($si)) 
        sz=scr[1].size-1
        if (!nohdr) { for i=0,sz {
              ii=scr[1].x[i]
              if (ii==-1) return -1
              if (fout) {
                  if (ii<0) tmpfile.printf("%s\t",is[-ii].s) else tmpfile.printf("%s(%d)\t",s[ii].s,ii)
                } else {
                  if (ii<0) printf("%s\t",is[-ii].s) else printf("%s(%d)\t",s[ii].s,ii)
                }
            } if (fout) tmpfile.printf("\n") else printf("\n") }
        for kk=min,max { jj=ind.x[kk]
            for i=0,sz { ii=scr[1].x[i]
                if (ii==-2) { printf(dblform,cob.scr.x[jj]) 
                  } else if (ii==-3) { 
                    if (fout) tmpfile.printf(dblform,cob.ind.x[jj]) else printf(dblform,cob.ind.x[jj])
                  } else {
                    if (fout) { prtval(tmpfile,(e=getval(ii,cob.v[ii].x[jj],jj)),tabform) 
                      } else if (sz) { prtval(   (e=getval(ii,cob.v[ii].x[jj],jj)),tabform) 
                      } else {         prtval(   (e=getval(ii,cob.v[ii].x[jj],jj)),tabform)  }
                    if (e==ERR) return ERR
                  }
              }
            if (fout) tmpfile.printf("\n") else if (sz) print "" else printf(" ")
          }
      } else {
        if (!nohdr) {
            if (fout) for ii=0,m-1 tmpfile.printf("%4.4s(%d)  ",s[ii].s,ii) else {
                for ii=0,m-1 printf("%4.4s(%d)  ",s[ii].s,ii) }
            if (fout) tmpfile.printf("\n") else print ""
          }
        for kk=min,max { jj=ind.x[kk]
            for ii=0,m-1 { 
                if (fout) prtval(tmpfile,e=getval(ii,cob.v[ii].x[jj],jj),tabform) else {
                    prtval(        e=getval(ii,cob.v[ii].x[jj],jj),"") 
                    printf("(%d)%s",ii,tabform)
                  }
                if (e==ERR) return  ERR
              }
            if (fout) tmpfile.printf("\n") else print ""
          }
      }
    if (fout) tmpfile.close
    return max-min+1
  }

//** prn() print out single index from vectors
proc prn () { local jj,ii,ix,max,e
    ix=$1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (numarg()==2) max=$2 else max=ix
    for jj=ix,max {
        if (jj<0 || jj>=cob.v[0].size) { 
            printf("prn: Index out of range (%d)\n",cob.v[0].size) return }
        for ii=0,m-1 {
            printf("%s:",s[ii].s)
            prtval(e=getval(ii,cob.v[ii].x[jj],jj)," ")
            if (e==ERR) return
          }
        print ""
      }
  }

//** zvec() -- clear -- resize all the vectors to 0
proc clear () { if (numarg()==1) zvec($1) else zvec() }
proc zvec () { local ii,sz
    cob=this
    if (numarg()==1) sz=$1 else sz=0
    // fcds.remove_all fcds.append(new String("`EMPTY'"))
    if (isassigned(fcdo)) fcdo.remove_all
    if (isassigned(fcdv)) fcdv.resize(0)
    if (sz>0) vlsz(vl,sz,0)
    vlsz(vl,0)
  }

//** pad() -- bring all vectors up to same length (of v[0])
func pad () { local sz,ii localobj v1
    sz=-1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    info.set("pads",v1=new Vector(m))
    for ii=0,m-1 v1.x[ii]=cob.v[ii].size
    for ii=0,m-1 if (cob.v[ii].size>sz) sz=cob.v[ii].size
    if (argtype(1)==0) sz=$1 else for ii=0,m-1 if (cob.v[ii].size>sz) sz=cob.v[ii].size
    if (argtype(2)==0) vlsz(cob.vl,sz,$2) else vlsz(cob.vl,sz) // optional fill value
    return sz
  }

obfunc unpad () { local sz,ii localobj v1
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    v1=info.get("pads")
    for ii=0,m-1 cob.v[ii].resize(v1.x[ii])
    return v1
  }

//** size() -- return num of vectors and size of each vector
func size () { local ii,sz,fl,sum
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (m==0) { print "0 x 0" return 0 } // empty
    sz=cob.v.size fl=0
    for ii=1,m-1 if (cob.v[ii].size!=sz) {fl=ii break}
    if (numarg()==1) {
        if ($1==2) {
            tog("DB") sum=0
            printf("\t%d cols x %d rows",m,sum+=v.buffer_size)
            for ii=1,m-1 printf(",%d",cob.v[ii].buffer_size)
            if (out!=nil) {
                printf("\nOUT:\t%d cols x %d rows",m,sum+=out.v.buffer_size)
                for ii=1,m-1 printf(",%d",sum+=out.v[ii].buffer_size)
              }
            printf("\nApprox. usage: %g kB\n",sum*8/1e3)
          } else if ($1==1 && fl && !noheader) { // warn about discrepant sizes
            printf("%s size WARN: cols are not same length: %s:%d %d\n",this,s[fl].s,cob.v[fl].size,sz) }
      } else { // print basic info when have no args
        printf("%d cols x %d rows",m,cob.v.size)
        if (fl) for ii=1,m-1 printf(",%d",cob.v[ii].size)
        print ""
      }
    return cob.v.size
  }

//** resize(#cols[,#rows]) -- augment or dec the number of vectors
// resize("LABEL1","LABEL2",...)
// resize("LABEL1",VEC1,"LABEL2",VEC2) ... put on some vecs of same size
// resize("COL1","COL2"...) if these col names already exist just make a new copy of col
// resize(NQS) -- a horizontal 'append'
func resize () { local oldsz,newsz,i,ii,jj,kk,vsz,na,appfl,o1m,padfl
    cob=this padfl=0
    na=numarg()
    vsz=-1
    if (argtype(na)==2) { i=na
        if (strcmp($si,"PAD")==0) { padfl=1 na-=1 }
      }
    if (na==1) { 
        if        (argtype(1)==0) { 
            appfl=0 
            if ($1<0) newsz=m+$1 else newsz=$1 
          } else if (argtype(1)==2) { 
            newsz=m+1 appfl=2 
          } else if (argtype(1)==1) { // an NQS
            if (size(1)!=$o1.size(1)) {
                printf("NQS resize(NQS) warning: rows differ %d %d\n",size(1),$o1.size(1))}
            o1m=$o1.m
            newsz=m+o1m
            appfl=3
          }
      } else {
        if (argtype(1)==0 && argtype(2)==0) { 
            newsz=$1 appfl=0 
            vsz=$2
          } else if (argtype(1)==2 && argtype(2)==2) { 
            newsz=m+na appfl=2
          } else {
            if (int(na/2)!=na/2) { printf("%s Resize ERR: require even # of args",this)  return -1}
            newsz=m+numarg()/2
            appfl=1
          }
      }
    oldsz=m
    if (m==newsz) {
        if(verbose) printf("No resize -- same size: %s\n",this) 
        return m
      } else if (newsz>m) {
        tmplist.remove_all vlist.remove_all
        for ii=0,m-1 { 
            tmplist.append(v[ii]) tmplist.append(s[ii]) 
            tmplist.append(out.v[ii])
          }
        objref v[newsz],s[newsz]
        if (isassigned(out)) out.resize2(newsz) // create vectors for .out
        jj=-1
        for ii=0,m-1 { 
            v[ii]=tmplist.object(jj+=1) out.s[ii]=s[ii]=tmplist.object(jj+=1) 
            out.v[ii]=tmplist.object(jj+=1)
          }
        for ii=m,newsz-1 { 
            v[ii]=new Vector() out.s[ii]=s[ii]=new String() 
            out.v[ii]=new Vector()
          }
        out.m=m=newsz
        tmplist.remove_all
      } else {
        for (ii=m-1;ii>=newsz;ii-=1) { out.v[ii]=v[ii]=nil out.s[ii]=s[ii]=nil }
        out.m=m=newsz
      }
    x.resize(m) x.fill(0) fcd.resize(m)
    out.x.resize(m) out.x.fill(0) out.fcd=fcd
    if (vsz>=1) for ii=0,m-1 v[ii].resize(vsz)
    if (appfl==1) { // append
        for (ii=1;ii<=na;ii+=2) { i=ii
            if (argtype(i)!=2) { printf("%s RESIZE ERR: arg %d should be str\n",this,i) return -1}
            s[oldsz+(ii-1)/2].s=$si  i+=1
            if (argtype(i)==0) { 
                if ($i>0) v[oldsz+(ii-1)/2].resize($i)
              } else if (argtype(i)==1) { 
                v[oldsz+(ii-1)/2].copy($oi)
              } else {  printf("%s RESIZE ERR2: arg %d should be num or obj\n",this,i) return -1}
          }
      } else if (appfl==2) { 
        for (i=1;i<=na;i+=1) {
            if (argtype(i)!=2) { printf("%s RESIZE ERR3: arg %d should be str\n",this,i) return -1}
            tstr=$si
            if ((jj=fi(tstr,"EXACT"))!=-1) {
                while (fi(tstr,"EXACT")!=-1) sprint(tstr,"%s0",tstr) // find unique name
              }
            kk=oldsz+i-1
            s[kk].s=tstr
            if (jj>=0) { 
                if (fcd.x[jj]==1) {
                    if (verbose) printf("NQS::Resize: converting OBJ Col '%s' to DBL Col '%s'\n",$si,tstr) 
                    v[kk].resize(v[jj].size)
                    for ii=0,v[jj].size-1 v[kk].x[ii]=oform(fcdo.o(v[jj].x[ii]))
                  } else {
                    if (verbose) printf("NQS::Resize copying Col %s to %s\n",$si,tstr) 
                    v[kk].copy(v[jj]) 
                  }
              }
          }
      } else if (appfl==3) {
        for i=0,o1m-1 {
            tstr=$o1.s[i].s
            while (fi(tstr,"NOERR")!=-1) sprint(tstr,"%s0",tstr)
            s[oldsz+i].s=tstr
            v[oldsz+i].copy($o1.v[i])
          }
      }
    chk()
    if (padfl) pad()
    cob=this
    return m
  }

// for resizing the vector for .out
proc resize2 () { local newsz
    newsz=$1
    objref v[newsz],s[newsz]
  }

// grow(NQS) append NQS of same size to this one
func grow () { local ii
    if (m==0) { cp($o1)
      } else if (m!=$o1.m) { printf("%s,%s off different size: %d %d\n",this,$o1,m,$o1.m) return 0 
      } else if (eqobj(cob,out)) {printf("%s ERR: run grow on full db\n",this)  return 0.
      } else for ii=0,m-1 v[ii].append($o1.v[ii])
    return v.size
  }

//** keepcols("LABEL1",...)
func keepcols () { local i,fl
    scr.resize(0)
    for i=1,numarg() {
        if (argtype(i)==2) { 
            if ((fl=fi($si))==-1) return -1 
          } else fl=$i
        scr.append(fl)
      }
    for (i=m-1;i>=0;i-=1) if (! scr.contains(i)) delcol(i)
    return m
  }

//** delcol("LABEL1"[,"LABEL2",...])
func delcol () { local ii,i,a localobj v1
    tog("DB") // start at full db
    a=allocvecs(v1)
    if (argtype(1)==1) {
        v1.copy($o1)
      } else for i=1,numarg() {
        if (argtype(i)==2) { if ((fl=fi($si))==-1) {dealloc(a) return -1} } else fl=$i
        v1.append(fl)
      }
    v1.sort v1.reverse // get rid of last ones first
    for i=0,v1.size-1 { fl=v1.x[i]
        if (fl>m-1 || fl<0) { printf("%s ERR: delcol OOB:%d\n",this,fl)  dealloc(a) return -1 }
        for (ii=fl;ii<m-1;ii+=1) { 
            v[ii]=v[ii+1] s[ii]=s[ii+1] 
            out.s[ii]=s[ii+1] 
          }
        v[m-1]=nil s[m-1]=nil 
        out.v[m-1]=nil out.s[m-1]=nil 
        // if ((x=fcd.x[fl])!=0) {} // ideally should go through and remove all objs or strs
        x.remove(fl) fcd.remove(fl)
        m -= 1
      }
    out.m=m
    chk()
    dealloc(a)
    return m
  }

//** delrow(#)
func delrow () { local ii,jj,kk
    if (numarg()==1) {
        if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
        kk=$1
        if (kk<0 || kk>=cob.v.size) {printf("delrow %d OOR (%d)\n",kk,cob.v.size-1) return -1}
        for ii=0,m-1 cob.v[ii].remove(kk)
      } else { // remove selected
        tog("DB")
        if (ind.size>1) {
            sprint(sstr,"Remove %d rows from main table?",ind.size)
            if (!boolean_dialog(sstr,"OK","Cancel")) { print "Cancelled" return -1 }
          }
        for ii=0,m-1 for (jj=ind.size-1;jj>=0;jj-=1) { kk=ind.x[jj]
            v[ii].remove(kk)
          }
      }
    return v.size
  }

//** getrow(#[,vec])
// getrow("COL",val[,vec]) // return 1st row where "COL" is val
obfunc getrow () { local i,ii,ix,fl,flag localobj v1
    if (argtype(2)==0) { // fetch first matching row according to arg
        if (argtype(1)==2) fl=fi($s1) else fl=$1
        if (fl==-1) return
        if (numarg()==3) v1=$o3 else v1=new Vector()
        v1.resize(m)
        v[fl].fetch($2,vl,v1)
      } else {
        if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
        ix=$1 i=2
        if (argtype(i)==1) {v1=$oi i+=1} else v1=new Vector()
        v1.l2p(cob.vl,ix)
        for (ii=0;argtype(i)!=-1 && ii<v1.size;{i+=1 ii+=1}) if (argtype(i)==3) $&i=v1.x[ii]
      }
    return v1
  }

//** getcol("name") getcol("name",1) returns pointer
// getcol("name"[,VEC]) copies to VEC
// getcol("name",0) -- copies to new vector and returns it
obfunc getcol () { local a,fl,ii localobj v1,vo,l
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return nil
    v1=vo=cob.v[fl]   //  default returns pointer
    if (fcd.x[fl]==1) {
        if (oform(fcdo.o(vo.x[0]))==NOP) {
            printf("NQS getcol Warning: returning indices for an object field\n")
          } else if (oform(fcdo.o(vo.x[0]))==OK) {
            l=new List()
            for ii=0,vo.size-1 l.append(fcdo.o(vo.x[ii]))
            return l
          } else {
            v1=new Vector(vo.size)
            for ii=0,vo.size-1 v1.x[ii]=oform(fcdo.o(vo.x[ii]))
            if (argtype(2)==1) {$o2.copy(v1) v1=$o2}
          }
      } else if (argtype(2)==0) {
        if ($2==0) v1=vo.c
      } else if (argtype(2)==1) { // copy into a preexisting vector
        v1=$o2
        v1.copy(vo)
      }
    return v1
  }

//** getsel("name"[,vec]) returns the select(-1,...) values
obfunc getsel () { local ii,fl localobj v1
    if (eqobj(cob,out)) printf("NQS WARNING: getsel() called after full select()\n")
    if (argtype(1)==2) fl=fi($s1) else fl=$1
    if (fl==-1) return v1
    if (numarg()<2) v1=new Vector(ind.size) else {v1=$o2 v1.resize(ind.size)}
    for ii=0,ind.size-1 v1.x[ii]=v[fl].x[ind.x[ii]]
    return v1
  }

//** renamecol("LABEL1","NEWNAME") -- rename column
func renamecol () { local oldsz
    if (argtype(1)==2) { 
        if ((fl=fi($s1))==-1) return 1
      } else fl=$1
    s[fl].s=$s2
    out.s[fl].s=$s2
    return 1
  }

//** renamecols("LABEL1","LABEL2"...) -- rename all column
proc renamecols () { local oldsz,i
    if (numarg()<m) printf("Renaming only %d/%d cols\n",numarg(),m)
    for i=1,numarg() s[i-1].s=$si
  }

//** sv(FNAME[,APPEND]) save the NQS
// to sv selected -- NQS.select(...) NQS.cp(NQS.out,1) NQS.sv()
proc sv () { local i,j,cd,cd1,vers,a,aflag,nx,no,ns,svinfo localobj xo,v1,v2
    aflag=cd1=0 // flags cd1=1 -- no single value vec compression;
    if (eqobj(cob,out) && verbose) printf("==== WARNING: Saving selected only ====\n") 
    a=allocvecs(v1,v2)
    if (numarg()>0) file=$s1 else aflag=2
    if (numarg()>=2) aflag=$2
    if (numarg()>=3) cd1=$3 // 1:flag for not compressing
    if (aflag==2) { // will just continue saving in the current file
        if (!tmpfile.isopen()) {printf("%s.sv() ERRA - attempt to append but no file open\n",this) return}
      } else if (aflag==1) { tmpfile.aopen(file)
      } else {
        if (tmpfile.ropen(file)) {
            if (batch_flag) {
                printf("NQS sv WARNING overwriting %s\n",file)
              } else if (!boolean_dialog("File exists","Overwrite","Cancel")) { 
                  print "Cancelled" return
              }
          }
        if (! tmpfile.wopen(file)) { printf("%s: can't open file\n",this) return }
      }
    v1.resize(m) v1.fill(0)
    // will be saved without full vectors
    if (cd1==0) for i=0,m-1 if (cob.v[i].ismono(0) && cob.v[i].size>10) {
        cd1=2 // 2 flag for using compression
        v1.x[i]=1
      }
    if (isassigned(fcdo)) foc=fcdo.count else foc=-1
    vers=version(1) // will assist for identifying file type if change format in future
    // dump info
    svninfo=nx=no=ns=0
    for ii=0,info.sz-1 {
        if(info.x[ii]!=ERR) nx+=1 
        if(info.o[ii]!=nil) { xo=info.o[ii]
            if (isojt(xo,v)) no+=1 else printf("NOT SAVING info %s\n",xo) }
      }
    ns=(sfunc.len(info.s)>0)+(sfunc.len(info.t)>0)+(sfunc.len(info.u)>0)+(sfunc.len(info.v)>0)
    if (nx>0 || no>0 || ns>0) { svinfo=1  if (verbose) {
            printf("%s.sv() WARN: saving info but ... 1.assumed sequential; 2. no labels:%d %d %d\n",\
                 this,nx,no,ns) }} else svinfo=0
    cd=mkcodf(cd1,noheader,svinfo,vers) // stuff flags in 1 place
    savenums(m,fcds.count,(cnt=fcd.count(-1)),foc,size(1),cd,vers,0,0) // extra for codes
    wrvstr(file) wrvstr(comment)
    if (!noheader) for i=0,m-1 wrvstr(s[i].s)
    if (svinfo) {
        savenums(nx,no,ns) 
        if (nx>0) savenums(&info.x,nx)
        for ii=0,no-1 {xo=info.o(ii) if (isojt(xo,v)) xo.vwrite(tmpfile) }
        if (ns>0) wrvstr(info.s) if (ns>1) wrvstr(info.t) 
        if (ns>2) wrvstr(info.u) if (ns>3) wrvstr(info.v) 
      }
    fcd.vwrite(tmpfile)
    for i=0,fcds.count-1 wrvstr(fcds.object(i).s)
    for i=0,foc-1 {
        if (isojt(fcdo.object(i),v)) { 
            fspitchar(1,tmpfile)  // 1 for vector
            fcdo.o(i).vwrite(tmpfile)
          } else if (isojt(fcdo.object(i),this)) { 
            fspitchar(2,tmpfile)  // 2 for NQS
            fcdo.o(i).sv()
          } else {
            printf("NQS:sv() WARNING: Can't save obj %s for %s\n",fcdo.o(i),this)
            fspitchar(0,tmpfile) // 0 for nil
          }
      }
    if (cnt>0) for i=0,fcd.size-1 if (fcd.x[i]==-1) { 
        savenums(fcdl.object(i).count)
        for j=0,fcdl.object(i).count-1 wrvstr(fcdl.object(i).object(j).s)
      }
    for i=0,m-1 {
        if (cd1==2 && v1.x[i]==1) {
            savenums(-1e9,cob.v[i].size,cob.v[i].x[0])
          } else if (fcd.x[i]==CODTY) {
            cob.v[i].vwrite(tmpfile,4) // must save CODE fully
          } else {
            cob.v[i].vwrite(tmpfile,svsetting)
          }
      }
    x.vwrite(tmpfile)
    if (aflag!=2) tmpfile.close 
    dealloc(a)
  }


//** rd(FNAME[,FLAG]) read format saved by sv()
// flag==2 - only read header
func rd () { local n,vers,hflag,cd,cd1,cd3,cd4,ii,oco,nx,no,ns,svinfo localobj v1,xo
    hflag=0 cob=this
    a=allocvecs(v1)
    if (numarg()>=1) if (argtype(1)==2) {
        if (!tmpfile.ropen($s1)) { printf("%s: can't open file %s\n",this,$s1) dealloc(a) return 0 }
      } // else continue reading from current point in file
    if (numarg()>=2) hflag=$2 // only read header 
    cnt=fc=foc=0 
    // backward compatible -- if only 2 vals then cnt=0, cd1-4 unused at present
    n=readnums(&ii,&fc,&cnt,&foc,&v0sz,&cd,&svvers,&cd3,&cd4)
    if (n==0) {dealloc(a) return 0}
    if (n<9) v0sz=cd1=svvers=cd3=cd4=-1
    if (svvers<640) { 
        cd1=cd noheader=cd3 svinfo=0
      } else { 
        uncodf(cd,&cd1,&noheader,&svinfo,&vers)
        if (vers!=svvers) printf("INT DECODE VERS ERR in NQS.rd(): %d %d\n",svvers,vers)
      }
    if (cd1==2 && hflag==1) printf("NQSrdWARN0: can't do partial reads on compressed: %s\n",$s1)
    if (ii!=m) resize(ii)
    rdvstr(file) rdvstr(comment)
    if (sfunc.len(file)==0 && numarg()>0) file=$s1
    if (!noheader) for i=0,m-1 rdvstr(s[i].s)
    if (svinfo) {
        readnums(&nx,&no,&ns) 
        if (nx>0) readnums(&info.x[0])
        for ii=0,no-1 {info.o(ii)=new Vector() info.o(ii).vread(tmpfile) }
        if (ns>0) rdvstr(info.s) if (ns>1) rdvstr(info.t) 
        if (ns>2) rdvstr(info.u) if (ns>3) rdvstr(info.v) 
      }
    fcd.vread(tmpfile)
    fcds.remove_all
    if (isassigned(fcdl)) fcdl.remove_all
    for i=0,fc-1 {  fcds.append(Xo=new String()) rdvstr(Xo.s) }
    if (foc>=0) { fcdo=new List() out.fcdo=fcdo }
    for i=0,foc-1 { 
        oco=fgchar(tmpfile)
        if (oco==1) {
            fcdo.append(Xo=new Vector()) 
            Xo.vread(tmpfile) 
          } else if (oco==2) {
            fcdo.append(Xo=new NQS())
            Xo.rd() 
          } else if (oco==0) {
            printf("NQS:rd() WARNING: Nil being read for %s\n",this)
          } else {
            printf("NQS:rd() ERROR: unrecognized char %d for %s\n",oco,this)
          }
      }
    if (cnt>0) for i=0,fcd.size-1 if (fcd.x[i]==-1) { 
        readnums(&cnt)
        Yo=new List()
        for j=0,cnt-1 {Xo=new String() Yo.append(Xo) rdvstr(Xo.s)}
        useslist(i,Yo)
      }
    if (hflag==1) { // v0sz will tell size of all vectors
        tell=tmpfile.tell
        tmpfile.seek(0,2)
        tellend=tmpfile.tell()
        if (v0sz==-1) printf("%s: rd header: can't do seeks since v's not same size:%s\n",this,file)
        dealloc(a)
        return v0sz
      } else {
        v0sz=-1 // indicates that everything has been read in
        for i=0,m-1 { 
            v[i].vread(tmpfile)
            if (v[i].size>2) if (v[i].x[0]==-1e9) { 
                ii=v[i].x[2]
                v[i].resize(v[i].x[1]) 
                v[i].fill(ii) 
              }
          }
        x.vread(tmpfile)
      }
    if (foc==0) for ii=0,fcd.size-1 if (fcd.x[ii]==1) v[ii].fill(-1) // clear obj pointers
    out.cp(this,0) // leave vectors empty
    chk()
    dealloc(a)
    return 1
  }

//** rdpiece() read a section of each vector
func rdpiece () { local ii,ix,end,jump,loc,bswap
    ix=$1
    if (numarg()>=2) bswap=$2 else bswap=0
    tmpfile.seek(tell+8) // start of first one
    if (ix<0) {printf("%s:rdpiece ERR: no room: neg index\n",this) return 0}
    if (ix*chunk>v0sz) return 0
    if ((ix+1)*chunk>v0sz) end=v0sz-ix*chunk else end=chunk
    for ii=0,m-1 {
        loc=tell+8+ii*(v0sz*4+8)+ix*4*chunk
        tmpfile.seek(loc)
        if (loc+end*4>tellend){printf("%s:rdpiece ERRA: ran out: %d %d",this,loc+end,tellend) return 0}
        v[ii].fread2(tmpfile,end,3+bswap)
      }
    return 1
  }

// rddif(filename) reads a .dif (data interchange format) file as can be exported from eg Excel
// NB to save to .dif from ooffice use File->Save as
// to save other than sheet#1 need to delete first (select didn't work): Edit->Sheet->Delete
proc rddif () { local a,ii,x,done,val,sz localobj oq,st
    st=new String2()
    empn=0 fln_lnum=1
    file=$s1 
    tmpfile.ropen(file)
    fln(3,st) // move forward 3 to pick up name
    sprint(comment,"'%s' from %s",st.s,file)
    x=fln(2,st) resize(x)
    x=fln(3,st) clear(x)  // should be 1 larger in size, fill with ERR
    fln("BOT",st,0) // look for precise string "BOT"
    for ii=0,m-1 {
        fln(2,st) 
        if (sfunc.len(st.s)>0) { repl_mstr(st.s," ","_") s[ii].s=st.s
          } else s[ii].s="UUEMPUU" // code for later
      }
    fln("BOT",st,0)
    // now pick up line pairs which are "text"\n1,0 OR 0,val\nV
    done=0
    while (!done) {
        for ii=0,m-1 {
            x=fln(1,st) val=x st.t=st.s // don't need to save st.t?
            y=fln(1,st)
            if (strcmp(st.s,"V")==0) {
                v[ii].append(val)
              } else if (sfunc.len(st.s)==0) { // empty
                if (fcd.x[ii]==2) { v[ii].append(0) // empty string
                  } else v[ii].append(OK)
              } else { // a string
                if (fcd.x[ii]!=2) { printf("STRDEC:%d,%d ",ii,v[ii].size+1) fcd.x[ii]=2 }
                sval=st.s
                v[ii].append(newval(2,ii))
              }
            // print val,st.t,x,st.s
          }
        fln(2,st)
        if (strcmp(st.s,"EOD")==0) { done=1 break }
        if (strcmp(st.s,"BOT")!=0) {
            printf("ERR: should be at BOT at line#: %d %d %s\n",_lnum,v.size,st.s)
            done=1 break // done is redundant
          }
      }
    sz=v.size
    for (ii=m-1;ii>=0;ii-=1) { // check the empty columns
        if (strcmp(s[ii].s,"UUEMPUU")==0) {
            if (v[ii].count(OK)!=sz) {
                printf("WARN: nonempty vector with empty header for col %d\n",ii)
                sprint(s[ii].s,"COL%d",ii)
              } else delcol(ii)
          }
      }
  }

// read a .atf file which is the ascii version of a pCLAMP .abf file
proc rdatf () { local ii,rows,cols localobj st,v1,v2
    st=new String2() v1=new Vector() v2=v1.c
    fln_lnum=1
    file=$s1 rows=file_len($s1) tmpfile.ropen($s1)
    fln(2,st)
    sscanf(st.s,"%*d%*[^0-9]%d",&cols)
    resize(cols)
    clear(rows)
    fln("CURRENT",st)
    repl_str(st.s,"CURRENT","") repl_mstr(st.s,"\t",",") sfunc.right(st.s,1)
    split(st.s,v1)  info.o=v1
    fln("^Time",st)
    if (v1.size!=cols-1) {printf("rdatf() discrepancy in %s: %d %d\n",$s1,v1.size,cols-1) return }
    s[0].s="t" 
    for ii=1,m-1 {
        sprint(s[ii].s,"pA%d",v1.x[ii-1]) 
        if (strm(s[ii].s,"-")) repl_str(s[ii].s,"-","m")
      }
    while (tmpfile.gets(st.s)!=-1) {
        fln_lnum+=1
        repl_mstr(st.s,"\t",",")
        split(st.s,v2)
        if (v2.size!=m) printf("rdatf() prob at line %d: %s\n",fln_lnum-1,st.s) else append(v2)
      }
  }


//** func rdcols()
// reads columns of ascii with optional labels at the top
// still won't read strings -- prob not worth fixing any more
// -- may want to go back to v617 for this
func rdcols () { local i,ii,cols,li,errflag,num,hflag,loc localobj fi,st
    hflag=1 errflag=0 st=new String2()
    if (argtype(1)==2) {
        fi=tmpfile st.s=$s1 loc=0 tflg=0
        if (! fi.ropen(st.s)) { printf("\trdcols ERR0: can't open file \"%s\"\n",st.s) return 0}
      } else {fi=$o1 fi.getname(st.s) hflag=0 loc=fi.tell() tflg=1}
    if (fcd.count(2)+fcd.count(0)!=m) {printf("\trdcols ERRA: only rd strs and dbls\n") return 0}
    if (fi.scanstr(sstr)==-1) {printf("\trdcols ERR1: file \"%s\"\n",st.s) return 0}
    if (fcd.count(2)==0 && !isnum(sstr)) hflag=1 else hflag=0 // hflag=0 -> no header
    if (numarg()>1) { // names of columns
        resize(numarg()-1)
        for i=2,numarg() sethdrs(i-2,$si)
        if (hflag) {
            printf("Header in file %s: %s vs %s\n",st.s,sstr,s[0].s)
            hflag=2
          }
      } else if (!hflag) printf("No Header read for %s\n",st.s)
    cols=0
    if (hflag==1) {
        fi.seek(0)
        fi.scanstr(sstr) 
        while (! isnum(sstr)) { 
            cols+=1 
            resize(sstr)
            fi.scanstr(sstr) 
          }
        fi.seek(0) fi.gets(sstr) loc=fi.tell
        ncols=cols
      } else {
        ncols=fcd.count(0) // assume that NQS was set up ahead
        cols=fcd.size
        if (cols==0) { 
            fi.seek(0)
            fi.gets(sstr) 
            ncols=cols=count_substr(sstr,"[-+0-9.][-+0-9.eE]*")
            if (cols==0) printf("NQS::ERR2A reading first line: %s (%s)\n",sstr,this)
            resize(cols)
            fi.seek(0)
          }
      }
    li=file_len(st.s)
    if (hflag || tflg) li-=1 // hack -- assuming that threw away one line 
    printf("%d cols; %d lines of data in %s.\n",cols,li,st.s)
    fi.seek(loc) // back to top
    // WARN: this will screw up if numbers are to be included as strings
    if (fcd.count(2)==0) {
        num=scr.scanf(fi,li*ncols) // note that this will skip over non-nums
        if (num!=li*ncols) { // err not reached since scanf dumps out
            printf("WARNING: expected %d vals; found %d\n",li*ncols,num) errflag=3 }
          if (fi.scanstr(sstr)>-1) { 
              printf("WARNING: %s found after reading in %d vals\n",sstr,li*cols) errflag=4 }
      }
    fi.seek(loc)
    for ii=0,cols-1 {
        if (fcd.x[ii]>0) continue
        v[ii].resize(li)
        v[ii].copy(scr,0,ii,li*ncols-1,1,cols) // v[ii].mcol(scr,ii,cols)
      }  
    if (fcd.count(0)!=m) {
        fi.seek(loc) // need to pick up some strings
        for ii=0,li-1 for jj=0,cols-1 { 
            if (fi.scanstr(st.t)==-1) {printf("ERR reading at %d\n",fi.tell()) errflag=5 return 0}
            if (fcd.x[jj]==2) v[jj].append(newval(2,jj))
          }
      }
    if (errflag) { printf("rdcols ERR%d\n",errflag) return 0 }
    return cols
  }

//** func rdcols2() -- goes line by line so as to handle cols with strings
func rdcols2 () { local i,ii,cols,li,errflag,num,loc,x,ln localobj fi,st,ol,sep
    errflag=0 st=new String2() ol=new List() sep=new String()
    if (argtype(1)==2) {
        fi=tmpfile st.s=$s1 loc=0 tflg=0 i=2
        if (! fi.ropen(st.s)) { printf("\trdcols2 ERR0: can't open file \"%s\"\n",st.s) return 0}
      } else {fi=$o1 fi.getname(st.s) loc=fi.tell() tflg=1}
    if (argtype(i=2)==2) sep.s=$si else sep.s="[ 	]+"
    if (strm(sep.s,"[^\\]\\|")) printf("WARNING: unescaped | (means OR) in separator '%s' for %s\n",sep.s,this)
    if (fcd.count(2)+fcd.count(0)!=m) {printf("\trdcols2 ERRA: only rd strs and dbls\n") return 0}
    if (fi.gets(sstr)==-1) {printf("\trdcols2 ERR1: file \"%s\"\n",st.s) return 0}
    if (m==0) {printf("\trdcols2 ERR1A: NQS %s must be preinitialized for rdcols2\n",this) return 0}
    ncols=fcd.count(0) cols=fcd.size() li=file_len(st.s)
    chop(sstr) split(sstr,ol,sep.s) // check that first lline
    if (ol.count!=cols) {printf("\trdcols2 ERR1B: %d != %d::",cols,ol.count) 
        for ii=0,ol.count-1 printf("%s;",ol.o(ii).s)
        print "" return 0}
    printf("%d cols; %d lines of data in %s.\n",cols,li,st.s)
    fi.seek(loc) // back to top
    for (ln=1;tmpfile.gets(sstr)!=-1;ln+=1) {
        chop(sstr)
        if ((num=split(sstr,ol,sep.s))!=cols) printf("rdcols2() PROBLEM (%d) on line %d: %s [%s]\n",num,ln,sstr,sep.s)
        // for i=0,ol.count-1 printf("%s ",ol.o(i).s) print "" // 
        for ii=0,m-1 {
            if (fcd.x[ii]==2) {
                sval=ol.o(ii).s 
                v[ii].append(newval(2,ii))
              } else { 
                sscanf(ol.o(ii).s,"%g",&x)
                v[ii].append(x)
              }
          }
      }
    return cols
  }

//** func svR([filename])
// saves in a format that can be read by my rdnqs.R program
func svR () { local ii,jj,cols,li,errflag,num
    errflag=0
    if (eqobj(cob,out) && verbose) printf(" *Selected* ") 
    if (numarg()==1) sstr=$s1 else {
        sstr=file
        repl_mstr(sstr,"\.nqs$",".nqR")
      }
    if (tmpfile.ropen(sstr)) if (!boolean_dialog("File exists","Overwrite","Cancel")) { 
        print "Cancelled" return 0 }
    if (! tmpfile.wopen(sstr)) { printf("\tsvR ERR0: can't open file \"%s\"\n",sstr) return 0}
    tmpfile.printf("%d\n",m)
    for ii=0,m-1 tmpfile.printf("%s\n",s[ii].s)
    for ii=0,m-1 cob.v[ii].vwrite(tmpfile)
    tmpfile.close()
    return ii
  }

//** func svcols(filename)
// saves numeric columns for R -- read with aa=read.table("filename")
func svcols () { local ii,jj,cols,li,errflag,num
    errflag=0
    if (tmpfile.ropen($s1)) if (!boolean_dialog("File exists","Overwrite","Cancel")) { 
        print "Cancelled" return 0 }
    if (! tmpfile.wopen($s1)) { printf("\tsvcols ERR0: can't open file \"%s\"\n",$s1) return 0}
    sstr2="\t"  // delimiter
    for ii=0,m-1 tmpfile.printf("%s%s",s[ii].s,sstr2)
    tmpfile.printf("\n")
    for ii=0,size(1)-1 {
        tmpfile.printf("%d%s",ii,sstr2)
        for jj=0,m-1 {
            getval(jj,v[jj].x[ii],ii) 
            tmpfile.printf("%g%s",nval,sstr2)
          }
        tmpfile.printf("\n")
      }
    tmpfile.close
    return ii
  }

//** join(nqs2,"PIVOT"[,"COLA",...]) 
// [selected fields of] nqs2 will be appended to this
// index field should only appear once in nqs2
func join () { local vn,vn1,vn2,i,ii,jj,kk,val localobj al,bl
    al=new List() bl=new List()
    if ((vn1=fi($s2))==-1 || (vn2=$o1.fi($s2))==-1) { 
        printf("NQS::join() %s not found in both %s %s\n",$s2,$o1,this) return -1 }
    if (!v[vn1].ismono) {   print "Sorting A..."  sort(vn1) }
    if (!$o1.v[vn2].ismono){print "Sorting B..."  $o1.sort(vn2) }
    if (!$o1.v[vn2].ismono(2)){ printf("Pivot B has repeats\n") return -1 }
    scr.resize($o1.m) scr.fill(-1)
    if (numarg()>2) for i=3,numarg() { 
        if ((vn=$o1.fi($si))==-1) return -1
        if (fi($si,"NOERR")!=-1) sprint(tstr,"%sB",$si) else tstr=$si
        scr.x[vn]=resize(tstr)-1 // index for this
      } else for ii=0,$o1.m-1 if (! strcmp($o1.s[ii].s,$s2)==0) { // don't add pivot field
        if ($o1.fcd.x[ii]!=0) {printf("%s:join ERRA not double field\n",this) return -1}
        if (fi($o1.s[ii].s,"NOERR")!=-1) sprint(tstr,"%sB",$o1.s[ii].s) else tstr=$o1.s[ii].s
        scr.x[ii]=resize(tstr)-1 // index for this
      } 
    pad()
    for jj=0,scr.size-1 { kk=scr.x[jj]
        if (kk!=-1) { al.append(v[kk]) bl.append($o1.v[jj]) }
      }
    v[vn1].join($o1.v[vn2],al,bl)
    return m
  }

//** cp(NQS[,VEC_COPY]) copy 1 NQS to another
// default: VEC_COPY==1; side effect of NO_VEC_COPY is no fcd,fcds creation
proc copy () { if (numarg()==2) cp($o1,$2) else cp($o1) }
proc cpout () { // copy .out to this
    cob=this
    for ii=0,m-1 v[ii].copy(out.v[ii])
  }
proc cp () { local ii,csz,veccp,outcp localobj o,oo
    cob=this
    csz=$o1.m
    outcp=0
    if (numarg()==2) veccp=$2 else veccp=1
    if (m!=csz) if (isassigned(out)) resize(csz) else {
        resize2(csz)
        outcp=1
      }
    noheader=$o1.noheader
    objl.remove_all
    for ii=0,$o1.objl.count-1 { objl.append($o1.objl.object(ii)) }
    file=$o1.file comment=$o1.comment
    if (outcp) for ii=0,m-1 { 
        if (!noheader) s[ii]=up.s[ii]
        v[ii]=new Vector()
      } else for ii=0,m-1 { 
        if (!noheader) s[ii].s=$o1.s[ii].s 
        if (veccp) v[ii].copy($o1.v[ii]) // 2nd arg to not copy vectors
      }
    if (veccp==1) {  // full copy
        fcd.copy($o1.fcd) 
        for ii=0,$o1.fcds.count-1 fcds.append($o1.fcds.object(ii)) // strings not being copies
        if (isobj($o1.fcdl,"List")) { fcdl=new List() out.fcdl=fcdl
            for ii=0,$o1.fcdl.count-1 fcdl.append($o1.fcdl.object(ii)) }
        if (isobj($o1.fcdo,"List")) { fcdo=new List() out.fcdo=fcdo
            o=$o1.fcdo
            for ii=0,o.count-1 {
                if (isojt(o.o(ii),v)) { // a vector
                    oo=new Vector(o.o(ii).size) oo.copy(o.o(ii)) fcdo.append(oo) 
                  } else if (isojt(o.o(ii),out)) { // a vector
                    oo=new NQS() oo.cp(o.o(ii)) fcdo.append(oo) 
                  } else {
                    printf("Can't copy a %s\n",o.o(ii)) return 
                  }
              }
          }
      } else if (! isassigned(fcd)) { // use pointers for .out
        fcd=$o1.fcd fcds=$o1.fcds fcdl=$o1.fcdl tmplist=$o1.tmplist
      } 
    x.copy($o1.x) x.resize(m)
    scr.copy($o1.scr) ind.copy($o1.ind)
  }

//** eq(NQS) -- just check the vecs
func eq () { local ii,jj,af,ix,epsilon,sz localobj v1,v2,o,xo,yo
    o=$o1
    if (numarg()>=2) { 
        af=1 
        if ($2!=1) epsilon=$2 else epsilon=1e-5
      } else af=0 // af is flag for approx eq
    if (o.m!=m) { printf("# of cols differ %d vs %d\n",m,o.m) return 0 }
    if (!noheader) for ii=0,m-1 if (strcmp(o.s[ii].s,s[ii].s)!=0) { 
        printf("%d col names differ: %s vs %s",ii,s[ii].s,o.s[ii].s) return 0 }
    for ii=0,m-1 if (o.v[ii].size != v[ii].size) { 
        printf("%d col lengths differ: %d vs %d",ii,v[ii].size,o.v[ii].size) return 0 }
    a=allocvecs(v1,v2)
    if ((sz=info.sz)!=o.info.sz) printf("Info sz difference") else {
        v1.resize(sz) v2.resize(sz) v1.d2v(&info.x) v2.d2v(&o.info.x) 
        if (!v1.eq(v2)) {printf("info x's differ:") vlk(v1) vlk(v2)}
        for ii=0,sz-1 { xo=info.o[ii] yo=o.info.o[ii]
            if (!isojt(xo,yo)) {printf("Different info objs at %d: %s %s\n",ii,xo,yo)
              } else if (isojt(xo,v) || isojt(xo,this)) if (!xo.eq(yo)) {
                printf("Different info objs at %d: %s %s %d %d\n",ii,xo,yo,xo.size,yo.size)}
          }
        xo=o.info
        if (strcmp(info.s,xo.s)!=0 || strcmp(info.t,xo.t)!=0 || strcmp(info.u,xo.u)!=0 ||\
            strcmp(info.v,xo.v)!=0) { printf("Different strings:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",\
                                               info.s,xo.s,info.t,xo.t,info.u,xo.u,info.v,xo.u) }
      }
    if (af) {
        for ii=0,m-1 {
            v1.copy(v[ii])
            v1.sub(o.v[ii])
            v1.abs()
            if (v1.max>epsilon) {
                ix=v1.max_ind
                if (noheader) {
                    printf("%d cols differ: \n",ii,ix,v[ii].x[ix],o.v[ii].x[ix])
                  } else printf("%s cols differ: \n",s[ii].s,ix,v[ii].x[ix],o.v[ii].x[ix])
              }
          }
        if (numarg()>=3) $o3.copy(v1)
      } else for ii=0,m-1 if (! o.v[ii].eq(v[ii])) { 
        if (noheader) { printf("%d cols differ at ",ii)
          } else printf("%s cols differ at ",s[ii].s)
        for jj=0,v[ii].size-1 if (o.v[ii].x[jj] != v[ii].x[jj]) {
            printf("element %d: %g vs %g",jj,v[ii].x[jj],o.v[ii].x[jj])
            if (o.v[ii].x[jj]-v[ii].x[jj]<1e-5) {
                printf("\n\tTry for approximately equal using flag: nq.eq(nq2,1)\n")}
            return 0
          }
      }
    if (! fcdseq(o)) return 0
    if (! fcdoeq(o)) return 0
    dealloc(a)
    return 1
  }

//** fcdseq() -- check that string lists are identical in two NQSs -- this is
// sufficient but not nec for comparing string columns for JOIN
// in order to use JOIN must share same fcds by setting up with strdec(NQS,...)
// (could break out separate lists for each str column -- tried in nqs.hoc220;
//  but separate lists would be problem: two columns might require same indices if 
//  either could be used to for "JOIN" to another nqs
func fcdseq () { local ii,jj,cnt
    cnt=fcds.count
    if (eqobj(fcds,$o1.fcds)) {
        printf("%s %s already share string list fcds\n",this,$o1)
        return 1
      }
    if (cnt!=$o1.fcds.count) {
        printf("DIFFERING (1) string lists (fcds) %d %d\n",fcds.count,$o1.fcds.count)
        return 0
      }
    for ii=0,cnt-1 if (!strcmp(fcds.object(ii).s,$o1.fcds.object(ii).s)==0) {
        printf("DIFFERING (2) string lists (fcds) %d:%s vs %s",ii,fcds.object(ii).s,$o1.fcds.object(ii).s)
        return 0
      }
    if (numarg()==2) return 1 // just check fcds and not fcd and fcdl
    if (! fcd.eq($o1.fcd)) {
        printf("DIFFERING (3) col keys (fcd) ") vlk(fcd) vlk($o1.fcd)
        return 0
      }
    if (! isassigned(fcdl) && isassigned($o1.fcdl)) {
          printf("DIFFERING (4) uselists() string lists: absent in %s\n",this)
          return 0
      }
    if (isassigned(fcdl)) {
        if (! isassigned($o1.fcdl)) {
            printf("DIFFERING (5) uselists() string lists absent in %s\n",$o1)
            return 0
          }
        if (fcdl.count!=$o1.fcdl.count) {
            printf("DIFFERING (6) uselists() list list counts %d vs %d",fcdl.count,$o1.fcdl.count)
            return 0
          }
        for ii=0,fcdl.count-1 if (fcd.x[ii]==-1) {
            if (!isobj(fcdl.object(ii),"List") || !isobj($o1.fcdl.object(ii),"List")) {
                printf("DIFFERING (7) uselists() string lists (fcdl.obj) %d:%s vs %s",ii,\
                           fcdl.object(ii),$o1.fcdl.object(ii))
                return 0
              }
            if (fcdl.object(ii).count != $o1.fcdl.object(ii).count) {
                printf("DIFFERING (8) uselists() string lists counts (fcdl.obj) %d:%d vs %d",ii,\
                           fcdl.object(ii).count,$o1.fcdl.object(ii).count)
                return 0
              }
            for jj=0,fcdl.object(ii).count-1 {
                if (!strcmp(fcdl.object(ii).object(jj).s,$o1.fcdl.object(ii).object(jj).s)==0) {
                    printf("DIFFERING (9) uselists() string lists (fcdl.obj) %d,%d:%s vs %s",ii,jj,\
                           fcdl.object(ii).object(jj).s,$o1.fcdl.object(ii).object(jj).s)
                    return 0
                  }
              }
          }
      }
    return 1
  }

//** fcdoeq() -- check that object lists are identical in two NQSs
func fcdoeq () { local ii,jj,cnt
    if (! isassigned(fcdo) && ! isassigned($o1.fcdo)) return 1
    if (! isassigned(fcdo)) {
        printf("No object list in %s\n",this)
        return 0
      }
    if (! isassigned($o1.fcdo)) {
        printf("No object list in %s\n",$o1)
        return 0
      }
    cnt=fcdo.count
    if (cnt!=$o1.fcdo.count) {
        printf("DIFFERING (1) object lists (fcdo) %d %d\n",fcdo.count,$o1.fcdo.count)
        return 0
      }
    for ii=0,cnt-1 {
        if (!isojt(fcdo.o(ii),$o1.fcdo.o(ii))) {
            printf("DIFFERING (2) obj lists (fcdo) %s,%s (%s,%s)",fcdo.o(ii),$o1.fcdo.o(ii),this,$o1)
            return 0
          }
        if (1) { // vector and nqs both use .eq() a vector: isojt(fcdo.o(ii),v)
            if (! fcdo.o(ii).eq($o1.fcdo.o(ii))) {
                printf("DIFFERING obj lists (fcdo) : differ %s,%s (%s,%s)",\
                       fcdo.o(ii),$o1.fcdo.o(ii),this,$o1)
                return 0
              }
          }
      }
    return 1
  }

//** strdec() -- declare these columns to be strings
func strdec () { local i,min
    min=1
    if (eqobj(cob,out)) {
        printf("strdec() ERR: string fields can only be declared at top level\n") return 0}
    if (numarg()==0) { 
        printf("strdec(NAME[,NAME1 ...])\n\tdeclare these field to be string fields\n") return 0}
    out.fcd=fcd
    if (argtype(1)==1) {
        if (fcds.count>0) if (! fcdseq($o1,1)) {  // just check fcds and not fcd, fcdl
            printf("Pre-existing string lists differ; unable to join %s %s\n",this,$o1)
            return 0
          }
        fcds=$o1.fcds // share string list to allow JOIN on a string field
        min=2 
      }
    for i=min,numarg() { 
        if (argtype(i)==2) fl=fi($si) else fl=$i
        if (fl>-1) {
            fcd.x[fl]=2
            sval="`EMPTY'"
            newval(2,fl)   // don't want to put on more than one
          }
      }
    return 1
  }

//** fdec() -- declare these columns to be files with methods
func fdec () { local i,j,na
    if (eqobj(cob,out)) {
        printf("fdec() ERR: object fields can only be declared at top level\n") return 0}
    na=numarg()
    if (na==0 || na%3!=0) { printf("fdec(\"COLNAME\",\"METHOD\",\"PATH\"[,...])\n") return 0 }
    out.fcd=fcd
    for (j=1;j<=na;j+=3) { 
        i=j
        if (argtype(i)==0) fl=$i else fl=fi($si)
        if (fl==-1) return 0 else fcd.x[fl]=FUNTY
        i+=1 s[fl].t=$si i+=1 
        if (sfunc.len($si)==0) { sprint(s[fl].t,"%s(\"",s[fl].t)
          } else sprint(s[fl].t,"%s(\"%s/",$si,s[fl].t)
      }
    if (! isobj(fcdo,"List")) { fcdo=new List() out.fcdo=fcdo }
    return 1
  }

obfunc methget () { local fl,ix localobj o
    fl=$1 ix=$2
    if ((o=fcdo.o(ix).o)!=nil) return o
    sprint(tstr,"tmpobj=%s%s\")",s[fl].t,fcds.o(fcdo.o(ix).x).s)
    execute(tstr)
    o=tmpobj // need to use external global tmpobj
    fcdo.o(ix).o=o
    return o
  }

//** coddec() -- declare these columns to be coded
func coddec () { local i,min
    min=1
    if (eqobj(cob,out)) {
        printf("coddec() ERR: CODE fields can only be declared at top level\n") return 0}
    if (numarg()==0) { 
        printf("coddec(NAME[,NAME1 ...])\n\tdeclare these field to be code fields\n") return 0}
    out.fcd=fcd
    for i=min,numarg() { 
        fl=fi($si)
        if (fl>-1) fcd.x[fl]=CODTY
      }
    return 1
  }

//** deriv("COLA",...) generates a col with differences between each row value and preceding
proc deriv () { local fl,i
    if (eqobj(cob,out)) { printf("Take deriv on whole set only\n") return }
    for i=1,numarg() { 
        if (argtype(i)==0) fl=$i else fl=fi($si)
        if (fl==-1) return
        sprint(tstr,"%s'",s[fl].s)   resize(tstr)
        v[m-1].deriv(v[fl],1,1)
        v[m-1].insrt(0,0)
      }
  }

//** odec() -- declare these columns to be objects
func odec () { local i
    if (eqobj(cob,out)) {
        printf("odec() ERR: object fields can only be declared at top level\n") return 0}
    if (numarg()==0) { 
        printf("odec(NAME[,NAME1 ...])\n\tdeclare these field to be object fields\n") return 0}
    out.fcd=fcd
    for i=1,numarg() { 
        if (argtype(i)==0) fl=$i else fl=fi($si)
        if (fl>-1) fcd.x[fl]=1
      }
    if (! isobj(fcdo,"List")) { fcdo=new List() out.fcdo=fcdo }
    return 1
  }

//** vdec() -- declare this column to be pointers to fcdv
func vdec () { local i
    if (eqobj(cob,out)) {
        printf("odec() ERR: vec ptr fields can only be declared at top level\n") return 0}
    if (numarg()==0) { 
        printf("vdec(NAME[,NAME1 ...])\n\tdeclare these field to be vec ptr fields\n") return 0}
    out.fcd=fcd
    for i=1,numarg() { 
        if (argtype(i)==0) fl=$i else fl=fi($si)
        if (fl>-1) fcd.x[fl]=VECTY // type for vec
      }
    if (! isobj(fcdv,"Vector")) { fcdv=new Vector(1e4) fcdv.resize(0) out.fcdv=fcdv }
    return 1
  }

//** mo([flag][,STAT1,...]) -- create global objectvars that point to the vectors
// first time use flag=1 to create new global objrefs, else just shift them
// flag=1 reassign objl but don't care if they already exist
// flag=2 don't print out the assignments
// flag=3 reassign objl; make sure they're unique
// flag=4 clear the vectors
// should we also create a set of global scalars to assign to in an iterator?
proc mo () { local ii,flag,i,hf
    if (numarg()>=1) flag=$1 else flag=0 // flag:create objrefs
    if (flag==1 || flag==3) {
        if (objl.count>0) {
            if (flag==3) if (batch_flag) {
                printf("NQS mo(3) WARNING: Renamed object pointers.\n")
              } else if (! boolean_dialog("New name object pointers?","YES","NO")) return
            if (flag==1) if (batch_flag) {
                printf("NQS mo(1) WARNING: Rassigned object pointers.\n")
              } else if (! boolean_dialog("Reassign object pointers?","YES","NO")) return
          }
        objl.remove_all
        for ii=0,m-1 if (sfunc.len(s[ii].s)>0) {
            sstr=s[ii].s
            repl_mstr(sstr,"[^A-za-z0-9]","",execstr)
            sprint(sstr,"%sv",sstr)
            if (flag==3) { // make sure it's unique
                hf=0
                while (name_declared(sstr)) { hf=1
                    printf("%s exists ... ",sstr)
                    sprint(sstr,"%sv",sstr) 
                  } 
                if (hf) printf(" -> %s\n",sstr)
              } else if (name_declared(sstr)) printf("%s reassigned: ",sstr)
            printf("%s -> v[%d] (%s)\n",sstr,ii,s[ii].s)
            sprint(execstr,"objref %s",sstr) execute(execstr)
            sprint(execstr,"%s=%s",sstr,v[ii]) execute(execstr)
            objl.append(new String(sstr))
          }
        sprint(execstr,"objref indv") execute(execstr)
        sprint(execstr,"indv=%s",ind) execute(execstr)
      } else {
        if (objl.count==0) { 
            printf("Must create vecs with mo(1)\n") 
          } else if (objl.count>m) { 
            printf("STAT:mo ERR: wrong objref count in objl: %d vs %d\n",objl.count,m)
            return
          } else {
            if (objl.count<m) { 
                printf("STAT:mo WARNING: unreferenced vecs for %s: refs %d<m %d\n",this,objl.count,m) }
            for ii=0,objl.count-1 {
                Xo=objl.object(ii)
                if (flag==0) printf("%s -> %s.v[%d] (%s)\n",Xo.s,this,ii,s[ii].s)
                if (flag==4) {sprint(execstr,"%s=nil",Xo.s) 
                  } else        sprint(execstr,"%s=%s",Xo.s,v[ii]) 
                execute(execstr)
              }
          }
        sprint(execstr,"objref indv") execute(execstr)
        if (flag!=4) { sprint(execstr,"indv=%s",ind) execute(execstr) }
      }
    if (numarg()>1) for i=2,numarg() { // propagate the objl to other STATs
        $oi.objl.remove_all
        for ii=0,objl.count-1 $oi.objl.append(objl.object(ii))
      }
  }

//** unnan() gets rid of nans and inf's in numerical cols
proc unnan () { local ii
    for ii=0,m-1 if (fcd.x[ii]==0) v[ii].unnan(0)
  }

func version () { local x
    if (numarg()==0) print nqsvers
    sfunc.tail(nqsvers,",v [1-9]*.",tstr)
    sscanf(tstr,"%d",&x)
    return x
  }

//* endtemplate
endtemplate NQS

//* array template -- an array pretending to be a list
begintemplate OARR
public oo,max,count,o,object,append,o2l,l2o,name,id
objref oo[1]
strdef name

proc init () {
    max=$1 id=count=0
    objref oo[max] 
  }
obfunc o () { // allow an array to pretend to be a list
    if ($1<0 || $1>=max) {printf("OOB for OARR: %d >= %d\n",$1,max) return oo[0]} 
    if ($1>=count) printf("OARR WARNING: %d not assigned\n",$1)
    return oo[$1]
  }
obfunc object () { // allow an array to pretend to be a list
    if ($1<0 || $1>=max) {printf("OOB for OARR: %d >= %d\n",$1,max) return oo[0]} 
    if ($1>=count) printf("OARR WARNING: %d not assigned\n",$1)
    return oo[$1]
  }
func append () {
    if (count>max-1) {printf("OARR ERR: out of room: %d\n",max) return -1}
    oo[count]=$o1 count+=1
    return count
  }
// copy array to a list
proc o2l () { local jj,min,mx
    if (numarg()==3) {min=$2 mx=$3} else {min=0 mx=count}
    for jj=min,mx $o1.append(oo[jj])
  }
// copy list to array
proc l2o () { local jj,min,mx
    if (numarg()==3) {min=$2 mx=$3} else {min=0 mx=$o1.count-1}
    for jj=min,mx oo[jj]=$o1.o(jj)
  }
endtemplate OARR

//* ancillary routines
//* sopset() returns symbolic arg associated with a string
proc sopset() { local i
    for i=1,21 sops[i-1]=$i // AUGMENT TO ADD NEW OPSYM
  }
sopset(ALL,NEG,POS,CHK,NOZ,GTH,GTE,LTH,LTE,EQU,EQV,EQW,EQX,EQY,NEQ,SEQ,RXP,IBE,EBI,IBI,EBE) // ADD NEW OPSYM NAME

proc sofset () {
    for scase(XO,"ALL","NEG","POS","CHK","NOZ","GTH","GTE","LTH","LTE","EQU","EQV","EQW","EQX","EQY","NEQ","SEQ","RXP","IBE","EBI","IBI","EBE") for j=1,5 { 
        sprint(tstr,"%s%d=%s*%d",XO.s,j,XO.s,j+1)
        execute(tstr)
      }
  }
sofset()

//** whvarg
func whvarg () { local ret
    ret=-1
    // ADD NEW OPSYM STRING
            // ALL  NEG   POS  CHK  NOZ   GTH  GTE  LTH LTE EQU   EQV   EQW   EQX  EQY NEQ  SEQ  RXP  IBE  EBI  IBI   EBE
    for scase("ALL","<0",">0","CHK","!=0",">",">=","<","<=","===","EQV","EQW","EQX","EQY","!=","=~","~~","[)","(]","[]","()") {
        if (strcmp($s1,temp_string_)==0) {ret=i1 break}
      }
    if (ret==-1) return ret else return sops[ret]
  }

//** whkey(KEY,STR) 
// place name of KEY from vecst.mod in temp_string_
func whkey () { local key
    for scase("ALL","NEG","POS","CHK","NOZ","GTH","GTE","LTH","LTE","EQU","EQV","EQW","EQX","EQY","NEQ","SEQ","RXP","IBE","EBI","IBI","EBE") { // ADD NEW OPSYM NAME
        sprint(tstr,"x=%s",temp_string_) execute(tstr)
        if (x==$1) {
            if (numarg()==2) $s2=temp_string_ else print temp_string_
            break
          }
      }
    return x
  }

//** varstr(tstr) -- make a variable out of string by removing nonalphanumeric characters
func varstr () { local a,z,A,Z,a0,a9,a_,len,ii,sflag
    a=97 z=122 A=65 Z=90 a0=48 a9=57 a_=95 // ascii codes
    if (numarg()==2) sflag=1 else sflag=0
    len = sfunc.len($s1)
    for ({x=0 ii=0};ii<len && !((x>=a&&x<=z)||(x>=A&&x<=Z));ii+=1) { // allowed first char
        sscanf($s1,"%c%*s",&x)
        sfunc.right($s1,1)
      }
    if (ii==len) { printf("varstr() ERR: no useable characters") return 0}
    sprint($s1,"%c%s",x,$s1)
    for (;ii<=len;ii+=1) {
        sscanf($s1,"%c%*s",&x)
        sfunc.right($s1,1)
        if ((x>=a&&x<=z)||(x>=A&&x<=Z)||(x>=a0&&x<=a9)||(x==a_)) { // allowed chars
            sprint($s1,"%s%c",$s1,x)
          }
      }
    if (sflag) { 
        sprint($s1,"strdef %s",$s1) 
        execute($s1) 
        sfunc.right($s1,7) // strip leading "strdef"
      } else {
        sprint($s1,"%s=0",$s1) 
        execute($s1) 
        sfunc.left($s1,sfunc.len($s1)-2) // strip the =0
      }
    return 1
  }

strdef h1
h1="Select operators: \nALL <0  >0  CHK !=0  >  >=  <   <=  ==  EQV EQW EQX EQY !=  =~  ~~  [)  (]  []  ()\nALL NEG POS CHK NOZ GTH GTE LTH LTE EQU EQV EQW EQX EQY NEQ SEQ RXP IBE EBI IBI EBE\nmost are obvious; those that are not\nEQV: value equal to same row value another column (takes string arg)\nEQW: value found in other vector (takes vector or NQS arg)\nSEQ: string equal (takes string)\nRXP: regular expression comparison (takes string)\nIBE,EBI...: I=inclusive, E=exclusive for ranges\n"

proc nqshelp () {
    if (numarg()==0) {
      } else {
        if (strcmp($s1,"select")==0) {
            // ed=new TextEditor(h1,9,160) ed.map
            printf("%s",h1)
          }
      }
  }

proc delnqs () { local i
    for i=1,numarg() nqsdel($oi) 
  }
proc nqsdel () { localobj iq,xo
    if (isassigned($o1)) {
        iq=$o1
        if (isassigned(iq.fcdo)) for ltr(xo,iq.fcdo) if (isojt(xo,iq)) nqsdel(xo)
        if (isassigned(iq.out)) {iq.out.cob=nil  iq.out=nil}
        iq.cob=nil iq=nil 
      }
    if (!istmpobj($o1)) $o1=nil
    tmpobj=nil
  }

//* pmap() maps a function arg1 onto multiple args or a , sep string of args
proc pmap () { local i,na localobj st
    st=new String()
    na=numarg()
    sprint(st.s,"%s(",$s1) // 1st arg is name of proc
    for i=2,na sprint(st.s,"%s%s,",st.s,$si)
    if (na>=2) chop(st.s)
    sprint(st.s,"%s)",st.s)
    execute1(st.s)
  }

func fmap () { local i,na localobj st,o
    st=new String() o=new DBL()
    na=numarg()
    sprint(st.s,"%s.x=%s(",o,$s1) // 1st arg is name of proc
    for i=2,na sprint(st.s,"%s%s,",st.s,$si)
    if (na>=2) chop(st.s)
    sprint(st.s,"%s)",st.s)
    execute1(st.s)
    return o.x
  }

// eg svnqss("filename","nq,nq[1],nq[2]")
proc svnqss () { local x localobj st,ol,xo
    st=new String2() ol=new List()
    st.s=$s2
    split(st.s,ol,",")
    for ltr(xo,ol,&x) {
        sprint(st.s,"%s.tog(\"DB\") %s.info.s=\"%s\"",xo.s,xo.s,xo.s)
        execute(st.s)
        if (x==0) { sprint(st.s,"%s.verbose=0 %s.sv(\"%s\") %s.verbose=1",xo.s,xo.s,$s1,xo.s) 
          } else      sprint(st.s,"%s.verbose=0 %s.sv(\"%s\",1) %s.verbose=1",xo.s,xo.s,$s1,xo.s)
        execute(st.s)
      }
  }

proc rdnqss () { localobj xo,st,ol
    xo=new NQS() st=new String2() ol=new List()
    xo.rd($s1)      
    ol.append(xo)
    print ol
    xo=new NQS()
    while (xo.rd) { ol.append(xo) xo=new NQS() }
    delnqs(xo)
    st.s=ol.o(0).info.s
    declare(st.s,"o[10]") // make default array of 10 of them if not there
    for ltr(xo,ol) { printf("%s size ",xo.info.s) xo.size() 
        st.t=xo.info.s
        if (name_declared(st.t)==2) { 
                   sprint(st.s,"delnqs(%s)",st.t) execute(st.s) 
          } else { sprint(st.s,"objref %s", st.t) execute(st.s) }
        sprint(st.s,"%s=new NQS() %s=%s",st.t,st.t,xo)
        execute(st.s)
      }
  }

//* bugs and missing features
// 1. for NQS, does resize work with object columns? i.e. 
// >  >  nqs1 has an object column
// >  >  can i do nqs2.resize(nqs1) to add nqs1 to nqs2?
// >  looks like I haven't bothered with that case (nqs.hoc_579:2188)
// 2. oc>  for ii=2,3 nq[ii-2].resize(nq[ii]) -- meaningless err messages?
// (~/nrniv/place/load.hoc_54:60)
// NQS[48] fi ERR: regexp matches more than once: 1 spkstats.killp
// NQS[48] fi ERR: regexp matches more than once: 2 spkstats.prup
// END nqs.hoc
//================================================================