begintemplate RunFitParm
public name, val, low, high, doarg, invalid, copy, set, play_one
public setln, getln, uselog, domain, domain_, setname, pval
strdef name, domain_
objref pval

proc init() {
	uselog = 0
	set($s1, 1, -1e6, 1e6, 1, 0)
}
	
proc set() {
	setname($s1)
	val = $2
	low = $3
	high = $4
	doarg = $5
	uselog = $6
	domain()
}
proc setname() {
	name = $s1
	if (strcmp(name, "") != 0) {
		pval = new Pointer(&val, name)
	}else{
		objref pval
	}
}

proc domain() {
	if (uselog && ((low > 0) || (high < 0))) {
		sprint(domain_, " X   %g  %g  %s", low, high, name)
	}else{
		sprint(domain_, "     %g  %g  %s", low, high, name)
	}
}

proc copy() {
	set($o1.name, $o1.val, $o1.low, $o1.high, $o1.doarg, $o1.uselog)
}
func invalid() {
	if (val < low || val > high) {
		return 1
	}
	return 0
}

proc play_one() {
	pval.assign(val)
}

proc setln() {
	if (uselog) {
		if (low > 0) {
			val = exp($1)
		}else if (high < 0) {
			val = -exp($1)
		}else{
			val = $1
		}
	}else{
		val = $1
	}
}

func getln() {
	if (uselog) {
		if (low > 0) {
			return log(val)
		}else if (high < 0) {
			return log(-val)
		}
			return val
	}else{
		return val
	}
}

endtemplate RunFitParm

begintemplate GenInfo
public gen, use, s, toggle
objref gen
strdef s
proc init() {
	gen = $o1
	if (numarg() > 1) {
		use = $o2.scanvar
	}else{
		use = 0
	}
	use = (use + 1) % 2
	toggle()
}
proc toggle() {
	use = (use+1)%2
	if (use) {
		sprint(s, "+ %s", gen.title)
	}else {
		sprint(s, "- %s", gen.title)
	}
}

endtemplate GenInfo

begintemplate ParmFitness
public parmlist, efun, doarg_get, generatorlist, title, errval, pc
public rfile, wfile, usegen, parmlist_use, putall, argget, randomize
public use_parallel, save_context, declare
public randomize_inrange
external classname
objref parmlist, generatorlist, tobj, sf, this, usegen, parmlist_use
objref ran, pc, parmvec
strdef title, tstr, tstr1

func efun() {local e, i
	if (use_parallel_) {
		return efun_parallel($1, &$&2)
	}
	for i=0, $1-1 {
		tobj = parmlist_use.object(i)
		tobj.setln($&2[i])
		if (tobj.invalid) {
printf("invalid %s low=%g val=%g high=%g\n", tobj.name, tobj.low, tobj.val,tobj.high)
			return 1e9
		}
		tobj.play_one()
	}
	e = 0
	for i=0, generatorlist.count-1 {
		if (generatorlist.object(i).use) {
			e += generatorlist.object(i).gen.efun()
			if (stoprun) {break}
		}
	}
	errval = e
	return e
}

func parm() {local i
	for i=0, $o1.size-1 {
		tobj = parmlist_use.object(i)
		tobj.setln($o1.x[i])
		if (tobj.invalid) {
printf("invalid %s low=%g val=%g high=%g\n", tobj.name, tobj.low, tobj.val,tobj.high)
			return 0
		}
		tobj.play_one()
	}
	return 1
}

func efun1() {
	if ($3 != context_) {
		printf("%s bad context: mine=%d needed=%d\n", \
			this, context_, $3)
		quit()
	}
	parm($o2)
	return generatorlist.object($1).gen.efun()
}

func efun_parallel() {local e, i, j, n, b
	if (parm(parmvec.from_double($1, &$&2)) == 0) {
		return 1e9
	}
	n = generatorlist.count
	for j=0, n - 1 {
		if (generatorlist.object(j).use) {
			break
		}
	}
	b = 0
	for i=j+1, n-1 {
		if (generatorlist.object(i).use) {
			pc.submit(this, "efun1", i, parmvec, context_)
			b = 1
		}
	}
	if (j < n) {
		e = generatorlist.object(j).gen.efun()
	}
	if (b) {
		while (pc.working()) {
			e += pc.retval
		}
	}
	errval = e
	return e
}

proc save_context() {local i
	context_ += 1
	sprint(tstr, "%s", this)
	pc.look_take(tstr)
	pc.pack(title, context_)

	pc.pack(generatorlist.count)
	for i=0, generatorlist.count-1 {
		tobj = generatorlist.object(i)
		classname(tobj.gen, tstr)
		pc.pack(tstr)
		tobj.gen.save_context(pc)
		pc.pack(tobj.use)
	}

	pc.pack(parmlist.count)
	for i=0, parmlist.count-1 {
		tobj = parmlist.object(i)
		pc.pack(tobj.name, tobj.val, tobj.low, tobj.high, \
			tobj.doarg, tobj.uselog)
	}
	sprint(tstr, "%s", this)
	pc.post(tstr)
	pc.context(this, "restore_context", tstr)
}

proc restore_context() {local i, n
	pc.look($s1)
	pc.unpack(title, &context_)

	generatorlist.remove_all
	n = pc.upkscalar
	for i=0, n-1 {
		pc.upkstr(tstr)
		sprint(tstr, "tobj = new GenInfo(new %s())", tstr)
		execute(tstr, this)
		tobj.gen.restore_context(pc)
		if (pc.upkscalar == 1) tobj.toggle()
		generatorlist.append(tobj)
	}

	parmlist.remove_all
	n = pc.upkscalar
	for i=0, n-1 {
		tobj = new RunFitParm(pc.upkstr(tstr))
		pc.unpack(&tobj.val, &tobj.low, &tobj.high, \
			&tobj.doarg, &tobj.uselog)
		tobj.domain()
		declare(tobj)
		tobj.play_one()
		parmlist.append(tobj)
	}
	def_parmlist_use()
}

proc use_parallel() {
	if ($1 == 1) {
		save_context()
	}
	use_parallel_ = $1
 }

proc init() {
	context_ = 1
	use_parallel_ = 0
	if (numarg() > 0) {
		title = $s1
	}
	// "this" is the stable context (all publics and descendants)
	pc = new ParallelContext()
	parmvec = new Vector()
	parmlist = new List()
	parmlist_use = new List()
	generatorlist = new List()
	sf = new StringFunctions()
}

proc randomize() { local i, x
	if (object_id(ran) == 0) {
		ran = new Random(startsw())
	}
	if ($1 <= 0) return
	ran.uniform(-log($1),log($1))
	for i=0, parmlist.count-1 {
		tobj = parmlist.object(i)
		if (tobj.doarg) {
			x = tobj.val * exp(ran.repick)
			tobj.val = x
			tobj.play_one()
		}
	}
}

proc randomize_inrange() { local i, x
	if (object_id(ran) == 0) {
		ran = new Random(startsw())
	}
	if ($1 <= 0) return
	ran.uniform(-log($1),log($1))
	for i=0, parmlist.count-1 {
		tobj = parmlist.object(i)
		if (tobj.doarg) {
			x = tobj.pval * exp(ran.repick)
			//x = tobj.val * exp(ran.repick)
			while( x < tobj.low || x > tobj.high ) {
			    print "\t",x, " out of range [",tobj.low,",	",tobj.high, "] -- repick"
			    x = tobj.pval * exp(ran.repick)
			    //x = tobj.val * exp(ran.repick)
			    //print "\t",x, " new value"
			}
			//print x, " is IN range [",tobj.low,", ",tobj.high, "]"
			tobj.pval = x
			//tobj.val = x
			//tobj.play_one()
		}
	}
}

proc def_parmlist_use() {local i
	parmlist_use.remove_all()
	for i=0, parmlist.count-1 {
		if (parmlist.object(i).doarg) {
			parmlist_use.append(parmlist.object(i))
//printf("def_parmlist_use %d %s\n", i, parmlist.object(i).name)
		}
	}
}

func doarg_get() {local i, n
	def_parmlist_use()
	n = parmlist_use.count
	$o1.resize(n)
	for i=0, n-1 {
		$o1.x[i] = parmlist_use.object(i).getln()
	}
	return n
}

proc argget() {local i
	j = 0
	$o1.resize(parmlist.count)
	for i=0, parmlist.count-1 {
		$o1.x[i] = parmlist.object(i).val
	}
}

proc putall() {local i
	for i=0, parmlist.count-1 {
		parmlist.object(i).play_one()
	}
}

proc wfile() {local i
	$o1.printf("ParmFitness: %s\n", title)
	for i=0, generatorlist.count-1 {
		generatorlist.object(i).gen.wfile($o1, $o2)
		$o2.printf("%d\n", generatorlist.object(i).use)
	}
	$o1.printf("\tParameters:\n")
	for i=0, parmlist.count-1 {
		tobj = parmlist.object(i)
		$o1.printf("\t\t\"%s\", %g, %g, %g, %g, %g\n", tobj.name,\
		  tobj.val, tobj.low, tobj.high, tobj.doarg, tobj.uselog)
	}
	$o1.printf("End ParmFitness\n")
}
proc rfile() {local use
	if (numarg() == 3) {
		tstr = $s3
	}else{
		$o1.gets(tstr)
	}
	sscanf(tstr,"ParmFitness: %[^\n]", title)

        // Read the FitnessGenerators information
	while (1) {
		$o1.gets(tstr)
		if (sf.len(tstr) == 1) continue
		sscanf(tstr, "%[^:]:", tstr1)
		if (sf.substr(tstr1, "Parameters") > -1) break
		sprint(tstr1, "tobj = new %s(0)", tstr1)
		execute(tstr1, this)
		tobj.rfile($o1, $o2, tstr)
		//printf("%s %s\n", tobj, tobj.title)
		generatorlist.append(new GenInfo(tobj, $o2))
	}
        // Read the Parameters information
	while (1) {
		$o1.gets(tstr)
		if (sf.substr(tstr, "End ParmFitness") > -1) break
		tobj = new RunFitParm("",0,0,0,0,0)
		sscanf(tstr, "%*[^\"]\"%[^\"]\",%g,%g,%g,%g,%g", tobj.name, &tobj.val,\
			&tobj.low, &tobj.high, &tobj.doarg, &tobj.uselog)
		tobj.setname(tobj.name)
		tobj.domain()
//		sscanf(tstr, "%*[^\"]\"%[^\"]\",%g,%g,%g,%g", tobj.name, &tobj.val,\
//			&tobj.low, &tobj.high, &tobj.doarg)
//		tobj.setname(tobj.name)
//		printf("%s |%s| %s", tobj, tobj.name, tstr)
		declare(tobj)
		tobj.play_one()
		parmlist.append(tobj)
	}
	objref tobj
}

// this is probably not necessary because the Pointer will do it automatically
proc declare() { // function fitters often declare their own variables
	if (sf.substr($o1.name, "$") == -1) {
		if(!execute1($o1.name, 0)) {
			sprint(tstr, "%s = %g", $o1.name, $o1.val)
			execute1(tstr)
		}
	}
}

endtemplate ParmFitness

begintemplate ParmFitnessGui
public pf, map, unmap, dact, mulfit, run, c2p, p2c
public showopt, gmode, gensel
public mapsave, gengui, showargs, showdomain, usepanel
objref pf, sf, symch_, mulfit
external hoc_obj_, parmfitness_generator_list_
external mulfit_optimizers_, mulfit_optimizer_names_
strdef amodestr, gmodestr, tstr, tstr1
objref vbox, hbox, this, tobj, domainlist
public uselog, domainlist
objref parmbox, domainbox
objref gengui_, genguibox_
objref usepanelbox_

proc init() {
	pf = $o1
	mulfit = $o2
	build()
	sf = new StringFunctions()
}

proc run() {
	pf.putall()
	tobj = new Vector(50)
	pf.doarg_get(tobj)
	if (tobj.size) {
		pf.efun(tobj.size, &tobj.x[0])
	}else{
		pf.efun(0)
	}
}

proc runall() {local i
	run()
	for i=0, pf.generatorlist.count-1 {
		if (!pf.generatorlist.object(i).use) {
			pf.generatorlist.object(i).gen.efun()
		}
	}
}

proc build() {local i
	vbox = new VBox(3)
	vbox.save("")
	vbox.dismiss_action("unmap()")
	vbox.intercept(1)
	xpanel(pf.title, 1)
		xvarlabel(pf.title)
		xpvalue("ErrorValue", &pf.errval, 0, "run()")
	xpanel()		
	hbox = new HBox(3)
	hbox.intercept(1)
	tobj = new VBox(3)
	tobj.intercept(1)
		xpanel("", 1)
		xmenu("Parameters")
			xmenu("Select Optimizer")
			for i= 0, mulfit_optimizers_.count - 1 {
				sprint(tstr, "showopt(%d)", i)
				xbutton(mulfit_optimizer_names_.object(i).s, tstr)
			}
			xmenu()
			xbutton("Parameter Panel", "showargs()")
			xbutton("Domain Panel", "showdomain()")
			xbutton("Add Parameter", "addarg()")
xradiobutton("Remove Parameter", "amode=3 amodestr=\"Remove\"")
xradiobutton("Change Parameter", "amode=4 amodestr=\"Change\"")
			xmenu("Parm import/export")
				xbutton("Top level to Parm", "t2p()")
				xbutton("Clipboard to Parm", "c2p()")
				xbutton("Parm to Clipboard", "p2c()")
			xmenu()
			if (pf.pc.nhost > 0) {
				xmenu("Parallel")
				xbutton("Update context", "update_context()")
				xbutton("Test context", "test_context()")
				xmenu()
			}
		xmenu()
		amode=1 amodestr=""
		xvarlabel(amodestr)
		xpanel()
		pf.parmlist.browser("", "name")
		pf.parmlist.accept_action("parmsel(hoc_ac_)")
	tobj.intercept(0)
	tobj.map()
	tobj = new VBox(3)
	tobj.intercept(1)
		xpanel("", 1)
		xmenu("Generators")
			xmenu("Add Fitness Generator")
xbutton("Add Run Fitness", "addgen(0)")
xbutton("Add Function Fitness", "addgen(1)")
xbutton("Add Fitness Primitive", "addgen(2)")
xbutton("Add Multiple Run Fitter", "addgen(3)")
for i=4, parmfitness_generator_list_.count - 1 {
	sprint(tstr, "addgen(%d)", i)
	sprint(tstr1, "Add %s", parmfitness_generator_list_.object(i).s)
	xbutton(tstr1, tstr)
}
			xmenu()
xradiobutton("Display Generator", "gmode=1 gmodestr=\"Display\"")
xradiobutton("Use Generator", "gmode=2 gmodestr=\"Toggle\"")
xradiobutton("Remove Generator", "gmode=3 gmodestr=\"Remove\"")
xradiobutton("Change Name", "gmode=4 gmodestr=\"Change Name\"")
xradiobutton("Clone Generator", "gmode=5 gmodestr=\"Clone\"")
xbutton("Reorder upward", "reorder(-1)")
xbutton("Rorder downward", "reorder(1)")
xbutton("Multiple protocol name", "chtitle()")
xbutton("View all Graphs", "gview()")
xbutton("Pop up \"Use\" panel", "usepanel()")
xbutton("Run all", "runall()")
		xmenu()
		gmode = 1  gmodestr="Display"
		xvarlabel(gmodestr)
		xpanel()
		pf.generatorlist.browser("", "s")
		pf.generatorlist.select_action("gensel(hoc_ac_, 0)")
		pf.generatorlist.accept_action("gensel(hoc_ac_, 1)")
	tobj.intercept(0)
	tobj.map()
	hbox.intercept(0)
	hbox.map()
	vbox.intercept(0)
}

proc update_context() {
	pf.save_context()
}

proc test_context() {local p
	tobj = new Vector(50)
	pf.doarg_get(tobj)
	pf.pc.context(this, "partest_efun", tobj)
	partest_efun(tobj)
}

proc partest_efun() {
	tobj = $o1
	if (tobj.size) {
		pf.efun(tobj.size, &tobj.x[0])
	}else{
		pf.efun(0)
	}
	print pf.errval
}
	

proc showopt() {
	sprint(tstr, "mulfit.opt = new %s(pf)", mulfit_optimizers_.object($1).s)
	execute(tstr, this)
	mulfit.showopt()
}

proc t2p() {local i
	for i=0, pf.parmlist.count-1 {
		tobj = pf.parmlist.object(i)
		if (sf.head(tobj.name, "\\$", tstr) == -1) {
			sprint(tstr, "%s.val = %s", tobj, tobj.name)
			execute1(tstr)
		}
	}
}
proc p2c() {
	hoc_obj_[0] = new Vector() pf.argget(hoc_obj_[0])
	hoc_obj_[1] = new Vector(hoc_obj_[0].size)
	hoc_obj_[1].indgen
}
proc c2p() {local i, n
	n = pf.parmlist.count
	if (n != hoc_obj_[0].size) {
		print "clipboard not same size as parmlist"
		sqrt(-1)
	}
	for i=0, n-1 {
		pf.parmlist.object(i).val = hoc_obj_[0].x[i]
	}
	pf.putall()
}

proc gview() {local i, j
	j = .2
	for i=0, pf.generatorlist.count-1 {
		tobj = pf.generatorlist.object(i)
//		if (tobj.use) {
			j = tobj.gen.gview(j, pf.title)
//		}
	}
}

proc usepanel() {local i
	usepanelbox_ = new VBox()
	usepanelbox_.save("")
	usepanelbox_.intercept(1)
	xpanel("")
	for i=0, pf.generatorlist.count-1 {
		tobj = pf.generatorlist.object(i)
		sprint(tstr, "use_action(%d)", i)
		xcheckbox(tobj.gen.title, &tobj.use, tstr)
	}
	xpanel()
	usepanelbox_.intercept(0)
	sprint(tstr, "%s use toggle", mulfit)
	if (numarg() > 3) {
		usepanelbox_.map(tstr, $1, $2, $3, $4)
	}else{
		usepanelbox_.map(tstr)
	}
	objref tobj
}

proc use_action() {local i
	i = pf.generatorlist.selected
	pf.generatorlist.select(-1)
	tobj = pf.generatorlist.object($1)
	tobj.use += 1
	tobj.toggle()
	pf.generatorlist.remove($1)
	pf.generatorlist.insrt($1, tobj)
	pf.generatorlist.select(i)
}

proc reorder() { local i //selected upward -1, downward 1
	i = pf.generatorlist.selected
	if (i != -1 && i + $1 > -1 && i + $1 < pf.generatorlist.count) {
		dgen()
		tobj = pf.generatorlist.object(i)
		pf.generatorlist.select(-1)
		pf.generatorlist.remove(i)
		pf.generatorlist.insrt(i+$1, tobj)
		pf.generatorlist.select(i+$1)
	}
}

proc chtitle() {
	tstr = pf.title
	if (string_dialog("Multiple Run Protocol name", tstr)) {
		pf.title = tstr
		mulfit.title = pf.title
	}
}

proc addgen() {
	if ($1 >= 0 && $1 < parmfitness_generator_list_.count) {
		sprint(tstr, "tobj = new %s(0)", parmfitness_generator_list_.object($1).s)
		execute(tstr, this)
		pf.generatorlist.append(new GenInfo(tobj))
		if (object_id(gengui_) != 0) {
			gengui_.intercept(1)
			pf.generatorlist.object(pf.generatorlist.count-1).gen.map()
			gengui_.intercept(0)
		}
		objref tobj
	}
}

proc parmsel() {
	if ($1 < 0) return
	if (amode == 3) {
		pf.parmlist.remove($1)
		objref domainbox, domainlist, parmbox
	}
	if (amode == 4) {
		tstr = pf.parmlist.object($1).name
		if (string_dialog("Change parameter name", tstr)) {
			tobj = pf.parmlist.object($1)
			tobj.setname(tstr)
			pf.parmlist.remove($1)
			pf.parmlist.insrt($1, tobj)
			objref tobj
			objref domainbox, domainlist, parmbox
		}
	}
}

proc addarg() {
	if (object_id(symch_) == 0) {
		symch_ = new SymChooser("Fit Parameter")
	}
	if (symch_.run()) {
		symch_.text(tstr)
		tobj = new RunFitParm(tstr)
		pf.declare(tobj)
		pf.parmlist.append(tobj)
		objref domainbox, domainlist, parmbox
		if (sf.head(tstr, "\\$", tstr1) == -1) {
			sprint(tstr, "%s.val = %s", tobj, tstr)
			execute1(tstr)
		}
	}
}

proc gensel() {local i
	i = $1
	if (gmode == 1) { // display
		gengui(i)
	}
	if ($2 == 0) return
	if (gmode == 2) { // use toggle
		tobj = pf.generatorlist.object(i)
		tobj.toggle()
		pf.generatorlist.remove(i)
		pf.generatorlist.insrt(i, tobj)
		pf.generatorlist.select(i)
	}
	if (gmode == 3) { // remove
		tobj = pf.generatorlist.object(i)
		pf.generatorlist.remove(i)
		pf.generatorlist.select(-1)
		objref tobj
		dgen()
	}
	if (gmode == 4) { // change name
		tobj = pf.generatorlist.object(i)
		tstr = tobj.gen.title
		if (string_dialog("Change generator title", tstr)) {
			tobj.gen.chtitle(tstr)
			tobj.toggle()  tobj.toggle()
			pf.generatorlist.remove(i)
			pf.generatorlist.insrt(i,tobj)
			pf.generatorlist.select(i)
			dgen()
		}
	}
	if (gmode == 5) { // clone
		pf.generatorlist.object(i).gen.clone(tobj)
		pf.generatorlist.insrt(i+1, new GenInfo(tobj))
		dgen()
	}
}

proc gengui() {local i
	doNotify() // without this, mswin gives error when mulrunfit window not the top window
	card_ = $1
	if (object_id(gengui_) == 0) {
		tobj = new VBox(3)
		tobj.intercept(1)
		tobj.save("")
//		tobj.ref(this)
		tobj.dismiss_action("dact()")
		gengui_ = new Deck()
		gengui_.intercept(1)
		for i=0, pf.generatorlist.count-1 {
			pf.generatorlist.object(i).gen.map()
		}
		gengui_.intercept(0)
		gengui_.flip_to($1)
		gengui_.map()
		tobj.intercept(0)
		sprint(tstr, "%s Generators", mulfit)
		if (numarg() > 4) {
			tobj.map(tstr, $2, $3, $4, $5)
		}else{
			tobj.map(tstr)
		}
		genguibox_ = tobj
	}
	gengui_.flip_to($1)
}
proc dgen() {
	if (object_id(genguibox_) != 0) {
		genguibox_.unmap
	}
	objref gengui_, usepanelbox_
}
proc dact() {
	dgen()
	objref parmbox, domainbox, domainlist
}

proc mapsave() {
	if (object_id(genguibox_)) if (genguibox_.ismapped) {
		$o2.resize(4)
		genguibox_.size(&$o2.x[0])
sprint(tstr, "{p.gengui(%d, %g, %g, %g, %g)}", card_, $o2.x[0], $o2.x[1], $o2.x[2], $o2.x[3])
		$o1.save(tstr)
	}
	if (object_id(parmbox)) if (parmbox.ismapped) {
		parmbox.size(&$o2.x[0])
sprint(tstr, "{p.showargs(%g, %g, %g, %g)}", $o2.x[0], $o2.x[1], $o2.x[2], $o2.x[3])
		$o1.save(tstr)
	}
	if (object_id(domainbox)) if (domainbox.ismapped) {
		domainbox.size(&$o2.x[0])
sprint(tstr, "{p.showdomain(%g, %g, %g, %g)}", $o2.x[0], $o2.x[1], $o2.x[2], $o2.x[3])
		$o1.save(tstr)
	}
	if (object_id(usepanelbox_)) if (usepanelbox_.ismapped) {
		usepanelbox_.size(&$o2.x[0])
sprint(tstr, "{p.usepanel(%g, %g, %g, %g)}", $o2.x[0], $o2.x[1], $o2.x[2], $o2.x[3])
		$o1.save(tstr)
	}
}

proc map() {
	sprint(tstr, "%s", mulfit)
	vbox.map(tstr)
}

proc unmap() {
	if (object_id(genguibox_) != 0) {
		genguibox_.unmap
	}
	vbox.unmap
}

proc showargs() {local n
	n = pf.parmlist.count
	if (n > 5) { //scrollbox
		parmbox = new VBox(3,1)
	}else{
		parmbox = new VBox()
	}
	parmbox.save("")
	parmbox.intercept(1)
	for i=0, n-1 {
		tobj = pf.parmlist.object(i)
		xpanel("ParmValues", 1)
		xcheckbox("", &tobj.doarg)
		tstr = tobj.name
		sprint(tstr1, "pf.parmlist.object(%d).play_one()", i)
		xpvalue(tobj.name, &tobj.val, 1, tstr1)
		xpanel()
	}
	parmbox.intercept(0)
	parmbox.save("")
	sprint(tstr, "%s parameters", mulfit)
	if (numarg() > 3) {
		parmbox.map(tstr, $1, $2, $3, $4)
	}else{
		parmbox.map(tstr)
	}
	objref tobj
}

proc chdomain() {
	tobj = domainlist.object($1)
	sprint(tstr1, "Enter: 0 low high or 1 low high for %s", tobj.name)
	sprint(tstr, "%d %g %g", tobj.uselog, tobj.low, tobj.high)
	while (string_dialog(tstr1, tstr)) {
		if (sscanf(tstr, "%g %g %g", &x1, &x2, &x3) == 3) {
			tobj.uselog = (x1 != 0)
			tobj.low = x2
			tobj.high = x3
			tobj.domain()
			domainlist.remove($1)
			domainlist.insrt($1, tobj)
			objref tobj
			break
		}else{
			sprint(tstr, "%d %g %g", tobj.uselog, tobj.low, tobj.high)
			continue_dialog(\
"Must enter three space separated items, e.g. \"0 1e-6 1e6\" or \"1 1e-6 1e6\"")
		}
	}
}

proc uselog() {local i
	domainlist.remove_all
	for i=0, pf.parmlist.count-1 {
		tobj = pf.parmlist.object(i)
		tobj.uselog = $1
		tobj.domain()
		domainlist.append(tobj)
	}
	objref tobj
}
proc limits() {
	domainlist.remove_all
	for i=0, pf.parmlist.count-1 {
		tobj = pf.parmlist.object(i)
		tobj.low = $1
		tobj.high = $2
		tobj.domain()
		domainlist.append(tobj)
	}
	objref tobj
}

proc showdomain() {local n
	x1 = x2 = x3 = 0
	n = pf.parmlist.count
	domainlist = new List()
	for i=0, n-1 {
		domainlist.append(pf.parmlist.object(i))
	}
	domainbox = new VBox()
	domainbox.save("")
	domainbox.intercept(1)
	xpanel("")
	xmenu("group attributes")
	xbutton("use log scale", "uselog(1)")
	xbutton("use linear scale", "uselog(0)")
	xbutton("positive definite limits", "limits(1e-9,1e9)")
	xbutton("unbounded limits", "limits(-1e6,1e6)")
	xmenu()
	xlabel("Log    low      high         name                           ")
	xpanel()
	domainlist.browser("", "domain_")
	domainlist.accept_action("chdomain(hoc_ac_)")
	domainbox.intercept(0)
	domainbox.save("")
	sprint(tstr, "%s Domain", mulfit)
	if (numarg() > 3) {
		domainbox.map(tstr, $1, $2, $3, $4)
	}else{
		domainbox.map(tstr)
	}
	objref tobj
}

endtemplate ParmFitnessGui