// $Id: snscode.hoc,v 1.42 2002/04/22 14:17:48 billl Exp $
// handles coding, syn assignment and syn graphics

// load_file("declist.hoc","declist")
load_proc("declist")
objref prelist  // list of all presynaptic mechanisms
prelist = new List("PRESYN")

//* CODE GENERATION AND RECOVERY 

objref snsgrlist
snsgrlist = new List()

chainlen = 1    // allow the user to change this as needed for synfind or connmat
maxflds = 4     // maximum number of codefields
cdflds = 4      // number of 'code fields'
double cdsep[maxflds+1] // code column separators
// default values see mkfields() call below
// cdsep[0] = 1e9  col 9 - 9
// cdsep[1] = 1e8  col 8 - 7
// cdsep[2] = 1e6  col 6 - 4
// cdsep[3] = 1e3  col 3 - 1
// cdsep[4] = 1e0

// labels can be entered for each of 4 fields, up to 8 labels in each
objref cdlbls[cdflds][8]
for ii=0,cdflds-1 for jj=0,7 cdlbls[ii][jj]=new String("")

//** mkfields() creates up to 4 fields (5 args: # fields, f1,f2,f3,f4)
// eg mkfields(4,1,2,3,3) to recreate the defaults
proc mkfields() { local sum,i
  cdflds = $1
  cdsep[4] = 1
  sum = $5
  cdsep[3] = 10 ^ sum
  sum = sum + $4
  cdsep[2] = 10 ^ sum
  sum = sum + $3
  cdsep[1] = 10 ^ sum
  sum = sum + $2
  cdsep[0] = 10 ^ sum
}

mkfields(4,1,2,3,3)

//** mkmkcode(): create the mkcode routine
// no arguments, should follow mkfields()
// not reliable if used under xopen()
proc mkmkcode() { local i
  printf("!{!\n")  
  printf("func mkcode() { \nreturn ")
  for (i=1;i<=cdflds;i=i+1) {
    printf("$%d*cdsep[%d] + ",i,i+maxflds-cdflds)
  }
  printf("0 \n}\n")
  printf("!}!\n")  
}

//** mkcode() creates a code of 9 cols in 4 fields, sized 1,2,3,3
func mkcode() { 
  // standard 9 cols
  // divide into 4 fields of size 1,2,3,3
  //col           8           7,6         5,4,3         2,1,0
  return $1*cdsep[1] + $2*cdsep[2] + $3*cdsep[3] + $4*cdsep[4]
}

//** cd(i,code) returns field i (1-4) from code
// $1 field number (from 1), $2 code
func cd() { local temp
  temp = ($2%cdsep[$1+(maxflds-cdflds)-1]) // remove the left side
  return int(temp/cdsep[$1+(maxflds-cdflds)])  // remove the right side
}

//** setcd(i,code,new) replaces field i (1-4) from code with new
// $1 field number (1 offset), $2 code, $3 new value
func setcd() { local temp, old
  temp = ($2%cdsep[$1+(maxflds-cdflds)-1]) // remove the left side
  old = cdsep[$1+(maxflds-cdflds)]* int(temp/cdsep[$1+(maxflds-cdflds)])
  return $2 - old + $3* cdsep[$1+(maxflds-cdflds)]
}

//** prcode(code) prints a code in readable form
// print a code of 9 cols in 4 fields, sized 1,2,3,3
proc prcode() { local i
  for (i=(maxflds-cdflds+1);i<maxflds;i=i+1) {
    printf("%d,",cd(i,$1))
  }
  printf("%d\n",cd(maxflds,$1))
}

//** prlbls(code) uses cdlbls to print out strings instead of numbers
//   when possible
proc prlbls() { local k,cde
  for (k=(maxflds-cdflds+1);k<=maxflds;k=k+1) {
    cde = cd(k,$1)
    if (k < cdflds && cde < 8) {
      if (strcmp(cdlbls[k][cde].s,"") == 0) {
        printf("%d,",cde)
      } else {
        printf("%s,",cdlbls[k][cde].s)
      }
    } else {
      printf("%d,",cde)
    }
  }
  print ""
}

//** chkpre(OBJ,CODE) searches through a syn OBJECT for this CODE
// check whether a code to be included has already been used
// 2 arguments: syn obj, comparison
// returns 1 if found, 0 if not
func chkpre () { local i
  for(i=0; i<$o1.nsyn; i=i+1) {     
    if ($o1.pre(i) == $2){ 
       return 1 } } 
  return 0 
} 

//** synfind list1 cd2 fld3 val4 cd5 fld6 val7
// print out codes on all the synapses that criteria
// cd2, cd5 are "pre" or "post" or "code"
// fld3, fld6 is the field
// val4, val7 are the values to look for matching
proc synfind () { local cdx,cdy,i,j,start
  if ((cdx = connmat_findstr($s2)) == -1) { printf("unknown string %s",$s2) return }
  if ((cdy = connmat_findstr($s5)) == -1) { printf("unknown string %s",$s5) return }
  for i=0,$o1.count-1 {
    for (j=0;j<$o1.object(i).nsyn-1;j=j+chainlen) {
      if (synmatch($o1.object(i),j,cdx,$3,$4,cdy,$6,$7)) {
        sprint(temp_string_,"pr = %s.pre(%d)",$o1.object(i),j)
        execute(temp_string_)
        printf("%s syn# %d: pre==",$o1.object(i),j)
        prlbls(pr)
        sprint(temp_string_,"pr = %s.post(%d)",$o1.object(i),j)
        execute(temp_string_)
        printf(" post==")
        prlbls(pr)
        print ""
      }
    }
  }
}

//*** synmatch obj1 ind2 cf3 fld4 val5 cf6 fld7 val8
// check object fields for matches to values given
func synmatch () { local cdd, cde, i, j
  cdd = connmat_newpt($o1,$3,$4,$2,0)
  cde = connmat_newpt($o1,$6,$7,$2,0)
  if (cdd == $5 && cde == $8) { return 1 } else { return 0 }
}

// parse the strings and return a number telling which it is
// 0:code, 1:pre, 2:post, 3:increment
func connmat_findstr () {
         if (strcmp($s1,"code") == 0) { return 0
  } else if (strcmp($s1,"pre")  == 0) { return 1
  } else if (strcmp($s1,"post") == 0) { return 2
  } else if (strcmp($s1,"inc")  == 0) { return 3
  } else {                              return -1 }
}

// look at return the field needed from the correct word
// word indication as returned by connmat_findstr()
// args: object, word, field, synnum, ind
func connmat_newpt () { local code
         if ($2==0) { code = $o1.code($4)
  } else if ($2==1) { code = $o1.pre($4)
  } else if ($2==2) { code = $o1.post($4)
  } else if ($2==3) { return $5 }
  if (code >= 0) {
    return cd($3,code)
  } else {
    return -1   // error
  }
}

//* FUNCTIONS OPERATING ACROSS ALL PRESYNS 

//** checksyns() goes through prelist and prints out divergence value
proc checksyns() { local i
  for i=0, prelist.count()-1 {
    printf("%d ",prelist.object(i).check())
  }
}

//** cleansyns() goes through prelist and collapses the synapse list
proc cleansyns() { local i
  for i=0, prelist.count()-1 {
    prelist.object(i).clean()
  }
}

//** clearsyns() goes through postsyn list and reinitializes everything
proc clearsyns() { local i,j
  tmplist.remove_all
  tmplist = new List("POSTSYN")
  for i=0, tmplist.count()-1 {
    for j=0, tmplist.object(i).mech_num - 1 {
      if (tmplist.object(i).mech[j].maxsyn > 0) {
        tmplist.object(i).mech[j].init_arrays(tmplist.object(i).mech[j].maxsyn)
      }
    }
  }
  cleansyns()
}

//* SYNAPSE ASSIGNMENT CODE 
// 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)

//** ranverge() produces randomized convergence
// used for convergence, rancon (randomized convergence) is given by 
//     ranverge(%conv,numpre,variance)
// returns a convergence number (number of syns onto a cell)
// that is randomly chosen from (%conv +/- variance)*pre
// if random not wanted just do int(pcon * numpre) instead
func ranverge () { local min, max, ran, pcon, numpre, var
  pcon = $1  numpre = $2  var = $3
  if (pcon == 1) { return numpre } // fully connected
  if (pcon == 0) { return 0 }      // no connection
  if (var != 0) {
    ran = pcon - var + (u_rand() * 2 * var)
  } else { ran = pcon }           // no variability
  return int(ran * numpre + u_rand())  // con * pre or div * post rounded up or down
}

//** pickpre() tries to reduce divergence variance
// pickpre(MECH_NUM,TEMP_PRELIST,MAXDIV)
// may want to create TEMP_PRELIST from PRELIST using sublist()
// maxdiv == -1 means to ignore divergence
// MUST do divvec.resize() and divvec.fill(0) before using
objref divvec,divvec1  // vectors to count how much div is from each nrn
divvec = new Vector(100)
divvec1 = new Vector(100) // a scratch vector

func pickpre () { local ran, maxdiv, maxpre, min, indx, mechnum
  mechnum = $1
  maxdiv = $3  // max divergence to be allowed
  maxpre = $o2.count-1 // number of presyn choices
  if (maxdiv == -1) { // just choose one randomly
    ran = fran(0,maxpre) // all possible synapses
    return ran
  }
  maxdiv *= mechnum  // need space for mult mechs together
  // if (divvec.size != prelist.size) { 
  //   printf("Pickpre full ERROR: divvec not sized properly\n") }
  min = divvec.min  // divvec should start all 0's
  if (min >= maxdiv) { // all full up
    printf("Pickpre full WARNING: %d %d\n",min,maxdiv)
    divvec.printf
  }
  divvec1.indvwhere(divvec,"==",min)  // look for all the smallest to fill up first
  ran = fran(0,divvec1.size-1) // pick index of one of these randomly
  indx = divvec1.x[ran]  // index is the randomly chosen entry in vec of ptrs.
  divvec.x[indx] += mechnum  // record that this one has been chosen
  return indx
}

// check that divergence in individual divvecs is same as divergence in PRESYN structure
// chkdiv(vec,PRELISTmin,PRELISTmax,num_mechs)
// vec is specific divergence vector
// PRELISTmin,max give the entries in prelist to compare
// num gives the number of mechanisms in a single post from this cell
proc chkdiv () { local min,max,num
  min = $2 max = $3
  if ($o1.size != max-min+1) { 
    printf("ERROR: divvec size must = # of prelist items: %d %d\n",$o1.size,max-min+1)
    return
  }
  for vtr(&x,$o1) {
    if (x != prelist.object(min+i1).nsyn) {
      printf("ERROR: %d in %s != nsyn %d\n",x,$o1,prelist.object(min+i1).nsyn)
    }
  }
}

//* template POSTSYN 

proc callback () {}

begintemplate POSTSYN
  public mech, mech_num, post
  public init_arrays, augment_array, conn
  public up
  external pickpre, callback
  objref mech[1],this,up	// to be resized later 

//** init() args are syn objects
  proc init() { 
    mech_num = numarg()
    objectvar mech[mech_num]
    if (mech_num >= 4) { mech[3] = $o4 }
    if (mech_num >= 3) { mech[2] = $o3 }
    if (mech_num >= 2) { mech[1] = $o2 }
    if (mech_num >= 1) { mech[0] = $o1 }
  }

//** augment_array() add $1 to current number of synapses
  func augment_array() { 
    num = $1
    for k=0,mech_num-1 {
      newmax = mech[k].maxsyn + num
      mech[k].init_arrays(newmax)
    }
    return newmax
  }
    

//** init_arrays() initialize all mechs to maxsyn = $1
  func init_arrays() { 
    num = $1
    for k=0,mech_num-1 {
      mech[k].init_arrays(num)
    }
    return num
  }

//** conn() find some places in a list to connect to
  // (list, con, maxdiv, [delay, gmax])
  // maxdiv == -1 means to ignore divergence
  func conn () {
    con = $2 maxdiv = $3
    numpre = $o1.count
    if (numarg()>3) {delay = $4  gmax = $5 setall=1 
    } else { setall = 0 }
    for j=0,con-1 { 
      if (con == numpre) {  // do all of them in order
        rannum = j
      } else {
        rannum = pickpre(mech_num,$o1,maxdiv)
        if (rannum==-1) { printf(": %d/%d done\n",j,con)
          return -1 }
      }
      for k=0,mech_num-1 {
        // post, pre, listcnt, syncnt, mechcnt, precnt
        mech[k].setlink($o1.object(rannum).link, $o1.object(rannum).nsyn, $o1.object(rannum).maxsyn)
        if (setall) {
          mech[k].delay(-1,delay) // set most recent (index nsyn-1)
          mech[k].gmax(-1,gmax)
        } else {
          callback(mech[k],k,rannum)
        }
      }
    } 
    return 0
  }

endtemplate POSTSYN