// func	area = totalarea( void )	-	returns total surface area of cell
	// dependency: none (stdlib and stdrun always assumed to be loaded and not listed in dependency)
	
// func	area = sectionarea( SectionList )	-	returns total surface area of sections in list
	// dependency: none

// proc FineEnds( SectionList, sections )	-	adds all end of branch sections to SectionList
	// dependency: none

// proc FineFieldBase( SectionList, sections, sections )	-	adds sections connecting sections to SectionList
	// dependency: none

// proc FindBranches( SectionList1, SectionList2, sections )	-	adds all parent and children of branch points to SectionLists
	// dependency: none

// proc BranchLists( SectionList, sections)	-	generate a List of SectionLists for every branch in sections
	// dependency: none


// proc DistanceMap( Vector, reference section)	- generate a vector of distances from refence point for every segment
	// dependency: none


func totalarea() { local sum
// area = totalarea( )
// calculate total surface area of all compartments (µm2)
//   finitialize()
  sum = 0
  forall for (x,0) sum += area(x)
  return sum
//  print "total surface area = ", sum, " um2"
}


func totalvolume() { local sum
// vol = totalvolume( )
// calculate total volume of all compartments (µm3)

  sum = 0
  forall for (x,0) sum += pi*(diam(x)/2)^2*L(x)
  return sum
}


// func SectionArea() {local sum
// // area = SectionArea( Sectionlist )
// 	sum = 0
// 	forsec $o1 for (x,0) sum += area(x)
// 	return sum
// }

func TotalLength() {local sum
// area = TotalLength()
	sum = 0
	forall sum += L
	return sum
}

func sectionarea() { local sum localobj sl
// area = sectionarea( Sectionlist )
// area = sectionarea( string )
// calculate total surface area (µm2) of all compartments within sectionlist
	finitialize()
	sum = 0

	if (argtype(1)==2)	{
		sl = new SectionList()
		forall ifsec $s1 sl.append()
		if (verbosity > 2) sl.printnames()
	} else if (argtype(1)==1)	sl = $o1

	forsec sl for (x,0) sum += area(x)
	return sum
//  print "total surface area = ", sum, " um2"
}


proc FindEnds() { local count	localobj sl, sref, chksecs
// FineEnds( SectionList )
// if no input argument finds branch ends for entire neuron
// FineEnds( SectionList, SectionList2 )
// finds the end compartment of every branch within SectionList2.
// FineEnds( SectionList, string )
// finds the end compartment of every branch within compartments specified by ifsec string.
// 

	count = numarg()
	sl = $o1
	if (isclass(sl,"NULLobject")) sl = new SectionList()

	if (count > 1) {
		if (argtype(2)==2)	{
			chksecs = new SectionList()
			forall ifsec $s2 chksecs.append()
			if (verbosity > 3) chksecs.printnames()
		} else if (argtype(2)==1)	chksecs = $o2

		forsec chksecs {
			sref = new SectionRef()
			if (sref.nchild==0) {
				sl.append()
			}
		}

	} else {
		forall {
			sref = new SectionRef()
			if (sref.nchild==0) {
				sl.append()
			}
		}
	}

	
}

proc FindFieldBase() { local ims	localobj sl, pars, childs, sref
// FineFieldBase( sectionlist, parent sections, child sections )
// finds the base compartment(s) of every branch within child sections that are connected to 
// parent sections

	ims = issplit()		// check if multisplit is on
	if (ims) stopPar()	// if multisplit is on, stop it so ModelView contains only 1 cell

	sl = $o1
	if (isclass(sl,"NULLobject")) sl = new SectionList()

	if (argtype(2)==2)	{
		pars = new SectionList()
		forall ifsec $s2 pars.append()
		if (verbosity > 3) pars.printnames()
	} else if (argtype(2)==1)	pars = $o2

	if (argtype(3)==2)	{
		childs = new SectionList()
		forall ifsec $s3 childs.append()
		if (verbosity > 3) childs.printnames()
	} else if (argtype(3)==1)	childs = $o3
	
	forsec childs {
		sref = new SectionRef()
		sref.parent() { 
			ifsec pars { sref.sec() sl.append() }
		}
	}

	if (ims) startPar()		// restart parallelization if needed

}


proc FindBranches() { local count 	localobj psl, csl, regsl, sref
// FindBranches( parent SectionList, child SectionList, section pool)
// find all branch points and add parent sections to $o1 and add all children sections to $o2

	count = numarg()
	psl = $o1
	if (isclass(psl,"NULLobject")) psl = new SectionList()

	csl = $o2
	if (isclass(csl,"NULLobject")) csl = new SectionList()

	if (count > 2) {
		if (argtype(3)==2)	{
			regsl = new SectionList()
			forall ifsec $s3 regsl.append()
			if (verbosity > 3) regsl.printnames()
		} else if (argtype(3)==1)	regsl = $o3

		forall {
			ifsec regsl {
				sref = new SectionRef()
				if ( sref.has_parent() && sref.nchild>1) {
					psl.append()
					for i=0,sref.nchild-1 sref.child[i] { csl.append() }
				}
			}
		}

	} else {
	
		forall {
			sref = new SectionRef()
			if ( sref.has_parent() && sref.nchild>1) {
				psl.append()
				for i=0,sref.nchild-1 sref.child[i] { csl.append() }
			}
		}
	}
}


proc BranchLists() { local count 	localobj psl, sl, bs, sref
// BranchLists( list, section pool)
// generate a list of SectionLists, one for each branch
	
	count = numarg()
	psl = $o1
	if (isclass(psl,"NULLobject")) psl = new List()
	
	bs = new SectionList()
	
	if (count > 1) {
		if (argtype(2)==2)	{
			sl = new SectionList()
			forall ifsec $s2 sl.append()
			if (verbosity > 2) sl.printnames()
		} else if (argtype(2)==1)	sl = $o2
	}

	if (object_id(sl) != 0) {
		forsec sl {
			bs.append()
			sref = new SectionRef()
			if ( sref.has_parent() && sref.nchild>1) {
				psl.append(bs)
				bs = new SectionList()
			}
		}
	} else {
		forall {
			bs.append()
			sref = new SectionRef()
			if ( sref.has_parent() && sref.nchild>1) {
				psl.append(bs)
				bs = new SectionList()
			}
		}
	}
}

proc ReplaceMorphology() {localobj sl

	ims = issplit()		// check if multisplit is on
	if (ims) stopPar()	// if multisplit is on, stop it

	forall {
		delete_section()
	}

	sl = new SectionList()
	if (argtype(1) == 2)  {
		load_file(1,$s1)
	} else {
	}
	
	if (ims) startPar()
}

proc DistanceMap() {	localobj psl
// DistanceMap( Vector, reference section)

	if (isclass($o1,"NULLobject")) psl = new Vector()


}

func DistanceArea() { local sum,i,c,min,max,I
// func DistanceArea( distance )
// return membrane area of a given section within distance $1 from origin

  	sum = 0
  	min = distance(0)
  	max = distance(1)
	if (min>$1)	return 0	/* completely out */
  	if (max<$1){ 	 /* completely in */
	  	for (x,0) sum += area(x)	return sum
	}
	/* in between */
	if (nseg==1) {
		return area(0.5)*($1-min)/L		// section area * percent length 
	}
	
	for i=1,nseg {				// loop through each segment
		max = distance(i/nseg)	// distance to segment end
		c = (i-0.5)/nseg		// segment middle
		if (max<$1) {			// if whole segment and beyond included
			sum += area(c)
		} else if (max==$1) {	// if exactly this segment included
			sum += area(c)
			return sum
		} if (max>$1) {			// if partial segment included
			min = distance((i-1)/nseg)
			sum += area(c)*($1-min)/L*nseg
			return sum
		}
	}
}

func DistanceDiam() {   local nosec,zl,zd,mx,mn,direct	localobj sl, diamvec, imp
// diameter = DistanceDiam( distance, frequency, SectionList )
// calculate Rall equivalent sum of diams for sections at dist $1 from cas
// if there is a second input argument, then $1 is treated as electrotonic distance (units of λ),
//		otherwise $1 is the distance (in µm) along the neurite path.
// $2 sets the signal frequency for measuring electrotonic distance
// if a sectionlist is given for $o3, only those sections are included, otherwise all sections are used

	diamvec = new Vector()

	if (numarg()>1) {
		if (argtype(2)==0) zl=1 else zl=0
	} else zl=0 

	strtmp = "micron"	// µm ('µ' isn't printable to stdout)
	if (zl!=0) {
		imp = new Impedance()
		imp.loc(0)
		imp.compute($2)
		zd = exp(-1*$1)
		strtmp = "lambda"	// λ ('λ' isn't printable)
	}
	
	if (verbosity > 2) printf("Calculating equiv diameter at distance %g %s\n", $1, strtmp)
	
	if (numarg()>2) {
		if (argtype(3)==2)	{
			sl = new SectionList()
			forall ifsec $s3 sl.append()
			if (verbosity > 3) sl.printnames()
		} else if (argtype(3)==1)	sl = $o3
	} else forall sl.append()

	distance()

	forsec sl {
		if (zl==0) {
			if (distance(1)>distance(0)) {
				mx=distance(1)		// maximum distance
				mn=distance(0)		// minimum distance
			} else {
				mx=distance(0)
				mn=distance(1)
			}
			if ((mx>=$1) && (mn<$1) ) {
				if (verbosity > 4) printf("DistanceDiam: Distance = %g %s. distance is %g to %g\n", \
					$1, strtmp, mn, mx)
				if (L==0) {
					diamvec.append(diam(0.5))
					if (verbosity > 1) printf("Section has no length\n")
				} else	diamvec.append(diam(($1-mn)/L))
			}
		} else {
			if (imp.ratio(1)>imp.ratio(0)) {
				mx=imp.ratio(1)
				mn=imp.ratio(0)
			} else {
				mx=imp.ratio(0)
				mn=imp.ratio(1)
			}
			if ((mx>=zd) && (mn<=zd) ) {
				if (verbosity > 4) printf("DistanceDiam: Distance = %g %s. lambda is %g to %g\n", \
					$1, strtmp, -1*log(mx), -1*log(mn))
				if (mn==mx) {
					if (verbosity > 1) printf("Section has no electrotonic length\n")
					diamvec.append(diam(0.5))
				} else {
					x=(zd-mn)/(mx-mn)
					if (verbosity > 2) if (x<0) printf("DistanceDiam: x<0. Distance = %g %s\n", $1, strtmp)
					if (verbosity > 2) if (x>1) printf("DistanceDiam: x>1. Distance = %g %s\n", $1, strtmp)
					diamvec.append(diam(x))
				}
			}
		}
	}

	nosec = diamvec.size()	// number of sections at distance
	if (nosec<1) {
		if (verbosity > 1) printf("Past end of cell. Distance = %g %s\n", $1, strtmp)
		return -1
	}

	diamvec.div(2)		// radii of each section
	diamvec.pow(3/2)	// radii of each section^1.5
	equiv_diam = 2*diamvec.sum()^(2/3)	// diameter for equivalent cylinder
	return equiv_diam

}

func DistanceList() {   local nosec,zl,zd,mx,mn,direct, tol	localobj sl, secs, imp
// List = DistanceDiam( List, distance, frequency, SectionList, tol )
// append a SectionList to List with all sections at dist $2 from cas
// if there is a numeric third input argument, then $2 is treated as electrotonic distance (units of λ),
//		otherwise $2 is the distance (in µm) along the neurite path.
// $3 sets the signal frequency for measuring electrotonic distance
// if a sectionlist is given for $o4, only those sections are considered, otherwise all sections are used

 	if (isclass($o1,"List")) secs = new SectionList() else secs = $o1

	if (numarg()>2) {
		if (argtype(3)==0) zl=1 else zl=0
	} else zl=0 

	strtmp = "micron"	// µm ('µ' isn't printable to stdout)
	if (zl!=0) {
		imp = new Impedance()
		imp.loc(0)
		imp.compute($3)
		zd = exp(-1*$2)
		strtmp = "lambda"	// λ ('λ' isn't printable)
	}
	
	if (numarg()>3) {
		if (argtype(4)==2)	{
			sl = new SectionList()
			forall ifsec $s4 sl.append()
			if (verbosity > 3) sl.printnames()
		} else if (argtype(4)==1)	sl = $o4
	} else forall sl.append()

	tol=0
	if (numarg()>4) {
		if (zl==0) {
			tol = $5
		} else tol = 1-exp(-1*$5)
	}
	
	distance()

	if (verbosity > 3) printf("Generating SectionList at distance %g +/- %2g %s\n", $2, tol, strtmp)

	forsec sl {
		if (zl==0) {
			if (distance(1)>distance(0)) {
				mx=distance(1)		// maximum distance
				mn=distance(0)		// minimum distance
			} else {
				mx=distance(0)
				mn=distance(1)
			}
			if ((mx>$2-tol) && (mn<=$2+tol) ) {
				secs.append()
			}
		} else {
			if (imp.ratio(1)>imp.ratio(0)) {
				mx=imp.ratio(1)
				mn=imp.ratio(0)
			} else {
				mx=imp.ratio(0)
				mn=imp.ratio(1)
			}
			if ((mx>zd-tol) && (mn<=zd+tol) ) {
				secs.append()
			}
		}
	}
	
	if (verbosity > 3) printf("Adding SectionList at distance %g %s to %s\n", $2, strtmp, $o1)

	nosec = SectionListCount(secs)	// number of sections at distance
	if (nosec<1) if (verbosity > 2) printf("Past end of cell. Distance = %g %s\n", $2, strtmp)

	if (isclass($o1,"List")) $o1.append(secs)

	return nosec
}

proc make_equivalent_cable() { local i,ed,nedc,neac,linc,l,f,ims	localobj gls,dvec,lvec,sl,sl2,fobj
//make_equivalent_cable()
//make_equivalent_cable( frequency, length increment )
// default frequency is 0, default increment is 0.005 λ
	
	ims = issplit()		// check if multisplit is on
	if (ims) stopPar()	// if multisplit is on, stop it so ModelView contains only 1 cell

	l = 0
	if (numarg()>0) f=$1 else f=0
	if (numarg()>1) linc=$2 else linc = 0.005
	
	dvec = new Vector()
	dvec.append( diam(0) )
	
	MakeSecList(sl,"Axon")
	sl2 = new SectionList()
	
	gls = new List()
	MechList(gls)
	
	forall sl2.append()
	sl2.remove(sl)
	while (ed != -1) {
		l += linc*2
		ed = DistanceDiam(l,f,sl)	// get equivalent diameter for each distance
		dvec.append(ed)
	}

	dvec.remove(dvec.size()-1)
	neac = dvec.size()
	
	ed = 0
	while (ed != -1) {
		l += linc
		ed = DistanceDiam(l,f,sl2)	// get equivalent diameter for each distance
		dvec.append(ed)
	}
	
	dvec.remove(dvec.size()-1)
	nedc = dvec.size()-neac

	if (verbosity > 1) printf("Calculated diameters of equivalent cylinder. Saving to file ... \n")
	
	lvec = new Vector(nedc)
	lvec.indgen(linc)

	sprint(filename, "%s/EquivCable_%g_%g_%g", DATADIR, linc, f, meanRm("",0,1))
	fobj = new File(filename)
	fobj.wopen()
	
	sprint(strtmp, "%s_params.txt", filename)
// 	SaveParams( strtmp )
	hoc_stdout(strtmp)
    
    PrintGlobals(gls)

    hoc_stdout()

	if (verbosity > 1) printf("Saved global and section parameters to file ... \n")

// 	fobj.printf( "create soma\n" )
	fobj.printf( "create axon[%d]\n", neac )
	fobj.printf( "create dend[%d]\n", nedc )
	fobj.printf( "nseg = 1\n\n" )
	
// 	fobj.printf( "soma.diam = %2.4f\n",  dvec.x[0] )
// 	fobj.printf( "soma.L = %2.4f\n",  linc )

	fobj.printf( "axon[0].diam = %2.4f\n",  dvec.x[0] )
	fobj.printf( "axon[0].L = %2.4f\n",  linc*2 )
	
 	for i = 1,neac-1 {
 		fobj.printf("axon[%d].diam = %2.4f\n",  i, dvec.x[i])
 		fobj.printf("axon[%d].L = %2.4f\n",  i, linc*2 )
 		fobj.printf("axon[%d] connect axon[%d](0), 1\n\n",i,i-1)
 	}

	fobj.printf( "dend[0].diam = %2.4f\n",  dvec.x[neac] )
	fobj.printf( "dend[0].L = %2.4f\n",  linc )
	fobj.printf("dend[0] connect axon[0](0), 0\n\n")

 	for i = 1,nedc-1 {
 		fobj.printf("dend[%d].diam = %2.4f\n",  i, dvec.x[neac+i])
 		fobj.printf("dend[%d].L = %2.4f\n",  i, linc )
 		fobj.printf("dend[%d] connect dend[%d](0), 1\n\n",i,i-1)
 	}

	fobj.close()
	
	if (ims) startPar()		// restart parallelization if needed

}

// equivalent cable (defined by sizofugal electrotonic distance)
// update to include same total axial resistivity, membrane conductance, capacitance, and 
// synaptic currents within each section
// using compartments at same λ set rall equiv diam, mean Gmax, same Isyn, rescaled to membrane area

// val = meanRm( sections, mechanism(s), 1)	loop through list and gls

objref LSL
proc ActiveEquivCable() { local d,ldc,k,i,n,gn,g,nedc,neac,linc,l,f,ims	localobj syns,gls,ls,sl,sl2,fobj,ms
//ActiveEquivCable()
//ActiveEquivCable( frequency, length increment, fields/branches )
// default frequency is 0, default increment is 0.01 λ
	
	ims = issplit()		// check if multisplit is on
	if (ims) stopPar()	// if multisplit is on, stop it so ModelView contains only 1 cell

	if (numarg()>0) f=$1 else f=0
	if (numarg()>1) linc=$2 else linc = 0.01
	
	LSL = new List()
	
	MakeSecList(sl,"Axon")
	sl2 = new SectionList()
	forall sl2.append()
	sl2.remove(sl)
	
	gls = new List()
// 	MechList(gls)
	getglist(gls)
	gn = gls.count()
	
	l = 0
	n=1
	while (n != 0) {
		l += linc
		n = DistanceList(LSL,l,f,sl)	// get equivalent diameter for each distance
	}

	if (verbosity > 2) printf("Generated section lists for axon. %d sections  ... \n", LSL.count())
	neac = LSL.count()-1
	LSL.remove(LSL.count())
	
	n = 1
	l=0
	while (n != 0) {
		l += linc
		n = DistanceList(LSL,l,f,sl2)	// get equivalent diameter for each distance
	}

	if (verbosity > 2) printf("Generated section lists for dendrites. %d sections  ... \n", LSL.count())
	
	nedc = LSL.count()-1-neac
	LSL.remove(LSL.count())

	if (verbosity > 1) printf("Generated section lists for active equivalent cable. Calculating conductances ... \n")
	
	sprint(filename, "%s/EquivCable_%g_%g_%g", DATADIR, linc, f, meanRm("",0,1))
// 	SaveParams( strtmp )
	hoc_stdout(filename)
    
	PrintGlobals()
//     forall psection()
	hoc_stdout()

	if (verbosity > 2) printf("Saved global parameters to file ... \n")

	fobj = new File(filename)
	fobj.aopen()

// 	fobj.printf( "create soma\n" )
	fobj.printf( "\ncreate axon[%d]\n", neac )
	fobj.printf( "create dend[%d]\n", nedc )
	fobj.printf( "nseg = 1\n\n" )
	
// 	fobj.printf( "soma.diam = %2.4f\n",  dvec.x[0] )
// 	fobj.printf( "soma.L = %2.4f\n",  linc )


// need to calculate lambda to µm conversion factor here
//	ldc = maxd/maxlambda
	ldc=205
 	
	for i = 0,neac-1 {	// for each distance
 		d=0
		forsec LSL.o(i) d+=(diam/2)^(3/2)
		fobj.printf("axon[%d] {\n", i)
 		fobj.printf("diam = %g\n", 2*d^(2/3))
 		fobj.printf("L = %g\n", linc*ldc )
 		
		for n=0,gn-1 {	// for each conductance
			ms = new MechanismStandard(gls.o(n).s,1)
			if (ms.count == 0) { 
				continue
			}
			size = ms.name(tmpstr, 0)
			sprint(strtmp, "tmp = %s", tmpstr)
			
			g=0
			forsec LSL.o(i) {	// for each compartment at the given distance
				if (ismembrane(gls.o(n).s)) {
					execute(strtmp)
					g += tmp*area(0.5)
				}
			}
			if (g>0) {
				fobj.printf( "insert %s { %s = %2.4f}\n", gls.o(n).s, tmpstr, g/sectionarea(LSL.o(i))  )
			}
		}
		fobj.printf("}\n")
 		if (i>0) fobj.printf("axon[%d] connect axon[%d](0), 1\n\n",i,i-1)
 	}
 	
 	for i = 0,neac-1 { LSL.remove(i) }
 	
 	if (verbosity > 2) printf("Saved axon section parameters to file ... \n")

// 	syns = new List()
// 	for i = 0,esyn.count()-1 {
// 		syns.append(esyn.o(i))
// 	}
// 	for i = 0,isyn.count()-1 {
// 		syns.append(isyn.o(i))
// 	}
	
	for i = 0,nedc-1 {
 		d=0
		forsec LSL.o(i) d+=(diam/2)^(3/2)
		fobj.printf("dend[%d] {\n", i)
		fobj.printf("diam = %2.2f\n", 2*d^(2/3))
 		fobj.printf("L = %2.2f\n", linc*ldc )
 		
		for n=0,gn-1 {	// for each conductance
			ms = new MechanismStandard(gls.o(n).s,1)
			if (ms.count == 0) { 
				continue
			}
			size = ms.name(tmpstr, 0)
			sprint(strtmp, "tmp = %s", tmpstr)
			
			g=0
			forsec LSL.o(i) {	// for each compartment at the given distance
				if (ismembrane(gls.o(n).s)) {
					execute(strtmp)
					g += tmp*area(0.5)
				}
			}
			if (g>0) {
				fobj.printf( "insert %s { %s = %2.4g}\n", gls.o(n).s, tmpstr, g/sectionarea(LSL.o(i))  )
			}
 		}
// 		ls = get_subset(syns, LSL.o(i),1)
// 		for n=0,ls.count()-1 {
// 			classname(ls.o(n), strtmp)
// 			ms = new MechanismStandard(strtmp,1)
// 			ms.in(ls.o(n))
// 			fobj.printf( "insert %s { ", strtmp )
// 			for k=0,ms.count()-1 {
// 				ms.name(tmpstr, k)
// 				fobj.printf( " %s = %g", tmpstr, ms.get(tmpstr)  )
// 			}
// 			fobj.printf( "}\n"  )
// 		}
		fobj.printf("}\n")
 		if (i>0) {
 			fobj.printf("dend[%d] connect dend[%d](0), 1\n\n",i,i-1)
 		} else fobj.printf("dend[0] connect axon[0](0), 0\n\n")
 	}

	fobj.close()
	
	if (ims) startPar()		// restart parallelization if needed

}