// $Id: syncode.hoc,v 1.53 2002/04/18 18:33:32 billl Exp $

proc syncode () {}

//* setup
load_file("decvec.hoc","decvec")
objref ncl, cells[10] // enough room for 10 cell types
objref CTYP[1],STYP[1] // for err messages involving cell and synapse types
ncl = new List()
strdef syn1,syn2

thresh = -20
// for ltr(XO,cvode.netconlist("", "", "")) print XO.precell,XO.postcell,XO.syn
//* synapse linking
//** geolink(s1,s2,s3) s1 for presyns and s2 for postsyns, connect s3's
// connects a layer to another assuming 1 dim netgeom
proc geolink() { local ii
  if (numarg()==0) { print "\tgeolink(s1,s2,s3,v_conn)"
    print "  make connections from s1 to s2 connecting onto s3 type PPs."
    print "  vector_connect gives the prob of connection according to cell number"
    return
  }
  sprint(temp_string_,"XO.soma ncl.append(new NetCon(&v(.5), YO.%s))",$s3)
  for ltr (XO,new List($s1)) { // presyn list
    for lvtr (YO,&x,new List($s2),$o4) { // postsyn list and vector
      if (x==1) { // connect
        print "connecting ",XO," to ",YO
        execute(temp_string_)
      }
    }
    $o4.rotate(1)
  }
  XO=nil YO=nil
}
      
//** nowraplk(s1,s2,s3) s1 for presyns and s2 for postsyns, connect s3's
// connects a layer to another assuming 1 dim netgeom
proc nowraplk() { local cnt
  if (numarg()==0) { print "\tnowraplk(s1,s2,s3,v_conn,dist)"
    print "  make connections from s1 to s2 connecting onto s3 type PPs."
    print "  like geolink() statt no wraparound, only look out by dist from the cell"
    return
  }
  sprint(temp_string_,"XO.soma ncl.append(new NetCon(&v(.5), YO.%s))",$s3)
  dist=$5
  tmplist=new List($s2)
  cnt=tmplist.count
  for ltr (XO,new List($s1),&y) { // presyn list
    for ii=y-dist,y+dist {
      if (ii>=0 && ii<cnt) if ($o4.x[ii]==1) {
        YO=tmplist.object(ii)
        print "connecting ",XO," to ",YO
        execute(temp_string_)
      }
    }
    $o4.rotate(1)
  }
}
//** synlink(s1,s2,s3,[gmax,del]) s1 for presyns and s2 for postsyns, connect s3's
//  eg synlink("PYR","INH","AMPA")
// provides full connectivity
proc synlink() { local gm,dl
  if (numarg()==0) { print "\tsynlink(s1,s2,s3)"
    print "  make connections from all of type s1 to one of type s2 "
    print "     only connecting onto s3 type PPs."
    print "  Matching done with regexps."
    return
  }
  if (numarg()==5) { gm=$4 dl=$5 } else { gm=0 dl=1 }
  tmplist = new List($s3) // list of postsyn point processes 
  for ltr (XO,new List($s1)) { // presyn list
    for ltr (YO,tmplist) {
      if (pp_loc(YO,$s2,syn2)) { 
        sfunc.head(syn2,"\\.",syn2)
        sprint(syn1,"%s",XO)
        if (strcmp(syn1,syn2)!=0) { // don't allow self connects
          print "connecting ",XO," to ",syn2
          XO.soma ncl.append(new NetCon(&v(.5), YO, thresh, gm, dl))
        }
      }
    }
  }
}
      
//** synconn(l1,s2,s3,%div) l1 list of presyns and s2 for postsyns, connect s3's with given density
// eg synconn(PYRi,PYRi,AMPAi,pmat[PYR][PYR])
// provides % connectivity based on %div==%conv
objref convec,convec1,convec2  // vectors to count how much div is from each nrn
convec = new Vector(100)
convec1 = convec.c // a scratch vector
convec2 = convec.c // copy of convec used temporarily to blot out self-connect and redundent connects
proc synconn () { local pdiv,con,div,ii,jj,pre,prn,post,pon,sy,targ
  if (numarg()==0) { print "\tsynconn(pre,post,mech,div)" return }
  pre=$1 post=$2 
  if (numarg()==3) { sy=0 pdiv=$3 } else { sy=$4 pdiv=$3 }
  prn = cells[$1].count  // number of possible presyn sources
  pon = cells[$2].count  // number of possible postyn targets
  div = int(pdiv*pon)  // # out from each
  con = int(pdiv*prn+1) // # into each; leave a little extra room
  if (pdiv==1) con=-1 // flag for full connectivity
  printf("\tdiv=%d,conv=%d\n",div,con)
  convec.resize(pon) convec.fill(0) // counter for convergence
  for ii=0,prn-1 { // sources
    convec2.copy(convec) convec2.x[ii]=1e10 // block self-connect
    for jj=1,div {
      targ = pickpost(pon,con) // pick desired target
      printf("SRC: %s -> TRG: %s (%s)\n",cells[pre].object(ii),cells[post].object(targ),cells[post].object(targ).syns(sy))
      cells[pre].object(ii).conn(cells[post].object(targ).syns(sy))
    }
  }
}

proc synconn2 () { local pdiv,con,div,ii,jj,pre,prn,post,pon,sy,targ
  if (numarg()==0) { print "\tsynconn(pre,post,mech,div)" return }
  pre=$1 post=$2 
  if (numarg()==3) { sy=0 pdiv=$3 } else { sy=$3 pdiv=$4 }
  prn = cells[$1].count  // number of possible presyn sources
  pon = cells[$2].count  // number of possible postyn targets
  div = int(pdiv*pon)  // # out from each
  con = int(pdiv*prn+1) // # into each; leave a little extra room
  if (pdiv==1) con=-1 // flag for full connectivity
  printf("\tdiv=%d,conv=%d\n",div,con)
  convec.resize(pon) convec.fill(0) // counter for convergence
  for ii=0,prn-1 { // sources
    convec2.copy(convec) convec2.x[ii]=1e10 // block self-connect
    for jj=1,div {
      targ = pickpost(pon,con) // pick desired target
      printf("SRC: %s -> TRG: %s (%s)\n",cells[pre].object(ii),cells[post].object(targ),cells[post].object(targ).syns(sy))
      cells[pre].object(ii).conn(cells[post].object(targ).syns(sy))
    }
  }
}
      
//*** pickpost() tries to reduce divergence variance
// pickpost(postlist,maxcon,YO)
// maxcon == -1 means to ignore convergence
// MUST do convec.resize() and convec.fill(0) before using
func pickpost () { local ran, maxcon, maxpo, min, indx
  maxcon = $2  // max convergence to be allowed
  maxpo = $1   // number of postsyn choices
  if (maxcon == -1) $o3 = $o1.object(fran(0,maxpo)) // just choose one randomly
  min = convec2.min  // convec should start all 0's
  if (min >= maxcon) { // all full up
    printf("Pickpost full WARNING: %d %d\n",min,maxdiv) vlk(convec2) }
  convec1.indvwhere(convec2,"==",min)  // look for all the smallest to fill up first
  inx = convec1.x[fran(0,convec1.size-1)]
  convec.x[inx]+=1
  convec2.x[inx]=1e10 // block from reconnecting here
  return inx
}

proc syn1to1 () { local pre,post,syn
  pre=$1 post=$2
  if (numarg()==3) syn=$3 else syn=0
  if (cells[pre].count !=cells[post].count) { 
    printf("\tERROR: 1-to-1 connectivity requires same # of %s (=%d) and %s (=%d).\n",\
           CTYP[pre].s,cells[pre].count,CTYP[post].s,cells[post].count) return }
  for ltr2(XO,YO,cells[pre],cells[post]) {
    printf("SRC: %s -> TRG: %s (%s)\n",XO,YO,YO.syns(syn))
    XO.conn(YO.syns[syn])
  }
}
 
//** simple netconnect routines: netconn(), netgen()
proc netconn () { local pre,post,syn,pri,poi
  pre=$1 post=$2 
  if (numarg()==5) { syn=$3 pri=$4 poi=$5 } else { syn=0 pri=$3 poi=$4 }
  cells[pre].object(pri).conn(cells[post].object(poi).syns[syn])
}

proc netgen () { ncl.append(new NetCon($o1, $o2)) }

//** syncopy() copies from one set of syns to another for colocalization
proc syncopy () { 
  if (numarg()==0) { printf("syncopy(to_type,[]): copy from type, post/type, pre/post/type\n") return }
  sprint(temp_string_,"XO.precell.soma ncl.append(new NetCon(&v(.5),XO.postcell.%s",$s1)
  if (numarg()==1) tmplist = cvode.netconlist("", "" , $s2)
  if (numarg()==2) tmplist = cvode.netconlist("", $s2, $s3)
  if (numarg()==3) tmplist = cvode.netconlist($s2, $s3, $s4)
  for ltr(XO,tmplist) execute(temp_string_)
}
      
//* synapse setting
//** scale gmax
proc synscgmax () {
  for ltr(XO,cvode.netconlist("" , "", $s1)) { XO.weight *= $2 }
}
//** set/get gmax
proc synsetgmax () {
  if (numarg()==0) { print "synsetgmax(pre,post,type,wt)"
  } else if (numarg()==2) {for ltr(XO,cvode.netconlist("" , "", $s1)) XO.weight=$2
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist("" , $s1, $s2)) XO.weight=$3
  } else if (numarg()==4) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) XO.weight=$4
  } else { print "ERROR: too many args" }
}

proc syngetgmax () {
  if (numarg()==1) {
    if (strcmp($s1,"help")==0) { printf("type,post/type,pre/post/type\n") } else {
      for ltr(XO,cvode.netconlist("" , "", $s1)) printf("%g ",XO.weight) }
  } else if (numarg()==2) {for ltr(XO,cvode.netconlist("" , $s1, $s2)) printf("%g ",XO.weight)
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) printf("%g ",XO.weight)
  } else for ltr(XO,cvode.netconlist("","","")) printf("%g ",XO.weight)
  print ""
}
 
//** set/get delay
proc synsetdel () {
  if (numarg()==2) {for ltr(XO,cvode.netconlist("" , "", $s1)) XO.delay=$2
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist("" , $s1, $s2)) XO.delay=$3
  } else if (numarg()==4) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) XO.delay=$4
  } else { print "ERROR: too many args" }
}

proc syngetdel () {
  if (numarg()==1) {for ltr(XO,cvode.netconlist("" , "", $s1)) printf("%g ",XO.delay)
  } else if (numarg()==2) {for ltr(XO,cvode.netconlist("" , $s1, $s2)) printf("%g ",XO.delay)
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) printf("%g ",XO.delay)
  } else for ltr(XO,cvode.netconlist("","","")) printf("%g ",XO.delay)
  print ""
}
 
//** set/get thresh
proc synsetthr () {
  if (numarg()==1) {for ltr(XO,cvode.netconlist("" , "", "")) XO.threshold=$1
  } else if (numarg()==2) {for ltr(XO,cvode.netconlist($s1 , "", "")) XO.threshold=$2
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist($s1, "", $s2)) XO.threshold=$3
  } else if (numarg()==4) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) XO.threshold=$4
  } else { print "ERROR: too many args" }
}

proc syngetthr () {
  if (numarg()==1) {for ltr(XO,cvode.netconlist($s1 , "", "")) printf("%g ",XO.threshold)
  } else if (numarg()==2) {for ltr(XO,cvode.netconlist($s1, "", $s2)) printf("%g ",XO.threshold)
  } else if (numarg()==3) {for ltr(XO,cvode.netconlist($s1 , $s2, $s3)) printf("%g ",XO.threshold)
  } else for ltr(XO,cvode.netconlist("","","")) printf("%g ",XO.threshold)
  print ""
}
 
//** synremove: remove post, pre/post, pre/post/type; synshow
proc synremove () {
  if (numarg()==0) { printf("synremove: remove post, pre/post, pre/post/type\n") return }
  if (numarg()==1) tmplist = cvode.netconlist("", $s1 , "")
  if (numarg()==2) tmplist = cvode.netconlist("", $s1, $s2)
  if (numarg()==3) tmplist = cvode.netconlist($s1 , $s2, $s3)
  if (tmplist.count>0) for ltr(XO,tmplist) ncl.remove(ncl.index(XO))
  tmplist = nil // need to remove these references too
  if (numarg()==1) tmplist = cvode.netconlist("", $s1 , "")
  if (numarg()==2) tmplist = cvode.netconlist("", $s1, $s2)
  if (numarg()==3) tmplist = cvode.netconlist($s1 , $s2, $s3)
  if (tmplist.count>0) for ltr(XO,tmplist) printf("ERROR: %s removed from ncl but still exists\n",XO)
  tmplist = nil
}
  
proc synshow () { 
  sprint(temp_string_,"x=XO.%s",$s2)
  for ltr(XO,cvode.netconlist("" , "", $s1)) { execute(temp_string_) printf("%g ",x) }
  print ""
}
 
//* informational procs
//** proc print_pp_location(PP), from doc/html/refman/nocmodl.html
proc print_pp_location() { local x //arg1 must be a point process
   x = $o1.get_loc()
   sectionname(section)
   printf("%s located at %s(%g)\n", $o1, section, x)
   pop_section()
}
//** pp_loc(PP,LOC,SCRATCH) returns true if point process PP is located in LOC (regexp match)
func pp_loc () { local x //arg1 must be a point process
   x = 0
   $o1.get_loc()
   if (numarg()==3) { sectionname($s3) }
   ifsec $s2 { x = 1 }
   pop_section()
   return x
}
 
//** ndivo, ncono, sdivo, scono: objects; ndivs, ncons, sdivs, scons: strings
func ndivo () { return cvode.netconlist($o1, "", "").count }
func ncono () { return cvode.netconlist("", $o1, "").count }
proc sdivo () {for ltr(XO,cvode.netconlist($o1, "", "")) prsxo() }
proc scono () {for ltr(XO,cvode.netconlist("", $o1, "")) prsxo() }
func ndivs () { return cvode.netconlist($s1, "", "").count }
func ncons () { return cvode.netconlist("", $s1, "").count }
proc sdivs () { for ltr(XO,cvode.netconlist($s1, "", "")) prsxo() }
proc scons () { 
  if (numarg()==0) for ltr(XO,cvode.netconlist("", "", ""))  prsxo()
  if (numarg()==1) for ltr(XO,cvode.netconlist("", $s1, ""))  prsxo()
  if (numarg()==2) for ltr(XO,cvode.netconlist("", $s1, $s2)) prsxo()
  if (numarg()==3) for ltr(XO,cvode.netconlist($s1, $s2,$s3)) prsxo()
}
 
// print pre,post,syntype,threshold,weight,delay
proc prsxo () {
  if (isobj(XO.precell,"NULLobject")) {
    printf("%s->%s (%s) (t%g,w%g,d%g)\n",XO.pre,XO.postcell,XO.syn,XO.threshold,XO.weight,XO.delay)
  } else {
    printf("%s->%s (%s) (t%g,w%g,d%g)\n",XO.precell,XO.postcell,XO.syn,XO.threshold,XO.weight,XO.delay)
  }
}
//* misc and commentary
// con=absolute convergence number, div=absolute div number
// con = %con * pre
// div * pre = con * post = S (total synapses)
// %div = div/post = S/(pre*post) = con/pre = %con
// div = %con * post
// maxdiv = int((1 + var) * div)
 
//** vari returns randomly chosen $1+/-$2
func frani () { return int(rdm.uniform($1,$2+1)) }
func uvari () { return $1 - $1*$2 + (rdm.uniform(0,1) * 2 * $1*$2) }
func gvari () { return $1 - (rdm.normal(0,1) * $1*$2) }
//** latlink() gives wrap-around
proc latlink () { local reach,ii
  reach=$2
  $o1.fill(0)
  if (numarg()==3) $o1.x[0]=$3 // connect within the column
  for ii=1,reach {
    $o1.x[ii]=1
    $o1.x[$o1.size-ii]=1
  }
}

//** clearsyns() 
proc clearsyns () { 
  for ltr(XO,ncl) XO.active(0)
  ncl.remove_all 
  for ltr(XO,new List("NetCon")) printf("**** CLEARSYN WARNING %s STILL EXISTS ****\n",XO)
}