Motor cortex microcircuit simulation based on brain activity mapping (Chadderdon et al. 2014)

 Download zip file   Auto-launch 
Help downloading and running models
Accession:146949
"... We developed a computational model based primarily on a unified set of brain activity mapping studies of mouse M1. The simulation consisted of 775 spiking neurons of 10 cell types with detailed population-to-population connectivity. Static analysis of connectivity with graph-theoretic tools revealed that the corticostriatal population showed strong centrality, suggesting that would provide a network hub. ... By demonstrating the effectiveness of combined static and dynamic analysis, our results show how static brain maps can be related to the results of brain activity mapping."
Reference:
1 . Chadderdon GL, Mohan A, Suter BA, Neymotin SA, Kerr CC, Francis JT, Shepherd GM, Lytton WW (2014) Motor cortex microcircuit simulation based on brain activity mapping. Neural Comput 26:1239-62 [PubMed]
Citations  Citation Browser
Model Information (Click on a link to find other models with that property)
Model Type: Realistic Network;
Brain Region(s)/Organism: Neocortex;
Cell Type(s): Neocortex L5/6 pyramidal GLU cell; Neocortex M1 L2/6 pyramidal intratelencephalic GLU cell; Neocortex fast spiking (FS) interneuron; Neocortex spiking regular (RS) neuron; Neocortex spiking low threshold (LTS) neuron;
Channel(s):
Gap Junctions:
Receptor(s): GabaA; AMPA; NMDA;
Gene(s):
Transmitter(s): Gaba; Glutamate;
Simulation Environment: NEURON;
Model Concept(s): Oscillations; Laminar Connectivity;
Implementer(s): Lytton, William [bill.lytton at downstate.edu]; Neymotin, Sam [Samuel.Neymotin at nki.rfmh.org]; Shepherd, Gordon MG [g-shepherd at northwestern.edu]; Chadderdon, George [gchadder3 at gmail.com]; Kerr, Cliff [cliffk at neurosim.downstate.edu];
Search NeuronDB for information about:  Neocortex L5/6 pyramidal GLU cell; Neocortex M1 L2/6 pyramidal intratelencephalic GLU cell; GabaA; AMPA; NMDA; Gaba; Glutamate;
/
src
README
infot.mod *
intf6.mod *
intfsw.mod *
matrix.mod
misc.mod *
nstim.mod *
staley.mod *
stats.mod *
vecst.mod *
boxes.hoc *
col.hoc
declist.hoc *
decmat.hoc *
decnqs.hoc *
decvec.hoc *
default.hoc *
drline.hoc *
filtutils.hoc *
gcelldata.hoc
gmgs102.nqs
grvec.hoc *
infot.hoc *
init.hoc
intfsw.hoc *
labels.hoc *
load.py
local.hoc *
main.hoc
misc.h *
miscfuncs.py
network.hoc
neuroplot.py *
nload.hoc
nqs.hoc *
nqsnet.hoc
nrnoc.hoc *
params.hoc
run.hoc
samutils.hoc *
saveoutput.hoc
saveweights.hoc
setup.hoc *
simctrl.hoc *
spkts.hoc *
staley.hoc *
stats.hoc *
stdgui.hoc *
syncode.hoc *
updown.hoc *
wdmaps2.nqs
xgetargs.hoc *
                            
//* $Id: grvec.hoc,v 1.672 2011/10/25 16:16:14 billl Exp $

//* Objects argtype: 0:double; 1:obj; 2:str; 3:double pointer
objref g[100],printlist,grv_,panobj,panobjl
strdef symb
symb = "O"
load_file("nqs.hoc")  // declare vectors
gnum=-1
declare("show_panel",1)
obfunc file_with_dot(){} // stubs to declare later, otherwise external barfs
obfunc filname(){} 
obfunc dirname(){}
func file_len(){}
proc tog (){if (numarg()==1) print $&1=1-$&1 else print gvmarkflag=1-gvmarkflag}
proc pvout2 () { }  // stub for manipulating printlist
proc rv2 () { }  // stub for manipulating the vectors before graphing
proc rv3 () { }  // stub for manipulating the label
func setfilt2 () { return 0 }  // stub for setting filter
func dir2mf2() {return 1} // stub for checking whether to include a trace

//* template vfile_line (vector file line) template gives information about a line
//  in a file that gives a vector: name, size and location
begintemplate vfile_line
  public name, size, loc, segs, num, ix, f, ty
  strdef name,f
  double loc[1]
  proc init () { 
    segs=1 ty=4
    if (argtype(5)==0) segs=$5 else if (argtype(5)==2) f=$s5
    name=$s1 size=$2 num=$4
    double loc[segs]
    loc[0] = $3
    ix=-1 // can be used as index if needed
  }
endtemplate vfile_line

//* template vitem (vector item) is an internal vector used to store output from 
// new vitem(var_name,vec_size,friendly_name)
// new vitem(var_name,vec_size,1) -- force tvec creation even if not cvode_local
// a simulation holds the name and the vector itself
tvflag_on=1
begintemplate vitem
  external cvode,tvflag_on,isassigned
  public tvec,vec,name,tvflag,pstep,o,code,resize
  objref tvec,vec,o,this // o not set here but used for Acells
  strdef tstr,name // variable name
  proc init () {
    name=$s1  tvflag=0 pstep=0 code=0
    if (cvode.active()) { if (cvode.use_local_dt()) tvflag=1 else tvflag=-1 }
    if (tvflag_on) tvflag=1
    if (argtype(2)==0) vec=new Vector($2) else if (argtype(2)==1) vec=$o2
    if (argtype(3)==0) { // very sloppy -- what if pstep=1??
      if ($3==1) tvflag=1 else {tvflag=0 pstep=$3} 
    } else if (argtype(3)==1) {
      tvec=$o3
      tvflag=1 // don't create another one
    }
    if (argtype(4)==0) if ($4==1) tvflag=1 else {tvflag=0 pstep=$4}
    if (tvflag==1 && !isassigned(tvec)) tvec=new Vector($2) 
    if (tvflag==-1) {
      sprint(tstr,"if (!isassigned(tvec)) tvec=new Vector(%d)",$2) execute(tstr) 
      sprint(tstr,"%s.tvec=tvec",this) execute(tstr)
    }
  }
  proc resize () {
    vec.resize($1) 
    if (isassigned(tvec)) tvec.resize($1)
    if (numarg()==2) { vec.resize($2)   // typically sizeup and then resize to 0
      if (isassigned(tvec)) tvec.resize($2) }
  }
endtemplate vitem

//* main template: GRV
// the attributes of a particular set of graphs including line colors,
// world coordinates, etc
// glist is a list of all graphs created from this panel
// llist is a list of available names and associated vectors or locations
begintemplate GRV
  public color,line,super,curcol,comment,vecpanel,mesg,tmplist,gxpan
  public glist,oglist,llist,tvec,size,shift,vsz,vjmp,tloc,vloc,remote
  public entries,segments,clear,keepgr
  public read_vfile,rvaltdisp,read_file,grvec,remgrs,clear,record,read_pclamp
  public cpplitem,chgplname,npl,new_pri,prlexp,numovecs,fchooser
  public read_vdotfile,rpanel,panobjl,ddir,filter
  public rlist,rvlist,attrpanl,wvpanl,remgrs,collapsegrs,viewplot,nvwall
  public geall,lblall,relbl,grall,grsel,tposvec,fliptb,setrange,setgrransel,grransel
  public chrange,rv,rv_readvec,rvec,rvl,read_rfile,gv
  public grrtsize,ge,pvall,pvother,pvplist,pvclose,vf2fwf
  public pvnext,pvout,prvec,pv,lpvec,nvplt,apbrowse,apkill
  public newpl,find_secname,mkmenu,dir2pr,dir2mf,read_mf
  public mkpanel,pbrgr,remprl,filename,onum,po,stub,go
  public rvtr,vrdr,prdr,tmpobj,printlist,output_file,attr0,attrnum,s,vite
  public glist,llist,tvec,ind,vec,vrtmp,tmpvec,tf1,tmpobj,apvb,printStep
  public gvmarkflag,gveraseflag,fchooser_flag,byte_store,bst,szstr,regexp,tmpfile
  public magga,mvga,ll,labelm,tstr

  double size[4],vsz[2],wvloc[4],x[4]
  objref glist, oglist, llist, tvec, ind, vec, vrtmp, tmpvec, tf1, tmpobj, apvb, ovb, printlist
  objref vite,scob,printlist,szstr[4],g,this,tmplist,panobj,panobjl,Xo,Yo,tmpfile, gcomms
  strdef comment,recstr,grvecstr,readtag,rvvec_name,grep,tstr,tstr2,ddir,filter
  strdef temp_string_,temp_string2_,output_file,s,filename,mesg,regexp
  external sfunc,osname,vfile_line,vitem,uname,nil,simname,gnum,XO,YO,graphList,isassigned
  external isobj,isit,mso,msoptr,allocvecs,dealloc,tstop,method,cvode,show_panel,rv2,rv3,setfilt2
  external cvode_active,cvode_local,datestr,runnum,i1,graphItem,GRV,strm,objnum,DBL,dir2mf2
  external file_with_dot,count_substr,file_len,filname,dirname,dir,grv_,repl_mstr,pvout2,symb

// list iterator ltr
// usage 'for ltr(YO, tmplist) { print YO }'
iterator ltr () { local i
  ii1=0
  for i = 0,$o2.count-1 {
    $o1 = $o2.object(i)
    iterator_statement
    ii1+=1
  }
  $o1=nil
}

proc init () { local flag,nopan
  flag=1e9
  if (numarg()==1) if (argtype(1)==0) flag=$1 
  if (numarg()==2) if (argtype(2)==0) flag=$2 else printf("2nd arg for GRV should be flag\n")
  nopan=0 color=1 line=1 curcol=1 keepgr=tloc=-1 nvec=bvec=super=0 vjmp=50 entries=1 segments=1
  fchooser_flag=0
  vsz[0] = 300  vsz[1] = 200
  glist = new List()
  tmpfile = new File()
  oglist = glist // save old glist for after superimpose
  llist=new List() gcomms=new List() printlist=llist
  tvec=new Vector(0) ind=tvec.c vec=tvec.c vrtmp=tvec.c tmpvec=tvec.c
  rvvec_name = "vec"
  ddir = "data/"
  printStep=gvmarkflag=gveraseflag=mff=0
  remote=0
  entries=segments=shift=0
  tloc=vloc=vjmp=x=y=luprd=0
  wvloc[0]=50 wvloc[1]=50 wvloc[2]=800 wvloc[3]=150
  onum=objnum(this)

  for ii=0,3 { szstr[ii] = new String() }
  if (sfunc.substr(osname,"inux")==1) grep="grep -a" else grep="grep"
  readtag = "^//[:pbCM ]"  // regexp used to identify header in mixed binary files
  { szstr[0].s="Set xmin" szstr[1].s="Set xmax" szstr[2].s="Set ymin" szstr[3].s="Set ymax" }
  multi_files = 1 // set 0 to show individual segments of multi-seg files
  dec_runnum = 0  // whether to decrement runnum when saving a new file
  byte_store = 4  // store as ascii (0), byte (1), int (2), float (3), double (4)
  tvec_bytesize = 4  // always store tvecs with more precision
  outvecint = 0     // dump vectors every outvecint time if not 0
  outvect = 0     // time for next vec dump
  labelm = 1       // set to 0 to turn off labeling
  attr0=0 // default is a panel for reading files
  if (flag==0) { // make a sim-recording panel
    if (numarg()>1) printf("GRV WARNING: creating simpan, ignoring filename\n")
    attr0=1 attrnum=0
    if (! isobj(panobjl,"List")) panobjl = new List() 
    attrnum=panobjl.append(this)-1
    if (attrnum!=0) printf("GRV WARNING: attr0 with attrnum=%d!\n",attrnum)
    if (show_panel) vecpanel()
    sprint(tstr,"printlist=%s.printlist",this)
    execute(tstr)
    sprint(s,"GRV[%d]:%s (sim vecs)",objnum(this),simname)
    return
  }
  panobjl=grv_.panobjl
  attrnum=panobjl.append(this)-1
  if (flag==-2) nopan=1 
  if (flag==-1) { fchooser() // ask user for filename
  } else if (numarg()>=1) {
    if (argtype(1)==2) read_vfile($s1)
  }
  if (!nopan) attrpanl()
}

//** newfile() calls fchooser
proc newfile () { localobj o
  o = apvb
  fchooser() 
  attrpanl()
}

//** bst() selects byte_store
func bst () { 
  if (numarg()>=1) byte_store=$1 
  if (numarg()>=2) tvec_bytesize=$2
  return byte_store 
}

//* attrpanl() gives attributes for a set of graphs
proc attrpanl () { local ii,jj
  sfunc.tail(filename,"data.*/",grvecstr)
  apvb=new VBox() 
  apvb.intercept(1) 
  xpanel(temp_string_)
  xvarlabel(filename)
  if (sfunc.len(mesg)>40) sfunc.left(mesg,40)
  xvarlabel(mesg)
  xvalue("Color(-1=multi)","color",1,"if (color==0) curcol=0",0,1)
  xvalue("Line","line",1,"",0,1)
  xpanel()
  xpanel("",1)
  xbutton("Superimpose: ","tog(&super) if (super==0) gnum=-1 sprint(mesg,\"super=%d\",super)")
  xbutton("Where?","sprimp()")
  xbutton("Restore","glist=oglist sprint(mesg,\"Restore graph list\")")
  xpanel()
  xpanel("",1)
  xbutton("Limits","wvpanl()")
  xbutton("Erase","geall()")
  xbutton("Mark","togmark()")
  if (attr0) xbutton("Panel","pbrgr(\"Graph\",\"gv\")") else {
    xbutton("New file","newfile()") }
  xpanel()
  xpanel("",1)
  xmenu("Graphs")
  xbutton("Erase/redraw","gveraseflag=-(gveraseflag-1) if (gveraseflag==1) super=1 else super=0 sprint(mesg,\"Erase=%d\",gveraseflag)")
  xbutton("Erase graphs","geall()")
  xbutton("Remove graphs","remgrs()")
  xbutton("Clean graph list","collapsegrs()")
  xbutton("Erase axes","setrange(3)")
  xbutton("Draw axes","setrange(0)")
  xbutton("Label graphs","lblall()")
  sprint(tstr,"execute(\"disptray(%d)\")",onum)
  xbutton("Make tray",tstr)
  xbutton("View = plot","for ltr(Xo,glist) Xo.exec_menu(\"View = plot\")")
  xbutton("Crosshair","for ltr(Xo,glist) Xo.exec_menu(\"Crosshair\")")
  xbutton("New view","for ltr(Xo,glist) Xo.exec_menu(\"NewView\")")
  xbutton("Zoom","for ltr(Xo,glist) Xo.exec_menu(\"Zoom in/out\")")
  xbutton("Delete Text","for ltr(Xo,glist) Xo.exec_menu(\"Delete\")")
  xbutton("Move Text","for ltr(Xo,glist) Xo.exec_menu(\"Move Text\")")
  xbutton("Change Text","for ltr(Xo,glist) Xo.exec_menu(\"Change Text\")")
  xmenu()
  // sprint(temp_string_,"remote",attrnum)
  // sprint(temp_string2_,"grall(%d)",attrnum)
  // xvalue("Graph all",temp_string_,0,temp_string2_)
  if (attr0) redo_printlist() else xbutton("Show full panel","rpanel()")
  xpanel()
  apvb.intercept(0) 
  if (attr0) {   sprint(s,"GRV[%d] %s SIM CONTROL",objnum(this),simname)
  } else       { 
    if (sfunc.len(filename)>0) filname(filename,grvecstr)
    sprint(s,"GRV[%d] %s:%s",objnum(this),simname,grvecstr) 
  }
  apvb.map(s)
}

//** sprimp() superimpose on another sim
proc sprimp () {
  super=1
  sprint(tstr,"SUPERIMPOSE %s ON?",s)
  ovb=new VBox() 
  ovb.intercept(1) 
  xpanel(tstr)
  xvalue("Superimpose on Graph[#]","gnum",1)
  for ltr(Xo,panobjl) {
    sprint(temp_string2_,"glist=%s.glist sprint(mesg,\"Using graph list from %s\") ovb.unmap() ovb=nil",Xo,Xo)
    xbutton(Xo.s,temp_string2_)
  }
  xpanel()
  ovb.intercept(0) 
  ovb.map(tstr)
  ovb.dismiss_action("ovb.unmap ovb=nil")
}

//** fchooser() finds file and then create panel from it using rpanel
proc fchooser () { 
  if (fchooser_flag == 0) {  // create panel first time only
    if (setfilt2(filter)) { // do nothing
    } else if (strm(filter,"\\*")) { // do nothing
    } else sprint(filter,"*%s*",datestr)
    tmpfile.chooser("","Read from a file",filter,"Open","Cancel",ddir)
  }
  fchooser_flag = 1
  if (tmpfile.chooser()) {
    // find out whether this is a vector file or not
    tmpfile.getname(filename)
    if (strm(filename,"\.mf")) read_mf() else read_vfile() 
  }
}

//** newpan() create a new panel and call fchooser from there
proc newpan () { tmpobj=new GRV(-1) }

//** read_vfile() creates a panattr object from information in a file
// (uses grep to avoid loading big file)
// assumes file in tmpfile
func read_vfile () { local flag, ii, sz, loc, mult, sze, cloc, segs
  if (numarg()>=2) if (strcmp(filename,$s1)==0) return 2 // check if file is already active 
  if (attr0) {
    if (!boolean_dialog("Look at file data instead of sim?","YES","NO")) {
      printf("Read file cancelled\n")
      return 0
    }
  } else { attr0=0 }
  if (numarg()>=1) filename=$s1 else tmpfile.getname(filename)
  if (strm(filename,"\.mf$")) return read_mf(filename)
  sprint(s,"GRV[%d] %s: %s",objnum(this),simname,filename)
  clear()
  // grab hold of the different lines using grep
  file_with_dot(filename,temp_string_) // put .filename into temp_string_
  if (!tmpfile.ropen(temp_string_)) {
    print "E1: Can't open ",temp_string_ // avoid grep error
    return 0
  } else flag = 1 // signifies that .file exists to use as key
  while ((numr = tmpfile.gets(tstr)) != -1) {  // throw out the leading '//'
    // read the line
    if (sfunc.head(tstr,"//[^b]",temp_string2_)==0) {
      read_vinfo()  // a line giving info about the file (eg comment)
    } else {   // NB: code in v60:516 to pickup byte_store value
      if (flag && entries > 1) { // a .file with MULTI segs
        tmpfile.seek(-numr,1) // backup to beginning of line
        read_vdotfile()
      } else if (segments > 1) { // mult segs: different times for same var
        tmpfile.seek(-numr,1) // backup to beginning of line
        segments = read_vsegs() //**** NEEDS to be recovered from grvec.hoc442
      } else {  // read each line in for itself
        if (  sscanf(tstr,"//b%1ld %g %s %ld %ld",&bvec,&nvec,tstr2,&sze,&loc)!=5) {
          if (sscanf(tstr,"//b%1ld %s %ld %ld",&bvec,tstr2,&sze,&loc)!=4) {
            printf("**** GRV read_vfile() parse ERR on %s in %s",tstr,filename)
          } else if (printStep==-2) nvec=-2 else nvec=-1 // guess
        }
        if (nvec==2) {
          printf("read_vfile forward compat. **** WARNING ****\n\t****consider edit of %s dot file to change 2 to -2\n",filename)
          nvec=-2
        }
        if (strcmp(tstr2,"CVODE1.0 tvec")==0) {
          tvec.resize(0)
          printStep=-1
          tloc = loc // where to find tvec later
        } else {
          tmpobj = new vfile_line(tstr2,sze,loc,nvec) // name size loc num
          tmpobj.ty=bvec
          llist.append(tmpobj)
          tmpobj = nil
        }
      }
    }
  }
  if (llist.count==0) {
    printf("grvec.hoc::read_vfile ERR no vecs read from %s\n",filename)}
  if (entries==1) entries = llist.count
  if (! flag && segments>1) write_vsegs()  // create key .file
  if (! tmpfile.ropen(filename)) { print "E3: Can't open ",filename
    return 0
  }
  if (printStep==-1) rtvec() // code for cvode_active()
  mff=0
  return 1
}

//** rtvec() reads tvec if it exists, returns -1 if it doesn't
func rtvec () {
  if (tloc > -1) {
    tmpfile.seek(tloc)
    tvec.vread(tmpfile)
    return 1
  } else {
    return 0
  }
}

proc write_vsegs () { print "NEEDS to be ported from grvec.hoc442" }

//** read_vinfo()
proc read_vinfo () {
  if (strm(tstr,"//printStep")) {
    sfunc.tail(tstr," ",tstr) // just take end of string following space
    sscanf(tstr,"%g",&printStep) // printstep==-1 means cvode
  } else if (strm(tstr,"^//:")) { // a comment
    sfunc.tail(tstr,"//: *",tstr)
    sfunc.head(tstr,"\n",comment) // chop final newline
    mesg=comment
  } else if (strm(tstr,"^//CPU")) { // the machine type for byte storage
    sfunc.tail(tstr," ",tstr)
    if (! strm(tstr,uname)) {
      printf("%s written from %s\n",filename,tstr)
    }
  } else if (strm(tstr,"^//MULTI")) { // multiple lines for each entry
    sfunc.tail(tstr," ",tstr)
    if (sscanf(tstr,"%d %d",&entries,&segments)==2) {
      if (! multi_files) printf("**************** GRV read_vinfo ERRa\n") 
    } else segments=1
  } else {
    printf("Line:\t%s\n\tnot recognized in %s\n",tstr,filename)
  }
}

//** read_vdotfile() read .file in abbreviated format (see write_vsegs)
proc read_vdotfile() { local loc,entries,segments,ii
  entries=entries segments=segments
  for i=1,entries {  // read this abbreviated file version (much faster)
    tmpfile.scanstr(temp_string_)
    loc = tmpfile.scanvar()
    tmpobj = new vfile_line(temp_string_,-1,loc,segments) // don't set size
    llist.append(tmpobj)
    for ii=1,segments-1 { tmpobj.loc[ii] = tmpfile.scanvar() }
  }
}

//** rpanel() creates a panel from information in llist
proc rpanel () { local ii
  if (llist.count > 8) { rlist() return }
  sprint(temp_string_,"%s ",simname)
  xpanel(temp_string_)
  xlabel(filename)
  for ii=0,llist.count-1 {
    sprint(temp_string2_,"rv(%d)",ii)
    xbutton(llist.object(ii).name,temp_string2_)
  }
  xbutton("Attributes","attrpanl()")
  sprint(temp_string_,"lpvec(filename,vrtmp,%g)",printStep)
  xbutton("Print last vec",temp_string_)
  xbutton("Erase","ge()")
  xpanel()
}

//** rlist(): like rpanel() but puts up a browser list instead of a panel
proc rlist () {
  sprint(tstr,"%d items on list: Enter regexp for subset or \"All\"",llist.count)
  if (llist.count>50 || numarg()>=1) {
    if (numarg()>=1) regexp=$s1 else if (!string_dialog(tstr,regexp)) return
    if (! strm(regexp,"[Aa][Ll][Ll]")) {
      if (! isobj(tmplist,"List")) tmplist = new List()
      tmplist.remove_all
      for ltr(Xo,llist) {
        Xo.ix=ii1
        if (strm(Xo.name,regexp)) tmplist.append(Xo)
      }
      tmplist.browser(filename,"name")
      tmplist.accept_action("rv(tmplist.object(hoc_ac_).ix)")
      printf("%d selected\n",tmplist.count)
      return
    }
  }
  llist.browser(filename,"name")
  llist.accept_action("rv(hoc_ac_)")
}

//** rvlist(): like rpanel() but puts up a browser list instead of a panel
proc rvlist () { local flag,rdstr
  rdstr = 1
  flag = $1
  if (numarg()==2) { recstr=$s2  rdstr=0 }
  if (flag==0) {
    llist.browser(filename,"name")
    llist.accept_action("rvec(hoc_ac_)")
  } else if (flag==1) { // proc(vec)
    if (rdstr) string_dialog("Procedure name: proc, called as proc(vec)",recstr)
    llist.browser(recstr,"name")
    sprint(temp_string_,"rv_readvec(hoc_ac_,%s) execute1(\"%s(%s)\")",rvvec_name,recstr,rvvec_name)
    llist.accept_action(temp_string_)
    print ":",recstr,":",temp_string_,":",rvvec_name
  } else if (flag==2) { // vec.command
    if (rdstr) string_dialog("comm: print vec.comm",recstr)
    llist.browser(recstr,"name")
    sprint(temp_string_,"{rvec(hoc_ac_) print %s.%s}",rvvec_name,recstr)
    llist.accept_action(temp_string_)
  }
}

//* rv() reads line of vector file into IV graph via vector
// rv(llist_ind1[,llist_ind2])
// rvaltdisp(tvec,vec,name)
func rvaltdisp () { return 0 } // if returns 1 means there is an alternate display for rv
obfunc rv () { local inx,inx2 localobj o
  // open the file and go to correct position
  if (numarg() == 0) { print "rv(ind1,ind2) reads into vrtmp bzw vec" return this}
  inx = $1
  if (attr0) {return gv(inx)}
  o=llist.object(inx)
  if (numarg()>1) inx2 = $2 else inx2 = -1 // to graph one vec against another 
  rv_readvec(inx,vrtmp)
  rv2(vrtmp,tvec)
  // create a new plot if necessary and set color
  if (vrtmp.size==0) { // assume this is a spike train in tvec
    nvplt(ind,vrtmp)
    ind.resize(tvec.size) ind.fill(0)
    ind.mark(graphItem,tvec,symb,line,curcol)
  } else if (inx2>-1) { // only make sense if they share the same tvec
    rv_readvec(inx2,vec)
    nvplt(vec,vrtmp)
    if (numarg() >= 3) {
      vec.mark(graphItem,vrtmp,$s3,line,curcol)
    } else {
      vec.mark(graphItem,vrtmp,symb,line,curcol)
    }
  } else if (o.num==-2) {
    nvplt(vrtmp,tvec)
    if (gvmarkflag) {
      if (! rvaltdisp(tvec,vrtmp,llist.object(inx).name)) {
        vrtmp.mark(graphItem,tvec,symb,line,curcol,4) }
    } else vrtmp.line(graphItem,tvec,curcol,line)
  } else if (o.num==-1) {
    printf("rv() PROBLEM: CVODE global read not implemented\n")
  } else {
    if (o.num==0) {
      printf("rv WARNING: taking printstep %g for %s\n",printStep,o.name)
      o.num=printStep
    }
    nvplt(vrtmp,o.num)
    if (gvmarkflag) {
           vrtmp.mark(graphItem,o.num,symb,line,curcol,4)
    } else vrtmp.line(graphItem,o.num,curcol,line)
  }
  // too much fussing with labels
  if (sfunc.substr(filename,"batch")!=-1 || \
      sfunc.substr(filename,"data")==-1) {
    grvecstr = filename
    } else sfunc.tail(filename,"data",grvecstr)
  if (sfunc.len(llist.object(inx).name)>40) {
    grvecstr=llist.object(inx).name } else {
    sprint(grvecstr,"%s:%s",grvecstr,llist.object(inx).name) }
  rv3(grvecstr)
  if (super == 0 && labelm) { graphItem.label(0,0.9,grvecstr)
  } else if (labelm) graphItem.label(0.0,0.95,grvecstr)
  return graphItem
}

//* gv(vnum) graphs vector
obfunc gv () {  local a,inx,lin localobj o,v1,vtmp
  inx=-1
  lin=line
  a=allocvecs(vtmp)
  if (numarg()==0) { inx = hoc_ac_ } else { 
    if (argtype(1)==0) inx = $1
    if (argtype(1)==2) {
      for ltr(Xo,printlist) if (strm(Xo.name,$s1)) inx=ii1
      if (inx==-1) {print $s1," not found" return this}}
  }
  if (numarg()>=2) { color=curcol=$2 }
  if (numarg()>=3) { lin=$3 }
  o = printlist.object(inx)
  vtmp.copy(o.vec)
  rv2(o.vec) // alters vtmp
  if (o.vec.size==0) { // assume that this is spk trace
    if (o.tvec.size==0) { printf("\tNO SPIKES IN %s\n",printlist.object(inx).name)
    } else {
      nvplt(o.tvec)
      ind.resize(o.tvec.size) ind.fill(1)
      ind.mark(graphItem,o.tvec,symb,lin,curcol)
    }
  } else { 
    if (o.tvflag!=0) { // o.tvec should point to tvec if tvflag==-1
      nvplt(o.vec,o.tvec)
      if (gvmarkflag) { o.vec.mark(graphItem,o.tvec,symb,lin,curcol)
      } else {          o.vec.line(graphItem,o.tvec,curcol,lin) }
    } else {
      if (o.pstep==0) {
        printf("gv WARNING: vitem.pstep not set with tvflag==0 (%s)\n",o)
        o.pstep=printStep
      }
      nvplt(o.vec,o.pstep)
      if (gvmarkflag) { o.vec.mark(graphItem,o.pstep,symb,lin,curcol)
      } else {          o.vec.line(graphItem,o.pstep,curcol,lin) }
    }
    if (labelm) {
      grvecstr=printlist.object(inx).name
      rv3(grvecstr) graphItem.label(0.,0.9,grvecstr) 
    }
  }
  o.vec.copy(vtmp) // in case has been changed by rv2()
  dealloc(a)
  return graphItem
}


// go(n) will goto location in the data file
obfunc go () { localobj o
  if (argtype(1)==0) { inx=$1 o=llist.object(inx) } else o=$o1
  tmpfile.seek(o.loc)
  return o
}

// rv_readvec(index,vec)
// read vector #index from file into vector vec
func rv_readvec () { local inx,ii,n,bvec localobj o
  if (argtype(1)==0) { inx=$1 o=llist.object(inx) } else o=$o1
  n=o.num
  if (mff) {tstr=filename filename=o.f}
  tmpfile.getname(temp_string_) // may not be necessary?
  if (strcmp(temp_string_,filename)!=0 || tmpfile.isopen()==0) { // don't reopen file if there
    if (! tmpfile.ropen(filename)) {
      print "ERROR rv() can't read ",filename 
      return 0
    }
  }
  tmpfile.seek(o.loc)
  bvec=o.ty
  if (numarg()>=3) { 
    if (n!=-2) {
      printf("ERROR rv() called with 2 vecs but only find 1 in %s %s %d\n",filename,o.name,inx)
      return 0
    }
    if (bvec>5) {
      $o2.fread(tmpfile,o.size,bvec-5) $o3.fread(tmpfile,o.size,bvec-5) 
    } else {
      $o2.vread(tmpfile) $o3.vread(tmpfile) 
    }
  } else if (n==-2) {
    if (n==-2) {
      if (bvec>5) {
        tvec.fread(tmpfile,o.size,bvec-5) // no error check
      } else if (!tvec.vread(tmpfile)) {
        printf("rv_readvec tvec READ failure in %s %s %d\n",filename,o.name,inx) return 0 }
      if (bvec>5) {
        $o2.fread(tmpfile,o.size,bvec-5) // no error check
      } else  if (! $o2.vread(tmpfile)) {
        printf("rv_readvec vec READ failure in %s %s %d\n",filename,o.name,inx) return 0 }
    }
    if (n==-2 && (tvec.size != $o2.size)) {
      printf("rv_readvec size mismatch in %s %s %d\n",filename,o.name,inx)
      return 0
    }
  } else $o2.vread(tmpfile) // fixed dt
  if (segments>1) { // needs rewrite
    tmpvec = new Vector($o2.size)
    for ii=1,segments-1 {
      tmpfile.seek(llist.object(inx).loc[ii])
      tmpvec.vread(tmpfile)
      $o2.copy(tmpvec,$o2.size)
    }
    tmpvec = nil
  }
  if (mff) filename=tstr // restore
  return n
}

//** vf2fwf() take a file in vformat and prints out as multiple fwrites
proc vf2fwf () { local ii localobj f
  f=new File()
  f.wopen($s1)
  for ii=0,entries-1 {
    rv_readvec(ii,vrtmp)
    vrtmp.fwrite(f)
    printf("%d ",vrtmp.size)
  }
  f.close
  printf("\n dt=%g\n",printStep)
}

//** rvec(num[,vec]) writes to vec, or contents of rvvec_name or
//  to vector of same name if rvvec_name is empty
proc rvec () { local flag,on
  flag=0
  if (sfunc.len(rvvec_name)==0) flag=1
  if (numarg()<1) on=hoc_ac_ else on=$1
  if (numarg()>1) sprint(rvvec_name,"%s",$o2)
  if (sfunc.len(rvvec_name)==0) rvvec_name=llist.object(on).name 
  printf("Copying %s to %s\n",llist.object(on).name,rvvec_name) 
  sprint(temp_string_,"%s.rv_readvec(%d,%s)",this,on,rvvec_name)
  if (flag) rvvec_name="" // clear it again
  if (! execute1(temp_string_)) print "ERROR: Declare target as a vector"
  if (numarg()==4) $o4.copy(tvec)
}

//** rvl() reads line of vector file into IV graph via vector
// rvl(name,pos[,pos2,pos3,etc])
proc rvl () { local i
  // open the file and go to correct position
  tmpfile.getname(temp_string_)
  if (strcmp(temp_string_,filename)!=0 || tmpfile.isopen()==0) {
    tmpfile.ropen(filename) }  // only open if necessary
  if (tmpfile.isopen==0) { printf("ERROR: %s not found.\n",filename)
      return }
  if (numarg() == 3) { 
    tmpfile.seek($3)
    tmpfile.gets(temp_string_) // throw away line
    vrtmp.vread(tmpfile)
  } else {
    tmpvec = new Vector()
    for i=3,numarg() {
      tmpfile.seek($i)
      tmpvec.vread(tmpfile)
      vrtmp.copy(tmpvec,vrtmp.size)
    } 
  }
  tmpvec = nil
  nvplt(vrtmp)
  vrtmp.line(graphItem,printStep,curcol,line)
  // graph it and label the graph
  if (sfunc.substr(filename,"batch")!=-1) { grvecstr = filename
  } else { sfunc.tail(filename,"data",grvecstr) }
  sprint(grvecstr,"%s:%s",grvecstr,$s2)
  if (super==0 && labelm) { graphItem.label(0,0.9,grvecstr)
  } else if (labelm) graphItem.label(grvecstr)
}

//* utility programs (not all used or even all usable)
//** nvplt() put up new voltage plot
obfunc nvplt () { local xs,ys,flag,prstep
  prstep=10
  if (super == 0) flag=1 else {
    if (gnum>-1) {
      sprint(tstr,"{Graph[%d]}",gnum)
      if (execute1(tstr,0)) { // Graph[gnum] exists
        if (Graph[gnum].view_count>0) { 
          graphItem=Graph[gnum] flag=0
        }               
      } 
    } else if (isobj(graphItem,"Graph")) if (graphItem.view_count() > 0) { 
      flag=0
    } else { flag=1 }                   // else need new graph
  }
  if (flag) {
    if (numarg()==2) if (argtype(2)==0) prstep=$2 else prstep=-1
    if (size[1] != 0) { // xmax is set
      newpl(size[0],size[1],size[2],size[3])
    } else if (prstep<0) {
      newpl(0,$o2.max,$o1.min,$o1.max)
    } else {
      newpl(0,$o1.size()*prstep,$o1.min,$o1.max)
    }
  } else if (gveraseflag) graphItem.erase_all
  if (color == -1) {
    curcol += 1
    if (curcol == 0 || curcol>7) curcol = 1 
  } else curcol = color
  graphItem.color(curcol)
  g=graphItem
  return g
}

//** grrtsize() use view=plot and then pad a little
proc grrtsize () { local h,w,frac
  if (numarg()>=1) tmpobj=$o1 else tmpobj=graphItem
  if (numarg()>=2) frac = $2 else frac=.05
  tmpobj.exec_menu("View = plot")
  tmpobj.size(&x)
  w=frac*(x[1]-x[0])  h=frac*(x[3]-x[2])
  x[0]-=2*w x[1]+=w x[2]-=4*h x[3]+=h // need extra padding on bottom
  tmpobj.size(x[0],x[1],x[2],x[3])
}

//** newpl()
proc newpl () { local w,h
  if (numarg()==5) newPlot($1,$2,$3,$4) // 5th arg is flag
  if (numarg()==8) {wvloc[0]=$5 wvloc[1]=$6 wvloc[2]=$7 wvloc[3]=$8}
  graphItem = new Graph(0) 
  g=graphItem
  graphItem.xaxis()	// view axis for x and y
  graphItem.view($1,$3,$2-$1,$4-$3,wvloc[0],wvloc[1],wvloc[2],wvloc[3])
  glist.append(graphItem)
}

//** find_secname(variable,result): put secname into result
proc find_secname () { localobj o
  if ((sfunc.head($s1,"\.[_A-Za-z0-9]+$",$s2))==0) { // strip off stuff after terminal .
    printf("grvec.hoc:find_secname ERR: no section found: %s\n",$s1) err() }
  if (    strm($s1,"\.[_A-Za-z0-9]+[(][0-9.]+[)]$")) {  // form eg v(0.5)
    sfunc.head($s1,"\.[_A-Za-z0-9]+[(][0-9.]+[)]$",$s2)
  } else {
    o=isit($s2)
    if (o.x) {  // the stem is an obj
    o.o.get_loc() 
    sectionname($s2)
    pop_section()
    } else {
      printf("grvec.hoc:f_s ERR0: Can't find sec: %s\n",$s1) err() 
    }
  }
}

//** vecpanel() main panel
proc vecpanel () {
  if (! attr0) {printf("vecpanel (main panel) can only be run from attr0\n") return }
  fchooser_flag = 0  // used to initialize the file chooser
  sprint(temp_string_,"%s Vectors",simname)
  xpanel(temp_string_)
  xbutton("Graph from file","newpan()")
  xbutton("Sim vectors","pbrgr(\"Graph\",\"gv\")")
  xbutton("Sim attributes","attrpanl(0)")
  xbutton("Save Sim","pvall()")
  xbutton("Panels","apbrowse()")
  redo_printlist()
  xpanel()
}

//** lpvec(title,vector,printstep) dumps a single vector onto the printer using jgraph
proc lpvec () { local inx,ii
  tmpfile.wopen("lptmp")
  tmpfile.printf("newgraph\nnewcurve pts\n")
  for ii = 0,$o2.size-1 {
    tmpfile.printf("%g ",ii*$3)
    $o2.printf(tmpfile,"%g",ii,ii)
  }
  tmpfile.printf("marktype none\nlinetype solid\ntitle : %s\n",$s1)
  tmpfile.close()
  system("jgraph -P lptmp > lptmp2")
  system("lpt lptmp2")
}

//** remgrs() -- clears glist
proc remgrs () { local ii
  for ltr(Xo,glist) Xo.unmap
  if (keepgr!=-1) {
    for (ii=glist.count-1;ii>0;ii-=1) glist.remove(ii) // leave #1
  } else glist.remove_all
}

//** clear() -- clears llist
proc clear () {
  entries=1 segments=1
  comment = ""
  llist.remove_all()
}

//** ll() same as external llist
proc ll () { 
  if (numarg()==1) {
    if (attr0==1) { for ltr(XO,printlist) if (strm(XO.name,$s1)) print ii1,XO.name,XO.vec.size
    } else          for ltr(XO,llist) if (strm(XO.name,$s1)) print ii1,XO.name,XO.size
  } else {
    if (attr0==1) { for ltr(XO,printlist) print ii1,XO.name,XO.vec.size
    } else          for ltr(XO,llist) print ii1,XO.name,XO.size
  }
}

//* read_pclamp(file,vscale,tscale): read physiol data file, similar to read_file()
proc read_pclamp () { local ii,cols,pt,length,pstep,tscale,vscale
  if (! tmpfile.ropen($s1)) { printf("\tERROR: can't open file \"%s\"\n",$s1) }
  if (numarg()>=2) vscale=$2 else vscale=1
  if (numarg()>=3) tscale=$3 else tscale=1e3
  printlist.remove_all()
  method("implicit") printStep=0.1
  tmpfile.gets(temp_string_)  
  length=1
  while (! strm(temp_string_,"^\"Time")) {
    length += 1
    tmpfile.gets(temp_string_)  // first word in line was not a number so next line
  }
  temp_string2_ = temp_string_ // column def line
  cols = count_substr(temp_string_,"[(]") // destructive function
  pt = tmpfile.tell()
  length = file_len($s1) - length
  vrtmp.scanf(tmpfile,length,1,cols) // tvec
  pstep=vrtmp.x[1]-vrtmp.x[0]
  pstep*=tscale // typically gives it in s statt ms
  print "Reading ", cols, " columns; ", length, " lines; tstep=",pstep
  for ii=2,cols { // pick up all of the columns
    tmpfile.seek(pt)
    vrtmp.scanf(tmpfile,length,ii,cols)
    vrtmp.mul(vscale) // correct for a common scaling
    npl("col",ii,vrtmp,pstep)
  }
  if (1) {
    sprint(temp_string2_,"%s:%s",$s1,temp_string2_)
    file_with_dot($s1,filename,"v") // put vfilename into name
    print "Saving to ",filename
    pvplist(filename,temp_string2_)
  }
}

//* read_file(file,cols[,length]): read multicolumn file
//  see also read_pclamp() above
func read_file () { local ii,cols,pt,length
  if (numarg()==0) { print "\tread_file(\"file\",cols)"
    print "\t(must set tstop and printStep.)"
    return 0
  }
  printStep=10
  if (cvode_status()!=0) print "WARNING: Turn off cvode."
  if (numarg()==3) { length = $3 } else { length=tstop/printStep }
  cols = $2
  if (! tmpfile.ropen($s1)) { printf("\tERROR: can't open file \"%s\"\n",$s1) return 0}
  //  printlist.remove_all()
  tmpfile.scanstr(temp_string_)  pt = 0
  // skip over a comment line; note that this will skip extra line if comment line is
  // just one word long
  while (sfunc.head(temp_string_,"[^-+0-9.e]",temp_string2_) != -1) {
    tmpfile.gets(temp_string_)  // first word in line was not a number so next line
    pt = tmpfile.tell()         // location at next line
    tmpfile.scanstr(temp_string_) // get first word here
    print temp_string2_
  }
  for ii=1,cols { // pick up all of the columns
    tmpfile.seek(pt)
    vrtmp.scanf(tmpfile,length,ii,cols)
    npl("col",ii,vrtmp)
  }
  return 1
}

//* read_rfile(file): read multirow file
//  use col2row to transpose columnar file first
proc read_rfile() { local num
  if (numarg()==0) { print "\tread_rfile(\"file\")" return }
  if (! tmpfile.ropen($s1)) { printf("\tERROR: can't open file \"%s\"\n",$s1) return}
  printlist.remove_all()
  while (tmpfile.scanstr(temp_string_) != -1) {  // read lines
    num = tmpfile.scanvar() // pick up number of items in col
    vrtmp.scanf(tmpfile,num)
    npl(temp_string_,vrtmp)
  }
}

//* redo_printlist() menu allows removal or addition of inidividual items
proc redo_printlist () {
  xmenu("Printlist")
  xbutton("Save Sim","pvall()")
  xbutton("Add var to printlist","redolist(0)")
  xbutton("Clear printlist","printlist.remove_all()")
  xbutton("Remove item from printlist","redolist(1)")
  xbutton("Vector.op","redolist(2)")
  xbutton("Proc(vector)","redolist(6)")
  xbutton("Link XO->vec,YO->tvec","redolist(7)")
  xbutton("Graph vector","redolist(4)")
  xbutton("Save printlist","redolist(5)")
  xbutton("Archive to file:","pbrgr(\"Archive\",\"pv\")")
  xbutton("Add all obj's of this type to printlist","redolist(3)")
  xmenu()
}

//* redolist() set of functions for altering the printlist called by redo_printlist()
proc redolist () { local ii,flag
  if (! isobj(printlist,"List")) printlist = new List()
  flag = $1  rdstr = 1
  if (numarg()==2) { recstr=$s2  rdstr=0 }
  if (flag==0) {
    if (! isobj(scob,"SymChooser")) scob = new SymChooser()
    if (scob.run()) {
      scob.text(temp_string_)
      npl(temp_string_)
    }
  } else if (flag==1) { // remove item
    printlist.browser("Double click on item to remove","name")
    printlist.accept_action("printlist.remove(hoc_ac_)")
  } else if (flag==2) { // .op
    if (rdstr) string_dialog("Enter operation to be run on vec",recstr)
    temp_string_ = "\"%s.%s = %g\\n\""
    sprint(temp_string_,"printf(%s,printlist.object(hoc_ac_).name,\"%s\",x=printlist.object(hoc_ac_).vec.%s)",temp_string_,recstr,recstr)
    printlist.browser(recstr,"name")
    printlist.accept_action(temp_string_)
  } else if (flag==3) { // put another set of things on list
    if (! isobj(scob,"SymChooser")) scob = new SymChooser()
    if (rdstr) string_dialog("String to be used as suffix for all items on list",recstr)
    scob.run()
    scob.text(temp_string_)
    tmplist = new List(temp_string_)
    record(tmplist,recstr)
  } else if (flag==4) { // show it
    pbrgr("Graph","gv")
  } else if (flag==5) {
    fchooser_flag = 0
    tmpfile.chooser("a","Add printlist to file")
    if (tmpfile.chooser()==1) {
      tmpfile.printf("\nproc make_printlist() { \n")
      tmpfile.printf("  printlist.remove_all()\n")
      for ii=0,printlist.count-1 {
        tmpfile.printf("  npl(\"%s\")\n",printlist.object(ii).name)
      }
      tmpfile.printf("}\nmake_printlist()\n")
      tmpfile.close()
    }
  } else if (flag==6) { // proc(vec)
    if (rdstr) string_dialog("Enter procedure name \"proc\",called as proc(vec,var,num)",recstr)
    printlist.browser(recstr,"name")
    sprint(temp_string_,"%s(printlist.object(hoc_ac_).vec,printlist.object(hoc_ac_).name,hoc_ac_)",recstr)
    printlist.accept_action(temp_string_)
  } else if (flag==7) { // XO is pointer to vec
    printlist.browser("XO","name")
    sprint(temp_string_,"{tmpobj=printlist.object(hoc_ac_) print hoc_ac_,tmpobj.name XO=tmpobj.vec YO=tmpobj.tvec}")
    printlist.accept_action(temp_string_)
  } 
}

//** mkmenu(title,action,proc) makes a menu from printlist
proc mkmenu () { local ii
  xmenu($s1)
  for ii=0,printlist.count-1 {
    sprint(temp_string_,"%s %s",$s2,printlist.object(ii).name)
    sprint(temp_string2_,"%s(%d)",$s3,ii)
    xbutton(temp_string_,temp_string2_)
  }
  sprint(temp_string_,"mkpanel(\"%s\",\"%s\",\"%s\")",$s1,$s2,$s3)
  xbutton("Leave up",temp_string_)
  xmenu()
}

//** pbrgr(browser name,action) is used to put up a browser
// note action given without '()'
proc pbrgr () {
  if (printlist.count == 1) {
    gv(0)
  } else if (printlist.count <= 8) {
    mkpanel("Vector",$s1,$s2)
  } else {
    sprint(temp_string_,"%s:%s",simname,$s1)
    printlist.browser(temp_string_,"name")
    sprint(temp_string2_,"%s()",$s2)
    printlist.accept_action(temp_string2_)
  }
}

//** mkpanel(title,action,proc) makes a panel from printlist
proc mkpanel () { local ii
  sprint(temp_string_,"%s:%s",simname,$s1)
  xpanel(temp_string_)
  for ii=0,printlist.count-1 {
    sprint(temp_string_,"%s %s",$s2,printlist.object(ii).name)
    sprint(temp_string2_,"%s(%d)",$s3,ii)
    xbutton(temp_string_,temp_string2_)
  }
  xpanel()
}

//** remprl() -- remove printlist item by name
proc remprl () { local flag
  flag=0
  if (numarg()==2) flag=1 else print "LISTING ONLY; rerun with 2 args to remove"
  for (ii=printlist.count-1;ii>=0;ii-=1) {
    Xo=printlist.object(ii)
    if (strm(Xo.name,$s1)) if (flag) printlist.remove(ii) else print Xo.name
  }
}

// for ltr(XO,glist) { print XO,XO.view_count }

//** wvpanl() 
proc wvpanl () { local  ii
  sfunc.tail(filename,"data.*/",grvecstr)
  sprint(temp_string_,"%s:%s (WVPANL)",simname,grvecstr)
  xpanel(temp_string_)
  sprint(temp_string_,"%d Vectors",llist.count)
  xlabel(temp_string_)
  for ii=0,3 {
     sprint(temp_string_,"size[%d]",ii)
     sprint(temp_string2_,"chrange(%d)",ii)
     xvalue(szstr[ii].s,temp_string_,0,temp_string2_,0,1)
  }
  xvalue("Shift L/R","shift",0,"chrange(-2)",0,1)
  xmenu("Other")
  xbutton("Clear/Set to G0","chrange(-1)")
  xbutton("View=plot","viewplot()")
  xbutton("0,tstop,-90,50","setrange(0,tstop,-90,50)")
  // xbutton("Clean graph list","collapsegrs()")
  xbutton("Attributes","attrpanl()")
  xmenu()
  xpanel()
  if (glist.count>0) for ii=0,3 if (size[ii]==0) size[ii]=glist.object(0).size(ii+1)
}

//** chrange() changes range for a set of graphs (called from attrpanl)
proc chrange () { local cnt, flag, ii, sz1, sz2, sz3, sz4
  if (numarg()==1) { flag = $1 } else { flag = -1 }
  cnt = glist.count()
  for (ii=cnt-1;ii>=0;ii=ii-1) if (glist.object(ii).view_count() == 0) glist.remove(ii)
  cnt = glist.count() // check again after removing any with no views
  if (cnt==0) { for ii=0,3 size[ii]=0 return }
  // flag -1 means set everything from the first graph
  if (flag==-1) for ii=0,3 size[ii] = glist.object(0).size(ii+1)
  if (flag==-2) for ii=0+luprd,1+luprd size[ii] += shift // shift right or left
  if (flag==5) { size[0]=0 size[1]=tstop } // just set x
  // for each of the graphs
  for ltr(Xo,glist) {
    sz1=Xo.size(1) sz2=Xo.size(2) sz3=Xo.size(3) sz4=Xo.size(4)
    if (flag==0) Xo.size(size[0],sz2,sz3,sz4)
    if (flag==1) Xo.size(sz1,size[1],sz3,sz4)
    if (flag==2) Xo.size(sz1,sz2,size[2],sz4)
    if (flag==3) Xo.size(sz1,sz2,sz3,size[3])
    if (flag==-1 || flag==4) Xo.size(size[0],size[1],size[2],size[3])
    if ((flag==-2 && !luprd) || flag==5) Xo.size(size[0],size[1],sz3,sz4)
    if (flag==-2 && luprd) Xo.size(sz1,sz2,size[2],size[3])
  }
  for ii=0,3 if (size[ii]==0) size[ii]=glist.object(0).size(ii+1)
}

//** grall() graphs all of the lines from the file
// use vector $o2 as indices for vectors (see tposvec)
proc grall () { local cnt,ii,min,max,gr,skip,iskp,vind,sind,a
  if (numarg()==0) {printf("grall(min,max,gr_offset,skipgr,iskp]): graph vectors.\n") return }
  sind=vind=0
  cnt = llist.count()
  min=0 max=cnt-1
  // will reset max if is vector with numarg()==2
  // with 2 args, vector $o2 gives indices for vectors (see tposvec)
  if (numarg()>=1) {
    if (argtype(1)==0) { min=$1
    } else { // a vector of indices or a string for prdr/vrdr
      a=allocvecs(1)
      if (argtype(1)==1) { vind=1 mso[a].copy($o1) } else sind=1
    }
  }
  if (numarg()>1) { max=$2 if (max<0) max+=llist.count }
  if (numarg()>2) gr=$3 else gr=0
  if (numarg()>3) skip=$4 else skip=1
  if (numarg()>4) iskp=$5 else iskp=1
  if (iskp==0) iskp=1
  if (super==1 && glist.count==0) { 
    remgrs()
    print "Creating plot"
    skip=0
    newPlot(0,tstop,-100,100) glist.append(graphItem)}
  if (super==0 && glist.count==0) size[1]=0
  if (sind) { 
    // for vrdr(aa,$s2,1,1) aa.x(0).append(ii1)
    vind=1
  }
  if (vind) { min=0 max=mso[a].size-1 }
  for (ii=min;ii<=max;ii+=iskp) {
    if (super == 1) { 
      if (gr >= glist.count()) break
      graphItem = glist.object(gr) 
      gr=gr+skip
    }
    if (vind) rv(mso[a].x[ii]) else rv(ii)
  }
  if (vind) dealloc(a)
}

// go through tmplist selected in rlist and graph onto glist
proc grsel () { localobj o
  for ii=0,tmplist.count-1 {
    rv_readvec((o=tmplist.o(ii)),vrtmp)
    if (ii<glist.count) graphItem=glist.o(ii) else {
      printf("GRV grsel() glist exhausted\n")
      return }
    if (o.num==-2) { // guess should do mark
      vrtmp.mark(graphItem,tvec,symb,line+2,curcol,4)
    } else vrtmp.line(graphItem,o.num,curcol,line)
  }
}    

// tposvec(rows,cols): generate the indices for a transposed matrix of rows x cols
proc tposvec () { local rows,cols,i,j
  rows=$2 cols=$3
  $o1.resize($2*$3)
  for (i=0;i<rows;i+=1) for (j=0;j<cols;j+=1) $o1.x[j*rows+i]=i*cols+j
}

// fliptb(rows,cols): generate the indices for a transposed matrix of rows x cols
proc fliptb () { local rows,cols,i,j,p
  rows=$2 cols=$3
  if ($o1.size != $2*$3) {print "Wrong size vector in fliptb()" return}
  p = allocvecs(1) mso[p].resize(rows)
  for (j=0;j<cols;j+=1) {
    mso[p].copy($o1,j*rows,(j+1)*rows-1)
    mso[p].reverse
    $o1.copy(mso[p],j*rows)
  }
}

//** setrange() sets the range
// setrange(x0,x1,y0,y1)
proc setrange () { local i,ii
  if (numarg()==0){print "setrange(x0,x1,y0,y1)\n setrange(3) erases axes"}
  if (numarg()==0) { for ltr(Xo,glist) Xo.size(0,tstop,-100,50)
  } else if (numarg()==1) { for ltr(Xo,glist) Xo.xaxis($1)
  } else if (numarg()==2) { for ltr(Xo,glist) Xo.size(0,tstop,$1,$2)
  } else {
    size[0]=$1
    for ltr(Xo,glist) Xo.size($1,$2,$3,$4)
    for i=1,4 size[i-1]=$i
  }
  for ii=0,3 if (size[ii]==0) size[ii]=glist.object(0).size(ii+1)
}

//* gxpan()
proc gxpan () { local x localobj xo,yo,g1
  gcomms.remove_all
  gcomms.append(new String2("Move","mvga"))
  gcomms.append(new String2("Mag","magga"))
  for ltr(xo,gcomms) for ltr(yo,glist) {
    yo.menu_remove(xo.s)
    yo.menu_tool(xo.s,xo.t)
  }
  gcomms.append(new String2("Crosshair",""))
  xpanel("Manipulate graphs",1)
  x=-1
  for ltr(xo,gcomms) {sprint(tstr,"mvgaset(%d)",x+=1) xradiobutton(xo.s,tstr)}
  xpanel()
}  

proc mvgaset () { localobj yo
  print $1,gcomms.o($1).s
  for ltr(yo,glist) yo.exec_menu(gcomms.o($1).s) 
}

proc mvga () { local type,x,y,keystate,sz1,sz2,sz3,sz4 localobj xo,yo
  type=$1 x=$2 y=$3 keystate=$4 // 2 for press, 1 for dragging, and 3 for release
  xo=glist.o(0)
  sz1=xo.size(1) sz2=xo.size(2) sz3=xo.size(3) sz4=xo.size(4)
  if (type == 2) {
    begx=x begy=y
  } else if (type==3) {
    x-=begx y-=begy
    size[0]=sz1-x size[1]=sz2-x size[2]=sz3-y size[3]=sz4-y
    for ltr(xo,glist) xo.size(sz1-x,sz2-x,sz3-y,sz4-y)
  }
}

proc magga () { local tmp,type,x,y,begx,begy,keystate,sz1,sz2,sz3,sz4 localobj xo,yo
  type=$1 x=$2 y=$3 keystate=$4 // 2 for press, 1 for dragging, and 3 for release
  xo=glist.o(0)
  sz1=xo.size(1) sz2=xo.size(2) sz3=xo.size(3) sz4=xo.size(4)
  if (type == 2) {
    begx=x begy=y
  } else if (type==3) {
    if (x<begx) { tmp=x x=begx begx=tmp }
    if (y<begy) { tmp=y y=begy begy=tmp }
    size[0]=begx size[1]=x size[2]=begy size[3]=y
    for ltr(xo,glist) xo.size(size[0],size[1],size[2],size[3])
  }
}

//* grransel() -- graph range select
proc setgrransel () {
  for ltr(Xo,glist) { 
    Xo.menu_remove("REVIEW")
    sprint(tstr,"proc p%d(){grransel($1,$2,$3,$4,%d,%s)}",ii1,ii1,Xo)
    execute1(tstr)
    sprint(tstr,"p%d",ii1)
    Xo.menu_tool("REVIEW", tstr)
    Xo.exec_menu("REVIEW")
  }
}
  
//** grransel(): 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
grrcnt=-1
// not debugged
proc grransel () { local type, x0, y0, keystate, ii, gr
  type=$1 x0=$2 y0=$3 keystate=$4 gr=$5
  graphItem=$o7 // = glist.object(gr)
  if (keystate==1 && type==2) { graphItem.exec_menu("Crosshair") // CTL-MOUSE-1
  } else if (keystate==3 && type==3) { 
    print grrcnt
    if (grrcnt>-1) { printf("Graphing %s\n",printlist.object(grrcnt).name)
      graphItem.erase_all()
      rv(grrcnt) 
      graphItem.exec_menu("View = plot")
    }
    graphItem.size(size[0],size[1],size[2],size[3])
  } else if (keystate==2 && type==2) { grrcnt=-1
  } else if (keystate==2 && type==1) { 
    x+=1 // slow it down five-fold
    if (x>5) { grrcnt=(grrcnt+1)%printlist.count
      printf("%d: %s\n",grrcnt,printlist.object(grrcnt).name)
      x=0
    }
  } else if (keystate==0 && type==2) { x=x0 y=y0 } else if (keystate==0 && type==3) {
    graphItem.size(x,x0,y,y0) // resize to chosen square
  } 
}

//** remgrs() gets rid of all of the graphs (called from attrpanl)
proc remgrs () { local ii,cnt
  if (isobj(graphItem,"Graph")) { graphItem.unmap graphItem = nil }
  for ltr (Xo,glist) Xo.unmap()
  glist.remove_all
}

//** collapsegrs () take off of glist graphs that have been closed on screen
proc collapsegrs () { local ii
  for (ii=glist.count-1;ii>=0;ii-=1) {
    if (glist.object(ii).view_count() == 0) { 
      glist.remove(ii)
    }
  }
}

//** viewplot() set the world for each graph correctly
proc viewplot () { local cnt,ii,flag,sz1,sz2,sz3,sz4
  if (numarg()==1) flag=$1 else flag=-1
  if (flag==0) { sz1=sz3=1e10 sz2=sz4=-1e10 }
  for ltr(Xo,glist) {
    Xo.size(&x[0])
    if (flag==0) { 
      if (x[0]<sz1) sz1=x[0]
      if (x[1]>sz2) sz2=x[1]
      if (x[2]<sz3) sz3=x[2]
      if (x[3]>sz4) sz4=x[3]
    } else if (flag==9) {
      Xo.size(0,tstop,x[2],x[3])
    } else { Xo.size(x[0],x[1],x[2],x[3]) }
  }
  if (flag==9) { size[0]=0 size[1]=tstop }
  if (flag==0) for ltr(Xo,glist) Xo.size(sz1,sz2,sz3,sz4)
}

//** nvwall() changes the size of the graphs
proc nvwall () { local cnt,ii,sz1,sz2,sz3,sz4,wd,ht
  if (numarg()==2) { wd=$1 ht=$2 } else { wd=vsz[0] ht=vsz[1] }
  cnt = glist.count()
  for (ii=cnt-1;ii>=0;ii=ii-1) { 
    if (glist.object(ii).view_count()==0) {glist.remove(ii)
    } else {
      sz1 = glist.object(ii).size(1)
      sz2 = glist.object(ii).size(2)
      sz3 = glist.object(ii).size(3)
      sz4 = glist.object(ii).size(4)
      glist.object(ii).unmap()
      vloc = vloc+vjmp
      if (vloc > 700) { vloc = 0 }
      glist.object(ii).view(sz1,sz3,sz2-sz1,sz4-sz3,0,vloc,wd,ht)
    }
  }
}

//** geall() erases all of the graphs
proc geall () { local cnt,ii
  cnt = glist.count()
  for ii=0,cnt-1 { 
    glist.object(ii).erase_all()
  }
}

//** lblall(label,#,xloc,yloc) put label on all of the graphs
// arg3 tells which single graph to put it on
proc lblall () { local cnt,ii,min,max,lx,ly
  if (numarg()==0) { printf("lblall([,loc])\n")  return }
  cnt = glist.count()
  if (numarg()>1) { min=max=$2 } else { min=0 max=cnt-1 }
  if (numarg()==5) { lx=$3 ly=$4 } else { lx=0.1 ly=0.8 }
  if (numarg()>1) { if (sfunc.len($s1)>0) { temp_string_ = $s1
  }}
  for ii=min,max { 
    glist.object(ii).color(color)
    glist.object(ii).label(lx,ly,temp_string_)
  }
}

//** relbl() put appropriate label on all of the graphs
proc relbl () { local cnt,ii,min,max,lx,ly
  if (numarg()==0) { printf("relbl([str])\n")  return }
  cnt = glist.count()
  if (numarg()==4) { lx=$3 ly=$4 } else { lx=0.1 ly=0.8 }
  if (numarg()>1) glist.object(0).label($s2)
  for ii=0,glist.count-1 {
    Xo=glist.object(0) Yo=llist.object(0)
    Xo.color(0)
    Xo.label(0.,.9,Yo.name) 
    Xo.color(color)
    Xo.label(lx,ly,Yo.vec.label)
  }
}

//** toggle functions
proc tog (){if (numarg()==0) print super=1-super else print $&1=1-$&1}
proc togmark () {
  if (gvmarkflag==0) { gvmarkflag=1 if (line<4) line+=1
  } else {             gvmarkflag=0 if (line>6) line-=1
  }
  sprint(tstr,"gvmarkflag=%d",gvmarkflag) // set external gvmarkflag as well
  execute(tstr)
  sprint(mesg,"mark=%d",gvmarkflag)
}

//* panobjl stuff
proc apbrowse () {
  panobjl.browser("attrpanls","s")
  panobjl.accept_action("panobjl.object(hoc_ac_).attrpanl()")
}

//** po(NUM) set global panobj to that number
proc po () { 
  sprint(tstr,"panobj=GRV[%d]",$1)
  execute(tstr)
}

proc apkill () {
  panobjl.browser("attrpanls","s")
  panobjl.accept_action("panobjl.remove(hoc_ac_)")
}

//* printlist stuff

//** record(list,what,vecptr) from items in $o1 object(ii).$s2 in vectors
// $o1 is the list arg $s2 is the thing to be recorded [eg soma.v(0.5)]
// optional $3 is the name of an object belonging to list items that will
// serve as a pointer to the recording vector
proc record () { local ii, dur, na3
  if (isobj($o1,"List")) for ltr(Xo,$o1) {
    sprint(recstr,"%s.%s",Xo,$s2)
    npl(recstr) 
    if (numarg()==3) { 
      sprint(temp_string_,"%s.%s=printlist.object(printlist.count-1).vec",Xo,$s3)
      execute(temp_string_) // only way to get call by ref for object
    }
  } else forsec $o1 { // assume sectionlist
    sprint(recstr,"%s.%s",secname(),$s2)
    npl(recstr) 
    if (numarg()==3) { 
      sprint(temp_string_,"%s.%s=printlist.object(printlist.count-1).vec",Xo,$s3)
      execute(temp_string_) // only way to get call by ref for object
    }
  } 
}

//** npl(name) adds this item to the printlist
//  npl(name,vec) adds this vec to the printlist
//  npl(name,vec,step) adds this vec to the printlist with printStep step
//  npl(name,vec,tvec) adds this vec to the printlist
//  npl(var,name) use name instead of variable name
//  npl(var,ptr) provide an objref to point to the vec
//  npl(name,num,vec)??adds this vec to the printlist under name_num0,name_num1,etc
proc npl () { local dur,nflag,tvflag,prstep  // METHOD DEPENDENT
  if (! isobj(printlist,"List")) printlist = new List()
  tvflag=nflag=0  prstep=0.05
  if (argtype(3)==0) {
    vite = new vitem($s1,$o2.size)
    vite.vec.copy($o2)
    vite.pstep=$3
    printlist.append(vite)
    return
  } else if (cvode_status()==0.0) {
    sprint(tstr,"%s.printStep=printStep",this)
    execute(tstr)
    prstep=printStep
  } else if (cvode_status()==1.1) {
    tvflag=1
  } else if (cvode_status()==1.0) { // ??
    printf("WARNING: Method 'global' is not currently supported for recording via grvec\n")
  }
  if (outvecint == 0) dur = tstop else dur = outvecint
  grvecstr = $s1
  repl_mstr(grvecstr," ","",temp_string2_) // no longer splits on '/'
  // eg npl(name,ii,vec)
  if (numarg()>=4) if ($4<=0) tvflag=1 else { tvflag=0 prstep=$4 }
  if (numarg()>=3) { // allows formation of tags on the fly
    if (argtype(3)==0) { 
      if ($3<=0) tvflag=1 else { tvflag=0 prstep=$3 }
    } else if (argtype(2)==0) {
      sprint(temp_string_,"%s%s",grvecstr,":%d")
      sprint(temp_string_,temp_string_,$2)
      vite = new vitem(temp_string_,$o3.size)
      vite.vec.copy($o3)
      if (numarg()==4) if (argtype(4)==1) {
        vite.tvec.copy($o4) vite.tvflag=1 vite.pstep=0
      } else if (argtype(4)==0) { vite.pstep=$4
      } else printf("Arg 4 unrecognized in npl?\n")
      printlist.append(vite)
      return
    } else if (argtype(2)==1) { 
      vite = new vitem(grvecstr,$o2.size,1)
      vite.vec.copy($o2) vite.tvec.copy($o3)
      printlist.append(vite)
      return
    }
  } 
  if (numarg()>=2) { // second arg is a vector to store
    if (argtype(2)==1) {
      vite = new vitem(grvecstr,$o2.size)
      vite.tvflag=tvflag vite.pstep=prstep
      vite.vec.copy($o2)
      printlist.append(vite)
      return
    } else if (argtype(2)==2) {  // give explicit name for the thing to store
      nflag=1
    }
  }
  if (cvode_status()==1.0) { 
    if (tvec.buffer_size==0){tvec.resize(dur/prstep+10) tvec.resize(0)}
    tvec.record(&t) 
  } else if (isobj(tvec,"Vector")) if (tvec.size!=0) { 
    tvec.resize(0) tvec.play_remove()          
  }
  if (nflag) { 
    if (tvflag) { vite = new vitem($s2,dur/prstep+10,1) } else {
                  vite = new vitem($s2,dur/prstep+10,prstep)   }
  } else {
    if (tvflag) { vite = new vitem(grvecstr,dur/prstep+10,1) } else {
                  vite = new vitem(grvecstr,dur/prstep+10,prstep) }
  }
  if (numarg()==2) if (argtype(2)==1) $o2=vite.vec // allow user to assign a pointer
  printlist.append(vite)
  if (tvflag) {
    vite.vec.resize(dur/prstep+10) vite.tvec.resize(dur/prstep+10)
    find_secname(grvecstr,temp_string_) // with lvardt, need to assign in proper context
    sprint(temp_string_,"%s {cvode.record(&%s,%s.vite.vec,%s.vite.tvec)}",temp_string_,grvecstr,this,this)
  } else if (cvode_status()==1.0) { // don't give an explicit prstep
    vite.vec.resize(dur/prstep+10)
    sprint(temp_string_,"%s.vite.vec.record(&%s)",this,grvecstr)
  } else {
    sprint(temp_string_,"%s.vite.vec.record(&%s,%g)",this,grvecstr,prstep)
  }
  if (! execute1(temp_string_)) print "Unable to excute ",temp_string_
  vite=nil
}

//** store cvode state in a double in form active.local
func cvode_status () { return cvode.active() + cvode.use_local_dt()/10 }

proc ulv () {  }

//** pvall() dumps all vectors to output_file
// Save logic:
// 1) Interactive mode: you're watching simulations while printing out
// data.  You see something you like and save it - the runnum in the
// index file then corrsponds to the number on the data file, it is
// subsequently augmented
// 2) Batch mode: you're running very long sims and saving directly to
// vector files.  You put together a simulation you want and save it
// immediately rather than waiting for the sim to return (in 1 or 2
// days).  To correspond, the data file (v*) must then be numbered
// 'runnum-dec_runnum'
proc pvall () { 
  if (numarg()>=1) { 
    comment = $s1 // a comment
  } else if (sfunc.len(comment)==0) {
    sprint(tstr,"%s.comment=comment",this) execute(tstr)
    sprint(tstr,"Use: %s ?",comment)
    if (!boolean_dialog(tstr)) return
  }
  if (numarg()>=2) output_file=$s2 else {
    sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum-dec_runnum)
    while (tmpfile.ropen(output_file)) { runnum = runnum+1
      printf("%s found, trying %sv%s.%02d\n",output_file,ddir,datestr,runnum-dec_runnum)
      sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum-dec_runnum) 
    }
  }
  printf("Saving to %s\n",output_file)
  pvplist(output_file,comment)
  sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum)
  comment=""
}

//** pvplist(file,comment,tback) print out the printlist with comment at head
proc pvother () {}  // user can dump other vectors at top with prvec()
proc pvplist () { local inx,tmin,tmax,tback localobj oq,xo
  output_file=$s1
  if (! pvplist0()) return // open file(s)
  pvplist1($s2) // print string header
  if (numarg()==3) if ($3>1) {
    tback=$3 tmax=printlist.o(0).tvec.max
    if (tmax>tback) {
      tmin=tmax-tback
      oq=new NQS() 
      for ltr(xo,printlist) {
        oq.setcols(xo.tvec,xo.vec)
        oq.select(0,">",tmin)
        oq.cpout()
        oq.resize(0)
      }
    }
  }
  pvout() 
  if (numarg()<3) { // leave for appending if $3==1
                     tmpfile.close() tf1.close()
  } else if ($3>1) { tmpfile.close() tf1.close() }
}

//** pvclose() -- close files used for writing
proc pvclose () {
  tmpfile.close()
  tf1.close()
}

//** pvplist0() -- open output_file and ancillary dot file if needed
func pvplist0 () { localobj st
  st=new String2()
  file_with_dot(output_file,st.s) // put .filename into st.s
  if (numarg()==0) {
    if (tmpfile.ropen(st.s)) { printf("WARNING: removing %s\n",st.s)
      sprint(st.t,"rm %s",st.s) system(st.t) }
    if (tmpfile.wopen(st.s)==0) { print "Can't open ",st.s  return 0}
    if (! isojt(tf1,tmpfile)) tf1=new File()
    tf1.wopen(output_file)
  } else {
    if (tmpfile.aopen(st.s)==0) { print "Can't open ",st.s," to append" return 0}
    if (! isojt(tf1,tmpfile)) tf1=new File()
    tf1.aopen(output_file)
  }
  return 1
}

//** prplist1()
proc pvplist1 () {
  tmpfile.printf("//: %s\n",$s1) // comment
  if (cvode_status()==1.1) {
    tmpfile.printf("//printStep -2\n")
  } else if (cvode_status()==1.0) {
    tmpfile.printf("//printStep -1\n")
  } else {
    sprint(tstr,"%s.printStep=printStep",this)
    execute(tstr)
    tmpfile.printf("//printStep %g\n",printStep)
  }
  if (byte_store) tmpfile.printf("//CPU %s\n",uname)
}

//** pvnext() append another printlist set to same file
proc pvnext () { local ii
  if ($1==0) { 
    pvplist(output_file,comment,1)
    return
  }
  pvplist0(0) // open for appending
  pvout()
  printf("Append to %s\n",output_file)
  tmpfile.close()
  tf1.close
}

//** pvout() called by pvplist() and pvnext(), actually puts out the vecs
proc pvout () {  // METHOD DEPENDENT
  pvout2()
  if (cvode_status()==1.0 && tvec.max>0) { 
    tmpfile.printf("//b%d 1 %s %d %d\n",tvec_bytesize,"CVODE1.0 tvec",tvec.size,tf1.tell)
    tvec.vwrite(tf1,tvec_bytesize) 
  }
  for ltr(Xo,printlist) { // no whitespace allowed
    if (Xo.code==2) continue
    if (sfunc.len(Xo.vec.label)>0) sprint(temp_string_,"%s__(%s)",Xo.vec.label,Xo.name) else {
      temp_string_=Xo.name } 
    pvpone(Xo)
  }
}

//** dir2pr(item#[,OUTFILE,OUTCOMMENT]) read the files in dir and add one item to printlist
// see also collect.hoc for batch use
proc dir2pr () { local ix,ps
  printlist.remove_all 
  ix=$1
  for ltr(Xo,dir) {
    read_vfile(Xo.s)
    rv_readvec(ix,vrtmp)
    ps=llist.o(ix).num // pstep: need if want to save a fixed step entry
    grv_.npl(comment,vrtmp,tvec)
  }
  if (numarg()==3) { grv_.pvplist($s2,$s3) read_vfile($s2) }
}

//** dir2mf(FILENAME,COMMENT) read the files in dir and create a master file
proc dir2mf () { local ix,ps,n localobj f
  f=new File()
  f.wopen($s1)
  f.printf("//: %s\n",$s2)
  for ltr(Xo,dir) {
    read_vfile(Xo.s)
    n=-1
    for ltr(Yo,llist) if (dir2mf2(n+=1)) {
      // repl_mstr(comment," ",";",temp_string2_) // no spaces allowed
      // sprint(temp_string_,"%s_%s",Yo.name,comment)
      f.printf("//b%d %g %s %d %d %s\n",bvec,Yo.num,Yo.name,Yo.size,Yo.loc,filename)
    }
  }
  f.close()
}

//** read_mf() reads a master file
// assumes file in tmpfile
func read_mf () { local ii,sz,loc,mult,sze,cloc,segs localobj o
  if (attr0) {
    if (!boolean_dialog("Look at file data instead of sim?","YES","NO")) {
      printf("Read file cancelled\n")
      return 0
    }
  } else { attr0=0 }
  if (numarg()==1) filename=$s1 else tmpfile.getname(filename)
  sprint(s,"GRV[%d] %s: %s",objnum(this),simname,filename)
  clear()
  if (!tmpfile.ropen(filename)) { print "E1: Can't open ",filename return 0 }
  while ((numr = tmpfile.gets(tstr)) != -1 && (sfunc.head(tstr,"//[^b]",temp_string2_)==0)) {
    read_vinfo()  // a line giving info about the file (eg comment)
  }
  tmpfile.seek(-numr,1) // back up
  ii=0
  while ((numr = tmpfile.gets(tstr)) != -1) {
    if (((ii+=1)%10000)==0) printf("%d ",ii)
    if (sscanf(tstr,"//b%1ld %g %s %ld %ld %s",&bvec,&nvec,tstr2,&sze,&loc,tstr)!=6) {
      printf("**** GRV read_mf() parse ERR on %s in %s",tstr,filename)
      return 0
    }
    llist.append(new vfile_line(tstr2,sze,loc,nvec,tstr))  // name size loc num
  }
  if (llist.count==0) {
    printf("grvec.hoc::read_mf ERR nothing read from %s\n",filename)
    return 0
  }
  entries=llist.count
  mff=1
  return 1
}

//** prvec(name,vec,byte_flag[,file])
proc prvec () { local bflag
  if (numarg()>3) { tmpfile.aopen($s4) }
  bflag = $3
  tmpfile.printf("//b%d 1 %s %d %d\n",bflag,$s1,$o2.size,tf1.tell())
  $o2.vwrite(tf1,bflag) 
  if (numarg()>3) { tmpfile.close() }
}

//** pv() dumps a single vector into output_file
proc pv () { local inx  
  sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum-dec_runnum)
  if (numarg()==0) { inx = hoc_ac_ } else { inx = $1 }
  printf("Printing %s to %s\n",printlist.object(inx).name,output_file)
  // string_dialog("Name for saved vector",printlist.object(inx).name)
  if (tmpfile.ropen(output_file)) { // file exists already
    file_with_dot(output_file,temp_string_) // put .filename into temp_string_
    tmpfile.aopen(temp_string_) tf1.aopen(output_file)
    printf("Appending to %s\n",output_file)
  } else { 
    pvplist0()
    pvplist1(comment)
  }
  pvpone()
  tmpfile.close()
  tf1.close
}

//** pvpone() -- write out a vector or vector pair to the file
proc pvpone () {  // METHOD DEPENDENT
  if (byte_store) {
    if ($o1.tvflag==1 && isassigned($o1.tvec)) {
      tmpfile.printf("//b%d -2 %s %d %d\n",byte_store,temp_string_,$o1.vec.size,tf1.tell)
      $o1.tvec.vwrite(tf1,tvec_bytesize) 
    } else if ($o1.pstep>0) {
      tmpfile.printf("//b%d %g %s %d %d\n",byte_store,$o1.pstep,temp_string_,$o1.vec.size,tf1.tell)
    } else {
      tmpfile.printf("//b%d -1 %s %d %d\n",byte_store,temp_string_,$o1.vec.size,tf1.tell)
    }
    $o1.vec.vwrite(tf1,byte_store)
  }
}

//** cpplitem([num]) copy X0 to new item in printlist, optional arg can be pos or neg
proc cpplitem () { local num,cnt
  cnt = printlist.count
  if (numarg()>0) {if ($1>=0) num=$1 else num=cnt+$1} else num=cnt-1
  if (num>cnt-1 || num<0) {
    printf("%d!: Only %d items (0-%d) in list.\n",num,cnt,cnt-1) return 
  }
  if (numarg()>1) grvecstr=$s2 else sprint(grvecstr,"Copy_of_%s",printlist.object(num).name)
  npl(grvecstr,printlist.object(num).vec)
  print printlist.count-1,":XO -> ",grvecstr
  XO = printlist.object(printlist.count-1).vec
}

//** chgplname([num],STR) change name of item in printlist
proc chgplname () { local num,cnt
  cnt = printlist.count
  if (numarg()>0) {if ($1>=0) num=$1 else num=cnt+$1} else num=cnt-1
  if (num>cnt-1 || num<0) {
    printf("%d!: Only %d items (0-%d) in list.\n",num,cnt,cnt-1) return 
  }
  printlist.object(num).name=$s2
}

// new_pri(NAME,TVEC,VEC) quick and dirty new_printlist_item
proc new_pri () {
  vite = new vitem($s1,$o2.size)
  if (numarg()==3) { vite.tvec.copy($o2) vite.vec.copy($o3) } else vite.vec.copy($o2)
  printlist.append(vite)
  vite=nil
}

//* prlexp(sz) expands all the vectors in printlist to size sz
proc prlexp () { 
  sz = $1
  tvec.resize(sz)
  for ltr(Xo,printlist) { Xo.vec.resize(sz) }
}

//* iterators for printlist and files
//** rvtr() read vector iterator
// usage 'for rvtr(vec) XO.vec.printf' where # is attrpanl#
iterator rvtr () { local i,flag,s4flag
  if (numarg()>=2) {$&2=0} else {i1 = 0}
  if (numarg()==3) s4flag=1 else s4flag=0
  flag=1
  for i = 0, entries-1 {
    tstr = llist.object(i).name
    if (s4flag) {if (strm(tstr,$s3)) flag=1 else flag=0}
    if (flag) {
      rv_readvec(i,$o1) 
      if (numarg()>=2) { $&2=i } else { i1=i }
      iterator_statement // rvtr(vec) -- name in .tstr, tvec in .tvec
    }
  }
}

//** vrdr(vlist[,REGEXP or INDV,flag,&y]) -- used for llist
// similar to rvtr() but does interpolation
// use regexp eg for prdr("PYR2.8") { etc }
// optional flag to NOT interpolate
// indv gives set of llist nums to search through (can use with "" as regexp)
// sets 4 vectors in vlist: voltage,times,interp v, interp t
iterator vrdr () { local flag,a,ii localobj rxp,v1,tv1,v2,tv2,ipt
  if (numarg()==0) printf("\t**** HELP::  vrdr(vlist[,REGEXP or INDV,flag,&y]) ****\n")
  curcol=0
  rxp=new String()
  a=allocvecs(ipt)
  if (!isojt($o1,llist)) $o1=new List()
  if ($o1.count==4) { // use the list we're given
    for ii=0,3 if (!isojt($o1.o(ii),vrtmp)) {
      printf("vrdr: Nonvector in vlist:%s\n",$o1.o(ii))
      return
    }
  } else {
    $o1.remove_all 
    for ii=0,3 $o1.append(new Vector())
  }
  v1=$o1.o(0) tv1=$o1.o(1) v2=$o1.o(2) tv2=$o1.o(3) 
  ipt.indgen(0,llist.count-1,1)
  if (numarg()>=2) if (argtype(2)==1) { ipt.copy($o2) 
            } else if (argtype(2)==2) { rxp.s=$s2
            } else { printf("vrdr() ERR arg 2 should be vector or string\n") }
  if (numarg()>=3) flag=$3 else flag=0
  if (numarg()>=4) {$&4=0} else {ii1 = 0}
  if (!flag) {
    if (tvec.max != tstop) printf("WARNING: tvec set?: %g %g\n",tstop,tvec.max)
    tv2.copy(tvec)  // tvec must be preassigned for interpolation
  }
  tmpfile.ropen(filename)
  for (ii1=0;ii1<llist.count;ii1+=1) {
    if (ipt.contains(ii1) && strm(llist.object(ii1).name,rxp.s)) {
      XO=llist.object(ii1)
      if (mff) if (strcmp(filename,XO.f)!=0) { filename=XO.f tmpfile.ropen(filename) }
      tmpfile.seek(XO.loc)
      if (XO.num==-2) tv1.vread(tmpfile) else {
        if (XO.num<=0) {printf("vrdr INTERR; ?printStep for interpolation\n") err()}
        tv1.indgen(0,tstop+0.01,XO.num) }
      v1.vread(tmpfile)
      v2.resize(0)
      if (XO.num==-2 && !flag) v2.interpolate(tv2,tv1,v1)
      i1=ii1
      iterator_statement
      if (numarg()>=4) { $&4+=1 }
    }
  }
  tmpfile.close
  dealloc(a)
}

//** prdr() -- used for printlist
// use regexp eg for prdr("PYR2.8") { etc }
// optional flag to NOT interpolate
// ind=tvec, vec1=original trace, vec interpolated on tvec
// note that i1 here gives list number, not sequential
// eg for panobj.prdr("V$",1) gv(i1)
iterator prdr () { local flag
  if (numarg()>1) flag=$2 else flag=0
  if (numarg()==3) {$&3=0} else {i1 = 0}
  v1=tv1=v2=tv2=allocvecs(4) tv1+=1 v2+=2 tv2+=3
  if (!flag) {
    if (tvec.max != tstop) printf("WARNING: tvec set?: %g %g\n",tstop,tvec.max)
    mso[tv2].copy(tvec)  // tvec must be preassigned for interpolation
  }
  curcol=0
  if (attr0) for (ii1=0;ii1<printlist.count;ii1+=1) {
    XO=printlist.object(ii1)
    if (strm(XO.name,$s1)) {
      sprint(tstr,"tstr=\"%s\"",XO.name) execute(tstr) // set global tstr
      mso[v1].copy(XO.vec) 
      if (isit(XO.tvec)) mso[tv1].copy(XO.tvec) 
      if (!flag && isit(XO.tvec)) mso[v2].interpolate(mso[tv2],XO.tvec,XO.vec)
      i1=ii1
      iterator_statement
      if (numarg()==3) { $&3+=1 }
    }    
  } else for (ii1=0;ii1<llist.count;ii1+=1) {
    if (strm(llist.object(ii1).name,$s1)) {
      XO=llist.object(ii1)
      i1=ii1
      iterator_statement
      if (numarg()>=3) { $&3+=1 }
    }
  }
  dealloc(v1)
}

//* outvec() routines for printing out in sections - NOT DEBUGGED

//** outvec_init([output_file,comment])
proc outvec_init() { local segs
  if (numarg()>0) { output_file = $s1 } else {
    sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum-dec_runnum)
    while (tmpfile.ropen(output_file)) { runnum = runnum+1 // don't allow an overwrite
      sprint(output_file,"%sv%s.%02d",ddir,datestr,runnum-dec_runnum) }
  }
  if (numarg()>1) { comment = $s2 }
  print "\nOutput to ",output_file
  if (print_flag) { print "WARNING: print_flag=1 --> 0\n"
    print_flag = 0 }
  if (outvecint==0 || outvecint>tstop) {
    printf("WARNING: outvecint being set to tstop\n")
    outvecint = tstop }
  outvect = outvecint
  segs = int(tstop/outvecint)
  if (tstop/outvecint > segs) { segs=segs+1 }
  tmpfile.wopen(output_file)
  if (strcmp(comment,"")!=0) { tmpfile.printf("//: %s\n",comment) }
  tmpfile.printf("//printStep %g\n",printStep)
  tmpfile.printf("//MULTI %d %d\n",printlist.count,segs)
  tmpfile.close()
}

//** outvecs()  : print out the vectors and reset them for recording
proc outvecs () { local ii
  if (t<outvect || outvecint == 0) { return }
  tmpfile.aopen(output_file)
  for ii=0,printlist.count-1 {
    tmpfile.printf("//b%d 1 %s %d %d\n",byte_store,printlist.object(ii).name,t-outvecint,tmpfile.tell())
    printlist.object(ii).vec.vwrite(tmpfile,byte_store)
    tmpfile.printf("\n")
    printlist.object(ii).vec.play_remove()
    sprint(temp_string_,"printlist.object(%d).vec.record(&%s,%g)",\
           ii,printlist.object(ii).name,printStep)
    execute(temp_string_)
  }
  tmpfile.close
  outvect = outvect+outvecint
}

//** outvec_finish () : put out the last section if needed, update the output_file name
proc outvec_finish() {
  if (t > outvect-outvecint+2*dt) {
    tmpfile.aopen(output_file)
    for ii=0,printlist.count-1 {
      tmpfile.printf("//b%d 1 %s %d %d\n",byte_store,printlist.object(ii).name,t-outvecint,tmpfile.tell())
      printlist.object(ii).vec.vwrite(tmpfile,byte_store)
      tmpfile.printf("\n")
    }
    tmpfile.close()
  }
}

//* endtemplate; assignments:
endtemplate GRV

printStep=0.1
grv_ = new GRV(0)
{panobj=grv_ printlist=grv_.printlist panobjl=grv_.panobjl}

//* external routines
//** new_printlist_item(name) adds this item to the printlist
//  new_printlist_item(name,vec) adds this vec to the printlist
//  new_printlist_item(name,vec,tvec) adds this vec to the printlist
//  new_printlist_item(var,name) use name instead of variable name
//  new_printlist_item(var,ptr) provide an objref to point to the vec
//  new_printlist_item(name,num,vec)??adds this vec to the printlist under name_num0,name_num1,etc
proc new_printlist_item () { local dur,nflag,tvflag
  if (! isassigned(grv_)) { grv_ = new GRV(0) printlist=grv_.printlist }
  if (numarg()==1) { grv_.npl($s1)
  } else if (numarg()==2) {
    if (argtype(2)==1) {
      grv_.npl($s1,$o2)
    } else if (argtype(2)==2) {
      grv_.npl($s1,$s2)
    } else grv_.npl($s1,$2)
  } else if (numarg()==3) {
    if (argtype(2)==1) {
      grv_.npl($s1,$o2,$o3)
    } else if (argtype(2)==0) {
      grv_.npl($s1,$2,$o3)
    }
  }
}

//** llist() print out contents of a list
proc llist () { local done localobj o,st,xo
  st=new String()  o=panobj
  if (numarg()==2) st.s=$s2
  if (numarg()>=1) { 
    if (argtype(1)==1) {
      if (isobj($o1,"List")) { 
        if ($o1.count==0) {print "empty list" return}
        if (isobj($o1.object(0),"String2")) {
          for ltr(xo,$o1) if (strm(xo.s,st.s)) print xo.s,xo.t
        } else if (isobj($o1.object(0),"String")) { 
          for ltr(xo,$o1) if (strm(xo.s,st.s))  print xo.s
        } else if (isobj($o1.object(0),"Vector")) {
          done=0
          if (name_declared("oform")) if (oform(vec)!=NOP) {
            for ltr(xo,$o1) print xo,oform(xo) 
            done=1 
          }
          if (!done) for ltr(xo,$o1) print xo,xo.size
        } else if (isobj($o1.object(0),"Union")) {
          for ltr(xo,$o1) if (strm(xo.s,st.s)) print xo,xo.s,xo.t,xo.u,xo.v
        } else for ltr(xo,$o1) print xo
        return
      } else o=$o1
    } else if (argtype(1)==2) st.s=$s1
  }
  if (o.attr0) { for ltr(xo,printlist) if (strm(xo.name,st.s)) print i1,xo.name,xo.vec.size
  } else         for ltr(xo,o.llist) if (strm(xo.name,st.s)) print i1,xo.name,xo.size      
}

//** cpprl(PRINTLIST,TMPLIST) copies printlist vitem's to tmplist
proc cpprl () { localobj xo,yo
  $o2.remove_all
  for ltr(xo,$o1) {
    $o2.append((yo=new vitem(xo.name,xo.vec.size)))
    yo.tvflag=xo.tvflag yo.pstep=xo.pstep yo.o=xo.o yo.vec.copy(xo.vec)
    if (yo.tvflag) {yo.tvec=new Vector() yo.tvec.copy(xo.tvec)}
  }
}

//** abbreviated proc calls
proc gvpwpl () { pwman_place(500,500) }
proc vp () { grv_.vecpanel }
obfunc gvnew () { 
  if (numarg()==1) {
    if (argtype(1)==0) {
      panobj=new GRV($1)
    } else if (argtype(1)==2) {
      panobj=new GRV($s1)
    } else if (argtype(1)==1) {
      $o1=new GRV(-2)
      panobj=$o1
    }
  } else if (numarg()==2) {
    if ($2>0) {
      panobj=panobjl.object($2)
      panobj.read_vfile($s1)
    } else panobj=new GRV($s1,$2) // file,flag
  } else panobj=new GRV(1) // default
  return panobj
}

proc ap () { local ii,attr0,nopan localobj o
  nopan=ii=0
  if (numarg()==0) { o=panobj 
  } else if (numarg()>=1) {
    if (argtype(1)==0) { ii=$1 
      o=grv_.panobjl.object(ii)
      if (ii<0 || ii>=grv_.panobjl.count) { 
        printf("**** ap(%d) ERR panobj #%d not found -- run gvnew() \n",ii,ii) return }
    } else o=$o1
  }
  if (numarg()>=2) if (argtype(2)==0) if ($2==-2) nopan=1
  attr0=o.attr0
  o.attrpanl()
  if (!nopan) if (attr0) o.pbrgr("Graph","gv") else o.rpanel()
  panobj=o
}
proc disptray () { print "Must load boxes.hoc to get trays" }

//** gg() graph vectors and functions directly
//** gs(#) select graph (by setting g and graphItem to this Graph#
proc gs () { 
  if (argtype(1)==1) {      g=$o1 graphItem=g 
  } else if (numarg()==2) { g[$1]=Graph[$2] 
  } else {                  g=Graph[$1] graphItem=g }
}
// gg(g[i],vec) gg(vec,step) gg(vec,ind) gg(g,"FUNC","min,max") [color,line,symbol]
gvmarkflag=0
obfunc gg () { local gp,na,newgr,clr,a,stp,i,tmp localobj ty,ts,abs,ord,o,go
  if (argtype(1)==2) if (strc($s1,"help")) {
    printf("eg gg(g[i],vec) gg(vec,step) gg(vec,ind) gg(g,'FUNC','min,max') [color,line,symbol]\n")
    return nil
  }
  a=allocvecs(ty,abs,ord)
  ts=new String2()
  na=numarg() newgr=1
  ty.resize(10) ty.fill(-1)
  for i=1,na ty.x[i]=argtype(i)
  i=1
  clr=panobj.color lne=panobj.line
  if (ty.x[i]==0) { 
    gp=$i                                         i+=1
  } else gp=0
  if (gp<100) {
    if (isassigned(g[gp])) if (g[gp].view_count>0) newgr=0
    if (newgr) g[gp]=new Graph()
    go=graphItem=g[gp]
    graphList[0].append(g[gp])  panobj.glist.append(g[gp])
  } else {
    graphItem=go=new Graph()
    graphList[0].append(go)  panobj.glist.append(go)
  }
  if (gvmarkflag) ts.t="mark" else ts.t="line"

  if (na==1 && ty.x[0]==0) { return  // gg(#) just put up the graph
  } else if (na==i && ty.x[i]==1) { 
    sprint(ts.t,"%s.%s(%s,1",$oi,ts.t,go)   // gg(vec)
  } else if (ty.x[i]==2) { // gg("FUNC","min,max,step")
    min=0 max=10 stp=0
    ts.s=$si                                      i+=1
    if (ty.x[i]==2) {
      split($si,abs,"[,:/]") min=abs.x[0] max=abs.x[1] 
      if (abs.size==3) stp=abs.x[2]               i+=1
    }
    if (stp==0) stp=(max-min)/200
    abs.indgen(min,max,stp) ord.copy(abs) 
    if (!name_declared(ts.s)) { // look for an x that will become $1
      if (!strm(ts.s,"[$]1")) { printf("gg ERR Can't find '$1' in %s\n",ts.s) return nil }
      sprint(ts.s,"func _gg_f(){return %s}",ts.s) execute1(ts.s)
      print ts.s
      ts.s="_gg_f"
    }
    ord.apply(ts.s)
    sprint(ts.t,"%s.%s(%s,%s",ord,ts.t,go,abs)
  } else if (ty.x[i]==1 && ty.x[i+1]==0) {     // gg(vec,step)
    o=$oi                                         i+=1
    if (isobj(o,"List")) {
      tmp=$i   i+=1
      if (int($i)!=$i) { // a timestep
        sprint(ts.t,"%s.%s(%s,%g",o.o(tmp),ts.t,go,$i)      i+=1  
      } else {
        sprint(ts.t,"%s.%s(%s,%s",o.o(tmp),ts.t,go,o.o($i)) i+=1  
      }
    } else { // vector
      sprint(ts.t,"%s.%s(%s,%g",o,ts.t,go,$i)     i+=1  
    }                                          
  } else if (ty.x[i]==1 && ty.x[i+1]==1) {     // gg(vec,ind)
    o=$oi                                         i+=1
    sprint(ts.t,"%s.%s(%s,%s",o,ts.t,go,$oi)   i+=1
  }
  if (ty.x[i]==0) { clr=$i                        i+=1 }
  if (ty.x[i]==0) { lne=$i                        i+=1 }
  if (ty.x[i]==2) { symb=$si                      i+=1 }
  if (sfunc.len(ts.t)>4) {
    if (gvmarkflag) { sprint(ts.s,"%s,\"%s\",%d,%d,4)",ts.t,symb,lne,clr)
    } else { sprint(ts.s,"%s,%d,%d)",ts.t,clr,lne) }
    execute(ts.s)
  }
  dealloc(a)
  return graphItem
}

//*** ge() erases IV graph
proc ge () { if (numarg()==0) graphItem.erase_all() else g[$1].erase_all }

//** gv() calls internal gv
proc gv () {  local inx,na // EXTERNAL VERSION -- same name in template
  na=numarg()
  if        (argtype(1)==0) { inx = $1
  } else if (argtype(1)==2) {
      for ltr(XO,printlist) if (strm(XO.name,$s1)) inx=i1
      if (inx==-1) {print $s1," not found" return }
  }
  if (na==1) grv_.gv(inx) else if (na==2) grv_.gv(inx,$2) else if (na==3) grv_.gv(inx,$2,$3)
}

//** restore_printlist() restores the plist from a file in an attrnum
objref vite
proc restore_printlist () { local cnt,ii,attrnum,savlvar localobj aa,st
  printf("NOT WORKING\n")
  st=new String()
  attrnum=$1
  printlist.remove_all
  panobj=grv_.panobjl.object(attrnum)
  for panobj.vrdr(aa,"",1) {
    vite= new vitem(st.s,aa.o(0).size)
    vite.vec.copy(aa.o(0))
    printlist.append(vite)
  }
}

//** dirname(full,path) filname(full,file) splits up path/file
// eg filname("/home/billl/nrniv/thal/params.hoc",temp_string_)
//    temp_string_ => params.hoc
obfunc dirname () { localobj st
  st=new String2()
  if (argtype(1)==1) st.s=$o1.s else st.s=$s1
  st.t=st.s
  sfunc.head(st.s,"[^/]+$",st.s)
  sfunc.tail(st.t,st.s,st.t)
  if (numarg()==2) $s2=st.s
  return st
}

//** filname()
obfunc filname () { localobj st
  st=new String2()
  if (argtype(1)==1) st.s=$o1.s else st.s=$s1
  sfunc.head(st.s,"[^/]+$",st.t)
  sfunc.tail(st.s,st.t,st.s)
  if (numarg()==2) $s2=st.s
  return st
}

//** filsuf()
obfunc filsuf () { localobj st
  st=new String2()
  if (argtype(1)==1) st.s=$o1.s else st.s=$s1
  sfunc.tail(st.s,"\\.",st.s)
  if (numarg()==2) $s2=st.s
  return st
}

//** filstem()
// allows composition: filstem(filname(aa)).s
obfunc filstem () { localobj st
  st=new String2()
  if (argtype(1)==1) st.s=$o1.s else st.s=$s1
  sfunc.head(st.s,"\\.",st.s)
  if (numarg()==2) $s2=st.s
  return st
}

//** file_with_dot(filename,[result,prefix]): put .filename into result
obfunc file_with_dot () { localobj st
  st=dirname($s1)
  if (numarg()==3) { sprint(st.s,"%s%s%s",st.s,$s3,st.t)
  } else             sprint(st.s,"%s.%s", st.s,    st.t)
  if (numarg()>=2) $s2=st.s
  return st
}

//* using grvec to save vectors 
proc grvclr () { printlist.remove_all() }
//* grvsv,grvwr,etc
//* grvsv(name,vec[,dt or tvec]) save a named vector
proc grvsv () { local ddt,tvf localobj st,dv,tv
  st=new String()
  tvf=0 ddt=1
  if (argtype(1)==2) st.s=$s1
  if (argtype(2)==1) dv=$o2
  if (argtype(3)==-1){tvf=0 ddt=0.025
  } else if (argtype(3)==1) {tv=$o3 tvf=1
  } else if (argtype(3)==0) ddt=$3
  if (ddt==1) ddt=1.00001 // since dt of 1 is used as a flag (bad hack)
  if (tvf) printlist.append(new vitem(st.s,dv,tv)) else printlist.append(new vitem(st.s,dv,ddt))
}
// grvwr(filename[,comment]) // comment appends date,user,hg tip
proc grvwr () { localobj st
  st=new String2()
  if (argtype(2)==2) st.t=$s2
  system("date",st.s)       
  chop(st.s) sprint(st.t,"%s:%s",st.t,st.s)
  system("echo $USER",st.s) 
  chop(st.s) sprint(st.t,"%s:%s",st.t,st.s)
  system("hg tip --template '{rev}:{node}:{date|isodate}'",st.s) // mysteriously adds a newline??
  if (sfunc.len(st.s)>2) {chop(st.s) sprint(st.t,"%s:%s",st.t,st.s)}
  GRV[0].pvplist($s1,st.t)
}
//** datasource=grvrd("filename"[,showflag]); with showflag puts up attribute panel; returns a datasource
obfunc grvrd () { local showflag
  showflag= 0 // default don't show
  if (argtype(2)==0) showflag=1
  if (showflag) panobj=new GRV($s1) else panobj=new GRV($s1,-2)
  return panobj
}

//** grvlist([GRV]) // list names and sizes
proc grvlist () { local x localobj xo,po
  po=panobj
  if (po.llist.count()==0) return
  if (argtype(1)==1) if (isobj($o1,"GRV")) po=$o1
  printf("# name size\n")
  if (eqobj(po.llist,printlist)) { for ltr(xo,po.llist,&x) print x,xo.name,xo.vec.size
  } else                           for ltr(xo,po.llist,&x) print x,xo.name,xo.size
}

//** vec=grvrv(datasource,num OR name, datavec, timevec) // read data vec(s) from a datasource
//   all arguments are optional but order of args not
//    datasource defaults to current datasource given by 'panobj' object pointer
//    num or dataname defaults to 0 (first vector in datasource)
//    if vec1 is missing routine creates a vector, fills with datavec and returns
//    vec2 if missing is ignored -- in order to pick up vec2 must also give a vector for vec1
//    examples: grvrv(3) grvrv(vec,tvec) grvrv("dataname",vec,tvec)
obfunc grvrv () { local i,num,x localobj po,st,v1,v2,xo
  i=1 st=new String2() po=panobj
  if (argtype(i)==1) if (isobj($oi,"GRV")) {po=$oi i+=1}
  if (argtype(i)==0) {num=$i i+=1} else num=0
  if (argtype(i)==2) { 
    st.s=$si i+=1 // name will overwrite num if redundantly given as num,name 
    for ltr(xo,po.llist,&x) if (strm(xo.name,st.s)) {num=x break}
    if (x==po.llist.count) printf("grvrv(): %s NOT FOUND in datasource %s; returning first item\n",st.s,po)
  }
  if (num>=po.llist.count || num<0) { 
    printf("grvrv(): %d not in datasource %s (max %d); returning first item\n",num,po,po.llist.count-1) 
    num=0 
  }
  if (argtype(i)==-1) { v1=new Vector(1e3)
  } else { // pick up 1 or 2 vectors or vector names
    if (argtype(i)==1) { v1=$oi i+=1 // vector
      if (isobj(v1,"NULLobject")) { v1=new Vector(1e3)
      } else if (!isobj(v1,"Vector")) {printf("grvrv ERRA: arg %d should be a vector\n",i) err()}
    } 
    if (argtype(i)==1) {v2=$oi i+=1 // vector
      if (isobj(v2,"NULLobject")) { v2=new Vector(1e3)
      } else if (!isobj(v2,"Vector")) {printf("grvrv ERRA: arg %d should be a vector\n",i) err()}
    }   
  }
  if (i!=numarg()+1) printf("grvrv ERRB: some args not parsed (%d %d)\n",i,numarg())
  tstr=po.llist.o(num).name // name of that data item
  if (v2==nil) po.rv_readvec(num,v1) else po.rv_readvec(num,v2,v1)
  return v1
}

//** vec=grvnq(datasource,num OR name, nq) // read data vec(s) from a datasource
//    all arguments are optional but order of args not
//    datasource defaults to current datasource given by 'panobj' object pointer
//    num or dataname defaults to 0 (first vector in datasource)
obfunc grvnq () { local i,num,x,a localobj po,st,oq,xo,v1,v2
  i=1 st=new String2() po=panobj
  if (argtype(i)==1) if (isobj($oi,"GRV")) {po=$oi i+=1}
  if (argtype(i)==0) {num=$i i+=1} else num=0
  a=allocvecs(v1,v2)
  if (argtype(i)==2) { 
    st.s=$si i+=1 // name will overwrite num if redundantly given as num,name 
    for ltr(xo,po.llist,&x) if (strm(xo.name,st.s)) {num=x break}
    if (x==po.llist.count) printf("grvnq(): %s NOT FOUND in datasource %s; returning first item\n",st.s,po)
  }
  if (num>=po.llist.count || num<0) { 
    printf("grvnq(): %d not in datasource %s (max %d); returning first item\n",num,po,po.llist.count-1) 
    num=0 
  }
  if (argtype(i)!=1) { oq=new NQS("t","ind")
  } else oq=$oi
  if (i!=numarg()+1) printf("grvnq ERRB: some args not parsed (%d %d)\n",i,numarg())
  tstr=po.llist.o(num).name // name of that data item
  if (po!=panobjl.o(0)) po.rv_readvec(num,v2,v1) else { v1.copy(printlist.o(num).vec) v2.copy(printlist.o(num).tvec) }
  oq.v[0].append(v2) oq.v[1].append(v1)
  return oq
}

//* misc routines
//** fexists()
func fexists () { localobj o
  o=new File()
  return o.ropen($s1)
}

//** ftype()
func ftype () { local ty localobj st
  ty=0
  st=new String2()
  sprint(st.t,"stat -c %%F %s 2>&1",$s1)
  system(st.t,st.s)
  chop(st.s)
  if (strm(st.s,"\n")) {               return 10 //  more than 1 file -- ie * used
  } else if (strm(st.s,"No such"))   { return -1
  } else if (strm(st.s,"directory")) { return 0
  } else if (strm(st.s,"symbolic")) {  return 8
  } else if (strm(st.s,"regular"))  {  return 2
  } else { // not identified
    printf("File %s is a %s\n",$s1,st.s)
    return -2
  }
}

//** file_len() uses wc
func file_len () { local x localobj st
  st=new String()
  sprint(st.s,"wc -l \"%s\"",$s1)
  system(st.s,st.s)
  sscanf(st.s,"%d",&x)
  return x
}

func cvode_status () { return cvode.active() + cvode.use_local_dt()/10 }

proc pvall () { local n localobj o
  o=panobjl.o(0)
  if (! o.attr0) { printf("pvall() ERR %s not attr0==0\n",o)
  } else { 
    n=numarg()
    if (n==1) o.pvall($s1) else if (n==2) o.pvall($s1,$s2) else o.pvall()
  }
}

//** procbutt() put up a single proc in a button
proc procbutt () {
  xpanel($s1)
  xbutton($s1,$s1)
  xpanel(500,500)
}

// mdl2view(g,X,Y) converts world coordinates to view coordinates
// eg {XO=mdl2view(g,12,533.7) g.label(XO.x,XO.x[1],"AA",2,2,0.5,0.5,1)}
// translate from world coordinates into view coordinates
// view coordinates are different from Graph model coordinates
//  3 coordinate systems: view, world, panel  -- ie the 0,1 used by g.label
obfunc mdl2view () { local a,ii,x0,y0 localobj o,v1,g
  g=$o1 x0=$2 y0=$3
  if (argtype(4)==1) v1=$o4 else {
    v1=new Vector(4)
    for ii=0,3 v1.x[ii]=g.size(ii+1) 
  }
  o=new Union()
  o.x=   (x0-v1.x[0])/(v1.x[1]-v1.x[0])
  o.x[1]=(y0-v1.x[2])/(v1.x[3]-v1.x[2])  
  o.x[2]=o.x[0]*0.8+0.1 
  o.x[3]=o.x[1]*0.8+0.1 // scale and move over to where graph usually is
  return o
}

//* cart2north() -- identify 0-360 degree N up for cartesian vector
// cart2north(x0,y0,x1,y1[,vec]) -- with vec also return amplitude in vec.x[1]
// cart2north(delx,dely[,vec]) 
func cart2north () { local theta,delx,dely,i
  if (numarg()<4) {delx=$1 dely=$2 i=3} else {delx=$3-$1 dely=$4-$2 i=5}
  theta=atan2(dely,delx)
  theta*=(180/PI)
  if (theta<0) theta+=360
  if (theta>=0 && theta<90) { theta= abs(theta-90)
  } else if (theta>=90 && theta<=360) theta = abs(450 - theta)
  if (numarg()==i) revec($oi,theta,sqrt(dely*dely+delx*delx))
  return theta
}

//** rmallgrs() gets rid of all extant graphs
proc rmallgrs () { local ii,cnt localobj xo,glist
  glist=new List("Graph")
  for ltr (xo,glist) xo.unmap()
}

proc stopper () {
  xpanel("STOP")
  xbutton("STOP","stoprun=1")
  xbutton("FINI","finish()")
  xbutton("CONT","time(\"cvode.solve(tstop)\")")
  xbutton("RUN","time()")
  xpanel()
}