# Expression stuff.
#
# expression_params is the current value of the parameter
# expression_param_choices is a list of the ones we might use
# expression_param_range is "active start end incr" (and is copied to choices)
# expression_param_random is used if expression_mode is random
# expression_param_by_cols 
#
# Copyright (c) 1998 Pacific Marketing and Consulting
# Author:  Wayne A. Christopher, wayne@pmac.com
#          Pacific Marketing and Consulting, Berkeley, California

proc expression_evaluate {expr {defval 0} {defunits ""} {param_func expression_param}} {
    global expression_evaluate_depth problem_temp
    
    if ![string match {*$*} $expr] { return $expr }

    # Some parameters might be using the values of the env variables
    # ICEPAK_ROOT, etc.  If this is the case just return.
    if [string match *_ROOT* $expr] { return $expr }
    
    if ![info exists expression_evaluate_depth] {
	set expression_evaluate_depth 0
    }
    incr expression_evaluate_depth
    if {$expression_evaluate_depth > 100} {
	set expression_evaluate_depth 0
	user_error "Expression evaluation in an infinite loop, expr = $expr"
	return
    }
    
    if {$defunits != ""} { set defval [unit_convert $defval $defunits 1] }
    
    #puts "eval $expr -> "
    regsub -all {\$([A-Za-z0-9_]+)} $expr {[eval $param_func \1 \"$defval\"]} expr
    #puts "expr is $expr -> "
    if [catch {expr $expr} r] {
	# This is to handle tensor values.  Kind of a hack.
	set rlist ""
	foreach expr1 $expr {
	    if [catch {expr $expr1} r1] {
		# This really is an error ...  Just redo the previous one.
		expr $expr
	    }
	    lappend rlist $r1
	}
	set r $rlist
    }
    #puts "    $r"
    incr expression_evaluate_depth -1
    
    if !$expression_evaluate_depth {
	# If this is the outermost level and there are "'s around the value then
	# strip them off
	if {[string index $r 0] == "\""} {
	    set r [string trim $r \"]
	}
    }
    
    return $r
}

proc expression_param {name defval} {
    global expression_params
    global expression_checking_shape expression_shapes_affected
    global expression_checking_object expression_objects_affected
    global expression_checking_other expression_other_affected
    global expression_param_choices expression_dont_ask_if_undef
    
    if [info exists expression_checking_shape] {
	lappend expression_shapes_affected($expression_checking_shape) $name
    }
    if [info exists expression_checking_object] {
	if [eval object_currently_uses_param $expression_checking_object] {
	    lappend expression_objects_affected($expression_checking_object) $name
	}
    }
    if [info exists expression_checking_other] {
	lappend expression_other_affected($expression_checking_other) $name
    }

    if ![info exists expression_params($name)] {
	if [info exists expression_dont_ask_if_undef] { return 0 }
	while 1 {
	    set v [question_box .paramvalue "Param value" \
		       "Initial value of $name ?" $defval]
	    if {$v == ""} {
		mess "Cancelled - using value of $defval for $name\n"
		set v $defval
	    }
	    if {[llength $v] != 1} {
		user_error "Please input one value for the initial value of the parameter."
		continue
	    }
	    break
	}
	if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
	    set v \"$v\"
	}
	set expression_params($name) $v
	set expression_param_choices($name) $v
    }
    set ret $expression_params($name)
    if {"\"[string trim $ret \"]\"" == $ret} {
	# This is so that expressions like {$a=="fan.1"} will work
	set ret [string trim $ret \"]
    }
	
    return [expression_evaluate $ret 0]
}

proc expression_rename_param {oldname newname objlist} {
    global expression_shapes_affected expression_objects_affected
    
    expression_find_affected
    foreach sh_p [array names expression_shapes_affected] {
	set sh [lindex $sh_p 0]
	set p [lindex $sh_p 1]
	set obj [$sh get -object]
	if ![lmember $objlist $obj] continue
	
	foreach v $expression_shapes_affected($sh_p) {
	    if {$v == $oldname} {
		set oexpr [$sh getexpr $p]
		# \M is the constrained escape sequence for end-of-word
		regsub \\\$$oldname\\M $oexpr \$$newname nexpr
		$sh setexpr $p $nexpr
	    }
	}
    }
    foreach obj_p [array names expression_objects_affected] {
	set obj [lindex $obj_p 0]
	set p [lindex $obj_p 1]
	if ![lmember $objlist $obj] continue
	foreach v $expression_objects_affected($obj_p) {
	    if {$v == $oldname} {
		set oexpr [$obj getval $p]
		# \M is the constrained escape sequence for end-of-word
		regsub \\\$$oldname\\M $oexpr \$$newname nexpr
		$obj setval $p $nexpr
	    }
	}
    }
}

proc expression_find_affected {} {
    global expression_params app_object_params_affect_shape
    global expression_checking_shape expression_shapes_affected
    global expression_checking_object expression_objects_affected
    global expression_checking_other expression_other_affected
    global problem_params_can_have_expr other_params_can_have_expr
    global app_object_params_change_disables_fast_trials
    
    catch {unset expression_shapes_affected}
    catch {unset expression_objects_affected}
    catch {unset expression_other_affected}
    foreach obj [db_list_objects_recursive "" "" alpha 1] {
	if {![string match *\$* [$obj getval active]] && ![$obj si_value active]} continue
	if {[$obj getval mat_lib_path ""] != ""} continue
	
	set type [$obj getval obtype]

	set aff_p [list active]
	set aff_sh ""
	if [info exists app_object_params_change_disables_fast_trials($type)] {
	    eval lappend aff_p $app_object_params_change_disables_fast_trials($type)
	}
	# Not quite right - we should make it record all the shapes
	# rather than the first ...  But good enough for fast trials.
	if {$aff_p != ""} {
	    set aff_sh [lindex [db_shapes $obj] 0]
	}
	
	set p_affect_s ""
	foreach p [$obj list] {
	    set expression_checking_object [list $obj $p]
	    
	    if {[lmember $aff_p "all"] || ([lsearch $aff_p $p] >= 0)} {
		set expression_checking_shape [list $aff_sh $p]
	    }
	    
	    $obj si_value $p 0
	    
	    catch {unset expression_checking_shape}
	}
	catch {unset expression_checking_object}
	if {$type == "material"} continue
	
	foreach sh [db_shapes $obj] {
	    foreach {p x} [$sh get_all_exprs] {
		if ![shape_currently_uses_param $sh $p] continue
		set expression_checking_shape [list $sh $p]
		if [catch {expression_evaluate $x}] {
		    user_error "Syntax error in expression \"$x\", object \"[$obj getval name]\", [$sh get -name], parameter $p"
		}
	    }
	    set expression_checking_shape [list $sh meshing]
	    catch {$sh mesher_input 0 0}
	}
	catch {unset expression_checking_shape}
    }
    if {[info command named_point_list_expressions] != ""} {
	foreach {name x} [named_point_list_expressions] {
	    set expression_checking_other [list point $name]
	    if [catch {expression_evaluate $x}] {
		user_error "Syntax error in expression \"$x\", point \"$name\""
	    }
	}
	catch {unset expression_checking_other}
    }
    set check ""
    if [info exists problem_params_can_have_expr] {
	foreach p $problem_params_can_have_expr {
	    lappend check problem_$p
	}
    }
    if [info exists other_params_can_have_expr] {
	foreach p $other_params_can_have_expr {
	    lappend check $p
	}
    }
    foreach x $check {
	global $x
	# Only some of these can have numerical values so first check for a $
	if ![info exists $x] continue
	if [array exists $x] {
	    foreach n [array names $x] {
		set expression_checking_other [list variable ${x}($n)]
		set val [set ${x}($n)]
		if [string match {*$*} $val] {
		    if [catch {expression_evaluate $val}] {
			user_error "Syntax error in expression \"$val\", variable \"${x}($n)\""
		    }
		}
		catch {unset expression_checking_other}
	    }
	} else {
	    set expression_checking_other [list variable $x]
	    set val [set $x]
	    if [string match {*$*} $val] {
		if [catch {expression_evaluate $val}] {
		    user_error "Syntax error in expression \"$val\", variable \"$x\""
		}
	    }
	    catch {unset expression_checking_other}
	}
    }
    
    foreach p [array names expression_shapes_affected] {
	foreach v $expression_shapes_affected($p) { set got($v) 1 }
    }
    foreach p [array names expression_objects_affected] {
	foreach v $expression_objects_affected($p) { set got($v) 1 }
    }
    foreach p [array names expression_other_affected] {
	foreach v $expression_other_affected($p) { set got($v) 1 }
    }
    return [array names got]
}

proc expression_no_shapes_in_trial {} {
    global expression_shapes_affected
    expression_find_affected
    return [expr ![info exists expression_shapes_affected]]
}

proc expression_update_from_range {v} {
    global expression_param_choices expression_param_range
    
    foreach n {act start end incr} p $expression_param_range($v) { set $n $p }
    if !$act return
    
    set vals ""
    if {$incr > 0} {
	for {set p $start} {$p <= $end} {set p [expr $p + $incr]} {
	    lappend vals $p
	}
    } else {
	for {set p $start} {$p >= $end} {set p [expr $p + $incr]} {
	    lappend vals $p
	}
    }
    set last [lindex $vals end]
    if {($last != "") && (abs($last - $end) < abs($incr * 0.5))} {
	set vals [lrange $vals 0 end-1]
    }
    lappend vals $end
    set expression_param_choices($v) $vals
}

proc expression_object_changes_in_trials {obj} {
    global expression_material_changes
    
    set vals [$obj list_values]
    # If the object has a $ in some parameter then it changes
    if [string match {*$*} $vals] { return 1 }
    # If it has a surface material that changes then it changes itself (not the
    # surface material)
    foreach v $vals {
	if {[string match *_material [lindex $v 0]] && \
		[info exists expression_material_changes([lindex $v 1])]} {
	    return 1
	}
    }
    return 0
}

proc expression_object_changes_in_trials_setup_materials {} {
    global expression_material_changes
    catch {unset expression_material_changes}
    foreach m [db_list_objects material] {
	if {[$m getval mat_type] != "surface"} continue
	if [string match {*$*} [$m list_values]] {
	    set expression_material_changes([$m getval name]) 1
	}
    }
}

proc expression_edit_setup {} {
    global expression_params expression_param_choices
    global expression_shapes_affected
    global expression_found expression_objects_affected
    global app_object_params_affect_shape
    global expression_edit_trial_how_solveid
    
    set params [expression_find_affected]
    
    catch {unset expression_found}
    
    foreach p $params {
	if [info exists expression_found($p)] {
	    incr expression_found($p)
	} else {
	    set expression_found($p) 1
	}
    }
    foreach p [array names expression_param_choices] {
	if [info exists expression_found($p)] {
	    incr expression_found($p)
	} else {
	    set expression_found($p) 0
	}
    }
    
    set names [lsort [array names expression_found]]
    foreach n $names {
	if ![info exists expression_param_choices($n)] {
	    if [info exists expression_params($n)] {
		set expression_param_choices($n) $expression_params($n)
	    } else {
		set expression_param_choices($n) 0
	    }
	}
    }
    return $names
}

proc expression_edit_appropriately {} {
    global app_name
    if {$app_name == "emax"} {
	expression_edit
    } else {
	expression_edit_values_and_trials
    }
}

proc expression_edit {} {
    global expression_params expression_param_choices
    global expression_no_trials_form
    
    set names [expression_edit_setup]
    
    if ![llength $names] {
	user_error "There are no parameters defined"
#	return
    }
    
    form_kill .edit_params
    set d [form_init .edit_params "Parameter values"]
    if {$d == ""} return
    
    set scr [form_scroller $d.scr 350 {0 0 -cols 2} 1]
    
    form_label $scr.namel Name {0 0}
    if ![info exists expression_no_trials_form] {
	form_label $scr.valuel Values {0 1}
    } else {
	form_label $scr.valuel Value {0 1}
    }
    #form_label $scr.usedbyl "Used by" {0 2}
    
    set i 1
    foreach n $names {
	form_label $scr.name$i $n "$i 0"
	form_entry $scr.value$i expression_param_choices($n) string "$i 1 -sticky ew"
	#form_button $scr.usedby$i "Show" "expression_print_usedby $n" "$i 2"
	incr i
    }
    form_label $d.newnl "New name" {1 0}
    form_label $d.newvl "New value" {1 1}
    form_entry $d.newn expression_new_name string {2 0}
    form_entry $d.newv expression_new_value string {2 1}
    
    form_label $d.expl "Values can be a list of numbers\nor expressions, separated by\ncommas or spaces." \
	{3 0 -cols 2}
    if ![info exists expression_no_trials_form] {
	form_button $d.trials "Define trials" expression_trials {4 0 -cols 2}
    }
    
    bind $d.newn <Key-Return> "expression_new $scr"
    bind $d.newv <Key-Return> "expression_new $scr"
    form_finish bot
    form_scroller_hack $d.scr
    
    if ![form_wait .edit_params] return
    form_kill .edit_params
    
    expression_edit_finish
}

proc expression_edit_finish {{only ""}} {
    global expression_params expression_param_choices
    global expression_shapes_affected expression_mode
    global expression_found expression_objects_affected
    global app_object_params_affect_shape opt_option
    
    if {$only == ""} {
	set all_vars [array names expression_params]
    } else {
	set all_vars [list $only]
    }
    
    switch $expression_mode {
	all - col {
	    foreach p $all_vars {
		set v $expression_params($p)
		if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
		    set v \"$v\"
		}
		set expression_params($p) $v
	    }
	    foreach p $all_vars {
		if ![info exists expression_param_choices($p)] continue
		set vs ""
		foreach v [split $expression_param_choices($p) " \t,"] {
		    if {[string index $v 0] == "\{"} { set v [lindex $v 0] } ;# ?????
		    if {$v != ""} {
			if {$vs != ""} { append vs " " }
			append vs $v
		    }
		}
		if {$vs == ""} {
		    if {[info exists expression_found($p)] && $expression_found($p)} {
			mess "Param $p not deleted - it is in use\n" red
		    } else {
			mess "Param $p has no values: deleted\n" red
			unset expression_param_choices($p)
			catch {unset expression_params($p)}
		    }
		    continue
		}
		set expression_param_choices($p) $vs
		
		# Can't use lsearch here because the list $expression_params($p)
		# is actually like   "b1" "b2"  and the tcl list ops strip off
		# the ""
		set choicelist ""
		foreach v $vs {
		    if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
			set v \"$v\"
		    }
		    lappend choicelist $v
		}
		
		if [info exists expression_params($p)] {
		    if {$expression_params($p) == ""} {
			set expression_params($p) [lindex $choicelist 0]
			if {$expression_params($p) == ""} {
			    set expression_params($p) 0  ;# just so it's something
			}
		    }
		    if {![info exists opt_option(type)] || ($opt_option(type) != "optim")} {
			if {[lsearch $choicelist $expression_params($p)] < 0} {
			    if {[llength $choicelist] > 0} {
				set mess "$expression_params($p) is not in the list of discrete values for $p.  Add it?"
				if {[tk_dialog .add_value "Add value" $mess "" 0 Yes No] == 1} {
				    set expression_params($p) [lindex $choicelist 0]
				    mess "Changing base value for $p back to $expression_params($p)\n" red
				} else {
				    mess "Adding value $expression_params($p) to the options for $p\n" red
				    append expression_param_choices($p) " $expression_params($p)"
				}
			    } else {
				# only keep current value if there is no 
				# other option in the choice list
				mess "Adding value $expression_params($p) to the options for $p\n" red
				append expression_param_choices($p) " $expression_params($p)"
			    }
			}
		    }
		} else {
		    set expression_params($p) [lindex $choicelist 0]
		}
		set v $expression_params($p)
		if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
		    set v \"$v\"
		}
		set expression_params($p) $v
	    }
	}
	random {
	    # For this mode we don't use expression_param_choices at all.
	}
    }
    
    set what_dirty ""
    foreach shp [array names expression_shapes_affected] {
	if {($only != "") && ![lmember $expression_shapes_affected($shp) $only]} continue
	set sh [lindex $shp 0]
	puts "sh $sh"
	if [$sh update_expression_values] {
	    set what_dirty mesh
	    update_decorations $sh
	    set obtype [[$sh get -object] getval obtype]
	    if {[info commands app_graphparams_$obtype] != ""} {
		shape_configure $sh
	    }
	}
	
    }
    foreach objp [array names expression_objects_affected] {
	if {($only != "") && ![lmember $expression_objects_affected($objp) $only]} continue
	set obj [lindex $objp 0]
	app_clean_up_[$obj getval obtype] $obj
	edit_update_active $obj
	set what_dirty model
    }
    if {$what_dirty != ""} { db_dirty $what_dirty }
    
    if {[info command named_point_update_all] != ""} {
	named_point_update_all
    }
}

proc expression_new {scr} {
    global expression_param_choices expression_new_name expression_new_value
    
    set nn [string trim $expression_new_name]
    if {$nn == ""} return
    if [info exists expression_param_choices($nn)] {
	set expression_param_choices($nn) $expression_new_value
    } else {
	set nv [expr [llength [array names expression_param_choices]] + 1]
	set expression_param_choices($nn) $expression_new_value
	form_label $scr.name$nv $nn "$nv 0"
	form_entry $scr.value$nv expression_param_choices($nn) string "$nv 1"
    }
    set expression_new_name ""
    set expression_new_value ""
}

# This returns the ones used in shapes or problem parameters first.

proc expression_currently_used {} {
    global expression_shapes_affected expression_objects_affected
    global expression_other_affected
    global expression_changes_geometry_or_problem
    
    expression_find_affected
    catch {unset expression_changes_geometry_or_problem}
    
    foreach i [array names expression_other_affected] {
	foreach p $expression_other_affected($i) {
	    set got_p($p) 1
	    lappend expression_changes_geometry_or_problem($p) $i
	}
    }
    foreach i [array names expression_shapes_affected] {
	foreach p $expression_shapes_affected($i) {
	    if ![info exists got_p($p)] {
		set got_s($p) 1
		lappend expression_changes_geometry_or_problem($p) $i
	    }
	}
    }
    foreach i [array names expression_objects_affected] {
	foreach p $expression_objects_affected($i) {
	    if {![info exists got_s($p)] && ![info exists got_p($p)]} {
		set got_o($p) 1
	    }
	}
    }
    set got [concat [lsort [array names got_p]] [lsort [array names got_s]] [lsort [array names got_o]]]
		     
    # Make sure that these guys actually have values.
    foreach g $got {
	expression_param $g 0.0
    }
    
    return $got
}

# If all is 1 then all trials will be returned
# If it's 0 then only active ones 
# If 2 then a list of lists of active trials will be returned,
# grouped into ones where only the non-geometric parameters change
# This returns lists of name=value strings.

proc expression_list_trials {{all 1}} {
    global expression_param_choices expression_mode expression_param_random
    global expression_trial_order expression_param_trials expression_param_by_cols
    
    set ecu [expression_currently_used]
    switch $expression_mode {
	all {
	    set by_cols ""
	    set tr [list ""]
	    foreach p $ecu {
		if {[info exists expression_param_by_cols($p)] && $expression_param_by_cols($p)} {
		    lappend by_cols $p
		    continue
		}
		if {[info exists expression_param_choices($p)] && \
			([llength $expression_param_choices($p)] > 1)} {
		    set new_trials ""
		    foreach ct $tr {
			foreach cc $expression_param_choices($p) {
			    if {$ct == ""} {
				lappend new_trials "$p=$cc"
			    } else {
				lappend new_trials "$ct;  $p=$cc"
			    }
			}
		    }
		    set tr $new_trials
		}
	    }
	    set new_trials ""
	    if {$tr == [list ""]} {
		# this happens if there is only one definition set
		# for each of the parameters
		set ct ""
		foreach p $ecu {
		    if {$new_trials == ""} {
			set new_trials "$p=$expression_param_choices($p)"
		    } else {
			set new_trials "$new_trials;  $p=$expression_param_choices($p)"
		    }		
		}
		set tr [list "$new_trials"]
	    }
	    
	    # Now add the by_cols variables
	    if {$by_cols != ""} {
		set i 0
		set new_tr ""
		foreach t $tr {
		    foreach p $by_cols {
			set ch [lindex $expression_param_choices($p) $i]
			if {$ch == ""} { set ch [lindex $expression_param_choices($p) end] }
			append t ";  $p=$ch"
		    }
		    lappend new_tr $t
		    incr i
		}
		set tr $new_tr
	    }
	}
	col {
	    set tr ""
	    for {set i 0} 1 {incr i} {
		set ch ""
		set nnew 0
		foreach p [array names expression_param_choices] {
		    set choices $expression_param_choices($p)
		    set len [llength $choices]
		    set semi ""; if {$ch != ""} { set semi ";  " }
		    if {$i < $len} {
			append ch "$semi$p=[lindex $choices $i]"
			incr nnew
		    } else {
			append ch "$semi$p=[lindex $choices [expr $len - 1]]"
		    }
		}
		if !$nnew break
		lappend tr $ch
	    }
	}
	random {
	    set tr3 ""
	    foreach rp [expression_param_random_order $all] {
		set ch ""
		set stuff $expression_param_random($rp)
		regsub -all -- ";" $stuff " " stuff
		regsub -all -- "=" $stuff " " stuff
		foreach {p v} $stuff {
		    set semi ""; if {$ch != ""} { set semi ";  " }
		    append ch "$semi$p=$v"
		}
		lappend tr3 $ch
	    }
	}
    }
    if {$expression_mode != "random"} {
	set maxn [expr {[llength $tr] + 2}]
	set tr2 ""
	foreach t $tr {
	    if {[info exists expression_trial_order($t)] && ($expression_trial_order($t) != "")} {
		lappend tr2 [list $expression_trial_order($t) $t]
	    } else {
		lappend tr2 [list $maxn $t]
		incr maxn
	    }
	}
	set tr3 ""
	foreach t [lsort -index 0 -integer $tr2] {
	    set tr [lindex $t 1]
	    if {($all != 1) && [info exists expression_param_trials($tr)] && !$expression_param_trials($tr)} continue
	    lappend tr3 $tr
	}
    }
    if {$all != 2} { return $tr3 }
    set tr4 ""
    set tr5 ""
    foreach tr $tr3 {
	if {$tr5 == ""} {
	    lappend tr5 $tr
	} elseif {[expression_trial_no_geo_change $tr [lindex $tr5 0]]} {
	    lappend tr5 $tr
	} else {
	    lappend tr4 $tr5
	    set tr5 [list $tr]
	}
    }
    if {$tr5 != ""} { lappend tr4 $tr5 }
    return $tr4
}

proc expression_param_random_order {all} {
    global expression_param_random expression_trial_order expression_param_trials
    set trs ""
    set ct 0
    foreach tr [array names expression_param_random] {
	if {!$all && !$expression_param_trials($tr)} continue
	if {![info exists expression_trial_order] || ($expression_trial_order($tr) == "")} {
	    set expression_trial_order($tr) $ct
	}
	incr ct
	lappend trs [list $expression_trial_order($tr) $tr]
    }
    set ret ""
    foreach tr [lsort -index 0 -integer $trs] {
	lappend ret [lindex $tr 1]
    }
    return $ret
}

proc expression_trial_no_geo_change {tr1 tr2} {
    global expression_changes_geometry_or_problem
    
    set changes ""
    foreach v $tr1 {
	set vv [split [string trim $v " ;"] =]
	set vals([lindex $vv 0]) [lindex $vv 1]
    }
    foreach v $tr2 {
	set vv [split [string trim $v " ;"] =]
	set p [lindex $vv 0]
	if ![info exists vals($p)] continue
	if {$vals($p) != [lindex $vv 1]} {
	    lappend changes $p
	}
    }
    foreach p $changes {
	if [info exists expression_changes_geometry_or_problem($p)] { return 0 }
    }
    return 1
}

proc expression_trials {} {
    global expression_param_choices expression_param_trials
    global expression_cur_trials expression_mode expression_allow_random
    global expression_param_by_cols
    
    switch $expression_mode {
	all {
	    set tot 1
	    foreach p [array names expression_param_choices] {
		if {[info exists expression_param_by_cols($p)] && $expression_param_by_cols($p)} {
		    continue
		}
		set np [llength $expression_param_choices($p)]
		if {$np > 1} { set tot [expr $tot * $np] }
	    }
	}
	col {
	    set tot 1
	    foreach p [array names expression_param_choices] {
		set np [llength $expression_param_choices($p)]
		if {$np > $tot} { set tot $np }
	    }
	}
	random {
	}
    }
    mess "Current number of possible trials: $tot\n"
    if {$tot < 2} {
	set mess "To run multiple trials you need to define\
		\nmultiple values for some parameters."
	tk_dialog .too_many_trials Trials $mess "" -1 OK
	return
    }
    
    if {($expression_mode == "all") && ($tot > 1000)} {
	set mess "Too many possible trials: maximum is 1000.\
		\nPlease reduce the number of parameter choices\nor\
		use By columns mode.  For now, switched to By columns."
	tk_dialog .too_many_trials Trials $mess "" -1 OK
	set expression_mode col
    }
    set expression_cur_trials [expression_list_trials]
    array set oldvals [array get expression_param_trials]
    catch {unset expression_param_trials}
    set opts ""
    foreach ct $expression_cur_trials {
	if [info exists oldvals($ct)] {
	    set expression_param_trials($ct) $oldvals($ct)
	} else {
	    set expression_param_trials($ct) 1
	}
	lappend opts [list $ct]
    }
    set but1 {
	form_button .select_trials.main.chk "Check trials" \
	    expression_check_trials {2 0}
	form_radiobutton .select_trials.main.allp "All combinations" \
	    expression_mode all {3 0}
	.select_trials.main.allp configure -command expression_trials
	form_radiobutton .select_trials.main.colsp "By column" \
	    expression_mode col {4 0}
	.select_trials.main.colsp configure -command expression_trials
	if [info exists expression_allow_random] {
	    form_radiobutton .select_trials.main.randp "Random" \
		expression_mode random {5 0}
	    .select_trials.main.randp configure -command expression_trials
	}
    }
    set but2 {
	bind .select_trials.main.check.list <Double-1> "expression_set_list .select_trials.main.check.list %y"
    }
    set mess "Select the trials you want to run.  Double-click to set the current values of the parameters."
    if ![check_box_form .select_trials $mess \
	     $opts 20 expression_param_trials 1 "" "" $but1 $but2 \
	    "Trial selection"] return
    
    set ns 0
    foreach pt [array names expression_param_trials] {
	if $expression_param_trials($pt) { incr ns }
    }
    mess "Current number of selected trials: $ns\n"
}

proc expression_set_list {w y} {
    global expression_param_trials
    set choice [$w get [$w nearest $y]]
    if {$choice != ""} {
	set expression_param_trials($choice) \
	    [expr !$expression_param_trials($choice)]
	expression_set_trial $choice
    }
}

proc expression_check_trials {} {
    global expression_next_trial
    
    if [info exists expression_next_trial] {
	set expression_next_trial 1
	return
    }
    mess "Hit the \"Next trial\" button to see each successive trial\n"
    set cur_t [expression_cur_trial]
    set but .select_trials.main.chk
    set bg [$but cget -bg]; set abg [$but cget -activebackground]
    $but configure -text "Next trial" -background \#FF8000 \
	-activebackground \#FFA080
    set expression_next_trial 1
    expression_foreach_trial {
	vwait expression_next_trial
	list 1
    }
    $but configure -text "Check trials" -bg $bg -activebackground $abg
    unset expression_next_trial
    expression_set_trial $cur_t
}

proc expression_set_trial {vals {fast 0} {noisy 1}} {
    global expression_params expression_current_trial expression_param_random
    global always_clean_up_when_switching_trials
    
    #regsub -all " " $vals "" expression_current_trial
    #regsub -all ";" $expression_current_trial "," expression_current_trial
    set expression_current_trial $vals
    
    set name ""
    if [info exists expression_param_random($vals)] {
	set name " $vals: "
	set vals $expression_param_random($vals)
	foreach {n v} $vals {
	    if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
		set v \"$v\"
	    }
	    set expression_params($n) $v
	}
    } else {
	foreach v $vals {
	    set vv [split [string trim $v " ;"] =]
	    set n [lindex $vv 0]; set v [lindex $vv 1]
	    if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
		set v \"$v\"
	    }
	    set expression_params($n) $v
	}
    }
    if !$fast {
	if $noisy {
	    mess "Set params:$name$vals\n"
	}
	expression_update_shapes
	edit_clean_up_all
    } else {
	foreach t [array names always_clean_up_when_switching_trials] {
	    foreach obj [db_list_objects_recursive $t] {
		app_clean_up_$t $obj
	    }
	}
    }
    catch { app_after_expression_set_trial }
}

proc expression_delete_trial {tr i} {
    global expression_param_random expression_edit_trial_subform
    
    foreach sn {num name params id ord sel set del} {
	catch {destroy $expression_edit_trial_subform.$sn$i}
    }
    foreach var {expression_param_trials expression_trial_name \
		     expression_trial_restartid expression_trial_order \
		     expression_param_random} {
	global $var
	catch {unset ${var}($tr)}
    }
}

proc expression_update_shapes {} {
    global app_has_graphparams

    set dirty 0
    foreach obj [db_list_objects_recursive "" "" alpha 1] {
	if {[string match *\$* [$obj si_expr active]]} {
	    edit_update_active $obj
	}
	if ![$obj si_value active] continue
	set obtype [$obj getval obtype]
	foreach sh [db_shapes $obj] {
	    if [$sh update_expression_values] {
		update_decorations $sh
		if [info exists app_has_graphparams($obtype)] {
		    shape_configure $sh
		}
		set dirty 1
	    }
	}
    }
    if $dirty { db_dirty mesh }
}

proc expression_cur_trial {} {
    global expression_params
    set vals ""
    foreach p [array names expression_params] {
	lappend vals "$p=$expression_params($p)"
    }
    return $vals
}

proc expression_foreach_trial {cmd {before_start_cmd ""}} {
    global expression_params expression_param_trials expression_cur_trials
    global post_no_invalidate_warn solution_terminated
    
    mess "\nRunning [expression_count_trials] trials...\n\n"
    set n 1
    set post_no_invalidate_warn 1
    foreach t [expression_list_trials] {
	if {![info exists expression_param_trials($t)] || \
		$expression_param_trials($t)} {
	    mess "Trial $n ------------------------------------------\n"
	    
	    if {$before_start_cmd != ""} { eval $before_start_cmd }
	    
	    expression_set_trial $t
	    set r [uplevel 1 $cmd]
	    if !$r {
		if ![info exists solution_terminated] {
		    mess "Error at trial $n\n\n" red
		}
		mess "Finished trials.\n"
		catch {unset post_no_invalidate_warn}
		return 0
	    }
	    mess "\n"
	    incr n
	}
    }
    mess "Finished trials.\n"
    catch {unset post_no_invalidate_warn}
    return 1
}

proc expression_count_trials {} {
    global expression_param_trials expression_cur_trials
    
    set n 0
    foreach t [expression_list_trials] {
	if {![info exists expression_param_trials($t)] || \
		$expression_param_trials($t)} { incr n }
    }
    return $n
}

proc expression_summarize {trialpref usetrialpref} {
    global expression_shapes_affected expression_objects_affected
    global expression_param_trials expression_param_choices
    
    set trials [expression_list_trials]
    
    expression_find_affected
    
    foreach shp [array names expression_shapes_affected] {
	set sh [lindex $shp 0]
	foreach v $expression_shapes_affected($shp) {
	    set obj [$sh get -object]
	    if {[$sh get -name] != "body"} {
		lappend on_var($v) "[$obj getval name] ([$sh get -name])"
	    } else {
		lappend on_var($v) "[$obj getval name]"
	    }
	}
    }
    foreach objp [array names expression_objects_affected] {
	set obj [lindex $objp 0]
	foreach v $expression_objects_affected($objp) {
	    lappend on_var($v) "[$obj getval name]"
	}
    }
    
    set nt [expression_count_trials]
    mess "\nTrial information ----------------------------------\n"
    mess "Number of trials = $nt\n"
    if !$nt return
    
    mess "Variables used by objects\n"
    foreach v [lsort [array names on_var]] {
	catch {unset ss}
	foreach s $on_var($v) { set ss($s) 1 }
	mess "    $v:"
	foreach tt [lsort [array names ss]] {
	    mess " $tt"
	}
	mess "\n"
    }
    mess "Variable values\n"
    foreach p [lsort [array names expression_param_choices]] {
	if [info exists on_var($p)] {
	    set unused ""
	} else {
	    set unused " (not currently used)"
	}
	mess "    $p: $expression_param_choices($p)$unused\n"
    }
    mess "Current defined trials\n"
    set i 1
    foreach t $trials {
	if $usetrialpref {
	    set trialname [format ${trialpref}%03d $i]
	} else {
	    set trialname ""
	    foreach ch [split $t ""] {
		switch -- $ch {
		    \t - * - $ - ! - - - + - / - ^ - ( - ) - = - , - ; - . - - {
			append trialname _
		    }
		    " " { }
		    default {
			append trialname $ch
		    }
		}
	    }
	}

	if {![info exists expression_param_trials($t)] || \
		$expression_param_trials($t)} {
	    mess "    $trialname : $t\n"
	    incr i
	}
    }
}

proc expression_print_usedby {n} {
    global expression_shapes_affected expression_objects_affected
    global expression_param_trials expression_param_choices
    
    expression_find_affected
    
    foreach sh [array names expression_shapes_affected] {
	foreach v $expression_shapes_affected($sh) {
	    lappend on_var($v) [lindex $sh 0]
	}
    }
    foreach obj [array names expression_objects_affected] {
	foreach v $expression_objects_affected($obj) {
	    set sh [[lindex $obj 0] getval body_shape]
	    if {$sh != ""} { lappend on_var($v) $sh }
	}
    }
    
    mess "Object parameters depending on $n:\n"
    foreach v [lsort [array names on_var]] {
	catch {unset ss}
	foreach s $on_var($v) { set ss($s) 1 }
	mess "    $v:"
	foreach sh [lsort [array names ss]] {
	    set obj [$sh get -object]
	    if {[$sh get -name] != "body"} {
		mess " [$obj getval name] ([$sh get -name])"
	    } else {
		mess " [$obj getval name]"
	    }
	}
	mess "\n"
    }
}

proc expression_set_some {vals} {
    global expression_params batch_mode
    
    foreach {n v} $vals {
	if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
	    set v \"$v\"
	}
	set expression_params($n) $v
    }
    
    foreach obj [db_list_objects_recursive] {
	foreach sh [db_shapes $obj] {
	    if [$sh update_expression_values] {
		if !$batch_mode {
		    update_decorations $sh
		    set obtype [[$sh get -object] getval obtype]
		    if {[info commands app_graphparams_$obtype] != ""} {
			shape_configure $sh
		    }
		}
	    }
	}
    }
}

proc expression_set_some_file {file} {
    set fp [open $file r]
    
    set vals ""
    while {![eof $fp]} {
	set line [gets $fp]
	if {[llength $line] != 2} continue
	lappend vals [lindex $line 0] [lindex $line 1]
	mess "[lindex $line 0] = [lindex $line 1]\n"
    }
    close $fp
    
    expression_set_some $vals
}

# ----------------------------

proc expression_edit_values_and_trials {{which ""}} {
    global expression_no_trials_form expression_allow_random
    
    catch {post_invalidate mesh}
    
    set names [expression_edit_setup]
    
    if ![llength $names] {
	mess "There are no parameters defined yet.\n"
	#return
    }
    
    if [info exists expression_no_trials_form] {
	set pdata {
	    { params Parameters }
	}
	set pname Parameters
	set buts {done {"New param" expression_edit_values_new} \
		      {"Clean up" expression_edit_values_cleanup} cancel help}
    } else {
	set pdata {
	    { params Parameters }
	    { trials {Select trials} }
	}
	set pname "Parameters and trials"
	if [info exists expression_allow_random] {
	    set buts {{"New param" expression_edit_values_new} \
			  {"New trial" expression_edit_trials_new {{expression_mode random}}} \
			  {"Update trials" "expression_edit_trials_reset 0"} \
			  {"Reset trials" expression_edit_do_reset} nl \
			  done \
			  {"Clean up" expression_edit_values_cleanup} \
			  cancel \
			  help}
	} else {
	    set buts {{"New param" expression_edit_values_new} \
			  {"Update trials" "expression_edit_trials_reset 0"} \
			  {"Reset trials" expression_edit_do_reset} nl \
			  done \
			  {"Clean up" expression_edit_values_cleanup} \
			  cancel \
			  help}
	}
    }
    
    form_kill .expression_values_trials
    set d [form_init .expression_values_trials $pname "" $buts "" 1 1 1]
    if {$d == ""} return
    
    set pages [form_notebook $d.nb $pdata {0 0 -sticky news}]
    grid rowconfigure $d 0 -weight 1000
    grid columnconfigure $d 0 -weight 1000
    
    set pa [form_scrollwin [lindex $pages 0].scr 500 400 {0 0 -sticky news}]
    grid rowconfigure [lindex $pages 0] 0 -weight 1000
    grid columnconfigure [lindex $pages 0] 0 -weight 1000
    expression_edit_values_form $pa $names
    if ![info exists expression_no_trials_form] {
	set tr [form_scrollwin [lindex $pages 1].scr 650 400 {0 0 -sticky news}]
	grid rowconfigure [lindex $pages 1] 0 -weight 1000
	grid columnconfigure [lindex $pages 1] 0 -weight 1000
	expression_edit_trials_form $tr $names
    }
    
    form_notebook_finish $d.nb
    if {$which != ""} {
	$d.nb raise $which
    }
    form_finish +300+300
    
    if [form_wait .expression_values_trials] {
	form_kill .expression_values_trials
	expression_edit_finish
    }
}

proc expression_edit_values_form {pa names} {
    global expression_params expression_param_choices
    global expression_shapes_affected expression_objects_affected
    global expression_other_affected
    global expression_edit_param_form expression_no_trials_form
    
    foreach shp [array names expression_shapes_affected] {
	set sh [lindex $shp 0]
	set obj [$sh get -object]
	if {[$sh get -name] == "body"} {
	    set bname ""
	} else {
	    set bname " [$sh get -name]"
	}
	foreach p $expression_shapes_affected($shp) {
	    lappend usedby($p) "[$obj getval name]$bname [lindex $shp 1]"
	}
    }
    foreach objp [array names expression_objects_affected] {
	set obj [lindex $objp 0]
	foreach p $expression_objects_affected($objp) {
	    lappend usedby($p) "[$obj getval name] [lindex $objp 1]"
	}
    }
    foreach oth [array names expression_other_affected] {
	foreach p $expression_other_affected($oth) {
	    lappend usedby($p) $oth
	}
    }
    
    form_label $pa.namel Name {0 0}
    form_label $pa.objectsl "Objects / properties" {0 1}
    form_label $pa.valuel Values {0 2}
    grid columnconfigure $pa 2 -weight 1000
    if ![info exists expression_no_trials_form] {
	form_label $pa.currentl Current {0 3}
    }
    
    set i 1
    foreach n $names {
	set objs ""
	if ![info exists usedby($n)] {
	    set objs " - not used - "
	} else {
	    foreach used $usedby($n) {
		if {$objs != ""} { append objs \n }
		append objs $used
	    }
	}
	expression_edit_values_add $pa $i $n $objs
	incr i
    }
    set expression_edit_param_form $pa
}

proc expression_edit_values_new {} {
    global expression_edit_param_form
    set name [question "New parameter name?"]
    if {$name == ""} return
    for {set i 1} {[winfo exists $expression_edit_param_form.name$i]} \
	{incr i} {}
    expression_edit_values_add $expression_edit_param_form $i $name \
	" - not used - "
}

proc expression_edit_values_cleanup {} {
    global expression_edit_param_form

    set old ""
    for {set i 1} {[winfo exists $expression_edit_param_form.name$i]} \
	{incr i} {
	    if {[winfo class $expression_edit_param_form.objects$i] != "Label"} continue
	    set text [$expression_edit_param_form.objects$i cget -text]
	    if {$text == " - not used - "} {
		lappend old [$expression_edit_param_form.name$i cget -text]
	    }
	}
    if {$old == ""} return
    
    form_kill .expression_values_trials
    expression_edit_finish
    foreach n $old {
	mess "Unused parameter \"$n\" deleted\n"
	foreach v {expression_param_choices expression_params} {
	    global $v
	    catch {unset ${v}($n)}
	}
    }
    expression_edit_values_and_trials
}

proc expression_edit_values_add {pa i n objs} {
    global expression_no_trials_form
    
    form_label $pa.name$i $n "$i 0 -sticky news -padx 0"
    $pa.name$i configure -relief sunken -borderwidth 2 -anchor nw -bg white
    if {[llength [split $objs \n]] > 10} {
	form_text $pa.objects$i "" "" "$i 1 -sticky news -padx 0" "" 30x10
	$pa.objects$i.text insert end $objs
	$pa.objects$i.text configure -state disabled
    } else {
	form_label $pa.objects$i $objs "$i 1 -sticky news -padx 0"
	$pa.objects$i configure -justify left -relief sunken -borderwidth 2 \
	    -bg white
    }
    form_entry $pa.value$i expression_param_choices($n) string \
	"$i 2 -sticky ewn -padx 0" "" 20
    bind $pa.value$i <Return> expression_update_shapes
    if ![info exists expression_no_trials_form] {
	form_entry $pa.current$i expression_params($n) string \
	    "$i 3 -sticky ewn -padx 0"
	bind $pa.current$i <Return> expression_update_shapes
    }
}

proc expression_edit_trials_form {d names} {
    global expression_edit_trial_subform expression_allow_random
    
    if [info exists expression_allow_random] {
	set ttypes "{all {All combinations}} {col {By column}} {random {Random}}"
    } else {
	set ttypes "{all {All combinations}} {col {By column}}"
    }
	
    set expression_edit_trial_subform $d.bot
    
    form_frame $d.top flat 0 {0 0}
    form_label $d.top.l "Type of trials:" {0 0}
    futil_menuentry_labels $d.top.t expression_mode \
	"list $ttypes" {0 1} "" "expression_edit_trials_reset 0" 16
    
    form_frame $d.bot flat 0 {1 0}
    expression_edit_trials_reset 0
}

proc expression_edit_trials_new {} {
    global expression_trial_name expression_trial_prefix expression_mode
    global expression_edit_trials_new_message expression_param_trials
    
    if {$expression_mode != "random"} {
	mess "This operation is only used for \"Random\" trials.\n" red
	return
    }
    if ![info exists expression_edit_trials_new_message] {
	set expression_edit_trials_new_message 1
	mess "In the \"Parameter values\" field please enter a list of alternating parameter names and values separated by spaces\n"
    }
    
    for {set i 1} {[info exists expression_trial_name([format $expression_trial_prefix%03d $i])]} {incr i} {}
    set tr [format $expression_trial_prefix%03d $i]
    set expression_trial_name($tr) $tr
    set expression_param_trials($tr) 1
    expression_edit_put_trial $tr $tr
}

proc expression_set_trial_names {force forcerestart} {
    global expression_trial_name expression_trial_restartid
    global expression_param_trials expression_trial_order
    global expression_edit_trial_how_solveid
    global expression_trial_prefix solve_jobtype solve_restid
    
    set trials [expression_list_trials]
    
    if {$solve_jobtype == "restart"} {
	set prev $solve_restid
    } else {
	set prev ""
    }
    
    foreach n $trials {
	if [info exists expression_trial_name($n)] {
	    set got($expression_trial_name($n)) $n
	}
    }
    
    set tnum 1
    foreach tr $trials {
	if {(![info exists expression_trial_name($tr)]) ||
	    ($expression_trial_name($tr) == "") || $force} {
	    switch $expression_edit_trial_how_solveid {
		nums {
		    set i 1
		    while 1 {
			set poss [format $expression_trial_prefix%03d $i]
			if {[info exists got($poss)] && ($got($poss) != $tr)} {
			    incr i
			    continue
			}
			set expression_trial_name($tr) $poss
			set got($poss) $tr
			break
		    }
		}
		vals {
		    set id tr_
		    foreach ch [split $tr ""] {
			switch -- $ch {
			    * - $ - ! - - - + - / - ^ - ( - ) - = -
			    , - ; - . - - {
				append id _
			    }
			    " " - \t { }
			    default {
				append id $ch
			    }
			}
		    }
		    set expression_trial_name($tr) $id
		}
	    }
	    
	    if {$force || ![info exists expression_param_trials($tr)]} {
		set expression_param_trials($tr) 1
	    }
	}
	
	# Always reset the restart id's (even if they are blank)
	#if {$forcerestart && (![info exists expression_trial_restartid($tr)] || ($expression_trial_restartid($tr) != ""))}
	if $forcerestart {
	    set expression_trial_restartid($tr) $prev
	} elseif ![info exists expression_trial_restartid($tr)] {
	    set expression_trial_restartid($tr) ""
	}
	
	if {[info exists expression_param_trials($tr)] && 
	    $expression_param_trials($tr) && 
	    [info exists expression_trial_name($tr)]} {
	    set prev $expression_trial_name($tr)
	}
	if {$force || ![info exists expression_trial_order($tr)]} {
	    set expression_trial_order($tr) $tnum
	}
	incr tnum
    }

    check_for_duplicated_trial_names
}
 
proc check_for_duplicated_trial_names {} {
    global expression_trial_name    

    set this_tr_num 0
    set duplicate_param_values 0
    set toolong 0
    set trials [expression_list_trials]
    foreach tr $trials {	
	set other_tr_num 0
	foreach other_tr $trials {	
	    if {$expression_trial_name($tr) == $expression_trial_name($other_tr) && $other_tr_num != $this_tr_num} {
		set duplicate_param_values 1
	    }
	    incr other_tr_num
	}
	incr this_tr_num
	
	if {[string length $expression_trial_name($tr)] > 120} {
	    incr toolong
	}
    }
    if $toolong {
	user_error "Some of the trial names are too long.  The maximum length for a solution id is 120 characters.  Please do \"Reset trials\" and use numbered trials rather than the value-based names before you run a solution, or correct the names by hand."
    }
    
    if {$duplicate_param_values} {
	set mess "The current settings create trials with identical parameter values or names.  Please check this is intended"
	tk_dialog .warning_type Warning $mess "" 0 Ok
    }
}

proc expression_edit_trials_reset {force} {
    global expression_trial_name expression_trial_restartid
    global expression_param_trials expression_trial_order
    global expression_edit_trial_subform
    global expression_edit_trial_how_solveid expression_param_trials
    global want_trial_restart_ids expression_edit_select_all
    global expression_mode expression_param_random

    if {![info exists expression_edit_trial_subform] ||
	![winfo exists $expression_edit_trial_subform]} return
    
    expression_edit_finish
    
    set pa $expression_edit_trial_subform
    foreach ch [winfo children $pa] { destroy $ch }
    
    form_label $pa.numl "Trial number" {0 0}
    form_label $pa.namel "Name of trial" {0 1}
    form_label $pa.paramsl "Parameter values" {0 2}
    if [info exists want_trial_restart_ids] {
	form_label $pa.idl "Restart trial ID" {0 3}
    }
    form_label $pa.ordl "Order" {0 4}
    form_checkbutton $pa.sell "Select" expression_edit_select_all {0 5 -cols 2}
    $pa.sell configure -command expression_edit_select_all
    
    if $force { catch {unset expression_trial_order} }
    
    set trials [expression_list_trials]
    set trialnames ""
    if {$expression_mode == "random"} {
	set trialnames [lsort [array names expression_param_random]]
    }
    expression_set_trial_names $force $force
    
    set ns 0; set nu 0
    foreach tr $trials trn $trialnames {
	if {$trn == ""} { set trn $tr }
	expression_edit_put_trial $tr $trn
	if $expression_param_trials($trn) { incr ns } else { incr nu }
	set got($trn) 1
    }
    set expression_edit_select_all [expr $ns > $nu]
    
    # In case we switched from some other mode to random ...
    foreach var {expression_param_trials expression_trial_name \
		     expression_trial_restartid expression_trial_order} {
	foreach pp [array names $var] {
	    if ![info exists got($pp)] { unset ${var}($pp) }
	}
    }
    
    edit_clean_up_all
    set proc [info command app_after_expression_set_trial]
    if {$proc != ""} { $proc }
}

proc expression_edit_select_all {} {
    global expression_edit_select_all expression_param_trials
    foreach tr [array names expression_param_trials] {
	set expression_param_trials($tr) $expression_edit_select_all
    }
}

proc expression_edit_put_trial {tr name} {
    global expression_edit_trial_subform expression_mode want_trial_restart_ids
    global expression_edit_select_all expression_trial_name
        
    if {$name == ""} { set name $tr }
    
    set pa $expression_edit_trial_subform
    for {set i 1} {[winfo exists $pa.num$i]} {incr i} {}
    
    form_label $pa.num$i $i "$i 0 -sticky news -padx 0"
    $pa.num$i configure -relief groove -borderwidth 2 -anchor nw \
	-bg \#e0e0ff -width 3
    form_entry $pa.name$i expression_trial_name($name) string \
	"$i 1 -sticky news -padx 0"
    if {$expression_mode == "random"} {
	form_entry $pa.params$i expression_param_random($name) string \
	    "$i 2 -sticky news -padx 0" "" 8
    } else {
	form_label $pa.params$i $name "$i 2 -sticky news -padx 0"
	$pa.params$i configure -justify left -relief groove -borderwidth 2 \
	    -bg \#e0e0ff
    }
    if [info exists want_trial_restart_ids] {
	futil_listentry $pa.id$i expression_trial_restartid($name) \
	    [list expression_edit_possible_restarts $name] \
	    "$i 3 -sticky ewn -padx 0"
    }
    form_entry $pa.ord$i expression_trial_order($name) int \
	"$i 4 -sticky ewn -padx 0" "" 4
    form_checkbutton $pa.sel$i "" expression_param_trials($name) \
	"$i 5 -sticky ewn -padx 0"
    form_button $pa.set$i "Set" [list expression_set_trial $name] \
	"$i 6 -sticky ewn -padx 0"
    if {$expression_mode == "random"} {
        form_button $pa.del$i "Delete" [list expression_delete_trial $name $i] \
	    "$i 7 -sticky ewn -padx 0"
    }
}

proc expression_edit_possible_restarts {except} {
    global expression_trial_name
    foreach s [lindex [solution_list_available restart *] 0] {
	set ok($s) 1
    }
    foreach s [array names expression_trial_name] {
	set ok($expression_trial_name($s)) 1
    }
    catch {unset ok($expression_trial_name($except))}
    return [lsort [array names ok]]
}

proc expression_edit_do_reset {} {
    global expression_trial_restartid expression_trial_name
    global expression_param_trials expression_edit_trial_how_solveid
    
    switch [tk_dialog .trials_how "Trial naming" \
		"Use numbered trials, or base names on values used?" \
		"" "" Numbered Values Cancel] {
		    0 { set expression_edit_trial_how_solveid nums }
		    1 { set expression_edit_trial_how_solveid vals }
		    2 return
		}
    expression_edit_trials_reset 1
}

proc expression_write_values {file tr} {
    set fp [open $file w]
    foreach v $tr {
	set vv [split [string trim $v " ;"] =]
	puts $fp $vv
    }
    close $fp
}

# This sets the values of the random trials.
# names are the names of the trials (may be "" in which case reasonable defaults are used)
# vals is a list of lists of name/value pairs for the parameters
# restartid is a list of the restart id's (may be "")
# order is the order each one is done in (may be "")
# set is a list of boolean flags saying whether to do this or not (may be "")
# replace is either 1 or 0 depending on whether to delete all previous random trials
# E.g.
# expression_set_random_trials {tr1 tr2} {{a .1 b .2} {a .3 b .4}} {"" tr1} {1 2} {1 1} 1
# or
# expression_set_random_trials "" {{a .1 b .2} {a .3 b .4}} "" "" "" 1

proc expression_set_random_trials {names vals restartid order set replace} {
    global expression_param_trials expression_trial_name expression_trial_restartid
    global expression_trial_order expression_param_random
    global expression_mode expression_trial_prefix
    
    set expression_mode random
    
    if $replace {
	foreach var {expression_param_trials expression_trial_name \
			 expression_trial_restartid expression_trial_order \
			 expression_param_random} {
	    catch {unset $var}
	}
    }
    set num 1
    set pref $expression_trial_prefix
    foreach n $names v $vals r $restartid o $order s $set {
	while {[info exists expression_trial_name([format $pref%03d $num])]} { incr num }
	set tr [format $pref%03d $num]
	
	if {$n == ""} { set n $tr }
	if {$s == ""} { set s 1 }
	if {$o == ""} { set o $num }
	set expression_param_trials($tr) $s
	set expression_trial_name($tr) $n
	set expression_trial_restartid($tr) $r
	set expression_trial_order($tr) $o
	set expression_param_random($tr) $v
	
	incr num
    }
    expression_edit_trials_reset 1
}

proc expression_ensure_variable_has_value {var val} {
    global expression_params expression_param_choices
    
    set ok 0
    if [info exists expression_param_choices($var)] {
	foreach v [split $expression_param_choices($var) " \t,"] {
	    if {![scan $v %g dummy] && ([string index $v 0] != "\"")} {
		set v \"$v\"
	    }
	    if {$v == $val} { set ok 1; break }
	}
    }
    if $ok return
    
    if ![info exists expression_param_choices($var)] {
	set expression_param_choices($var) $val
	set expression_params($var) $val
    } else {
	append expression_param_choices($var) " $val"
    }
}
