/* $Id: synmap.hoc,v 2.4 2000/03/08 22:04:29 karchie Exp $
 * synmap.hoc
 * Distribute synapses on a neuron.  Reads an array to determine which
 * of the evenly distributed possible sites should actually have
 * synapses.  The map is more or less locally distance-preserving, in
 * that two synapses close to each other in the array are probably
 * close to each other on the neuron.
 *
 * Copyright (c) 1996,1998  Kevin A. Archie
 * Send comments to: karchie@lnc.usc.edu
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *     
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

synmap_verbose = 0

/*
 * synm_set_secname()
 * Set the section name used by the default check mechanism to weed
 * out sections that can't have synapses.
 */
 
strdef synm_secname_string
synm_secname_string = ""

proc synm_set_secname() {
  // Assign the string.
  synm_secname_string = $s1

  // Reset the cached cell length so it will be recomputed.
  synm_total_length = 0
}


/*
 * synm_sections()
 * Iterate over the set of sections on which synapses can be placed.
 * Redefine this iterator to control synapse site selection.
 *
 * Note that "forsec" might not be the best way to do this; what we
 * really want is a depth-first traversal of the sections.  In
 * practice, this gives more or less depth-first for j4.  If we come
 * across a cell for which it doesn't work, or if we need to extend
 * this to work with multiple cells, we can dig out Hines's tree
 * traversal scheme.
 */
 
iterator synm_sections() {
  forsec synm_secname_string {
    iterator_statement
  }
}


/*
 * synm_get_total_length()
 * Determine the total length of all sections.
 *
 * BUGS: This function allows only a single length group.
 */

synm_total_length = 0		// Cached cell length

func synm_get_total_length() {
  if (synm_total_length == 0) {
    for synm_sections() {
      synm_total_length = synm_total_length + L
    }
  }

  return(synm_total_length)
}
    
  
/*
 * synm_place_synapses(tot_nsites, fileobjref)
 * Distribute synapses on the cell.
 *
 * tot_nsites is the total number of sites on the cell.
 * fileobjref is the object reference to the input vector file.
 *
 * BUGS: This procedure relies on synm_get_total_length(), so it
 * supports only one length group.
 */

proc synm_place_synapses() {local running_L, last_L, nsites, site_idx, \
				 L_per_site, next_loc, f, nsyns
  // Reset the running L total.  last_L is the running length at the
  // 0.0 end of the current segment; running_L is the running length
  // at the 1.0 end.
  running_L = 0
  last_L = 0

  // How many synapse sites?
  nsites = $1
  if (nsites == 0) {
    return
  }

  // How far apart are sites?
  L_per_site = synm_get_total_length() / nsites
  if (synmap_verbose) {
    print nsites, "synapse sites ", L_per_site, "um apart."
  }

  // The first site (site 0) is half a site distance from the origin..
  site_idx = 0
  next_loc = L_per_site / 2
  nsyns = 0

  // Use synm_sections() to iterate over all suitable segments.
  for synm_sections() {
    // Include the length of the current segment in the running total.
    running_L = running_L + L

    // Is there a next site?  Is it in the current section?
    while (site_idx < nsites && running_L >= next_loc) {
      // Yes.  Read the frequency and relative strength for the
      // synapse and place it in the next site if f > 0.  The location
      // argument is between 0 and 1 because:
      // last_L <= next_loc <= running_L and last_L+L=running_L,
      // so (next_loc - last_L) is in the range [0..L].
      f = $o2.scanvar() * 100	// convert to Hz in the range [0, 100]
      w = $o2.scanvar()		// use weight factor to set initial value.
      if (f * tstop / 1000 > 0.1) { // If > 0.1 spikes, make a synapse.
	synm_insert_synapse((next_loc - last_L) / L, f, w, site_idx)
	nsyns = nsyns + 1
      }

      // Get the next location.
      next_loc = next_loc + L_per_site
      site_idx = site_idx + 1
    }

    // We're done with this section.
    last_L = running_L
  }

  if (synmap_verbose) {
    print "Placed ", nsyns, "synapses."
  }
}


/*
 * synm_insert_synapse()
 * Insert a single synapse at the given position on the current
 * segment.  This is a placeholder function; it must be redefined in
 * another file to perform the actual insertion.
 */

proc synm_insert_synapse() {
  print "Error: No mechanism for inserting synapses is available."
  print "       A procedure synm_insert_synapse() must be defined."

  stop	// We're probably in a modified section stack, so this command
	// may aggravate a NEURON bug.  But who cares -- it's an error
	// anyway. 
}