#!/usr/bin/env perl
use Carp qw(confess);
use warnings;
use strict;
use 5.010;
use feature "state";
$|=1; # autoflush 
#
# Window to the STARS
#
# Version
my $version="Version 0.39 18/06/2015";

#
# A frontend to the STARS,  TWIN, BINSTAR and BONNFIRES
#  stellar evolution codes, made using GTK2 and Perl.
#
# Requires: Perl, GTK2, Perl-GTK2, Rob's STARS Perl module, Rob's misc module
# (for slurp), Perl's File module (for tail, basename and copy functions),
# various Perl modules from CPAN, gnuplot, 
# probably convert (from ImageMagick) and ghostscript.
# 
# It is useful to have 'ps' but who doesn't? (Macs have a different ps!)
#
# All of these are freely available (see INSTALL).
# For a description see README.
#
# Also needs (re)nice, (presumably) bash (or some other shell), kill(all)
# zip and unzip if you don't have Archive::Zip
#
# See CHANGES for code update log.
#
# See $version for the version number.
#

# TODO not crash on unknown option
# TODO fix zoom in structure plots
# TODO move settings variables into hashes
# TODO gnuplot errors to screen (?)
# TODO regions in HRD (instabilities?)
# TODO options search
# TODO key in HRD when not using stellar colours
# TODO button to many options at once (e.g. all mass loss)
# TODO 92 bug in internals list
# TODO in STARS.pm read stars_standards files if available instead
#      of my hard-coded parameter lists for TWIN
# TODO replace my dodgy code with http://search.cpan.org/~wdobler/Fortran-F90Namelist-0.5.1/lib/Fortran/F90Namelist.pm
# pixbuf bug to FAQ
# TODO units on quantities 

use Time::HiRes qw(gettimeofday tv_interval) ;
my $start_time=[Time::HiRes::gettimeofday()];

# suck in required modules
use locale;
use POSIX qw(strftime locale_h);
use Glib qw/TRUE FALSE/;
use Gtk2 '-init';
use Gtk2::Gdk::Keysyms;

use Module::Load qw(load);
load_evcode_module();
use rob_misc qw(determine_if_we_have_procps session_id touch MAX MIN empty_dir slurp ok_file mem_usage f_number_to_c_number is_numeric log10 dumpfile gnuplot_ps_to_png gnuplot_ps_to_png2 binslurp certain_kill flip heximage extract_gnuplot_range ynstring gpshow list_modules ok_directory);
use File::Tail;
#use File::Temp;
use File::ReadBackwards;
use File::Copy qw(copy); 
use File::Basename qw(basename dirname);
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
use Math::Trig qw(acos);
use Data::Dumper;
use Proc::ProcessTable;
use English '-no_match_vars';
use Gtk2::Ex::NoShrink;

# set the locale to 'C'
use locale;
use POSIX qw(strftime locale_h log10);
my $default_locale = 'C';
foreach my $LC (qw/ALL CTYPE NUMERIC/)
{
    setlocale((eval 'LC_'.$LC), $default_locale);
}

# constants
use constant PI => acos(0.0)*2.0;
use constant PI4 => 4.0*PI;
use constant logmin => 1e-30;
use constant value_of_log10 => log(10.0);
use constant ivalue_of_log10 => (1.0/log(10.0));
use constant G => 6.67259E-8;
use constant logG => log10(6.67259E-8);
use constant Msun => 1.9891E33;
use constant logMsun => log10(1.9891E33);
use constant Rsun => 6.9598E10;
use constant logRsun => log10(6.9598E10);
use constant loggfac => (-2.0*logRsun + logMsun + logG);
use constant Lsun => 3.8515E33;
use constant stefan_boltzmann => 5.67051E-5;
check_consts();

use constant LEFT_MOUSE_BUTTON => 1;
use constant MIDDLE_MOUSE_BUTTON => 2;
use constant RIGHT_MOUSE_BUTTON => 3;


sub load_evcode_hashes
{
    print "LAOD LOAD LOAD\n";
    exit;
}

# variable declarations

use vars (# General options
	  qw($vb $expvb $unlink_tempfiles $timings %timings $max_mem  $pwd 
	     $runname $window_title $extended_title
	      $evcoderoot $useperlzip
	     $tmp $have_symlinks $home $os $nice_command $slash @fontpaths %fonthash %fontfilehash
	     $gtk2_tab_bug_workaround $auto_window_title
             $evcode $evcode_module $binstar_par_textview  $use_block_surface

             %modinfo $persist_gnuplot %persist_gnuplot %updating
             @wttslayers @wttslayers_toggles $wttslayernum $wttslayers_window
             $optsdir $wttslayers_options %gtkrc
	     ), 
	  
	  # external program information and options
	  qw(%external_programs
	     %external_programs_version @gnuplot_fonts %gnuplot_fonts_reverse_hash 
	     $ps_shape %pm3d_palette @pm3d_palettes $gdfontpath
	     $aspect_ratio $procps),
	  
	  # session and cache
	  qw($cache_last_cleared
             $session $cacheroot $cachedir $session_timeout
	     %image_cache %pixbuf_cache $window_image_cache
	     %image_cache_timestamp %cache_filenames $disk_cache_count $image_timeout
	     ),
	  
	  # misc
	  qw(@ps_filenames  $running_nice $code_has_been_run %allocated_colors
	     $running_the_code $just_run_the_code $have_run_the_code
	     @current_status @start_status
	     $currently_showing_noimage 
	     @models_available %models_available 
	     $file_selector  $colour_window %block_location
	     @plt1cols %plt1cols $plt1tail @plt1data
             @system_cols %system_cols
             $webserver @prevwebl $prevtstat $web_status
             @last_plt1_modification_time @last_status $start_time
             %dragging $force_status_updates @starswitches
             %next_update_time $next_update_delay
	     ),
	  
	  # main window
	  qw(@frames @tab_table @made_tab
	     $evolve_button $stop_button $evolve_log_textview $log_timestamps
	     @keys_pressed $main_window $main_window_status 
             $main_notebook %main_notebook_refs
	     $mouse_pointer @tab_labels @tab_keys @tab_tooltips $tab_bar 
	     $tooltips $current_tab $windowx $windowy
	     @status_bar $n_status_bar_lines $status_bar_frame %tab_numbers
	     ),

	  # Options tab
	  qw(
	     $options_tree_store @options_list %options_widgets
	     $zams_z_eventbox $zams_m1_eventbox
	     $zams_m2_eventbox $zams_m_eventbox
	     $zams_z_menu  $zams_m1_menu $zams_m2_menu $zams_m_count
	     @zs @ms $start_from_zams_radiobutton
	     $start_model_label1 $start_model_label2
	     $start_from_file_radiobutton
	     $nstar $internals_options_state
	     
	     ), 

	  # Evolve tab and TWIN options
	  qw($follow_button $clear_button $update_button
	     $follow_evolve_log @evolve_log_file_tails $i_am_a_child 
	     %variable_help $evolve_iter
	     ),

	  # Log tab
	  qw($log_textview $log_file_tail $follow_log $log_iter
            ),

	  # HRD tab
	  qw($HRD_image @HRD_range_widgets  
	     @HRD_spacings @starbuttons $HRD_options_window
	     $prev_plotted_hrd_string 
             %HRD_image_properties $shrd
	     ),

	  # rho-T tab
	  qw( 
             $rhot %RhoT_image_properties
	     $RHOTstar $RhoT_image
	     @RhoT_range $RhoT_label_col $RhoT_label_distance
	     @RhoT_spacings @starbuttons $prev_plotted_RhoT_string @colours
	     ),

	  # histogram tab
	  qw(
             $histograms
	     $histogram_xaxismenu
	     $histogram_weight_menu
	     $prev_plotted_histogram_string
	     $histogram_binwidth $histogram_image $HISTOGRAMstar
	     $histogram_bin_min $histogram_bin_max
             ),

	  # System tab
	  qw($systemtab
             $system_image %system_image_properties
             @system_yaxisbuttons $system_xaxismenu
	     @system_logscalex_buttons @system_logscaley_buttons @system_rangebox
	     $prev_plotted_system_string
             @system_labels @system_axes @system_ll 
	     ),

	  # Structure tab
	  qw($structure_image %structure_image_properties
             @structure_yaxisbuttons $structure_xaxismenu
	     @structure_logscalex_buttons @structure_logscaley_buttons @structure_rangebox
	     $prev_plotted_structure_string $structurestar
             @structure_labels @structure_axes @structure_ll 
	     ),
	  
	  # Internals tab
	  qw($prev_internal $nsphere $internals_drawtype @internals_drawtype
	     $internalsdir $max_anim_count 
	     @animation_models $anim_frame_update
	     @models_required @internals_y_variables %internals_y_variables
             @internals_y_labels @internals_tooltips
	     @y_variables_toplot @titles @internals_y_buttons
	     $internals_leftvbox $internals_menu
	     $internals_xaxis_menu
	     @internals_buttons @internals_anim_buttons 
	     @internals_anim_adjustments $anim_speed
	     $anim_count @anim_buffer @anim_buffer_models
	     @internals_img @internals_handlers 
	     $internals_log10 $internals_log10x
	     @internals_range $current_internals_tab
	     @internals_tab_cache $new_internals_tab $internals_notebook
	     @internals_eventbox @model_to_menu 
	     $updating_internals 
             @internals_image_properties %internals_grange
             $prev_internals_plotstring
	     ) ,
	  
	  # Kippenhahn tab
	  qw($kippenhahn_state @kippenhahn_state $kippenhahn_image
	     @Kippenhahn_labels $stop_kipp_plot
	     @kippenhahn_model_cache $use_kippenhahn_cache
	     $block_surface $kipp_plot_button
             %kippenhahn_image_properties @kippenhahn_rangebox
             $use_cubehelix
             ),
	  
	  # Misc tab
	  qw(@misc_opts_widgets @misc_opts_values
	     $installation_information_window
             $gnuplot_options_window %gnuplot_options %gnuplot_options_default
             $misc_log_textview 
	     ),
	  
	  # Load/Save tab
	  qw($zippid @model_set_files
	     ),

	  # About tab
	  );

toggle_hourglass(1);

# set up default parameters
default_variable_settings();

show_tabs_first_time();

# eval command line options again
parse_args_by_eval();

# call update_view once to initialise things
update_view();

if($webserver)
{
    # update the web server data
    Glib::Timeout->add(1000,\&update_webserver_data);
    update_webserver_data();
}

# every 1000ms we update the window
Glib::Timeout->add(1000,\&update_view);

# every 5000ms we update the window title
# (this uses a lot of CPU/disk, so not too often)
Glib::Timeout->add(5000,\&update_window_title);

# TODO: these timeouts only have to be active at 
#       certain times: better to activate/deactive them

# every 100ms we update the animation
Glib::Timeout->add(10,\&update_animation);

# save for dragging
Glib::Timeout->add(100,\&update_drag);

toggle_hourglass(0);

# wait loop
Gtk2->main;

# all done
exit(0);

############################################################
### Subroutines follow
############################################################

sub update_view
{
    # the update function, called every 1000ms to update
    # the window of the user with the latest results
    my $retval;
    if($i_am_a_child!=0)
    {
	print "Child: do not update view\n" if ($vb);
	$retval=FALSE;
    }
    else
    {
	my $time0 = [gettimeofday()] if($timings);
	$retval=TRUE;
	if($procps)
	{
	    my $time0 = [gettimeofday()] if($timings);
	    # check if we're using too much memory (only if we have procps)
	    my $m=mem_usage($procps);
	    if(($m ne '?')&&($m>$max_mem))
	    {
		print STDERR "Too much memory is being used! ($m MB > $max_mem MB)\n";
		emergency_exit();
	    }
	    increment_timing('procps memory_check',$time0)if($timings);
	}
	print "Update view! Current tab=$current_tab\n" if ($vb);
	
	if(!defined($cache_last_cleared) || (time()-$cache_last_cleared > 3600))
	{
	    # clear cache first time or every hour
	    clear_up_caches($image_timeout);
	    touch($cachedir.'/touch'); # keep this touched regularly
	}

	# always update the status bar
	# NB you have to check if the plt1 file exists, rather than
	# just use ok_file, because after evolution has started the 
	# file will be of size 0 ... and it may then crash immediately.
	if((-f maindir().'/'.$modinfo{plt1file_stub}.$nstar))
	{
	    $have_run_the_code=1; # just in case this is not set

	    set_status_bar(update_current_status());

	    # code is running, perhaps renice?
	    my $nice=get_opt(3);
	    if(($nice != $running_nice)&&($os eq 'unix')&&
	       ($nice!=-1))
	    {
		my $pid=running_pid();
		print "(attempt to) Renice $pid to $nice\n" if ($vb);
		`renice $nice $pid 2>/dev/null`;
		$running_nice=MAX(1,$nice);
		update_nice_command();
	    }

	    # check for evcode termination
	    if(has_evcode_terminated()==2)
	    {
		my $reason=analyse_evcode_log_for_termination_reason();
		set_status_bar("Crashed or terminated ".$reason);
		stop_wrapper();
	    }
	}

	# check for an error in the evolution
	check_for_evcode_error();

	if($current_tab==$tab_numbers{Internals})
	{
	    # only update from here if we're not animating
	    update_Internals_tab(0) if((test_for_animation()==0)||($max_anim_count==0)||($internals_options_state==1));
	}
	else
	{
	    eval 'update_'.$tab_labels[$current_tab].'_tab(0);';
	    if($@)
	    {
		print "Eval error on update_{$tab_labels[$current_tab]}_tab(0):\n$@\n";
		exit;
	    }
	}

	if($timings)
	{
	    increment_timing('update_view',$time0);

	    state %prev;
	    
	    # calculate total time in the code
	    my $tottime=0.0;
	    map
	    {
		$tottime+=$timings{$_};
	    }keys %timings;
	    printf "% 30s : %g s\n",'Window To The Stars (total)',tv_interval($start_time,[gettimeofday()]);
	    printf "% 30s : %g s\n",'WTTS (logged total)',$tottime;
	    
	    # output per-subroutine time
	    my $f=100.0/$tottime;
	    foreach my $k (sort {-$timings{$a}<=>-$timings{$b}} keys %timings)
	    {
		my $frac = $f*$timings{$k};
 		my $arrow = defined $prev{$k} ? ($frac>$prev{$k} ? '^' : 'v') : ''; 
		printf "% 30s : % 15g s : % 8.2f %% %s\n",$k,$timings{$k},$frac,$arrow;
		$prev{$k} = $frac;
	    }
	    print "\n";
	}
    }
    return $retval;
}

sub set_status_bar
{
    return if(!defined($_[0]));
    my @s=split(/\n/o,$_[0]);
    my $p=running_pid();
    chomp $s[0];
    $s[0] = 'Status: '.$s[0];    
    $s[0].=' '.mem_usage($procps).'Mb' if($procps==1);
    $s[0].=" ev (pid $p):"if($p>0);
    $s[0].=':'.mem_usage($procps,$p).'Mb' if(($procps==1)&&($p>0));
    print "Set status bar text @s\n" if ($vb);
    for(my $i=0;$i<=$#status_bar;$i++)
    {
	if(defined($s[$i]))
	{
	    my $x=$s[$i];
	    # exponents
	    $x=~s/[dDeE]\+?(-?)0(\d+)/<span stretch=\"condensed\">x10<\/span><span stretch=\"ultracondensed\" size=\"smaller\" rise=\"10000\">$1$2<\/span>/g;
	    $status_bar[$i]->set_markup($x);
	}
    }
    clear_events_queue();
}

sub update_current_status
{
    # update the @current_status array with the latest values
    my $time0 = [gettimeofday()] if($timings);    
    my $prevmodel;
    print"Update status\n"if($vb); 

    my $plt1file=maindir().'/'.$modinfo{plt1file_stub}.$nstar;
    print "Plt1file is $plt1file\n" if($vb);

    # change to working wtts layer (data directory)
    wttslayer();

    # first time:
    # open the tail to the plt1 file, also check 
    # which models are really available 
    print "Plt1tail is $plt1tail : num = $#plt1data\n" if($vb);
    if((!defined($plt1tail))||($#plt1data<=0))
    {
	print "plt1tail not defined: opening tail\n" if ($vb);
	$plt1tail=File::Tail->new(name=>$plt1file,
				  ignore_nonexistant=>1, # ?
				  nowait=>1,
				  tail=>0,
				  debug=>1
				  );
	print "done : reading .plt data ... " if ($vb);

	# fix to read in all of the .plt file without
	# using File::Tail's very buggy supposedly similar capability
	if(-s $plt1file > 0)
	{
	    open(PLT1TAIL,'<'.$plt1file)||confess;
	    while(<PLT1TAIL>)
	    {
		next if(/^\s*\#/o);
		$prevmodel=add_to_plt1($_,$prevmodel);
	    }
	    close PLT1TAIL;
	}

	print " tail read finished\n" if ($vb);
	 
	$last_plt1_modification_time[$nstar]=-1; # unfeasible modification time: forces update 
    }

    print "last mod time $last_plt1_modification_time[$nstar] c.f. ".(-M $plt1file)."\n" if($vb);
        my $s;

    print "Force plt1 update\n"if($force_status_updates && $vb);

    if((!$force_status_updates) && 
       ($last_plt1_modification_time[$nstar] == (-M $plt1file)))
    {
	print "Use previous status = $last_status[$nstar]\n"if($vb);
	$s= $last_status[$nstar];
    }
    else
    {
	# set last_plt1_modification_time : we should only do something if it has changed
	# or not been loaded before
	$last_plt1_modification_time[$nstar]=-M $plt1file;

	print "Set last mod time $last_plt1_modification_time[$nstar]\n"if($vb);

	if(defined($plt1tail))
	{ 
	    # load in plt1 data, it is used in many places
	    my $l;
	    print "Reading tail of plt1file\n" if ($vb);
	    while(defined($l=$plt1tail->read)&&($l ne ''))
	    {
		next if($l=~/^\s*\#/);
		$prevmodel=add_to_plt1($l,$prevmodel);
	    } 
	    print "Done\n" if($vb);
	    
	    # perhaps get the start status, which is in line 1 
	    # because line 0 contains the number of data items
	    if(defined($plt1data[1]))
	    {
		@start_status=split(/\s+/o,$plt1data[1]);
		print STDERR "Start status unknown error\n" if($#start_status==-1);

		# get the status now (at the end of plt1)
		my @l=split(/\s+/o,$plt1data[$#plt1data]);

		# get for both stars
		my @l1=split(/\s+/o,tailoffile(maindir().'/'.$modinfo{plt1file_stub}.'1'));
		my @l2=split(/\s+/o,tailoffile(maindir().'/'.$modinfo{plt1file_stub}.'2'));

		if($#l1>4)
		{
		    my $z= defined($zams_z_menu) ? $zs[$zams_z_menu->get_active] : undef;
		    $z='' if(!defined($z));

		    # check for termination of evolution code
		    my $x=has_evcode_terminated();
		    
		    if($x==1)
		    {
			my $reason=analyse_evcode_log_for_termination_reason();
			$s = defined($reason) ? 'Evolved : Terminated because : <span foreground="Red"><i>'.$reason.'</i></span> ' : 'Evolved/Terminated';
		    }
		    elsif($x==2)
		    {
			# code died
			$s='Crashed?';
		    }
		    else
		    { 
			$s = $running_the_code ? 'Evolving ' : 'Stopped ';
		    }
		    
		    $s .= status_string(1,\@l1,$z);

		    if($#l2>4)
		    {
			# star 2 status string
			$s.=status_string(2,\@l2,$z);

			# binary status string
			my $per_col;
			my $sep_col;
			if($evcode_module eq 'binstar')
			{
			    $sep_col = $plt1cols{'Orbital separation/Rsun'};
			    $per_col = $plt1cols{'Orbital period/years'};
			}
			elsif($evcode_module=~/_WTTS$/o)
			{
			    # pending
			    my @x=split(/\s+/o,tailoffile('system'));
			    
			    my $p=$x[$system_cols{'Orbital Period days'}];
			    my $sep=$x[$system_cols{'Orbital Separation Rsun'}];
			    my $jorb=$x[$system_cols{'Orbital Angular Momentum'}];
			    my $rlof=$x[$system_cols{'RLOF Mass transfer rate'}];
			    my $beta=$rlof>0.0 ? $x[$system_cols{'RLOF Mass accretion rate'}]/
				$x[$system_cols{'RLOF Mass transfer rate'}]:0.0;

			    $s.= sprintf "\nBinary: P=%gd a=%gRsun Jorb=%g RLOF=%g beta=%g",$p,$sep,$jorb,$rlof,$beta;
			}
			else
			{
			    $per_col=21;
			    $sep_col=26;
			}

			if(!($evcode_module =~/_WTTS$/o))
			{
			    my $p=$l2[$per_col]*$modinfo{time_conversion_factor}; # period in years

			    if(($p != 0.0) && ($l2[$sep_col] ne 0))
			    {
				$p=dtconvert($p);
				$s.= $evcode eq 'binstar' ?
				    sprintf "\nBinary : P=%s, a=%g Rsun, J=%g, e=%g\n",
				    $p,
				    1.0*$l2[$sep_col],
				    $l2[$plt1cols{'Orbital angular momentum'}],
				    $l2[$plt1cols{'Orbital eccentricity'}] : 
				    sprintf "\nBinary : P=%s, a=%g Rsun, J=%g, e=%g\n",
				    $p,1.0*$l2[55],$l2[26],$l2[33];
			    }
			}
		    }
		    else
		    {
			$s.="\nStar 2 : Pending...";
		    }
		    @current_status=@l;
		}
	    }
	}
	my @old_models=@models_available;

	print "Call get_available_models()\n"if($vb);

	# determine the currently available models
	my $mod_av=get_available_models();

	print "mod_av=$mod_av, about to convert pointer to array\n"if($vb);

	@models_available= defined($mod_av) ? @$mod_av : ();

	print "Models available list length = $#models_available\n"if($vb);
	
	if(($use_kippenhahn_cache)&&($#models_available < $#old_models))
	{
	    # model list has shrunk, must have reset something, or
	    # restarted the evolution, clean the cache
	    @kippenhahn_model_cache=();
	}

	# put in reverse hash
	map{$models_available{$_}=1;}@models_available;

	$last_status[$nstar]=$s;
    }

    increment_timing('update_current_status',$time0) if($timings);    

    return($s);
}



############################################################
### Options tab
############################################################

sub show_Options_tab
{
    # add to the table with a scroller
    my $scroller=Gtk2::ScrolledWindow->new;
    my $time0 = [gettimeofday()] if($timings);
    my $ntab=$tab_numbers{Options};

    if($made_tab[$ntab] == 0)
    {	
	if(defined($main::modinfo{options_tab_function}))
	{
	    # if an options tab vbox function is given by the evcode module, use it instead
	    my $func=$main::modinfo{options_tab_function};
	    $tab_table[$ntab]=&{$func};
	}
	else
	{
	    # make the options tab as a Vbox, I know it's called table, but...
	    $tab_table[$ntab]=Gtk2::VBox->new;
	    
	    # first get all the variables and their values in hash form
	    load_evcode_hashes();

	    my $tophbox=Gtk2::HBox->new;
	    my $leftvbox=Gtk2::VBox->new;
	    my $rightvbox=Gtk2::VBox->new;

	    if($evcode eq 'TWIN')
	    {
		map
		{
		    $tophbox->pack_start($_,FALSE,FALSE,0);
		}($leftvbox,Gtk2::VSeparator->new,$rightvbox,Gtk2::VSeparator->new,starbuttons('v'));
	    }

	    $tab_table[$ntab]->pack_start($tophbox,FALSE,FALSE,0);
	    
	    # make ZAMS selector labelx
	    my $label=Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b>Select starting model </b></span>');
	    $label->set_alignment(0.1,0);
	    $leftvbox->pack_start($label,FALSE,FALSE,0);

	    my $zams_table=Gtk2::Table->new(6,1,FALSE);
	    $leftvbox->pack_start($zams_table,FALSE,TRUE,0)if($evcode eq 'TWIN');
	    # get a list of available ZAMS models (assume we want
	    # to evolve from one of them) and put them in the list
	    print "Get zams Zs\n"if($vb);
	    @zs=zams_zs();
	    print "Get zams Ms\n"if($vb);
	    @ms=zams_ms($zs[0]);
	    
	    # menu for Z, menu for M (defined at top)
	    if(defined($zams_m1_menu))
	    {
		$zams_m1_menu->destroy;
		$zams_m2_menu->destroy;
	    }
	    $zams_m1_menu=Gtk2::ComboBox->new_text;
	    $zams_m2_menu=Gtk2::ComboBox->new_text;
	    $zams_m1_menu->set_wrap_width(5);
	    $zams_m2_menu->set_wrap_width(5);
	    
	    $zams_z_menu=Gtk2::ComboBox->new_text;
	    $zams_z_menu->signal_connect('changed'=>\&zams_z_menu_changed);
	    map
	    {
		$zams_z_menu->append_text($_);
	    }@zs;

	    $zams_z_menu->set_active(0);
	    
	    my ($zz) = grep { $zs[$_]==0.02 } 0..$#zs; 
	    $zams_z_menu->set_active($zz) if(defined $zz);

	    set_tooltip($zams_m1_eventbox,'The initial mass to be used for the primary star');
	    set_tooltip($zams_m2_eventbox,'The initial mass to be used for the secondary star');
	    set_tooltip($zams_z_eventbox,'The initial metallicity to be used for the stellar evolutionary run. Metallicity is the total mass fraction of everything heavier than hydrogen or helium.');
	    
	    # BUT if there is a .info file saved, we use Z from that,
	    # rather than the default (first) option
	    set_from_info();

	    $start_from_zams_radiobutton=Gtk2::RadioButton->new_with_label_and_tooltip(undef,'From ZAMS Library:','Enable this to start from a model in the Zero-Age Main Sequence library'); 
	    $start_from_zams_radiobutton->signal_connect(clicked=>\&model_select_callback);

	    my $start_model_radio_group=$start_from_zams_radiobutton->get_group();

	    $zams_table->attach($start_from_zams_radiobutton,0,1,0,1,'shrink','shrink',0,0);

	    $zams_z_eventbox->add($zams_z_menu);
	    $zams_m1_eventbox->add($zams_m1_menu);
	    $zams_m2_eventbox->add($zams_m2_menu);

	    # ZAMS models
	    my $h=Gtk2::HBox->new(0,0);

	    # select Z
	    $h->pack_start(Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b>Z=</b></span>'),FALSE,FALSE,0);
	    $h->pack_start($zams_z_eventbox,FALSE,FALSE,0);

	    # select M1,2
	    $h->pack_start(Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b> M1=</b></span>'),FALSE,FALSE,0);
	    $h->pack_start($zams_m1_eventbox,FALSE,FALSE,0);
	    $h->pack_start(Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b> M2=</b></span>'),FALSE,FALSE,0);
	    $h->pack_start($zams_m2_eventbox,FALSE,FALSE,0);

	    $zams_table->attach($h,1,2,0,1,'shrink','shrink',0,0);


	    my $options_notebook=Gtk2::Notebook->new;
	    $options_notebook->popup_enable();

	    if($evcode eq 'TWIN')
	    {
		# provide the option to load a starting model file

		$start_from_file_radiobutton=Gtk2::RadioButton->new_with_label_and_tooltip($start_model_radio_group,'From File:','Enable this to start stellar evolution from a previously saved model file');
		$start_from_file_radiobutton->signal_connect(clicked=>\&model_select_callback);

		# check if we had models loaded on previous shutdown
		print "Previous models: $misc_opts_values[11], $misc_opts_values[12]\n"if($vb);

		# make labels, perhaps load starting models
		if($misc_opts_values[11] ne 'Undefined')
		{
		    my $labeltext=$misc_opts_values[11];
		    $labeltext=~s/.*\/(.*)$/$1/;
		    $start_model_label1=Gtk2::Button->new_with_label($labeltext);
		    print "Set start model $misc_opts_values[11]\n"if($vb);
		    
		    set_evcode_start_model(1,$misc_opts_values[11]);
		    set_evcode_start_model(2,$misc_opts_values[11]);
		}
		else
		{
		    $start_model_label1=Gtk2::Button->new_with_label('Undefined');
		}

		if($misc_opts_values[12] ne 'Undefined')
		{
		    my $labeltext=$misc_opts_values[12];
		    $labeltext=~s/.*\/(.*)$/$1/;
		    $start_model_label2=Gtk2::Button->new_with_label_and_tooltip($labeltext);
		    print "Set start model $misc_opts_values[12]\n"if($vb);
		    #symlink_start_model(2,$misc_opts_values[12]);
		}
		else
		{
		    $start_model_label2=Gtk2::Button->new_with_label('Undefined');
		}

		$start_from_file_radiobutton->set_active($misc_opts_values[19]);

		set_tooltip($start_model_label1,'The model file which will be used to start the evolution in star 1, if the "From file" button is set. Note "Undefined" means you still need to load a file'); 
		set_tooltip($start_model_label2,'The model file which will be used to start the evolution in star 2, if the "From file" button is set. Note "Undefined" means you still need to load a file'); 
		$start_model_label1->signal_connect(clicked=>\&load_model_file_selector,1);
		$start_model_label2->signal_connect(clicked=>\&load_model_file_selector,2);

		my $load_model_hbox=Gtk2::HBox->new();
		$load_model_hbox->pack_start($start_model_label1,FALSE,FALSE,0);
		$load_model_hbox->pack_start($start_model_label2,FALSE,FALSE,0);

		$zams_table->attach($start_from_file_radiobutton,0,1,1,2,'shrink','shrink',10,10);	
		
		$zams_table->attach($load_model_hbox,1,2,1,2,'shrink','shrink',10,10);	
		
		
		# reset
		my $reset_button=Gtk2::Button->new_with_label_and_tooltip('Reset from defaults','Reset all stellar evolution options to their default values');
		$reset_button->signal_connect(clicked=>\&reset_options);

		# load/save/import menus
		my $menubar = Gtk2::MenuBar->new;
		map
		{
		    my $y=$_;
		    $y=~s/^(\S+)\s+//o;
		    my $x=$1;
		    my $b=Gtk2::MenuItem->new($x);

		    my @i=split(/\s+/o,$y);
		    my $m=Gtk2::Menu->new;
		    $b->set_submenu($m);

		    foreach my $i (@i)
		    {
			$i=~s/-/ /go;
			my $c=Gtk2::MenuItem->new($i);
			$c->signal_connect (activate=>\&lsmenu_selected,"$x $i");
			$m->append($c);
		    }
		    $menubar->append($b);
		}('Load WTTS-Options','Save WTTS-Options');

		# add plugins menu (if available)
		evcode_plugin_menu($menubar);

		map
		{
		    $rightvbox->pack_start($_,FALSE,FALSE,0);
		}(Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b>Option Control</b></span>'),
		  $reset_button,$menubar);
		
		my @variables;

		$options_notebook->set_tab_pos('top');
		$options_notebook->set_current_page(0);
		$options_notebook->set_scrollable(TRUE);
		$options_notebook->set_show_border(TRUE);
		my @options_frames;
		my $table;
		my $tline;
		my $wrap=60; # text wrap width
		foreach my $option (@options_list)
		{
		    if($option=~/^Section\: (.*)$/o)
		    {
			# append a new section
			push(@options_frames,Gtk2::Frame->new);
			$options_notebook->append_page($options_frames[$#options_frames],$1);
			$table=Gtk2::Table->new(2,10,FALSE);
			$options_frames[$#options_frames]->add($table);
			$tline=0;
		    }
		    elsif($option=~/^Function (\S+) (.*)/)
		    {
			# special function
			my $func=$1;
			my $b=Gtk2::Button->new_with_label($2);
			my $code="\$b->signal_connect(clicked=>\\\&$1);";
			#print "EVAL $code\n";
			eval $code;
			if($@)
			{
			    print "error in function eval while setting up options: $@\n";
			    exit;
			}
			$table->attach($b,0,1,$tline,$tline+1,'fill','shrink',0,0);
			$tline++;
		    }
		    else
		    {
			if($option=~/^(\S+) in (\S+)$/o)
			{
			    # append a variable
			    my $k=$1;
			    my $file=$2;
			    $block_location{$k}=$file;
			    my $label=evcode_value_from_hash($file,$k);
			    print "Set label '$label' from file $file from k=$k, block_location $block_location{$k}: eval \$$file\{$k\}\n"if($vb);
			    $k=~/[^\.]+\.(\S+)/o;
			    my $h=$variable_help{$1};
			    $h=' ' if(!defined($h));

			    my $l=Gtk2::Label->new($k);
			    $l->set_alignment(0,0.5);
 			    
			    my $editable=1;
			    if(!($h=~/DO_NOT_SHOW/o))
			    {
				if($h=~s/NOT_EDITABLE//o)
				{
				    $editable=0;
				}

				# The help file has been annotated with various strings
				# to explain what we should do here
				
				# SPIN $1 $2 $3 $4
				#  Makes a spinbutton widget from $1 to $2 with step $3, default $4
				# CHOOSE x y z...
				#  Makes a menu with choices x,y,z,...
				# Otherwise we assume a text box for manual entry

				if($h=~s/\[SPIN (\d+) (\d+) (\d+) (\d+)\]//o)
				{
				    # make a spinner
				    my $opts=$1;
				    my $menu=Gtk2::SpinButton->new_with_range($1,$2,$3);
				    $menu->set_value(defined($label) ? $label : $4);
				    $h=wrap_newlines($h,$wrap);
				    map
				    {
					$menu->append_text($_);
				    }split(/\s+/o,$opts);
				    $menu->signal_connect(value_changed=>\&spinbutton_variable_edited,$k);
				    $menu->set_update_policy('always');

				    $options_widgets{$k}=$menu;

				    my $lh=Gtk2::Label->new($h);
				    $lh->set_alignment(0,0.5); 

				    $table->attach($l,0,1,$tline,$tline+1,'fill','fill',0,0);
				    $table->attach($menu,1,2,$tline,$tline+1,'fill','shrink',0,0);
				    $table->attach($lh,2,3,$tline,$tline+1,'fill','fill',0,0);
				}
				elsif($h=~s/\[CHOOSE (.*)\]//o)
				{
				    # make a menu
				    my $opts=$1;
				    my $menu=Gtk2::ComboBox->new_text;
				    $options_widgets{$k}=$menu;
				    $h=wrap_newlines($h,$wrap);
				    
				    my @opts=split(/\s+/o,$opts);
				    my $active=-1;
				    for(my $i=0;$i<=$#opts;$i++)
				    {
					$menu->append_text($opts[$i]);
					$active=$i if((defined($label))&&($opts[$i] eq $label));
				    }
				    $menu->set_active($active>-1 ? $active : 0);
				    $menu->signal_connect(changed=>\&menu_variable_edited,$k);

				    my $lh=Gtk2::Label->new($h);
				    $lh->set_alignment(0,0.5); 

				    $table->attach($l,0,1,$tline,$tline+1,'fill','fill',0,0);
				    $table->attach($menu,1,2,$tline,$tline+1,'fill','shrink',0,0);
				    $table->attach($lh,2,3,$tline,$tline+1,'fill','fill',0,0);
				}
				else
				{
				    # text entry
				    
				    # help text
				    $h=wrap_newlines($h,$wrap);

				    if(defined $label && $label=~/(\S+)\s+(\S+)/)
				    {
					# fortran array 
					$l->set_alignment(0,0);
 			    
					my @help = split(/\:\:/,$h);
					my @ar = split(/\s+/,$label);
					my @topts=('shrink','shrink',0,0);
					for(my $i=0;$i<=$#ar;$i++)
					{
					    # data text entry
 					    my $t = Gtk2::Entry->new_with_text($ar[$i]);
					    $t->signal_connect(changed=>\&text_variable_edited,[$k,$i]);
					    $t->set_editable(FALSE) if($editable==0);
					     
					    # help
					    my $hh = $help[$i] // $help[0] // 'no help available';
					    $hh=~s/^\s+//; $h=~s/\s+$//;
					    my $lh=Gtk2::Label->new($hh);
					    $lh->set_alignment(0,0.5); 

					    $table->attach(Gtk2::Label->new($k.'['.($i+1).']'),0,1,$tline,$tline+1,@topts);
					    $table->attach($t,1,2,$tline,$tline+1,@topts);
					    $table->attach($lh,2,3,$tline,$tline+1,'fill','fill',0,5);
					    $tline++;
					}
					$options_widgets{$k}=$table;
				    }
				    else
				    {
					# fortran scalar

					# help label
					my $lh=Gtk2::Label->new(' '.$h);
					$lh->set_text(' '.$h);
					$lh->set_alignment(0,0.5);

					# text entry label 
					my $t=Gtk2::Entry->new_with_text($label);
					$t->set_editable(FALSE) if($editable==0);
					$t->signal_connect(changed=>\&text_variable_edited,[$k,undef]);

					# align label
					$l->set_alignment(0,0.5);

					$options_widgets{$k}=$t; # required?
					
					# attach
					$table->attach($l,0,1,$tline,$tline+1,'fill','fill',0,0);
					$table->attach($t,1,2,$tline,$tline+1,'fill','shrink',0,0);
					$table->attach($lh,2,3,$tline,$tline+1,'fill','fill',0,0);
				    }
				}
				$tline++;
			    }
			    $k=~s/^.*\.//o;
			}
			elsif($option=~/^Label\: (.*)$/o)
			{
			    # append a label 
			    $h=wrap_newlines($1,$wrap);
			    my $lh=Gtk2::Label->new(' '.$h);
			    $lh->set_alignment(0,0.5);
			    $table->attach($lh,2,3,$tline,$tline+1,'fill','fill',0,0);
			    $tline++;
			}
		    }
		}
		
		$scroller->add_with_viewport($options_notebook) ;
		$tab_table[$ntab]->pack_start($scroller,TRUE,TRUE,0);
		$scroller->set_policy('automatic','automatic');
		$scroller->set_shadow_type('etched-out');
	    }
	    elsif($evcode eq 'binstar')
	    {
		# show the contents of binstar.par
		my $binstar_par_window=Gtk2::ScrolledWindow->new(undef,undef);
		$binstar_par_window->set_shadow_type('etched-out');
		$binstar_par_window->set_policy('automatic','automatic');
		$binstar_par_window->set_border_width(5);
		$binstar_par_textview=Gtk2::TextView->new();
		update_Options_binstar_parview();
		
		$binstar_par_textview->hide;
		$binstar_par_textview->set_cursor_visible(FALSE);
		$binstar_par_textview->set_wrap_mode('word');
		$binstar_par_textview->set_left_margin(1);
		$binstar_par_textview->set_right_margin(1);
		$binstar_par_textview->set_left_margin(1);
		$binstar_par_textview->set_left_margin(1);
		$binstar_par_textview->set_editable(FALSE);
		my $font_desc = Gtk2::Pango::FontDescription->from_string ('Courier 9');
		$binstar_par_textview->modify_font ($font_desc);
		$binstar_par_window->add($binstar_par_textview);
		
		$tab_table[$ntab]->pack_start($binstar_par_window,TRUE,TRUE,0);
	    }
	}
	$made_tab[$ntab]=1;
	$frames[$ntab]->add(scrolled($tab_table[$ntab]));
	$frames[$ntab]->show_all;
    }


    increment_timing('show_Options_tab',$time0) if($timings);
    
    TRUE;
}

sub update_Options_binstar_parview
{
    # update binstar.par data if available
    if((-e 'binstar.par') && (-s 'binstar.par' > 0))
    {
	my $buffer=$binstar_par_textview->get_buffer;
	$buffer->set_text(slurp('binstar.par'));
    }
}

sub set_from_info
{
    # if the run info file exists, use it to set menu options
    print "Check for .info\n" if ($main::vb);
    if(-f "$main::runname.info")
    {
	print "OK\n" if ($main::vb);
	# change to working wtts directory
	wttslayer();
	open(INFO,"<$main::runname.info")||confess("cannot open $main::runname.info : $!");
	while(<INFO>)
	{
	    if((defined($main::zams_z_menu))&&(/^Metallicity (\S+)/o))
	    {
		print "Set Z=$1\n" if ($main::vb);
		# set metallicity to $1
		my $z=$1;
		for(my $i=0;$i<=$#main::zs;$i++)
		{
		    if($main::zs[$i]==$z)
		    {
			$main::zams_z_menu->set_active($i);
		    }
		}
	    }
	    elsif((defined($main::zams_m1_menu))&&(/^Mass (\S+)/o))
	    {
		print "Set M(1)=$1\n" if ($main::vb);
		# set mass to $1
		my $m=f_number_to_c_number($1);
		for(my $i=0;$i<=$#main::ms;$i++)
		{
		    if(abs($main::ms[$i]-$m)<1e-5)
		    {
			$main::zams_m1_menu->set_active($i);
		    }
		}
	    }
	    elsif((defined($main::zams_m2_menu))&&(/^Mass2 (\S+)/o))
	    {
		print "Set M2=$1\n" if ($main::vb);
		# set mass to $1
		my $m=f_number_to_c_number($1);
		for(my $i=0;$i<=$#main::ms;$i++)
		{
		    if(abs($main::ms[$i]-$m)<1e-5)
		    {
			$main::zams_m2_menu->set_active($i);
		    }
		}
	    }
	    
	}
	close INFO;
    }
}

sub toggle_tab
{
    # change to another tab
    my $x=$_[0]->get_current_page();
    my $cmd='\&show_'.$tab_labels[$x].'_tab()';
    print "Toggle tab: selected page $x, cmd $cmd, but current tab $current_tab\n" if ($vb);
    $current_tab=$x;
    eval($cmd);
    if($@)
    {
	print "Eval error toggling to tab $x : $@\n";
    }
    GTK2_tab_bug_workaround();
}

sub GTK2_tab_bug_workaround
{
    no warnings;
    eval Gtk2->main_do_event('buggy') if($gtk2_tab_bug_workaround);
    use warnings;
}

sub toggle_internals_tab
{
    my $x=$_[0]->get_current_page();
    print "Toggle internals tab from $current_internals_tab to $x\n" if($vb);
    switch_internals_minitab($x);
    $current_internals_tab=$x;
    GTK2_tab_bug_workaround();
}

sub reset_options
{
    # reset options! this involves resetting lots of stuff...sorry!
    toggle_hourglass(1);
    reset_evcode_options_to_defaults();
    load_evcode_defaults();
    reset_options_tab();
    TRUE;
}

sub reset_options_tab
{
    my $ntab=$tab_numbers{Options};
    $tab_table[$ntab]->hide;
    
    $zams_m1_menu->destroy;
    $zams_m2_menu->destroy;
    $zams_z_menu->destroy;
    undef($options_tree_store);
    
    # reset metallicity/masses available
    @zs=();
    @ms=();
    
    reset_evcode_options();

    # destroy tab table 0 and all its children
    map
    {
	$frames[$ntab]->remove($_);
    }$frames[$ntab]->get_children;
    $tab_table[$ntab]->destroy; # just in case!

    $made_tab[$ntab]=0;
    show_Options_tab(1);
    toggle_hourglass(0);    
    TRUE;
}

sub variable_edited
{
    # we have edited variable $var to value $t
    my $var=$_[0];
    my $t=$_[1];

    # if $index is set, then we just want to edit array entry $index
    my $index=$_[2];

    return if ($t=~/^\s*$/o); # if empty do NOT set
    $var=~/^(.*)\.(.*)/o;

    my $block=$1;
    my $item=$2;
    
    # get the block data name (i.e. the file name)
    my $block_data=$block_location{$var};
    print "block_data = $block_data, item=$item\n"if($vb);

    if(defined $index)
    {
	# array variable : just set the appropriate data
	my @vals = split(/\s+/,evcode_value_from_hash($block_data,$var));
	print "\$index = $index defined : Array was @vals\n"if($vb);
	$vals[$index]=$t;
	$t = join(' ',@vals); 
	print "New array @vals\n"if($vb);
    }
    
    # enter the data
    my $cmd="\$$block_data=set_evcode_var(\$$block_data,'$block','$item','$t');";
    print "Attempt eval of $cmd\n"if($vb);
    evcode_eval($cmd);
    print "Eval successful\n"if($vb);
    save_evcode_options();
}

sub text_variable_edited
{
    # get have changed a text variable
    my $w=$_[0];
    my ($var,$index)=@{$_[1]};

    print "Text variable edited (w=$w, var=$var, index=$index) :"if($vb);
    my $t=$w->get_text;
    print "text=$t\n"if($vb);
    variable_edited($var,$t,$index);
}

sub menu_variable_edited
{
    # we have changed a menu variable
    my $w=$_[0];
    my $var=$_[1];
    my $t=$w->get_active_text_local;
    variable_edited($var,$t);
}
sub spinbutton_variable_edited
{
    # we have changed a spinny thing
    my $w=$_[0];
    my $var=$_[1]; 
    my $t=$w->get_value;
    print "Spin $var = $t\n";
    variable_edited($var,$t);
    $w->update;
}


sub setup_variable_help
{
    # set up variable help from in format:
    # BLOCK.VAR1 some help text...
    # BLOCK,VAR2 more help text... 
    my %variable_help;

    map
    {
	$variable_help{$1}=$_ if(s/^(\S+)\s+//o);
    }variable_help_array();
    
    return(%variable_help);
}

sub clear_events_queue
{
    # clear the gtk events queue before doing heavy CPU things
    if(defined($main_window))
    {
	Gtk2->main_iteration while (Gtk2->events_pending);
    }
    TRUE;
}


sub update_Options_tab
{
    return if($evcode ne 'TWIN');
    # Update the options tab (currently unused, but useful to 
    # have a function for the future)
    print "Update options tab\n" if $vb;
    TRUE;
}


sub zams_z_menu_changed
{
    # metallicity menu has changed, update mass options based on Z selection
    my $z=$zams_z_menu->get_active;

    my $activem1=$zams_m1_menu->get_active; 
    my $activem2=$zams_m2_menu->get_active;

    my $m1was = $activem1>0 ? $ms[$activem1] : 1;
    my $m2was = $activem2>0 ? $ms[$activem2] : 1;

    # remove all old items
    while($zams_m_count-->=0)
    {
	$zams_m1_menu->remove_text(0);
	$zams_m2_menu->remove_text(0);
    }

    $z=$zs[$z];
    $zams_m_count=0;
    @ms=zams_ms($z);
    
    # default to the first item
    $zams_m1_menu->set_active(0);
    $zams_m2_menu->set_active(0);

    if($#ms==-1)
    {
	$zams_m1_menu->set_sensitive(FALSE);
	$zams_m2_menu->set_sensitive(FALSE);
    }
    else
    {
	$zams_m1_menu->set_sensitive(TRUE);
	$zams_m2_menu->set_sensitive(TRUE);
	map
	{
	    $zams_m_count++;
	    $zams_m1_menu->append_text(sprintf '%g',$_);
	    $zams_m2_menu->append_text(sprintf '%g',$_);
	    if(defined($m1was)&&(abs($_-$m1was)<1e-5))
	    {
		$zams_m1_menu->set_active(MAX(0,$zams_m_count-1));
	    }
	    if(defined($m2was)&&(abs($_-$m2was)<1e-5))
	    {
		$zams_m2_menu->set_active(MAX(0,$zams_m_count-1));
	    }
	}@ms;
    }

    TRUE;
}




############################################################
### Evolve tab
############################################################

sub show_Evolve_tab
{
    my $ntab=$tab_numbers{Evolve};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0) 
    {
	toggle_hourglass(1);
	if(defined($main::modinfo{evolve_tab_function}))
	{
	    # if an options tab vbox function is given by the evcode module, use it instead
	    $tab_table[$ntab]=&{$main::modinfo{evolve_tab_function}};
	}	
	elsif($evcode eq 'TWIN')
	{
	    $tab_table[$ntab]=Gtk2::Table->new(3,1,FALSE);
	    # add Evolve, Stop buttons etc and output window
	    
	    my $topline=Gtk2::Table->new(7,1,FALSE);
	    
	    $evolve_button=Gtk2::Button->new_with_label_and_tooltip('Evolve','Click to start stellar evolution');
	    $stop_button=Gtk2::Button->new_with_label_and_tooltip('Terminate','Click to stop stellar evolution');
	    $clear_button=Gtk2::Button->new_with_label_and_tooltip('Clear Log Window','Click here to clear the log window of its contents');
	    
	    $update_button=Gtk2::Button->new_with_label_and_tooltip('Update Log Window','Click here to update the log window contents. If you hold shift and then click you will refresh the entire log, otherwise only the most recent part of the log will be updated.');

	    $follow_button=Gtk2::ToggleButton->new_with_label_and_tooltip('Follow Log','Click here to follow the bottom of the log file.');

	    $evolve_button->signal_connect (clicked => \&evolve_wrapper);
	    $stop_button->signal_connect (clicked => \&stop_wrapper);
	    $clear_button->signal_connect(clicked => sub
					  {
					      # clear the log window in the evolve tab
					      $evolve_log_textview->get_buffer->set_text('');
					      TRUE;
					  });
	    $update_button->signal_connect(clicked => \&update_Evolve_logview);
	    $follow_button->signal_connect(clicked => sub
					   {
					       $follow_evolve_log=1-$follow_evolve_log;
					       move_to_end_of_log() if($follow_evolve_log==1);
					   });

	    $evolve_button->set_sensitive(TRUE);
	    $stop_button->set_sensitive(FALSE);
	    $clear_button->set_sensitive(TRUE);
	    $follow_button->set_sensitive(TRUE);

	    # add text window which tails the out file
	    my $log_window=Gtk2::ScrolledWindow->new(undef,undef);
	    $log_window->set_shadow_type('etched-out');
	    $log_window->set_policy('automatic','automatic');
	    $log_window->set_border_width(5);
	    $evolve_log_textview=Gtk2::TextView->new();
	    update_Evolve_logview();

	    # set the content
	    $evolve_log_textview->hide;
	    $evolve_log_textview->set_cursor_visible(FALSE);
	    $evolve_log_textview->set_wrap_mode('word');
	    $evolve_log_textview->set_left_margin(1);
	    $evolve_log_textview->set_right_margin(1);
	    $evolve_log_textview->set_left_margin(1);
	    $evolve_log_textview->set_left_margin(1);
	    $evolve_log_textview->set_editable(FALSE);
	    $evolve_log_textview->get_buffer->signal_connect(mark_set => \&Evolve_markset);
	    $evolve_iter=$evolve_log_textview->get_buffer->get_start_iter;

	    # set a small font so we can fit everything in (sorry)
	    my $font_desc = Gtk2::Pango::FontDescription->from_string ('Courier 7');
	    $evolve_log_textview->modify_font ($font_desc);

	    $topline->attach($evolve_button,0,1,0,1,'shrink','shrink',5,5);
	    $topline->attach(Gtk2::Label->new(''),1,2,0,1,'expand','shrink',5,5);
	    $topline->attach($stop_button,2,3,0,1,'shrink','shrink',5,5);
	    $topline->attach($clear_button,3,4,0,1,'shrink','shrink',5,5);
	    $topline->attach($update_button,4,5,0,1,'shrink','shrink',5,5);
	    $topline->attach($follow_button,5,6,0,1,'shrink','shrink',5,5);
	    $topline->attach(starbuttons('h'),6,7,0,1,'shrink','shrink',5,5);

	    $tab_table[$ntab]->attach($topline,0,1,0,1,'expand','shrink',0,0);
	    $tab_table[$ntab]->attach($log_window,0,1,1,2,['expand','fill'],['expand','fill'],5,5);
	    $log_window->hide;
	    $log_window->add($evolve_log_textview);	    
	}
	$frames[$ntab]->add(scrolled($tab_table[$ntab]));
	$made_tab[$ntab]=1;
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }

    increment_timing('show_Evolve_tab',$time0) if($timings);
    TRUE;
}

sub evolve_wrapper
{
    # wrapper : called when evolution starts
    my $time0 = [gettimeofday()] if($timings);

    # You should call run_evcode and perhaps 
    # some other things here.
    save_evcode_options();
    
     # set nice value
    $running_nice=MAX(0,get_opt(3));
    update_nice_command();

    # clear up global caches
    clear_global_caches();

    # disable layer changing and close layers dialogue
    $wttslayers_options->set_sensitive(FALSE);
    if(defined $wttslayers_window)
    {
	$wttslayers_window->destroy;
	$wttslayers_window=undef;
    }

    # run evolution code, do NOT pass anything to
    # the function, as if you pass 1 then the evolution
    # code will run and return, rather than fork, and you
    # want it to fork if you're to follow the output
    # (also do not pass a second argument)
    run_evcode();

    # force update of tabs, as they may well be out of date
    update_HRD_tab(1);
    update_Structure_tab(1);
    update_System_tab(1);
    update_Internals_tab(1);
    $just_run_the_code=1;
    $code_has_been_run=1;

    $wttslayers_options->set_sensitive(FALSE);
  
    increment_timing('evolve_wrapper',$time0) if($timings);
}


sub stop_wrapper
{
    print "stop_wrapper()\n"if($vb);
    my $time0 = [gettimeofday()] if($timings);

    # Stop the evolution code cleanly
    if($running_the_code==1)
    {
	print "Call stop\n" if ($vb);
	set_status_bar("Stopping  ...");

	# stop the evolution code
	stop_evcode();
	pidkill();

	set_status_bar("Stopped");
	$running_the_code=0;

	if(defined($evolve_button))
	{
	    print "Update evolve tab\n"if($vb);
	    $evolve_button->set_sensitive(TRUE);
	    $stop_button->set_sensitive(FALSE);
	    update_Evolve_tab(1);
	}
	print "Update HRD tab\n"if($vb);
	update_HRD_tab(1);
	@start_status=();
	@current_status=();
    }

    # save options and allow layer changes
    $wttslayers_options->set_sensitive(TRUE) if(defined $wttslayers_options);

    print "Save misc opts\n"if($vb);
    save_misc_opts();
    save_evcode_options();
    
    if($evcode_module eq 'STARS')
    {
	my $m1=$main::ms[$main::zams_m1_menu->get_active];
	my $m2=$main::ms[$main::zams_m2_menu->get_active];
	my $z=$main::zs[$main::zams_z_menu->get_active];
	save_info($z,$m1,$m2);
    }

    increment_timing('stop_wrapper',$time0) if($timings);
    FALSE;
}

sub save_info
{
    # save .info file, this stores information about the run
    # which is NOT in the .dat or .run file, such as the metallicity!
    # (m,z) are passed in
    no warnings;
    my $s = "Metallicity $_[0]\nMass $_[1]\nMass2 $_[2]\n";
    state $prevs;
    if(!defined $prevs || $s ne $prevs)
    {
	dumpfile($main::runname.'.info',$s);
    }
    $prevs = $s;
    use warnings;
}

sub update_Evolve_logview
{
    return if ($evcode eq 'binstar');
    my $content=' ';
    my $time0 = [gettimeofday()] if($timings);
    toggle_hourglass(1);
    print "Look for log in $runname.out$nstar\n"if($vb);
    my $prefix = $log_timestamps ? datestamp_prefix() : '';

    my $file=$runname.'.out'.$nstar;
    if(ok_file($file)==1)
    {
	# get log from the previous run, and show it,
	# but not if it is >1MB! (this slows things down a lot)
	print "Getting tail of previous log file\n"if($vb);
	if((-s $runname.'.out'.$nstar<1e6)||("@keys_pressed"=~/Shift_[LR]/o))
	{
	    print "Slurp\n"if($vb);
	    $content=slurp("$runname.out$nstar");
	    print "Slurp finished\n"if($vb);
	}
	else
	{
	    # get the final 300 lines of the file
	    print "open tail\n"if($vb);
	    
	    if((-s $file > 10)&&($os ne 'windows'))
	    {
		# use tail command, not File::Tail
		# to get the previous lines of the file
		$content=`tail -300 $runname.out$nstar`;
	    }
	    my $tail=File::Tail->new(name=>$file,
				     tail=>0,
				     debug=>1,
				     ignore_nonexistant=>1,
				     nowait=>1
		);
	    my @c;
	    my $l;
	    print "Reading tail\n"if($vb);
	    while(defined($tail)&&(defined($l=$tail->read))&&($l ne ''))
	    {
		push(@c,$l);
	    }
	    $content.=join('',@c);
	}
    }
    
    if((!(defined($content)))||($content eq '')||($content=~/^\s*$/o))
    {
	# nothing to show
	$content='Log file output will go here...';
    }  
    else
    {
	chomp($content);
	$content.="\n";
	$content=~s/^/$prefix/mg if(defined($prefix));
    }

    # add newlines before model descriptions
    print "Add newlines to \$content of length ",length($content),"\n"if($vb);
    $content=~s/(\n\s+-?\d+\s+M\/dty\/\s+Po\/P.*)/\n$1/g;

    # now set the content
    print "Setting buffer ... $content\n"if($vb);
    if(defined($evolve_log_textview))
    {
	my $buffer=$evolve_log_textview->get_buffer;
	if(defined($evolve_iter))
	{
	    # not first time
	    $buffer->set_text($content);
	}
	else
	{
	    # first time  : set on a timeout so GUI loads more quickly
	    # and we then set the text
	    Glib::Timeout->add(10,sub{
		$buffer->set_text($content);
		evcode_colour_log_text($buffer);
		$evolve_iter=$evolve_log_textview->get_buffer->get_start_iter ;
		FALSE;
			       });
	}
	print "Buffer set\n"if($vb);
    }

    # try to get around encoding problems by removing dodgy null
    # characters
    $content=~s/\x00//go;
    
    move_to_end_of_evolve_log();
    toggle_hourglass(0);  
    increment_timing('update_Evolve_logview',$time0) if($timings);
}

sub update_Evolve_tab
{ 
    my $time0 = [gettimeofday()] if($timings);
    
    if((($running_the_code==1)||($_[0]==1))
       && defined($evolve_log_textview))
    {
	# code is running, use File::Tail to get info 
	# and output to the screen
	toggle_hourglass(1);
	# update log view
	my $buffer=$evolve_log_textview->get_buffer;

	if(defined($buffer) && defined($evolve_log_file_tails[$nstar-1]))
	{
	    print "Tail log file\n"if($vb);
	    my @buf;
	    my $lim = $_[0]==1 ? 1000000 : 10000;
	    print "Lim = $lim\n"if($vb);

	    # read as much of the file as you can, but limit to $lim lines
	    # at a time to prevent blocking (this is quite arbitrary and
	    # a "better way" should be found, but it seems to work!)
	    print "While...(star $nstar, buflen $#buf)\n"if($vb);
	    my $l;
	    while(defined($l=$evolve_log_file_tails[$nstar-1]->read)&&($l ne '')
		  &&($#buf<$lim))
	    {
		print "Push \"$l\"\n"if($vb);
		push(@buf,$l);
	    }


	    print "Insert buffer\n"if($vb);
	    if($#buf>-1)
	    {

		my $buf=join('',@buf);
		my $prefix = $log_timestamps ? datestamp_prefix() : '';
		$buf=~s/^/$prefix/mg if(defined($prefix));

		$buffer->insert($buffer->get_end_iter,$buf);
		if($just_run_the_code==1)
		{
		    # update the colour of the whole log the first
		    # time after the code has just been run
		    evcode_colour_log_text($buffer);
		    $just_run_the_code=0;
		}
		else
		{
		    # update the colour of the lines which are new and the
		    # previous, say, five, just in case they are connected
		    # to the current lines. If your log is more complicated
		    # you might want to increase 10 to a suitably higher number.
		    evcode_colour_log_text($buffer,$#buf+10);    
		}
	    }
	}
	elsif($vb)
	{
	    print "Log file tail not defined for $nstar\n";
	}

	move_to_end_of_evolve_log();
	toggle_hourglass(0);
    }
    increment_timing('update_Evolve_tab',$time0) if($timings);
    TRUE;
}

sub move_to_end_of_evolve_log
{
    # scroll to the end of the evolve log
    if($follow_evolve_log==1)
    {
	clear_events_queue();
	Evolve_jump_to_end();
    }
}

sub Evolve_markset
{
    $evolve_iter=$_[1];
}


sub log_markset
{
    $log_iter=$_[1];
}

sub Evolve_jump_to_top
{
    # move to the top of the log
    clear_events_queue();
    $evolve_log_textview->scroll_to_iter($evolve_log_textview->get_buffer->get_start_iter,0.0,TRUE,0.0,0.0);
}

sub Evolve_jump_to_nan
{
    clear_events_queue();
    my $buffer=$evolve_log_textview->get_buffer;
    my $mark=$buffer->get_insert;
    my $iter=$buffer->get_iter_at_mark($mark);
    my ($iter1,$iter2)=$iter->forward_search('NaN','text-only',undef);
    $evolve_log_textview->scroll_to_iter($iter,0.0,TRUE,0.0,0.0);
}

sub Evolve_jump_to_previous_nan
{
    clear_events_queue();
    $evolve_log_textview->scroll_to_iter($evolve_log_textview->get_buffer->get_iter_at_mark->forward_search('NaN'),
				  0.0,TRUE,0.0,0.0);
}

sub Evolve_jump_to_end
{
    # move to the top of the log
    clear_events_queue();
    $evolve_log_textview->scroll_to_iter($evolve_log_textview->get_buffer->get_end_iter,0.0,TRUE,0.0,0.0);
	
}

sub pidkill
{
    # if the code is running, it will have a pid number
    # stored in a file the cachedir

    # kill the pid, and remove the file
    my $f="$main::cachedir/wtts.pid";
    my $running_pid=running_pid();

    if($running_pid!=0)
    {
	if($os eq 'windows')
	{
	    # use Win32::Process to kill the process
	    my $exitcode=0;
	    eval 'Win32::Process::KillProcess($running_pid,$exitcode);';
	}
	else
	{
	    # use native Unix function
	    certain_kill($running_pid);
	}
    }
    unlink $f;
    return(0);
}

sub running_pid
{
    # determine the process ID of the evolutionary code
    # that is currently running
    my $f="$cachedir/wtts.pid";
    if(-f $f)
    {
	if(open(PID,"<$f")!=0) # don't die!
	{ 
	    while(<PID>)
	    {
		chomp;
		if(/^\d+$/o)
		{
		    close PID;
		    return ($_);
		}
	    }
	    close PID;
	}  
    }
    return(0);
}


############################################################
### Log window
############################################################

sub show_Log_tab
{
    return if($evcode ne 'TWIN');
    my $ntab=$tab_numbers{Log};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0) 
    {
	toggle_hourglass(1);
	if(defined($main::modinfo{log_tab_function}))
	{
	    # if an options tab vbox function is given by the evcode 
	    # module, use it instead
	    my $func=$main::modinfo{log_tab_function};
	    $tab_table[$ntab]=&{$func};
	}	
	elsif($evcode eq 'TWIN')
	{
	    $tab_table[$ntab]=Gtk2::Table->new(3,1,FALSE);
	    	    
	    my $topline=Gtk2::Table->new(7,1,FALSE);
	    
	    my $clear=Gtk2::Button->new_with_label_and_tooltip('Clear Log Window','Click here to clear the log window of its contents');
	    my $update=Gtk2::Button->new_with_label_and_tooltip('Update Log Window','Click here to update the log window contents. If you hold shift and then click you will refresh the entire log, otherwise only the most recent part of the log will be updated.');
	    my $follow=Gtk2::ToggleButton->new_with_label_and_tooltip('Follow Log','Click here to follow the bottom of the log file.');

	    $clear->signal_connect(clicked => sub
					  {
					      # clear the log window 
					      $log_textview->get_buffer->set_text('');
					      TRUE;
					  });
	    $update->signal_connect(clicked => \&update_Log_logview);
	    $follow->signal_connect(clicked => sub
					   {
					       $follow_log=1-$follow_log;
					       move_to_end_of_log() if($follow_log==1);
					   });

	    $clear->set_sensitive(TRUE);
	    $follow->set_sensitive(TRUE);

	    # add text window which tails the out file
	    my $log_window=Gtk2::ScrolledWindow->new(undef,undef);
	    $log_window->set_shadow_type('etched-out');
	    $log_window->set_policy('automatic','automatic');
	    $log_window->set_border_width(5);
	    $log_textview=Gtk2::TextView->new();
	    update_Log_logview();

	    # set the content
	    $log_textview->hide;
	    $log_textview->set_cursor_visible(FALSE);
	    $log_textview->set_wrap_mode('word');
	    $log_textview->set_left_margin(1);
	    $log_textview->set_right_margin(1);
	    $log_textview->set_left_margin(1);
	    $log_textview->set_left_margin(1);
	    $log_textview->set_editable(FALSE);
	    $log_textview->get_buffer->signal_connect(mark_set => \&log_markset);
	    $log_iter=$log_textview->get_buffer->get_start_iter;

	    # set a small font so we can fit everything in (sorry)
	    my $font_desc = Gtk2::Pango::FontDescription->from_string ('Courier 7');
	    $log_textview->modify_font ($font_desc);


	    $topline->attach($clear,3,4,0,1,'shrink','shrink',5,5);
	    $topline->attach($update,4,5,0,1,'shrink','shrink',5,5);
	    $topline->attach($follow,5,6,0,1,'shrink','shrink',5,5);

	    $tab_table[$ntab]->attach($topline,0,1,0,1,'expand','shrink',0,0);
	    $tab_table[$ntab]->attach($log_window,0,1,1,2,['expand','fill'],['expand','fill'],5,5);
	    $log_window->hide;
	    $log_window->add($log_textview);	    
	}
	$frames[$ntab]->add(scrolled($tab_table[$ntab]));
	$made_tab[$ntab]=1;
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }

    increment_timing('show_Log_tab',$time0) if($timings);
    TRUE;
}


sub update_Log_logview
{
    return if ($evcode ne 'TWIN');
    my $content=' ';
    my $time0 = [gettimeofday()] if($timings);
    toggle_hourglass(1);
    print "Look for log in $runname.log\n"if($vb);
    my $prefix = $log_timestamps ? datestamp_prefix() : '';
    
    if(ok_file( "$runname.log")==1)
    {
	# get log from the previous run, and show it,
	# but not if it is >1MB! (this slows things down a lot)
	print "Getting tail of previous log file\n"if($vb);
	if((-s "$runname.log"<1e6)||("@keys_pressed"=~/Shift_[LR]/o))
	{
	    print "Slurp\n"if($vb);
	    $content=slurp("$runname.log");
	    print "Slurp finished\n"if($vb);
	}
	else
	{
	    # get the final 300 lines of the file
	    print "open tail\n"if($vb);
	    
	    if((-s "$runname.log" > 10)&&($os ne 'windows'))
	    {
		# use tail command, not File::Tail
		# to get the previous lines of the file
		$content=`tail -300 $runname.log`;
	    }
	    my $tail=File::Tail->new(name=>"$runname.log",
				     tail=>0,
				     debug=>1,
				     ignore_nonexistant=>1,
				     nowait=>1
				     );
	    my @c;
	    my $l;
	    print "Reading tail\n"if($vb);
	    while(defined($tail)&&(defined($l=$tail->read))&&($l ne ''))
	    {
		push(@c,$l);
	    }
	    $content.=join('',@c);
	}
    }
    
    if((!(defined($content)))||($content eq '')||($content=~/^\s*$/o))
    {
	# nothing to show
	$content='Log file output will go here...';
    }
    else
    {
	chomp($content);
	$content.="\n";
	$content=~s/^/$prefix/gm if(defined($prefix));
    }

    # add newlines before model descriptions
    print "Add newlines to \$content of length ",length($content),"\n"if($vb);
    $content=~s/(\n\s+-?\d+\s+M\/dty\/\s+Po\/P.*)/\n$1/g;

    # now set the content
    print "Setting buffer ... $content\n"if($vb);
    my $buffer=$log_textview->get_buffer;
    $buffer->set_text($content);
    print "Buffer set\n"if($vb);
    $log_iter=$log_textview->get_buffer->get_start_iter if(!defined($log_iter));
    evcode_colour_log_text($buffer);

    # try to get around encoding problems by removing dodgy null
    # characters
    $content=~s/\x00//go;
    
    move_to_end_of_log();
    toggle_hourglass(0);  
    increment_timing('update_Log_logview',$time0) if($timings);
}

sub move_to_end_of_log
{
    # scroll to the end of the log
    if($follow_log==1)
    {
	clear_events_queue();
	$log_textview->scroll_to_iter($log_textview->get_buffer->get_end_iter,0.0,TRUE,0.0,0.0);
    }
}

sub update_Log_tab
{
    return if(!defined $log_textview);
    return if($evcode ne 'TWIN');
    my $buffer=$log_textview->get_buffer;
    if(defined($log_file_tail))
    {
	print "Tail log file\n"if($vb);
	my @buf;
	my $lim = $_[0]==1 ? 1000000 : 10000;
	print "Lim = $lim\n"if($vb);

	# read as much of the file as you can, but limit to $lim lines
	# at a time to prevent blocking (this is quite arbitrary and
	# a "better way" should be found, but it seems to work!)
	print "While...(buflen $#buf)\n"if($vb);
	my $l;
	while(defined($l=$log_file_tail->read)&&($l ne '')
	      &&($#buf<$lim))
	{
	    print "Push \"$l\"\n"if($vb);
	    push(@buf,$l);
	}

	print "Insert buffer\n"if($vb);
	if($#buf>-1)
	{
	    my $buf=join('',@buf);
	    my $prefix = $log_timestamps ? datestamp_prefix() : '';
	    $buf=~s/^/$prefix/mg if(defined($prefix));
	    
	    $buffer->insert($buffer->get_end_iter,$buf);
	    if($just_run_the_code==1)
	    {
		# update the colour of the whole log the first
		# time after the code has just been run
		evcode_colour_log_text($buffer);
		$just_run_the_code=0;
	    }
	    else
	    {
		# update the colour of the lines which are new and the
		# previous, say, five, just in case they are connected
		# to the current lines. If your log is more complicated
		# you might want to increase 10 to a suitably higher number.
		evcode_colour_log_text($buffer,$#buf+10);    
	    }
	}
    }

    1;
}


############################################################
### HR Diagram tab 
############################################################

sub show_HRD_tab
{
    my $ntab=$tab_numbers{HRD};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	# make HR diagram tab
	$tab_table[$ntab]=Gtk2::Table->new(3,2,FALSE);
	
	# make range boxes
	@HRD_range_widgets=make_rangeboxes(@{$gnuplot_options{'HRD range'}});
	my $h=Gtk2::HBox->new;
	my $h2=Gtk2::HBox->new;

	map
	{
	    $h->pack_start($_,FALSE,FALSE,0);	   
	}(Gtk2::Label->new('X Range'),$HRD_range_widgets[0],$HRD_range_widgets[1],
	  Gtk2::Label->new('Y Range'),$HRD_range_widgets[2],$HRD_range_widgets[3],
	  $HRD_range_widgets[6]);
	$HRD_range_widgets[6]->signal_connect(clicked=>sub{
	    $prev_plotted_hrd_string=undef; # forces redraw
				      });
	      	
	# make options for labels
	my $label_col_menu=Gtk2::ComboBox->new_text;
 	my $spacing_menu=Gtk2::ComboBox->new_text;
	$spacing_menu->set_size_request(150,30);
	$h2->pack_start(Gtk2::Label->new('Label With'),FALSE,FALSE,0);
	map
	{
	    $label_col_menu->append_text($_);
	}('None',@plt1cols,'logg','calL');
	
	$label_col_menu->set_wrap_width(3);
	$label_col_menu->signal_connect(changed=>\&set_hrd_col_menu);
	$label_col_menu->set_active($gnuplot_options{'HRD label col'}+1);

	$h2->pack_start(set_tooltip_with_eventbox($label_col_menu,'Here you can select what labels you would like to plot in the HR diagram'),FALSE,FALSE,0);

	# label spacing menu
	$h2->pack_start(Gtk2::Label->new('Label Spacing'),FALSE,FALSE,0);
	map
	{
	    $spacing_menu->append_text($_);
	}@HRD_spacings;
	$spacing_menu->signal_connect(changed=>\&set_hrd_label_spacing);
	$spacing_menu->set_active($gnuplot_options{'HRD label spacing index'});
	$h2->pack_start(set_tooltip_with_eventbox($spacing_menu,'Use this menu to select the distance between labels on the HR diagram. A smaller number means labels are closer together.'),FALSE,FALSE,0);

	# labels for the start of curves
	$h2->pack_start(Gtk2::Label->new('Start Label'),FALSE,FALSE,0);
	my $start_menu = Gtk2::ComboBox->new_text;
	$start_menu->set_size_request(150,30);
	map
	{
	    $start_menu->append_text($_);
	}('None',@plt1cols);

	if($evcode_module eq 'STARS')
	{
	    my ($initdat,$initrun) = evcode_hashes(maindir(),$runname); 
	    map
	    {
		$start_menu->append_text($_);
	    }(sort keys(%$initdat),sort keys(%$initrun));
	}

	$start_menu->set_wrap_width(3);
	$start_menu->signal_connect(changed=>sub{
	    $gnuplot_options{'HRD start col'} = $_[0]->get_active-1;
	    $gnuplot_options{'HRD start col text'} = $_[0]->get_active_text;
				    });
	$start_menu->set_active($gnuplot_options{'HRD start col'}+1);
	$h2->pack_start(set_tooltip_with_eventbox($start_menu,
						  'Here you can select what label you would like to plot at the start of each track in the HR diagram.'),FALSE,FALSE,0);

	# choose star(s) to plot
	my $nstars_menu=Gtk2::ComboBox->new_text;

	map
	{
	    $nstars_menu->append_text($_);
	}@starswitches;

	$nstars_menu->set_active($gnuplot_options{'HRD star'}-1);
	$nstars_menu->signal_connect(changed=>sub{
	    # select HRD star for plotting
	    $gnuplot_options{'HRD star'}=starswitch($_[0]->get_active_text_local());
	    print "Set HRD star $gnuplot_options{'HRD star'}\n" if ($vb);
				     });

	map
	{
	    $h->pack_start($_,FALSE,FALSE,0);
	}
	(
	 text_combobox({labels=>['No Colours',
				 'Stellar Colours',
				 'Stellar Colours (extended)',
				 @plt1cols],
			tooltip=>'Choose \'true\' stellar colours, or a different variable to use as the colour',
			var=>\$gnuplot_options{'HRD colours'},
			active=>$gnuplot_options{'HRD colours'}}),
	 togglebutton({label=>'Colour box',
		       tooltip=>'Show gnuplot\'s colourbox',
		       var=>\$gnuplot_options{'HRD cbox'},
		       active=>$gnuplot_options{'HRD cbox'}}),
	 togglebutton({label=>'Radii',
		       tooltip=>'Click here to plot lines of constant radius',
		       var=>\$gnuplot_options{'HRD radii'},
		       active=>$gnuplot_options{'HRD radii'}}),
	 togglebutton({label=>'Status',
		       tooltip=>'Toggle status of the star(s) in the HRD',
		       var=>\$gnuplot_options{'HRD status'},
		       active=>$gnuplot_options{'HRD status'}}),
	 palettes_combobox({var=>\$gnuplot_options{'HRD palette'},
			    x=>100,
			    active=>$gnuplot_options{'HRD palette'}})
	);

	if($shrd)
	{
	    $h->pack_start(
		 togglebutton({label=>'sHRD',
			       tooltip=>'Click here to convert to an sHRD',
			       var=>\$gnuplot_options{'HRD sHRD'},
			       active=>$gnuplot_options{'HRD sHRD'}}),
		FALSE,FALSE,0);
	}


	$h2->pack_start(set_tooltip_with_eventbox($nstars_menu,'Select whether to plot star 1, star 2 or both stars'),FALSE,FALSE,0);

	# update with the current image
	$HRD_image=Gtk2::Image->new_from_pixbuf((no_image())[1]);

	# image properties and callbacks
	%HRD_image_properties=('image'=>'HRD',
			       'image widget'=>$HRD_image,
			       'noimage'=>1);
	
	# align image to the left
	$tab_table[$ntab]->attach(set_image_callbacks('HRD',
						      $HRD_image
						      ,\%HRD_image_properties),
				  0,1,2,3,'expand','expand',10,10);

	$tab_table[$ntab]->attach($h,0,1,0,1,'expand','fill',5,5);
	$tab_table[$ntab]->attach($h2,0,1,1,2,'expand','fill',5,5);
	$tab_table[$ntab]->attach(Gtk2::Label->new('No Image'),0,1,2,3,'expand','fill',5,5);

	my $container = Gtk2::EventBox->new;
	my $s = scrolled($tab_table[$ntab]);
	$container->add($s);
	$frames[$ntab]->add($container);
	$made_tab[$ntab]=1;
	$frames[$ntab]->show_all;

	my $offset = $h->allocation->height + $h2->allocation->height + 40;
	
	sub HRD_image_size
	{
	    no warnings;
	    my $y = $container->allocation->height - $offset - 5*$gtkrc{slider_width};
	    my $x=$frames[$ntab]->allocation->width - 4*$gtkrc{slider_width};
	    use warnings;
	    return (MAX(1,$x),MAX(1,$y));
	}

	toggle_hourglass(0);
    }

    update_HRD_tab(1);
    increment_timing('show_HRD_tab',$time0) if($timings);
    TRUE;
}

sub layerdata_plt_sizes
{
    # return the sizes of the plt files
    my $wttslayer=$_[0];
    my @x;
    # check plt1,2
    foreach my $stub (1,2)
    {
	my $file = $wttslayer.'/'.$modinfo{plt1file_stub}.$stub;
	$x[$stub-1] += (-f $file ? -s $file : 0); 
    }
    return @x;
}

sub layerdata_plt_wc
{
    # return the sizes of the plt files
    my $wttslayer=$_[0];
    my @x;
    # check plt1,2
    foreach my $stub (1,2)
    {
	my $f=$wttslayer.'/'.$modinfo{plt1file_stub}.$stub;
	if((-f $f) && (-s $f>0))
	{
	    $x[$stub-1]=rob_misc::file_linecount($f);
	}
    }
    return @x;
}


sub pltdata_ok
{ 
    # check if plt1,2 data is ok and plottable

    # default to data NOT being ok, then if there is ANY data in
    # ANY layer that is ok, set dataok=1
    my @datafilessize;
    my $dataok;
    foreach my $wttslayer (active_wttslayers())
    {
	my @s = layerdata_plt_sizes($wttslayer);
	foreach my $x (0,1)
	{
	    $datafilessize[$x] += $s[$x];
	}
    }
    no warnings;
    $dataok = ($datafilessize[0] + $datafilessize[1] > 0)*1;
    use warnings;
    return ($dataok, @datafilessize);
}

sub update_HRD_tab
{
    return if($updating{HRD} || time()<$next_update_time{HRD});
    $updating{HRD}=1;

    my $ntab=$tab_numbers{HRD};
    my $time0 = [gettimeofday()] if($timings);
    my @active_wttslayers=active_wttslayers();
    my ($dataok,@datafilessize) = pltdata_ok();
    my $colourstring = set_up_stellar_colours();

    printf "update_HRD : check datafilessizes 1=%d 2=%d; data ok ? %d; code running %d have run %d?\n",
    $datafilessize[0],$datafilessize[1],$dataok,$running_the_code,$have_run_the_code if($vb);

    if($dataok && defined $tab_table[$ntab])      
    {
	my $range = $gnuplot_options{'HRD range'};
	@$range=get_graphrange(@HRD_range_widgets);
	my @widgetrange=@$range;
	print "GRAPHRANGE @$range\n" if($vb);

	# scale image
	my ($width,$height)=HRD_image_size();

	# set in misc opts values (for pixbuf resizing)
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;

	no warnings;
	my $HRD_string=join(' ',@datafilessize,@misc_opts_values,@$range,gnuplot_options_string());
	use warnings;

	if(!(defined $prev_plotted_hrd_string &&
	   $HRD_string eq $prev_plotted_hrd_string))
	{
	    toggle_hourglass(1);

	    # make HR diagram
	    mkdir $cachedir.'/hrd';

	    # make strings for plot columns (gnuplot's 'using')
	    my @using=offset_cols(plt1using('log Teff','log Luminosity'));
 	    
	    if($shrd && $gnuplot_options{'HRD sHRD'})
	    {
		print "Use SHRD\n";
		
		my $Mcol = $plt1cols{Mass};
		my $Rcol = $plt1cols{'log Radius'};
		my $Teffcol = $plt1cols{'log Teff'};

		@using = offset_cols($plt1cols{'log Teff'},
					       # log10( (Teff^4 / g)/Lsun)
					       # g = GM/R^2
				     '(log10('.
				     '((10.0**$'.$Teffcol.')**4.0)'. # (Teff/K) **4
				     '/'.
				     '($'.$Mcol.'/(10.0**$'.$Rcol.'*10.0**$'.$Rcol.')*'.(G*Msun/(Rsun*Rsun)).'*'.Lsun.')'.
				     '))');
		print "USING @using\n";
	    }

	    # choose Z : perhaps use stellar colours, or some other column 
	    # (remember -2 offset for the "Stellar Colours" menu items)
	    my $zusing = ($gnuplot_options{'HRD colours'}==1 || 
			  $gnuplot_options{'HRD colours'}==2) ? 
			  $using[0] : $plt1cols{$plt1cols[$gnuplot_options{'HRD colours'}-2]};
	    
	    # do we want logg?
	    my $loggcol = $#plt1cols+1;
	    my $calLcol = $loggcol + 1;
	    my $wantlogg = 1;# $gnuplot_options{'HRD label col'} == $loggcol;

	    my $labeldata=get_pltdata($gnuplot_options{'HRD star'},$wantlogg);
	    
	    # shrd todo : fix data range ! move labels as well
	    my @datarange=datarange($labeldata,$range,
				    -$modinfo{teffcol},
				    ($shrd && $gnuplot_options{'HRD sHRD'}) ? $calLcol : -$modinfo{lumcol});
	  
	    if("@datarange" =~/\*/)
	    {
		# no data in one of the ranges
		$HRD_image->set_from_pixbuf((nodatainrange_image())[1]);
	    }
	    else
	    { 
		my $dir = "$cachedir/hrd";
		my $pltfile=$dir.'/'.rand();
		
		open(HRD,">$pltfile.plt")||confess("cannot open $pltfile.plt for output : $!");

		# get numeric data ranges for creation of labels etc.
		foreach (0..3)
		{
		    $$range[$_]=$datarange[$_] if(!is_numeric($$range[$_]));
		}
		
		# extend slightly for plot (prevents gnuplot errors) if autoscaling
		{
		    my $f=0.05; # extension amount
		    if(($$range[0] ne '*') && ($widgetrange[0] eq '*') && ($widgetrange[1] eq '*'))
		    {
			my $dx=$$range[1]-$$range[0];
			$$range[0] -= $f*$dx;
			$$range[1] += $f*$dx;
		    }
		    if(($$range[2] ne '*') && ($widgetrange[2] eq '*') && ($widgetrange[3] eq '*'))
		    {
			my $dy=$$range[3]-$$range[2];
			$$range[2] -= $f*$dy;
			$$range[3] += $f*$dy;
		    }
		}
		
		# label curve
		if($gnuplot_options{'HRD label col'}>=0)
		{
		    map
		    {
			print HRD 'set label "{/*'.$gnuplot_options{'auto labelling font factor'}.' '.$$_[2].'" at first '.$$_[0].','.$$_[1]." front\n";
		    }labellist($gnuplot_options{'HRD label col'},
			       $gnuplot_options{'HRD label spacing'},
			       $labeldata,
			       -$modinfo{teffcol},-$modinfo{lumcol},$range);
		}

		# start labels
		if($gnuplot_options{'HRD start col'}>=0)
		{
		    # loop over stars and layers
		    my @files = ($nstar==1)||($nstar==2) ? ($nstar) : (1,2);
		    foreach my $layer (@active_wttslayers)
		    {
			foreach my $x (@files)
			{
			    my $f="$layer/$modinfo{plt1file_stub}$x";
			    if(ok_file($f))
			    {
				open(READPLTDATA,'<'.$f);
				<READPLTDATA>;  #ignore first line
				my $l = <READPLTDATA>;
				close READPLTDATA;
				$l=~s/^\s+//o;
				chomp $l;
				my @x;
				if($gnuplot_options{'HRD start col'} < (scalar @plt1cols))
				{
				    @x=(split(/\s+/o,$l))
					[-$modinfo{teffcol},-$modinfo{lumcol},$gnuplot_options{'HRD start col'}];
				}
				else
				{
				    @x=(split(/\s+/o,$l))
					[-$modinfo{teffcol},-$modinfo{lumcol}];
				    

				    # INIT_DAT or INIT_RUN variable : update me!
				    my ($initdat,$initrun) = evcode_hashes($layer,$runname);

				    $x[2] = defined $$initdat{$gnuplot_options{'HRD start col text'}} ? 
					$$initdat{$gnuplot_options{'HRD start col text'}} : $$initrun{$gnuplot_options{'HRD start col text'}};
				}
				
				if(defined $gnuplot_options{'HRD start label format'} && 
				   $gnuplot_options{'HRD start label format'} ne '')
				{
				    # use custom format
				    $x[2] = sprintf $gnuplot_options{'HRD start label format'},$x[2];
				}
				else
				{
				    # default to just a string
				    $x[2] = sprintf '%s',$x[2];
				}

				# I assume the track goes left to right, hence we want the label
				# declared 'right' 
				print HRD "set label \"$x[2]\" at first $x[0],$x[1] ",$gnuplot_options{'HRD start label position'}."\n";
			    }
			}
		    }
		}
		
		# flip ranges : should always have red stars in the top right
		if(is_numeric(@{$range}[0,1]) && ($$range[0]<$$range[1]))
		{
		    ($$range[0],$$range[1])=flip($$range[0],$$range[1]);
		}

		print HRD gnuplot_header($pltfile.'.png'),
		"set xrange\[$$range[0]:$$range[1]\] reverse
set yrange\[$$range[2]:$$range[3]\]
set title \"Hertzsprung-Russell Diagram\"
set xlabel \"log_{10} Teff\"
set ylabel \"log_{10} Luminosity\"
";

		# 1 or 2 == true stellar colours
		my $palette;
		if($gnuplot_options{'HRD colours'}==1 || $gnuplot_options{'HRD colours'}==2)
		{
		    # set up stellar colours
		    my @cbrange=(1000.0,37200.0);
		    map{$_=log10(logmin>$_ ? logmin : $_)}@cbrange if($evcode_module ne 'binstar');
			
		    my $cbrange= $gnuplot_options{'HRD colours'} == 2 ? 
			    # base colour range on data range    
			    $datarange[0].':'.$datarange[1] : 
			    # base colour range on true colours
			    $cbrange[0].':'.$cbrange[1];

		    print HRD "set cbrange\[$cbrange\]\n";
		    $palette="defined ($colourstring)";
		}
		elsif($gnuplot_options{'HRD colours'}>2)
		{
		    $palette=convert_palette($pm3d_palettes[$gnuplot_options{'HRD palette'}]);
		}
		
		my $extra_rmargin=0.0;

		# status information
		if($gnuplot_options{'HRD status'})
		{
		    my @l1=split(/\s+/o,tailoffile(maindir().'/'.$modinfo{plt1file_stub}.'1'));
		    my @l2=split(/\s+/o,tailoffile(maindir().'/'.$modinfo{plt1file_stub}.'2'));
		    my %opts = 
			(
			 '-1 Age/y'=>['Age','%g'],
			 '-0.9 dt/y'=>['Timestep','%g'],
			 '0 M/Msun'=>['Mass','%g'],
			 '0.1 M_{c,He}/Msun'=>['Helium core mass','%g'],
			 '0.2 M_{c,CO}/Msun'=>['CO core mass','%g'],
			 '0.3 M_{c,ONe}/Msun'=>['ONe core mass','%g'],
			 '1 L/Lsun'=>['Luminosity','%g'],
			 '1.1 L_{H}/Lsun'=>['Luminosity of hydrogen burning','%g'],
			 '1.2 L_{He}/Lsun'=>['Luminosity of helium burning','%g'],
			 '1.3 L_{C}/Lsun'=>['Luminosity of carbon burning','%g'],
			 '1.4 L_{/Symbol n}/Lsun'=>['Luminosity in neutrinos','%g'],

			 '2 T_{eff}/K'=>['Teff','%g'],
			 '3 R/Rsun'=>['Radius','%g'],
			 '4 logT_{max}'=>['log Maximum Temperature','%g'],
			 '4.1 logT_{c}'=>['log Central Temperature','%g'],
			);
		    		    
		    my $out1;
		    my $out2;
		    no warnings;
		    foreach my $opt (sort {($a=~/^(\S+)/)[0]<=>
					       ($b=~/^(\S+)/)[0]}keys %opts)
		    {
			my $optx = $opt;
			$optx=~s/^\S+\s*//o;
			$out1 .= $optx.'=';

			my $name = $opts{$opt}[0];
			my $format = $opts{$opt}[1];

			if($gnuplot_options{'HRD star'}==1)
			{
			    $out1 .= (sprintf $format,plt1data($name,\@l1))."\n"; 
			}
			elsif($gnuplot_options{'HRD star'}==2)
			{
			    $out1 .= (sprintf $format,plt1data($name,\@l2))."\n"; 
			}
			else
			{
			    $out1 .= (sprintf $format,plt1data($name,\@l1))."\n"; 
			    $out2 .= (sprintf $format,plt1data($name,\@l2))."\n";
			}
		    }

		    my $sol = solarsymbol();
		    map{
			s/\n/\\n/g;
			s/sun/_{$sol}/g;
		    }($out1,$out2);
		    use warnings;
		    if($gnuplot_options{'HRD star'}<=2)
		    {
			print HRD "set label \"$out1\" at graph 0.7,0.5\n";
		    }
		    else
		    {
			print HRD "set label \"$out1\" at graph 0.5,0.5
			           set label \"$out2\" at graph 0.75,0.5\n";
		    }
		}
		
		# lines of constant radius
		if($gnuplot_options{'HRD radii'})
		{
		    map{ print HRD $_ } (HRD_radii($range)); 
		    $extra_rmargin+=0.1;
		}
		$extra_rmargin+=0.1 if($gnuplot_options{'HRD cbox'});

		my $using=$using[0].':'.$using[1];
		my $title=' notitle ';
 				
		print HRD gnuplot_overrides(
		    {
			# extra margin for the colourbox
			extra_right_margin => -$extra_rmargin
		    }
		    );
		
		# if each line is coloured differently, it makes sense to have a key
		if($gnuplot_options{'HRD colours'}==0)
		{
		    print HRD "set key below\n";
		}
		else
		{
		    print HRD "unset key\n";
		}
		
		if($gnuplot_options{'HRD cbox'})
		{
		    print HRD "set colorbox";
		    print HRD " user origin 0.83,0.18 size 0.035,0.72 " if($gnuplot_options{'HRD radii'});
		    print HRD "\nset cblabel \"",
		    $gnuplot_options{'HRD colours'}<=2 ? 'log_{10} T_{eff}' : $plt1cols[$gnuplot_options{'HRD colours'}-3]
			,"\"\n"; 
		}
		else
		{
		    print HRD "unset colorbox\n";
		}
		print HRD "set palette $palette\n" if(defined($palette));

		if(($gnuplot_options{'HRD star'}==1)||
		   ($gnuplot_options{'HRD star'}==2)||
		   (! -f $modinfo{plt1file_stub}.'2') ||
		   (-s $modinfo{plt1file_stub}.'2' < 10))
		{
		    # plot just one star
		    print HRD 'plot ';
		    my $n=1;
		    print HRD join(',',map { 
			'"'.$_.'/'.$modinfo{plt1file_stub}.$gnuplot_options{'HRD star'}."\" using $using ".
			    ($gnuplot_options{'HRD colours'} ? 
			     ':'.$zusing.' '.strip_lc(withstring($gnuplot_options{'HRD star'})).' palette z ' : 
			     ' '.withstring($n++))
			    ." $title "
				   }@active_wttslayers);
		    print HRD "\n";
		}
		else
		{
		    # plot both stars
		    if($gnuplot_options{'HRD colours'})
		    {
			print HRD 'plot ';
			print HRD join(',',map 
				       {
					   "\"$modinfo{plt1file_stub}1\" using $using:$zusing ".strip_lc(withstring(1))." palette z $title"
				       }@active_wttslayers);
			print HRD ', ';
			print HRD join(',',map
				       {
					   "\"$modinfo{plt1file_stub}2\" using $using:$zusing ".strip_lc(withstring(2))." palette z $title" 
				       }@active_wttslayers);
			print HRD "\n";
		    }
		    else
		    { 
			print HRD 'plot ';
			print HRD join(',',map
				       {
					   "\"$modinfo{plt1file_stub}1\" using $using ".withstring(1)." $title"
				       }@active_wttslayers);
			print HRD ', ';
			print HRD join(',',map
				       {
					   "\"$modinfo{plt1file_stub}2\" using $using ".withstring(2)." $title"
				       }@active_wttslayers);
			print HRD "\n";
		    }
		}
		print HRD gpshow() if(!$persist_gnuplot);
		close HRD;
		
		my $f=$pltfile.'.plt';
		my $grange; # gnuplot's range (hash)
		my $err;
		if($gnuplot_options{'terminal type'}=~/postscript/io)
		{
		    if($persist_gnuplot)
		    {
			#print "Persist $f, $pltfile, ",get_opt(0),", $persist_gnuplot{in} $persist_gnuplot{out} $persist_gnuplot{err}, $gnuplot_options{'invert image'}\n";
			my $h = gnuplot_ps_to_png2(
			    { 
				'input file'=>$f,
				'png output file'=>$pltfile.'.png',
				dpi=>get_opt(0),
				'gnuplot stream in'=>$persist_gnuplot{in},
				'gnuplot stream out'=>$persist_gnuplot{out},
				'gnuplot stream err'=>$persist_gnuplot{err},
				invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
			    }
			    );
			$grange = $$h{'graph range'};
			$err = $$h{err};
		    }
		    else
		    {
			($grange,$err) = gnuplot_ps_to_png($f,$pltfile.'.png',get_opt(0));
		    }
		    
		}
		else
		{
		    # PNG
		    my $cmd=prepend_gdfontpath(format_filename($external_programs{gnuplot}.' '.$f));
		    reformat_pltfile($f);
		    my $res=`$cmd 2>&1`;
		    ($grange,$err)=extract_gnuplot_range(\$res);
                }
		push_to_Misc_log($err);

		# update image properties 
		%HRD_image_properties=('image'=>'HRD',
				       'image widget'=>$HRD_image,
				       'grange'=>$grange,
				       'width'=>get_opt(1),
				       'height'=>get_opt(2),
				       'lmargin'=>$gnuplot_options{lmargin},
				       'rmargin'=>$gnuplot_options{rmargin},
				       'tmargin'=>$gnuplot_options{tmargin},
				       'bmargin'=>$gnuplot_options{bmargin},
				       'xrange widgets'=>[$HRD_range_widgets[0],$HRD_range_widgets[1]],
				       'yrange widgets'=>[$HRD_range_widgets[2],$HRD_range_widgets[3]],
				       
		    );
		
		if(ok_file($pltfile.'.png'))
		{
		    # add (prescaled) HRD image
		    $HRD_image->set_from_pixbuf((get_image_from_file($pltfile.'.png'))[1]);
		    
		    # save postscript location
		    $ps_filenames[$current_tab]=$pltfile.'.ps';

		    # remove files
	 	    unlink ($pltfile.'.plt',$pltfile.'.png') if($unlink_tempfiles);
	        }
                $prev_plotted_hrd_string=$HRD_string;

		# reset graphrange from widgets
		@$range=get_graphrange(@HRD_range_widgets);
            }
	    $tab_table[$ntab]->show_all;
	    toggle_hourglass(0);
        }	
    }
    increment_timing('update_HRD_tab',$time0) if($timings);
    $updating{HRD}=0;
    TRUE;
}
  
sub set_hrd_label_spacing
{
    # HRD label spacing has changed
    my $s=$_[0]->get_active;
    $s=~s/\s+$//o;
    $gnuplot_options{'HRD label spacing'}=$HRD_spacings[$s];
    $gnuplot_options{'HRD label spacing index'}=$s;
}

sub set_hrd_col_menu
{
    # HRD label column
    my $w=$_[0];
    my $s=$w->get_active;
    $gnuplot_options{'HRD label col'}=$w->get_active-1; # -1 because first col is 'None'
}

sub set_up_stellar_colours
{
    state %prev;
    state $prevs;
    my @vars=('HRD brightness','HRD hue','HRD contrast');

    if(defined $prevs)
    {
	my $match=1;
	foreach my $var (@vars)
	{
	    if($prev{$var} ne $gnuplot_options{$var})
	    {
		$match=0; 
		last;
	    }
	}
       
	if($match)
	{
	    # match : return previous
	    return $prevs;
	}    
    }

    # set up colours for HRD plot
    state $dt;
    state $f;
    state $setup=0;
    state @trange;
    state @starcolours;
    if(!$setup)
    {
	$setup=1;
	@trange=(log10(1000.0),log10(37200.0));
	@starcolours=('1000 #ff3800','1100 #ff4700','1200 #ff5300','1300 #ff5d00','1400 #ff6500','1500 #ff6d00','1600 #ff7300','1700 #ff7900','1800 #ff7e00','1900 #ff8300','2000 #ff8912','2100 #ff8e21','2200 #ff932c','2300 #ff9836','2400 #ff9d3f','2500 #ffa148','2600 #ffa54f','2700 #ffa957','2800 #ffad5e','2900 #ffb165','3000 #ffb46b','3100 #ffb872','3200 #ffbb78','3300 #ffbe7e','3400 #ffc184','3500 #ffc489','3600 #ffc78f','3700 #ffc994','3800 #ffcc99','3900 #ffce9f','4000 #ffd1a3','4100 #ffd3a8','4200 #ffd5ad','4300 #ffd7b1','4400 #ffd9b6','4500 #ffdbba','4600 #ffddbe','4700 #ffdfc2','4800 #ffe1c6','4900 #ffe3ca','5000 #ffe4ce','5100 #ffe6d2','5200 #ffe8d5','5300 #ffe9d9','5400 #ffebdc','5500 #ffece0','5600 #ffeee3','5700 #ffefe6','5800 #fff0e9','5900 #fff2ec','6000 #fff3ef','6100 #fff4f2','6200 #fff5f5','6300 #fff6f8','6400 #fff8fb','6500 #fff9fd','6600 #fef9ff','6700 #fcf7ff','6800 #f9f6ff','6900 #f7f5ff','7000 #f5f3ff','7100 #f3f2ff','7200 #f0f1ff','7300 #eff0ff','7400 #edefff','7500 #ebeeff','7600 #e9edff','7700 #e7ecff','7800 #e6ebff','7900 #e4eaff','8000 #e3e9ff','8100 #e1e8ff','8200 #e0e7ff','8300 #dee6ff','8400 #dde6ff','8500 #dce5ff','8600 #dae4ff','8700 #d9e3ff','8800 #d8e3ff','8900 #d7e2ff','9000 #d6e1ff','9100 #d4e1ff','9200 #d3e0ff','9300 #d2dfff','9400 #d1dfff','9500 #d0deff','9600 #cfddff','9700 #cfddff','9800 #cedcff','9900 #cddcff','10000 #ccdbff','10100 #cbdbff','10200 #cadaff','10300 #c9daff','10400 #c9d9ff','10500 #c8d9ff','10600 #c7d8ff','10700 #c7d8ff','10800 #c6d8ff','10900 #c5d7ff','11000 #c4d7ff','11100 #c4d6ff','11200 #c3d6ff','11300 #c3d6ff','11400 #c2d5ff','11500 #c1d5ff','11600 #c1d4ff','11700 #c0d4ff','11800 #c0d4ff','11900 #bfd3ff','12000 #bfd3ff','12100 #bed3ff','12200 #bed2ff','12300 #bdd2ff','12400 #bdd2ff','12500 #bcd2ff','12600 #bcd1ff','12700 #bbd1ff','12800 #bbd1ff','12900 #bad0ff','13000 #bad0ff','13100 #b9d0ff','13200 #b9d0ff','13300 #b9cfff','13400 #b8cfff','13500 #b8cfff','13600 #b7cfff','13700 #b7ceff','13800 #b7ceff','13900 #b6ceff','14000 #b6ceff','14100 #b6cdff','14200 #b5cdff','14300 #b5cdff','14400 #b5cdff','14500 #b4cdff','14600 #b4ccff','14700 #b4ccff','14800 #b3ccff','14900 #b3ccff','15000 #b3ccff','15100 #b2cbff','15200 #b2cbff','15300 #b2cbff','15400 #b2cbff','15500 #b1cbff','15600 #b1caff','15700 #b1caff','15800 #b1caff','15900 #b0caff','16000 #b0caff','16100 #b0caff','16200 #afc9ff','16300 #afc9ff','16400 #afc9ff','16500 #afc9ff','16600 #afc9ff','16700 #aec9ff','16800 #aec9ff','16900 #aec8ff','17000 #aec8ff','17100 #adc8ff','17200 #adc8ff','17300 #adc8ff','17400 #adc8ff','17500 #adc8ff','17600 #acc7ff','17700 #acc7ff','17800 #acc7ff','17900 #acc7ff','18000 #acc7ff','18100 #abc7ff','18200 #abc7ff','18300 #abc7ff','18400 #abc6ff','18500 #abc6ff','18600 #aac6ff','18700 #aac6ff','18800 #aac6ff','18900 #aac6ff','19000 #aac6ff','19100 #aac6ff','19200 #a9c6ff','19300 #a9c5ff','19400 #a9c5ff','19500 #a9c5ff','19600 #a9c5ff','19700 #a9c5ff','19800 #a9c5ff','19900 #a8c5ff','20000 #a8c5ff','20100 #a8c5ff','20200 #a8c5ff','20300 #a8c4ff','20400 #a8c4ff','20500 #a8c4ff','20600 #a7c4ff','20700 #a7c4ff','20800 #a7c4ff','20900 #a7c4ff','21000 #a7c4ff','21100 #a7c4ff','21200 #a7c4ff','21300 #a6c4ff','21400 #a6c3ff','21500 #a6c3ff','21600 #a6c3ff','21700 #a6c3ff','21800 #a6c3ff','21900 #a6c3ff','22000 #a6c3ff','22100 #a5c3ff','22200 #a5c3ff','22300 #a5c3ff','22400 #a5c3ff','22500 #a5c3ff','22600 #a5c3ff','22700 #a5c2ff','22800 #a5c2ff','22900 #a5c2ff','23000 #a4c2ff','23100 #a4c2ff','23200 #a4c2ff','23300 #a4c2ff','23400 #a4c2ff','23500 #a4c2ff','23600 #a4c2ff','23700 #a4c2ff','23800 #a4c2ff','23900 #a4c2ff','24000 #a3c2ff','24100 #a3c2ff','24200 #a3c1ff','24300 #a3c1ff','24400 #a3c1ff','24500 #a3c1ff','24600 #a3c1ff','24700 #a3c1ff','24800 #a3c1ff','24900 #a3c1ff','25000 #a3c1ff','25100 #a2c1ff','25200 #a2c1ff','25300 #a2c1ff','25400 #a2c1ff','25500 #a2c1ff','25600 #a2c1ff','25700 #a2c1ff','25800 #a2c1ff','25900 #a2c0ff','26000 #a2c0ff','26100 #a2c0ff','26200 #a2c0ff','26300 #a2c0ff','26400 #a1c0ff','26500 #a1c0ff','26600 #a1c0ff','26700 #a1c0ff','26800 #a1c0ff','26900 #a1c0ff','27000 #a1c0ff','27100 #a1c0ff','27200 #a1c0ff','27300 #a1c0ff','27400 #a1c0ff','27500 #a1c0ff','27600 #a1c0ff','27700 #a1c0ff','27800 #a0c0ff','27900 #a0c0ff','28000 #a0bfff','28100 #a0bfff','28200 #a0bfff','28300 #a0bfff','28400 #a0bfff','28500 #a0bfff','28600 #a0bfff','28700 #a0bfff','28800 #a0bfff','28900 #a0bfff','29000 #a0bfff','29100 #a0bfff','29200 #a0bfff','29300 #9fbfff','29400 #9fbfff','29500 #9fbfff','29600 #9fbfff','29700 #9fbfff','29800 #9fbfff','29900 #9fbfff','30000 #9fbfff','30100 #9fbfff','30200 #9fbfff','30300 #9fbfff','30400 #9fbeff','30500 #9fbeff','30600 #9fbeff','30700 #9fbeff','30800 #9fbeff','30900 #9fbeff','31000 #9fbeff','31100 #9ebeff','31200 #9ebeff','31300 #9ebeff','31400 #9ebeff','31500 #9ebeff','31600 #9ebeff','31700 #9ebeff','31800 #9ebeff','31900 #9ebeff','32000 #9ebeff','32100 #9ebeff','32200 #9ebeff','32300 #9ebeff','32400 #9ebeff','32500 #9ebeff','32600 #9ebeff','32700 #9ebeff','32800 #9ebeff','32900 #9ebeff','33000 #9ebeff','33100 #9ebeff','33200 #9dbeff','33300 #9dbeff','33400 #9dbdff','33500 #9dbdff','33600 #9dbdff','33700 #9dbdff','33800 #9dbdff','33900 #9dbdff','34000 #9dbdff','34100 #9dbdff','34200 #9dbdff','34300 #9dbdff','34400 #9dbdff','34500 #9dbdff','34600 #9dbdff','34700 #9dbdff','34800 #9dbdff','34900 #9dbdff','35000 #9dbdff','35100 #9dbdff','35200 #9dbdff','35300 #9dbdff','35400 #9dbdff','35500 #9dbdff','35600 #9cbdff','35700 #9cbdff','35800 #9cbdff','35900 #9cbdff','36000 #9cbdff','36100 #9cbdff','36200 #9cbdff','36300 #9cbdff','36400 #9cbdff','36500 #9cbdff','36600 #9cbdff','36700 #9cbdff','36800 #9cbdff','36900 #9cbdff','37000 #9cbdff','37100 #9cbdff','37200 #9cbcff');
	
	$dt=$trange[1]-$trange[0];
	$f=1.0/255.0;
	# HRD colours
    }    
    # options are between 0 and 200
    #
    # 100 is 'natural' so values of 100 should do 'nothing'
    #
    # scale 0-200 > 0.0 to 2.0 allowing for custom offsets
    # which make the Sun's HRD look good at default values
    my $darkfac=(40.0+$gnuplot_options{'HRD brightness'}) * 0.01;
    my $contrast=(70.0+$gnuplot_options{'HRD contrast'})* 0.01;
    my $hue=(40.0+$gnuplot_options{'HRD hue'})*0.01;

    foreach my $var (@vars)
    {
	$prev{$var} = $gnuplot_options{$var};
    }

    my $s = 
	join(',',
	     map {
		 /(\d+) \#(..)(..)(..)/;
		 my @c=(hex($2)*$f,hex($3)*$f,hex($4)*$f);
		 
		 # contrast : value of 1.0 does nothing
		 map { $_=$_**($contrast**4.0); }@c;
		 
		 # hue 0 = mostly red, 1 = mostly blue
		 $c[0] *= 2.0-$hue;
		 $c[2] *= $hue;
		 
		 # global darkness
		 map { $_*=$darkfac; }@c;
		 
		 # limit to range 0 - 1
		 map { $_=MAX(0.0,MIN(1.0,$_)); }@c;
		 
		 my $t=(log10(logmin>$1 ? logmin : $1)-$trange[0])/$dt;
		 $t=MAX(1e-3,MIN(0.999,$t));
		 "$t @c";
	     }@starcolours
	);

    $prevs = $s;
    return $s;
}



############################################################
### Histogram tab
############################################################

sub show_Histogram_tab
{
    return if(!$histograms);
    my $time0 = [gettimeofday()] if($timings);
    my $ntab=$tab_numbers{Histogram};
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);

	print "Make histogram\n"if($vb);
	
	# make the histogram tab
	$made_tab[$ntab]=1;

	my $leftcol=Gtk2::VBox->new(0,1);
	$tab_table[$ntab]=Gtk2::HBox->new(0,1);

	# star selector
	my $nstars_menu=Gtk2::ComboBox->new_text;
	map
	{
	    $nstars_menu->append_text($_);
	}@starswitches;

	$nstars_menu->set_active(0);
	$nstars_menu->signal_connect(changed=>\&toggle_Histogram_stars);
	$HISTOGRAMstar=1;

	$leftcol->pack_start(set_tooltip_with_eventbox($nstars_menu,'Select whether to plot star 1, star 2 or both stars'),FALSE,FALSE,0);

	# x-data selector
	$histogram_xaxismenu=Gtk2::ComboBox->new_text;
	map
	{
	    $histogram_xaxismenu->append_text($_);
	}@plt1cols;

	$histogram_xaxismenu->append_text('logg');

	$histogram_xaxismenu->set_active(1);
	$histogram_xaxismenu->set_wrap_width(3);
	

	my $h=Gtk2::HBox->new(0,0);
	$h->pack_start(Gtk2::Label->new('X axis'),FALSE,FALSE,0);
	$h->pack_start(set_tooltip_with_eventbox($histogram_xaxismenu,'Select a data type to be used as the X axis of the plot'),FALSE,FALSE,0);
	$leftcol->pack_start($h,FALSE,FALSE,0);

	# weight selector
	$h=Gtk2::HBox->new(0,0);
	$histogram_weight_menu=Gtk2::ComboBox->new_text;
	map
	{
	    $histogram_weight_menu->append_text($_);
	}@plt1cols;
	$histogram_weight_menu->set_wrap_width(3);
	$histogram_weight_menu->set_active(2);

	$h->pack_start(Gtk2::Label->new('Weighting'),FALSE,FALSE,0);
	$h->pack_start(set_tooltip_with_eventbox($histogram_weight_menu,'Select a data type to be used as the weighting (usually timestep)'),FALSE,FALSE,0);
	$leftcol->pack_start($h,FALSE,FALSE,0);

	$h=Gtk2::HBox->new(0,0);

	# label 
	$h->pack_start(Gtk2::Label->new('X bin width'),FALSE,FALSE,0);

	# bin width
	my $entry=Gtk2::Entry->new_with_text_and_tooltip('1.0','Enter the histogram bin width');
	$entry->set_size_request(60,25);
	$entry->signal_connect(changed=>\&set_histogram_bin_width);
	$h->pack_start($entry,FALSE,FALSE,0);
	$leftcol->pack_start($h,FALSE,FALSE,0);
	$histogram_binwidth=1.0;

	# X min/max
	$h=Gtk2::HBox->new(0,0);
	$h->pack_start(Gtk2::Label->new('Xmin'),FALSE,FALSE,0);
	$entry=Gtk2::Entry->new_with_text_and_tooltip('*','Enter the X min');
	$entry->set_size_request(60,25);
	$entry->signal_connect(changed=>\&set_histogram_bin_min);
	$h->pack_start($entry,FALSE,FALSE,0);
	$h->pack_start(Gtk2::Label->new('Xmax'),FALSE,FALSE,0);
	$entry=Gtk2::Entry->new_with_text_and_tooltip('*','Enter the X max');
	$entry->set_size_request(60,25);
	$entry->signal_connect(changed=>\&set_histogram_bin_max);
	$h->pack_start($entry,FALSE,FALSE,0);
	$histogram_bin_min='*';
	$histogram_bin_max='*';
	$leftcol->pack_start($h,FALSE,FALSE,0);

	# right stack = image
	my $rightstack=Gtk2::VBox->new(0,1);

	$histogram_image=Gtk2::Image->new_from_pixbuf((no_image())[1]);
	$rightstack->pack_start(set_image_callbacks('histogram',$histogram_image),TRUE,TRUE,0);

	$tab_table[$ntab]->pack_start($leftcol,'shrink','shrink',0);
	$tab_table[$ntab]->pack_start($rightstack,'shrink','shrink',0);
	$frames[$ntab]->add($tab_table[$ntab]);
	$frames[$ntab]->show_all;

	toggle_hourglass(0);
    }
    increment_timing('show_Histogram_tab',$time0) if($timings);
}

sub set_histogram_bin_width
{
    my $s=$_[0]->get_text();
    $histogram_binwidth=1.0*$s;
}
sub set_histogram_bin_min
{
    my $s=$_[0]->get_text();
    $histogram_bin_min= is_numeric($s) ? $s : '*';
}
sub set_histogram_bin_max
{
    my $s=$_[0]->get_text();
    $histogram_bin_max= is_numeric($s) ? $s : '*x';
}


sub toggle_Histogram_stars
{
    return if(!$histograms);
    # select HISTOGRAM star for plotting
    $HISTOGRAMstar = starswitch($_[0]->get_active_text_local());
    print "Set HISTOGRAM star $HISTOGRAMstar\n" if ($vb);
}

sub update_Histogram_tab
{
    return if(!$histograms);
    return if(time() < $next_update_time{Histogram});
    my $time0 = [gettimeofday()] if($timings);
     if(((($running_the_code==1)||($have_run_the_code==1))||
       ($_[0]==1)))
     {
	toggle_hourglass(1);
	my $xaxis=$histogram_xaxismenu->get_active;
	my $weightcol=$histogram_weight_menu->get_active;
	my $xaxislabel;

	if($xaxis<=$#plt1cols)
	{
	    $xaxislabel=$plt1cols[$xaxis];
	}
	elsif($xaxis == $#plt1cols+1)
	{
	    $xaxislabel = 'logg';
	}

	return if($histogram_binwidth <=0.0);
	return if(!is_numeric($histogram_binwidth));

	print "Update histogram tab\n"if($vb);

	# autoscale
	my ($width,$height)=autosize($histogram_image->parent->parent->parent->parent);
	
	# for some reason we have to make it smaller
	$width*=0.95;
	$height*=0.95;
	
	# keep aspect ratio at $aspect_ratio
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;
	print "Autoscaled width,height=: $width $height\n"if($vb);
		
	my $histogram_string=join(' ',$xaxis,$weightcol,$HISTOGRAMstar,$histogram_binwidth,-s pltfile(),@misc_opts_values,$histogram_bin_min,$histogram_bin_max,gnuplot_options_string());

	if(!(defined($prev_plotted_histogram_string))||
	   ($histogram_string ne $prev_plotted_histogram_string))
	{
	    print "Plot histogram\n"if($vb);
	    $prev_plotted_histogram_string=$histogram_string;
	
	    # make histogram
	    my $pltfile="$cachedir/histogram/".rand();
	    
	    print "Pltfile $pltfile (cache in $cachedir/histogram)\n"if($vb);

	    mkdir "$cachedir/histogram";
	    open(HISTOGRAM,">$pltfile.plt")||confess("cannot open $pltfile.plt for output : $!");
	  

	    
	    # make x-axis label
	    my $xl="\{$xaxislabel\}";
	    $xl=~s/10\^\{log/\{/o; # obvious really
	    
	    print HISTOGRAM gnuplot_header($pltfile.'.png'),"
set xlabel \"$xl\"
set xrange[$histogram_bin_min:$histogram_bin_max]
";
	    print HISTOGRAM gnuplot_overrides();
  
	    # get data and bin it
	    my @files;
	    if($HISTOGRAMstar==1)
	    {
		# star 1
		push(@files,"$modinfo{plt1file_stub}1");
	    }
	    if($HISTOGRAMstar>=2)
	    {
		# star 2
		push(@files,"$modinfo{plt1file_stub}2");
	    }
	    print "Extract files: @files\n"if($vb);

	    my $plotstring=' ';
	    my $c=0;
	    foreach my $file (@files)
	    {
		open(HISTOGRAM_FP,"<$wttslayers[$wttslayernum]/$file")||confess("cannot open $file for input in update histogram function");
		open(HISTOGRAM_OP,">$pltfile.$c.dat")||confess("cannot open $pltfile.$c.dat for output in update histogram function");
		# get col $xaxis
		print "Get data from $file : Extract column $xaxis\nOutput to $pltfile.$c.dat\n"if($vb);
		
		my %hash;
		my $ibinwidth=1.0/$histogram_binwidth;
		
		<HISTOGRAM_FP>; # skip first line
		while(my $l=<HISTOGRAM_FP>)
		{
		    $l=~s/^\s+//o;  chomp $l;
		    my @data=split(/\s+/o,$l);

		    # add logg
		    my $xxx=6.67259E-8*1.9891E33*$data[3]/((6.9598E10*10.0**$data[7])**2);
		    $xxx=1e-30 if(abs($xxx)<1e-30);
		    $xxx=log10(logmin,$xxx);
		    push(@data,$xxx) if($data[3]>0);
 
		    my $binx=$histogram_binwidth*int($data[$xaxis]*$ibinwidth);

		    print "From data $data[$xaxis] to binx=$binx (bin width = $ibinwidth) weight $data[$weightcol]\n"if($vb);

		    if((defined($data[$xaxis]))&&(defined($data[$weightcol])))
		    {
			$hash{$binx}+=$data[$weightcol]; # 3rd col = timestep 
		    }
		}

		print "Normalize histogram?\n"if($vb);
		# normalize?
		my $max=0.0;
		my $imax;
		map
		{
		    $max=MAX($hash{$_},$max);
		}sort {$a<=>$b} keys %hash;

		$imax = $max>0 ? 1.0/$max : 1.0;

		print "Send to output file\n"if($vb);
		map
		{
		    print HISTOGRAM_OP $_,' ',$hash{$_}*$imax,' ',$histogram_binwidth,"\n";
		}sort {$a<=>$b} keys %hash;
 		close HISTOGRAM_FP;

		$plotstring .= ", \"$pltfile.$c.dat\" with boxes notitle ";
		$c++;
	    }
	    $plotstring =~s/,/plot/o;
	    print HISTOGRAM "$plotstring\n";

	    close HISTOGRAM;

	    my $f=$pltfile.'.plt';
	    if($gnuplot_options{'terminal type'}=~/postscript/io)
	    {
		gnuplot_ps_to_png($f,$pltfile.'.png',get_opt(0));
	    }
	    else
	    {
		# native PNG
		my $cmd=prepend_gdfontpath($external_programs{gnuplot}.' '.$f);
		reformat_pltfile($f);
		`$cmd 2>&1`;
	    }
	    $histogram_image->set_from_pixbuf((get_image_from_file($pltfile.'.png'))[1]);
	    
	    $ps_filenames[$current_tab]=$pltfile.'.ps';

		
    # remove files
	    unlink ($pltfile.'.plt',$pltfile.'.png') if($unlink_tempfiles);

	}

	toggle_hourglass(0);
     }

    increment_timing('update_Histogram_tab',$time0) if($timings);
}


############################################################
### Rho-T tab
############################################################


sub show_RhoT_tab
{
    return if(!$rhot);
    my $time0 = [gettimeofday()] if($timings);
    my $ntab=$tab_numbers{RhoT};
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	# make Rho-T diagram tab
	$tab_table[$ntab]=Gtk2::Table->new(3,2,FALSE);
	
	# make range boxes
	@RhoT_range=make_rangeboxes();
	my $h=Gtk2::HBox->new;
	my $h2=Gtk2::HBox->new;

	foreach my $w (Gtk2::Label->new('X Range'),
		       $RhoT_range[0],$RhoT_range[1],
		       Gtk2::Label->new('Y Range'),
		       $RhoT_range[2],$RhoT_range[3],
	    $RhoT_range[6])
	{
	    $h->pack_start($w,FALSE,FALSE,0);
	}
	$RhoT_range[6]->signal_connect(clicked=>sub{
	    $prev_plotted_RhoT_string=undef; # forces redraw
				       });
    	
	# make options for labels
	my $label_col_menu=Gtk2::ComboBox->new_text;
	my $l3a=Gtk2::Label->new('Label With');
	my $spacing_menu=Gtk2::ComboBox->new_text;
	my $l3b=Gtk2::Label->new('Label Spacing');
	$h2->pack_start($l3a,FALSE,FALSE,0);
	map
	{
	    $label_col_menu->append_text($_);
	}('None',@plt1cols); 
	$label_col_menu->append_text('logg');

	$label_col_menu->set_wrap_width(3);
	$label_col_menu->set_size_request(150,30);
	$label_col_menu->signal_connect(changed=>sub{
	    $RhoT_label_col=$label_col_menu->get_active-1;
					});
	$label_col_menu->set_active($RhoT_label_col);
	
	my $ea=set_tooltip_with_eventbox($label_col_menu,'Here you can select what labels you would like to plot in the Rho-T diagram');

	$h2->pack_start($ea,FALSE,TRUE,0);
	$h2->pack_start($l3b,FALSE,FALSE,0);
	map
	{
	    $spacing_menu->append_text($_);
	}@RhoT_spacings;
	#$spacing_menu->set_size_request(75,35);
	$spacing_menu->signal_connect(changed=>sub{
	    my $s=$spacing_menu->get_active;
	    $s=~s/\s+$//o;
	    $RhoT_label_distance=$RhoT_spacings[$s];
				      });

	$spacing_menu->set_active(0);

	my $eb=set_tooltip_with_eventbox($spacing_menu,'Use this menu to select the distance between labels on the Rho-T diagram. A smaller number means labels are closer together.');

	$h2->pack_start($eb,FALSE,FALSE,0);

	# choose star(s) to plot
	my $nstars_menu=Gtk2::ComboBox->new_text;
	map
	{
	    $nstars_menu->append_text($_);
	}@starswitches;

	$nstars_menu->set_active(0);
	$nstars_menu->signal_connect(changed=>\&toggle_RhoT_stars);
	my $nstars_menu_e=set_tooltip_with_eventbox($nstars_menu,'Select whether to plot star 1, star 2 or both stars');


	# add buttons to menus
	$h2->pack_start($nstars_menu_e,FALSE,FALSE,0);
	
	# update with the current image
	$RhoT_image=Gtk2::Image->new;

	# image propertires and callbacks
	%RhoT_image_properties=('image'=>'RhoT',
				'image widget'=>$RhoT_image,
				'noimage'=>1);
	$RhoT_image->set_from_pixbuf((no_image())[1]);
	$tab_table[$ntab]->attach(set_image_callbacks('RhoT',$RhoT_image,\%RhoT_image_properties),0,1,2,3,'expand','expand',10,10);

	$tab_table[$ntab]->attach($h,0,1,0,1,'expand','fill',5,5);
	$tab_table[$ntab]->attach($h2,0,1,1,2,'expand','fill',5,5);

	my $container = Gtk2::EventBox->new;
	$container->add(scrolled($tab_table[$ntab]));
	$frames[$ntab]->add($container);
	$made_tab[$ntab]=1;
	$frames[$ntab]->show_all;

	my $offset = $h->allocation->height + $h2->allocation->height + 40;

	sub RhoT_image_size
	{
	    no warnings;
	    my $y = $container->allocation->height - $offset - 5*$gtkrc{slider_width};
	    my $x = $frames[$ntab]->allocation->width - 4*$gtkrc{slider_width};
	    use warnings;
	    return (MAX(1,$x),MAX(1,$y));

	}

        toggle_hourglass(0);
    }

    update_RhoT_tab(1);
    increment_timing('show_RhoT_tab',$time0) if($timings);
    TRUE;
}

sub update_RhoT_tab
{
    return if(!$rhot || $updating{RhoT} || time()< $next_update_time{RhoT});
    $updating{RhoT}=1;

    my $time0 = [gettimeofday()] if($timings);
    my $ntab=$tab_numbers{RhoT};
    my @active_wttslayers=active_wttslayers();
    my ($dataok,@datafilessize) = pltdata_ok();

    if($dataok && defined $tab_table[$ntab])
    {
	my @range=get_graphrange(@RhoT_range);
	my $lt=1;

	# autoscale
	my ($width,$height)=RhoT_image_size();

	# keep aspect ratio at $aspect_ratio
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;
	print "AUTOSCALE RHOT $width $height\n"if($vb);

	my $RhoT_string=join(' ',@datafilessize,@misc_opts_values,@range,
			     $RhoT_label_distance,$RHOTstar,$RhoT_label_col,
			     gnuplot_options_string(),@active_wttslayers);

	if(!(defined $prev_plotted_RhoT_string && 
	     $RhoT_string eq $prev_plotted_RhoT_string))
	{
	    toggle_hourglass(1);

	    # make Rho-T diagram
	    my $dir = $cachedir.'/RhoT';
	    mkdir $dir;
	    my $pltfile=$dir.'/'.rand();
	    open(RHOT,">$pltfile.plt")||confess("cannot open $pltfile.plt for output : $!");
	    print RHOT gnuplot_header($pltfile.'.png',
				      {solid=>'dashed'}
		),
	    "
set title \"Temperature - Density\"
set xlabel \"log_{10} Density\"
set ylabel \"log_{10} Temperature\" offset 1,0
set key below
";
	    print RHOT gnuplot_overrides();
	    
	    # try to get the latest position for the Now label
	    # by updating the status now (there might still be some 
	    # delay while gnuplot runs)
	    update_current_status();
	    my @labels;

	    my $rcoln;
	    my $rcol;
	    if(defined($plt1cols{'Central Density'}))
	    {
		$rcoln=-($plt1cols{'Central Density'}+1);
		$rcol='(log10($'.abs($rcoln).'))';
	    }
	    else
	    {
		$rcoln=($plt1cols{'log Central Density'}+1);
		$rcol='($'.($rcoln).')';
	    }
	    
	    my $tcoln;
	    my $tcol;
	    if(defined($plt1cols{'Central Temperature'}))
	    {
		$tcoln=-($plt1cols{'Central Temperature'}+1);
		$tcol='(log10($'.abs($tcoln).'))';
	    }
	    else
	    {	
		$tcoln=($plt1cols{'log Central Temperature'}+1);
		$tcol='($'.($tcoln).')';
	    }

	    my $cols=$rcol.':'.$tcol;
	    
	    my $maxcols = defined($plt1cols{'Density at Maximum Temperature'}) ?
		'(log10($'.($plt1cols{'Density at Maximum Temperature'}+1)
		.')):(log10($'.($plt1cols{'Maximum Temperature'}+1).'))' :
		'($'.($plt1cols{'log Density at Maximum Temperature'}+1)
		.'):($'.($plt1cols{'log Maximum Temperature'}+1).')';
	    
	    my $labeldata=get_pltdata($RHOTstar,0);
	    my @datarange=datarange($labeldata,\@range,$rcoln,$tcoln);

	    foreach (0..3)
	    {
		$range[$_]=$datarange[$_] if(!is_numeric($range[$_]));
	    }

	    my $rhoT_lines = make_rhoT_lines($dir);

no warnings;
 	    print RHOT $$rhoT_lines{labels},"\n";
use warnings;

	    print RHOT "
set xrange\[$range[0]:$range[1]\] 
set yrange\[$range[2]:$range[3]\]
";

	    if($RhoT_label_col>=0)
	    {	
		map
		{
		    print RHOT 'set label "{/*'.$gnuplot_options{'auto labelling font factor'}.' '.$$_[2].'" at first '.$$_[0].','.$$_[1]." front\n";
		}labellist($RhoT_label_col,$RhoT_label_distance,$labeldata,$rcoln,$tcoln,\@range);
	    }
	    
	    my $plotcomma='plot';
	    foreach my $layer (@active_wttslayers)
	    {
		my $t = (scalar @active_wttslayers > 1) ? $layer.': ' : '';
		$t=apply_key_regexp($t);
		
		if(($RHOTstar==1)||($RHOTstar==2)||
		   (-s "$layer/$modinfo{plt1file_stub}2" < 10))
		{
		    print RHOT "$plotcomma \"$layer/$modinfo{plt1file_stub}$RHOTstar\" using $cols ",withstring($lt++)," title \"$t Central\", \"$layer/$modinfo{plt1file_stub}$RHOTstar\" using $maxcols ",withstring($lt++)," title \"$t Maximum\" ";
		    
		}
		else
		{
		    print RHOT "$plotcomma \"$layer/$modinfo{plt1file_stub}1\" using $cols ",withstring($lt++)," title  \"$t Central-1\" , \"$layer/$modinfo{plt1file_stub}2\" using $cols ",withstring($lt++)," title \"$t Central-2\",\"$layer/$modinfo{plt1file_stub}1\" using $maxcols ",withstring($lt++)," title  \"$t Maximum-1\", \"$layer/$modinfo{plt1file_stub}2\" using $maxcols ",withstring($lt++)," title \"$t Maximum-2\" ";
		}
		$plotcomma=',';
	    }

	    print RHOT $$rhoT_lines{lines},"\n";
	    print RHOT gpshow() if(!$persist_gnuplot);
	    close RHOT;

	    my $f=$pltfile.'.plt';
	    my $grange; # gnuplot's range (hash)
	    my $err; # gnuplot error
	    if($gnuplot_options{'terminal type'}=~/postscript/io)
	    {
		if($persist_gnuplot)
		{
		    my $h = gnuplot_ps_to_png2(
			{ 
			    'input file'=>$f,
			    'png output file'=>$pltfile.'.png',
			    dpi=>get_opt(0),
			    'gnuplot stream in'=>$persist_gnuplot{in},
			    'gnuplot stream out'=>$persist_gnuplot{out},
			    'gnuplot stream err'=>$persist_gnuplot{err},
			    invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
			}
			);
		    $grange = $$h{'graph range'};
		    $err = $$h{err};
		}
		else
		{
		    ($grange,$err) = gnuplot_ps_to_png($f,$pltfile.'.png',get_opt(0));
		}
	    }
	    else
	    {
		# PNG
		my $cmd=prepend_gdfontpath(format_filename($external_programs{gnuplot}.' '.$f));
		reformat_pltfile($f);
		my $res=`$cmd 2>&1`;
		($grange,$err)=extract_gnuplot_range(\$res);
	    }
	    push_to_Misc_log($err);

	    # image properties 
	    %RhoT_image_properties=('image'=>'RhoT',
				    'image widget'=>$RhoT_image,
				    'grange'=>$grange,
				    'width'=>get_opt(1),
				    'height'=>get_opt(2),
				    'lmargin'=>$gnuplot_options{lmargin},
				    'rmargin'=>$gnuplot_options{rmargin},
				    'tmargin'=>$gnuplot_options{tmargin},
				    'bmargin'=>$gnuplot_options{bmargin},
				    'xrange widgets'=>[$RhoT_range[0],
						       $RhoT_range[1]],
				    'yrange widgets'=>[$RhoT_range[2],
						       $RhoT_range[3]],
				    
		);

	    if(ok_file($pltfile.'.png'))
	    {
		# add (prescaled) RHOT image
		$RhoT_image->set_from_pixbuf((get_image_from_file($pltfile.'.png'))[1]);
		
		# save postscript location
		$ps_filenames[$current_tab]=$pltfile.'.ps';

		# remove files
		unlink ($pltfile.'.plt',$pltfile.'.png') if($unlink_tempfiles);
	    }
	    $prev_plotted_RhoT_string=$RhoT_string;
	    $tab_table[$ntab]->show_all;
	    toggle_hourglass(0);
	}
    }

    increment_timing('update_RhOT_tab',$time0) if($timings);
    $updating{RhoT}=0;
    TRUE;
}
  
sub toggle_RhoT_stars
{
    # select RHOT star for plotting
    $RHOTstar = starswitch($_[0]->get_active_text_local());
    print "Set RHOT star $RHOTstar\n" if ($vb);
}

############################################################
### Structure tab
############################################################

sub show_Structure_tab
{
    my $ntab=$tab_numbers{Structure};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	print "MAKE STRUCT TAB\n" if($vb);
	# these are the columns in the .plt1 file
	# make tab of structure vs time
	$made_tab[$ntab]=1;

	my $leftcol=Gtk2::VBox->new(0,1);
	my $axis_buts=Gtk2::VBox->new(0,1);
	$tab_table[$ntab]=Gtk2::HBox->new(0,1);

	# star selector
	my $nstars_menu=Gtk2::ComboBox->new_text;
	map
	{
	    $nstars_menu->append_text($_);
	}@starswitches;
	$nstars_menu->set_active(0);
	$nstars_menu->signal_connect(changed=>\&toggle_structure_stars);

	$leftcol->pack_start(set_tooltip_with_eventbox($nstars_menu,'Select whether to plot star 1, star 2 or both stars'),FALSE,FALSE,0);
	
	# add vertical pack to the left column
	my $leftstack=Gtk2::VBox->new(0,1);

	$leftcol->pack_start($axis_buts,FALSE,FALSE,0);

	# make a menu for the X axis
	$structure_xaxismenu=Gtk2::ComboBox->new_text;
	map
	{
	    $structure_xaxismenu->append_text($_);
	}@plt1cols;
	$structure_xaxismenu->set_active(1);
	$structure_xaxismenu->set_wrap_width(3);

	$leftcol->pack_start(set_tooltip_with_eventbox($structure_xaxismenu,'Select a data type to be used as the X axis of the plot'),FALSE,FALSE,0);

	my $leftscroller=Gtk2::ScrolledWindow->new;
	$leftscroller->add_with_viewport($leftstack);
	$leftscroller->set_policy('automatic','automatic');
	$leftscroller->set_size_request(240,300);
	$leftscroller->set_shadow_type('etched-out');

	# logscale and 10^x buttons for X
	$structure_logscalex_buttons[0]=Gtk2::RadioButton->new_with_label_and_tooltip(undef,'Linear X Axis','Set X axis to a linear scale');
	$structure_logscalex_buttons[1]=Gtk2::RadioButton->new_with_label_and_tooltip($structure_logscalex_buttons[0]->get_group,'Log X Axis',"Log the data (base 10) and then plot (NB data < ".logmin." (logmin) will be set to ".logmin." to avoid log(0))");
	$structure_logscalex_buttons[2]=Gtk2::RadioButton->new_with_label_and_tooltip($structure_logscalex_buttons[0]->get_group,'10^ X Axis','Take 10^X for each value, then plot (useful for convert log(x) to x, if log(x) is the only option)');

	$structure_logscalex_buttons[0]->set_active(TRUE);

	my $hlog=Gtk2::HBox->new;
	{
	    my $v1=Gtk2::VBox->new;
	    map{$v1->pack_start($_,FALSE,FALSE,0);}@structure_logscalex_buttons;
	    $hlog->pack_start($v1,FALSE,FALSE,0);
	}
	$hlog->pack_start(Gtk2::VSeparator->new,FALSE,FALSE,0);

	# resolution slider
	{
	    my $v=Gtk2::VBox->new;
	    $v->pack_start(Gtk2::Label->new('Res'),FALSE,FALSE,0);
	    $v->pack_start(res_slider(),FALSE,FALSE,0);
	    $hlog->pack_start(Gtk2::VSeparator->new,FALSE,FALSE,0);
	    $hlog->pack_start($v,FALSE,FALSE,0);
	}

	$leftcol->pack_start($hlog,FALSE,FALSE,0);

	# range boxes
	@structure_rangebox=make_rangeboxes();
	
	{
	    my $hh=Gtk2::HBox->new(FALSE);
	    my $t=Gtk2::Table->new(3,4,FALSE);
	    my @topts=('shrink','shrink',3,3);

	    # header column
	    my $i=0;
	    foreach my $x ('Axis','Range')
	    {
		$t->attach(Gtk2::Label->new($x),$i++,$i,0,1,@topts);
	    }

	    # rows    
	    $i=1;
	    foreach my $ax ('X','Y','Y2')
	    { 
		# label
		$t->attach(Gtk2::Label->new($ax),0,1,$i,$i+1,@topts);
		
		# ranges
		my $h=Gtk2::HBox->new;
		$h->pack_start($structure_rangebox[2*$i-2],FALSE,FALSE,0);
		$h->pack_start($structure_rangebox[2*$i-1],FALSE,FALSE,0);
		$t->attach($h,1,2,$i,$i+1,@topts);
		
		$i++; # last
	    }
	   
	    # add range reset button  
	    $t->attach($structure_rangebox[6],2,3,$i-2,$i-1,@topts);
	    $structure_rangebox[6]->signal_connect(clicked=>sub{
	    $prev_plotted_structure_string=undef; # forces redraw
				      });

	    # add button to clear selection	    
	    my $b=Gtk2::Button->new_with_label_and_tooltip('Clear Selection','Press this to clear the items in the list below - you will then be able to start plotting afresh.');
	    $b->signal_connect(clicked=>sub{
		map
		{
		    $_->set_active(FALSE);
		}@structure_yaxisbuttons;
			       });
	    $t->attach($b,2,3,$i-1,$i,@topts);

	    $hh->pack_start($t,FALSE,FALSE,0);

	    $leftcol->pack_start($hh,FALSE,FALSE,0);
	}

	$leftcol->pack_start($leftscroller,TRUE,TRUE,0);

	$structure_image=Gtk2::Image->new_from_pixbuf((no_image())[1]);
	%structure_image_properties=('image'=>'Structure',
				     'image widget'=>$structure_image,
				     'noimage'=>1);

	my $rightstack=Gtk2::VBox->new(0,1);

	$rightstack->pack_start(set_image_callbacks('Structure',$structure_image,\%structure_image_properties),TRUE,TRUE,0);

	{

	    if($evcode eq 'binstar')
	    {
		# new order packing! much more compact...
		foreach my $l (@structure_labels)
		{
		    my $h=Gtk2::HBox->new;
		    foreach my $b (@$l)
		    {
			my $n = $b=~s/=(.*)//o ? $plt1cols{$1} : $plt1cols{$b};
			$structure_yaxisbuttons[$n]=Gtk2::ToggleButton->new_with_label_and_tooltip($b,"Plot $plt1cols[$n] on the Y axis");
			$h->pack_start($structure_yaxisbuttons[$n],TRUE,TRUE,0);
		    }
		    $leftstack->pack_start($h,FALSE,FALSE,0);
		}

		# check all buttons are packed in!
		{
		    my $fail=0;
		    for(my $i=0;$i<=$#plt1cols;$i++)
		    {
			if(!defined($structure_yaxisbuttons[$i]))
			{
			    print "Button $i = $plt1cols[$i] not defined!\n";
			    $fail=1;
			}
		    }
		    exit if($fail);
		}
	    }
	    else
	    {
		# standard-order packing
		my @eboxes;
		for(my $i=0;$i<=$#plt1cols;$i++)
		{
		    $structure_yaxisbuttons[$i]=Gtk2::ToggleButton->new_with_label($plt1cols[$i]);
		    my $j=$i;
		    $structure_axes[$i]='x1y1'; # default axis
		    $structure_ll[$i]='Linear'; # default to linear
		    $structure_yaxisbuttons[$i]->signal_connect('button-release-event'=>sub{
			my ($widget,$event)=@_;
			return FALSE unless $event->button==RIGHT_MOUSE_BUTTON;

			# popup
			my $menu=Gtk2::Menu->new;
			my $axislabel=Gtk2::MenuItem->new('Axes');
			$axislabel->signal_connect(activate=>sub{
			    # axis menu
			    my $axismenu=Gtk2::Menu->new;
			    foreach my $ax ('x1y1','x1y2',
					    #'x2y1','x2y2'
				)
			    {
				my $post;
				$post='*' if($structure_axes[$j] eq $ax);
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ax.$post);
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    $structure_axes[$j] = $ax;
							  });
				$axismenu->append($menuitem);
			    }
			    $axismenu->popup(
				undef, # parent menu shell
				undef, # parent menu item
				undef, # menu pos func
				undef, # data
				$event->button,
				$event->time
				);
			    $axismenu->show_all;
						   });
			$menu->append($axislabel);
			
			# log, linear, 10^
			my $lllabel=Gtk2::MenuItem->new('Lin/Log/10^');
			$lllabel->signal_connect(activate=>sub{
			    
			    my $llmenu=Gtk2::Menu->new;
			    my @m=('Linear','log10','10^');
			    foreach my $ll (@m)
			    {
				my $post;
				$post='*' if($structure_ll[$j] eq $ll);
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ll.$post);
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    $structure_ll[$j] = $ll;
							  });
				$llmenu->append($menuitem);
			    }
			    $llmenu->append(Gtk2::SeparatorMenuItem->new);
			    foreach my $ll (@m)
			    {
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ll.' all');
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    map{$_=$ll}@structure_ll;
							  });
				$llmenu->append($menuitem);
			    }

			    $llmenu->popup(
				undef, # parent menu shell
				undef, # parent menu item
				undef, # menu pos func
				undef, # data
				$event->button,
				$event->time
				);
			    $llmenu->show_all;
						 });
			$menu->append($lllabel);
			
			# show main menu
			$menu->show_all;
			$menu->popup(
			    undef, # parent menu shell
			    undef, # parent menu item
			    undef, # menu pos func
			    undef, # data
			    $event->button,
			    $event->time
			    );
								});

		    $eboxes[$i]=set_tooltip_with_eventbox($structure_yaxisbuttons[$i],"Plot $plt1cols[$i] on the Y axis");
		}
		
		map
		{
		    $leftstack->pack_start($_,FALSE,FALSE,0);
		}@eboxes;
	    }	    
	}

	$tab_table[$ntab]->pack_start($leftcol,'shrink','fill',0);
	$tab_table[$ntab]->pack_start($rightstack,'expand','shrink',0);
	$frames[$ntab]->add(scrolled($tab_table[$ntab]));
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }
    
    update_Structure_tab(1);
    increment_timing('show_Structure_tab',$time0) if($timings);
    TRUE;

}

sub toggle_structure_stars
{
    # select structure star for plotting
    $structurestar = starswitch($_[0]->get_active_text_local());
    print "Set structure star $structurestar\n" if ($vb);
}

sub update_Structure_tab
{
    return if($updating{structure}||time()<$next_update_time{Structure});
    $updating{structure}=1;

    # Structure of the star
    my $time0 = [gettimeofday()] if($timings);
    my @active_wttslayers=active_wttslayers();
    my $datadir = maindir();
    my ($dataok,@datafilessize) = pltdata_ok();

    if($dataok && defined $structure_xaxismenu)
    {
	toggle_hourglass(1);
	#print "Yes update structure tab\n";
	# determine which columns to plot
	# these are the columns in the .structure_ file
	
	# get xaxis from $structure_xaxismenu
	my $xaxis=$structure_xaxismenu->get_active;
	my $xaxislabel=$plt1cols[$xaxis];
	
	# get y axes from @structure_yaxisbuttons
	my @yaxes;
	for(my $i=0;$i<=$#structure_yaxisbuttons;$i++)
	{
	    push(@yaxes,$i) if($structure_yaxisbuttons[$i]->get_active==TRUE);
	}

	my $logx=0;
	if($structure_logscalex_buttons[1]->get_active==TRUE)
	{
	    $logx=1;
	}
	elsif($structure_logscalex_buttons[2]->get_active==TRUE)
	{
	    $logx=2;
	}
	
	# autoscale
	my ($width,$height)=autosize($structure_image->parent->parent->parent->parent);
	
	# for some reason we have to make it smaller
	$width*=0.95;
	$height*=0.95;
	
	# keep aspect ratio at $aspect_ratio
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;
	print "AUTOSCALE STRUCTURE $width $height\n"if($vb);
	# axis ranges
	my @range=get_graphrange(@structure_rangebox);
	
	my $structure_string=join(' ',$xaxis,@yaxes,$logx,@structure_ll,$structurestar,
				  $dataok,@datafilessize,@misc_opts_values,@range,@structure_axes,gnuplot_options_string());

	my $lt=1;

	if(!(defined($prev_plotted_structure_string))||
	   ($structure_string ne $prev_plotted_structure_string))
	{
	    if($#yaxes>-1)
	    {
		my $xl=' ';
		if($logx==1)
		{
		    $xl='log_{10}';
		}
		elsif($logx==2)
		{
		    $xl='10^';
		}

		toggle_hourglass(1);

		# make structure diagram
		my $pltfile="$cachedir/structure/".rand();
		mkdir "$cachedir/structure";
		open(STRUCTURE,">$pltfile.plt")||confess("cannot open $pltfile.plt for output : $!");

		# make x-axis label
		$xl="$xl\{$xaxislabel\}";
		$xl=~s/10\^\{log/\{/o; # obvious really
		
		print STRUCTURE gnuplot_header($pltfile.'.png'),"
set xrange\[$range[0]:$range[1]\]
set yrange\[$range[2]:$range[3]\]
set y2range\[$range[4]:$range[5]\]
set xlabel \"$xl\"
unset xtics
unset ytics
unset x2tics
unset y2tics
";

		# tics for non-standard axes
		foreach my $yaxis (@yaxes)
		{
		    print STRUCTURE "set xtics ",($structure_axes[$yaxis]=~/x2/o ? "nomirror; set x2tics" : "mirror"),"\nset ytics ",($structure_axes[$yaxis]=~/y2/o ? "nomirror; set y2tics" : "mirror"),"\n"if(defined($structure_axes[$yaxis]));
		}

		print STRUCTURE gnuplot_overrides();
		    
		if(($#yaxes==0)&&($plt1cols[$yaxes[0]] eq 'nphase'))
		{
		    print STRUCTURE "
set ytics (\"PreMS\" 1, \"centH\" 2, \"RGB\" 3, \"centHe\" 4, \"AGB\" 5, \"centC\" 6, \"centNe\" 7)\n";

		}

		my $plotcomma='plot ';

                foreach my $yaxis (@yaxes)
		{
		    foreach my $layer (@active_wttslayers)
		    {
			my $s=$structurestar==3 ? 1 : $structurestar;
			print STRUCTURE "$plotcomma \"$layer/$modinfo{plt1file_stub}$s\" using ";
			
			if($logx==0)
			{
			    print STRUCTURE ($xaxis+1);
			}
			elsif($logx==1)
			{
			    print STRUCTURE '(log($',($xaxis+1),'+'.logmin.')*'.ivalue_of_log10.')';
			}
			elsif($logx==2)
			{
			    print STRUCTURE '(10.0**($',($xaxis+1),'))';
			}

			print STRUCTURE ':';

			my $yl;
			if($structure_ll[$yaxis] eq 'Linear')
			{
			    print STRUCTURE ($yaxis+1);
			    $yl='';
			}
			elsif($structure_ll[$yaxis] eq 'log10')
			{
			    print STRUCTURE '(log($',($yaxis+1),'+'.logmin.')*'.ivalue_of_log10.')';
			    $yl='log_{10}';
			}
			else
			{
			    print STRUCTURE '(10.0**($',($yaxis+1),'))';
			    $yl='10^';
			}

			# TODO : repeat for x2,y2

			# make line label
			my $t=$yl.'{'.format_ps_title($plt1cols[$yaxis]).'}';
			$t=~s/10\^\{log/\{/o; # obvious really

			$t="Star $s: ".$t if($structurestar>=2);

			# if we're multilayer, prepend titles with the directory
			$t = $layer.': '.$t if(scalar @active_wttslayers > 1);
			
			# apply key regexp
			$t=apply_key_regexp($t);
						
			# choose resolution
			my $every=guess_every();
			print STRUCTURE " every $every ";
			
			# choose axes
			print STRUCTURE " axes $structure_axes[$yaxis] ";
			
			# with string
			my $with=withstring($lt++);
			print STRUCTURE $with." title \"$t\" ";

			if($structurestar==3)
			{
			    # plot other star as well
			    print STRUCTURE ", \"$layer/$modinfo{plt1file_stub}2\" using ";
			    
			    if($logx==0)
			    {
				print STRUCTURE ($xaxis+1);
			    }
			    elsif($logx==1)
			    {
				print STRUCTURE '(log($',($xaxis+1),'+'.logmin.')*'.ivalue_of_log10.')';
			    }
			    elsif($logx==2)
			    {
				print STRUCTURE '(10.0**($',($xaxis+1),'))';
			    }

			    print STRUCTURE ':';

			    if($structure_ll[$yaxis] eq 'Linear')
			    {
				print STRUCTURE ($yaxis+1);
				$yl='';
			    }
			    elsif($structure_ll[$yaxis] eq 'log10')
			    {
				print STRUCTURE "(log(\$",($yaxis+1),"+".logmin.")*".ivalue_of_log10.')';
				$yl='log_{10}';
			    }
			    else
			    {
				print STRUCTURE "(10.0**(\$",($yaxis+1),"))";
				$yl='10^';
			    }
			    
			    print STRUCTURE " every $every ",withstring($lt++)," title \"Star 2: $yl\{",format_ps_title($plt1cols[$yaxis]),'}" ';
			}
			$plotcomma=',';
		    }
		}

		print STRUCTURE gpshow() if(!$persist_gnuplot);
		close STRUCTURE;
		toggle_hourglass(1);
		
		my $grange; # gnuplot's range (hash)
		my $err;
 		my $f=$pltfile.'.plt';
		if($gnuplot_options{'terminal type'}=~/postscript/io)
		{
		    if($persist_gnuplot)
		    {
			my $h = gnuplot_ps_to_png2(
			    { 
				'input file'=>$f,
				'png output file'=>$pltfile.'.png',
				dpi=>get_opt(0),
				'gnuplot stream in'=>$persist_gnuplot{in},
				'gnuplot stream out'=>$persist_gnuplot{out},
				'gnuplot stream err'=>$persist_gnuplot{err},
				'colourblind'=>1,
				invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
			    }
			    );
			$grange = $$h{'graph range'};
			$err = $$h{err};
		    }
		    else
		    {
			($grange,$err) = gnuplot_ps_to_png($f,$pltfile.'.png',get_opt(0),1); 
		    }

		}
		else
		{
		    # native PNG
		    my $cmd=prepend_gdfontpath($external_programs{gnuplot}.' '.$f);
		    reformat_pltfile($f);
		    my $res=`$cmd 2>&1`;
		    ($grange,$err)=extract_gnuplot_range(\$res);
           	}
		push_to_Misc_log($err);

		%structure_image_properties=('image'=>'structure', 
					     'image widget'=>$structure_image,			       
					     'grange'=>$grange,
					     'width'=>get_opt(1),
					     'height'=>get_opt(2),
					     'lmargin'=>$gnuplot_options{lmargin},
					     'rmargin'=>$gnuplot_options{rmargin},
					     'tmargin'=>$gnuplot_options{tmargin},
					     'bmargin'=>$gnuplot_options{bmargin},
					     'xrange widgets'=>[$structure_rangebox[0],$structure_rangebox[1]],
					     'yrange widgets'=>[$structure_rangebox[2],$structure_rangebox[3]],
					     'y2range widgets'=>[$structure_rangebox[4],$structure_rangebox[5]],
		    );

		toggle_hourglass(0);

		# update image
		$structure_image->set_from_pixbuf((get_image_from_file($pltfile.'.png'))[1]);

		$ps_filenames[$current_tab]=$pltfile.'.ps';

		# remove files
		unlink ($pltfile.'.plt',$pltfile.'.png')if($unlink_tempfiles);
		$prev_plotted_structure_string=$structure_string;
		toggle_hourglass(0);
	    }
	    $tab_table[$tab_numbers{Structure}]->show_all;
	}
	toggle_hourglass(0);
    }
    increment_timing('update_Structure_tab',$time0) if($timings);
    $updating{structure}=0;
    TRUE;
}



############################################################
### System tab
############################################################

sub show_System_tab
{
    return if(!$systemtab);
    my $ntab=$tab_numbers{System};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	print "MAKE SYSTEM TAB\n" if($vb);
	# these are the columns in the system file
	# make tab of system vs time
	$made_tab[$ntab]=1;

	my $leftcol=Gtk2::VBox->new(0,1);
	my $axis_buts=Gtk2::VBox->new(0,1);
	$tab_table[$ntab]=Gtk2::HBox->new(0,1);

	# add vertical pack to the left column
	my $leftstack=Gtk2::VBox->new(0,1);

	$leftcol->pack_start($axis_buts,FALSE,FALSE,0);

	# make a menu for the X axis
	$system_xaxismenu=Gtk2::ComboBox->new_text;
	map
	{
	    $system_xaxismenu->append_text($_);
	}@system_cols;
	$system_xaxismenu->set_active(1);
	$system_xaxismenu->set_wrap_width(3);

	$leftcol->pack_start(set_tooltip_with_eventbox($system_xaxismenu,'Select a data type to be used as the X axis of the plot'),FALSE,FALSE,0);

	my $leftscroller=Gtk2::ScrolledWindow->new;
	$leftscroller->add_with_viewport($leftstack);
	$leftscroller->set_policy('automatic','automatic');
	$leftscroller->set_size_request(240,300);
	$leftscroller->set_shadow_type('etched-out');

	# logscale and 10^x buttons for X
	$system_logscalex_buttons[0]=Gtk2::RadioButton->new_with_label_and_tooltip(undef,'Linear X Axis','Set X axis to a linear scale');
	$system_logscalex_buttons[1]=Gtk2::RadioButton->new_with_label_and_tooltip($system_logscalex_buttons[0]->get_group,'Log X Axis',"Log the data (base 10) and then plot (NB data < ".logmin." (logmin) will be set to ".logmin." to avoid log(0))");
	$system_logscalex_buttons[2]=Gtk2::RadioButton->new_with_label_and_tooltip($system_logscalex_buttons[0]->get_group,'10^ X Axis','Take 10^X for each value, then plot (useful for convert log(x) to x, if log(x) is the only option)');

	$system_logscalex_buttons[0]->set_active(TRUE);

	my $hlog=Gtk2::HBox->new;
	{
	    my $v1=Gtk2::VBox->new;
	    map{$v1->pack_start($_,FALSE,FALSE,0);}@system_logscalex_buttons;
	    $hlog->pack_start($v1,FALSE,FALSE,0);
	}
	$hlog->pack_start(Gtk2::VSeparator->new,FALSE,FALSE,0);

	# resolution slider
	{
	    my $v=Gtk2::VBox->new;
	    $v->pack_start(Gtk2::Label->new('Res'),FALSE,FALSE,0);
	    $v->pack_start(res_slider(),FALSE,FALSE,0);
	    $hlog->pack_start(Gtk2::VSeparator->new,FALSE,FALSE,0);
	    $hlog->pack_start($v,FALSE,FALSE,0);
	}

	$leftcol->pack_start($hlog,FALSE,FALSE,0);

	# range boxes
	@system_rangebox=make_rangeboxes();
	
	{
	    my $hh=Gtk2::HBox->new(FALSE);
	    my $t=Gtk2::Table->new(3,4,FALSE);
	    my @topts=('shrink','shrink',3,3);

	    # header column
	    my $i=0;
	    foreach my $x ('Axis','Range')
	    {
		$t->attach(Gtk2::Label->new($x),$i++,$i,0,1,@topts);
	    }

	    # rows    
	    $i=1;
	    foreach my $ax ('X','Y','Y2')
	    { 
		# label
		$t->attach(Gtk2::Label->new($ax),0,1,$i,$i+1,@topts);
		
		# ranges
		my $h=Gtk2::HBox->new;
		$h->pack_start($system_rangebox[2*$i-2],FALSE,FALSE,0);
		$h->pack_start($system_rangebox[2*$i-1],FALSE,FALSE,0);
		$t->attach($h,1,2,$i,$i+1,@topts);
		
		$i++; # last
	    }
	    
	    # add range reset button  
	    $t->attach($system_rangebox[6],2,3,$i-2,$i-1,@topts);
	    $system_rangebox[6]->signal_connect(clicked=>sub{
		$prev_plotted_system_string=undef; # forces redraw
						});

	    # add button to clear selection	    
	    my $b=Gtk2::Button->new_with_label_and_tooltip('Clear Selection','Press this to clear the items in the list below - you will then be able to start plotting afresh.');
	    $b->signal_connect(clicked=>sub{
		map
		{
		    $_->set_active(FALSE);
		}@system_yaxisbuttons;
			       });
	    $t->attach($b,2,3,$i-1,$i,@topts);

	    $hh->pack_start($t,FALSE,FALSE,0);

	    $leftcol->pack_start($hh,FALSE,FALSE,0);
	}

	$leftcol->pack_start($leftscroller,TRUE,TRUE,0);

	$system_image=Gtk2::Image->new_from_pixbuf((no_image())[1]);
	%system_image_properties=('image'=>'System',
				  'image widget'=>$system_image,
				  'noimage'=>1);

	my $rightstack=Gtk2::VBox->new(0,1);

	$rightstack->pack_start(set_image_callbacks('System',$system_image,\%system_image_properties),TRUE,TRUE,0);

	{

	    if($evcode eq 'binstar')
	    {
		# new order packing! much more compact...
		foreach my $l (@system_labels)
		{
		    my $h=Gtk2::HBox->new;
		    foreach my $b (@$l)
		    {
			my $n = $b=~s/=(.*)//o ? $system_cols{$1} : $system_cols{$b};
			$system_yaxisbuttons[$n]=Gtk2::ToggleButton->new_with_label_and_tooltip($b,"Plot $system_cols[$n] on the Y axis");
			$h->pack_start($system_yaxisbuttons[$n],TRUE,TRUE,0);
		    }
		    $leftstack->pack_start($h,FALSE,FALSE,0);
		}

		# check all buttons are packed in!
		{
		    my $fail=0;
		    for(my $i=0;$i<=$#system_cols;$i++)
		    {
			if(!defined($system_yaxisbuttons[$i]))
			{
			    print "Button $i = $system_cols[$i] not defined!\n";
			    $fail=1;
			}
		    }
		    exit if($fail);
		}
	    }
	    else
	    {
		# standard-order packing
		my @eboxes;
		for(my $i=0;$i<=$#system_cols;$i++)
		{
		    $system_yaxisbuttons[$i]=Gtk2::ToggleButton->new_with_label($system_cols[$i]);
		    my $j=$i;
		    $system_axes[$i]='x1y1'; # default axis
		    $system_ll[$i]='Linear'; # default to linear
		    $system_yaxisbuttons[$i]->signal_connect('button-release-event'=>sub{
			my ($widget,$event)=@_;
			return FALSE unless $event->button==RIGHT_MOUSE_BUTTON;

			# popup
			my $menu=Gtk2::Menu->new;
			my $axislabel=Gtk2::MenuItem->new('Axes');
			$axislabel->signal_connect(activate=>sub{
			    # axis menu
			    my $axismenu=Gtk2::Menu->new;
			    foreach my $ax ('x1y1','x1y2',
					    #'x2y1','x2y2'
				)
			    {
				my $post;
				$post='*' if($system_axes[$j] eq $ax);
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ax.$post);
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    $system_axes[$j] = $ax;
							  });
				$axismenu->append($menuitem);
			    }
			    $axismenu->popup(
				undef, # parent menu shell
				undef, # parent menu item
				undef, # menu pos func
				undef, # data
				$event->button,
				$event->time
				);
			    $axismenu->show_all;
						   });
			$menu->append($axislabel);
			
			# log, linear, 10^
			my $lllabel=Gtk2::MenuItem->new('Lin/Log/10^');
			$lllabel->signal_connect(activate=>sub{
			    
			    my $llmenu=Gtk2::Menu->new;
			    my @m=('Linear','log10','10^');
			    foreach my $ll (@m)
			    {
				my $post;
				$post='*' if($system_ll[$j] eq $ll);
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ll.$post);
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    $system_ll[$j] = $ll;
							  });
				$llmenu->append($menuitem);
			    }
			    $llmenu->append(Gtk2::SeparatorMenuItem->new);
			    foreach my $ll (@m)
			    {
				no warnings;
				my $menuitem = Gtk2::MenuItem->new($ll.' all');
				use warnings;
				$menuitem->signal_connect(activate=>sub{
				    map{$_=$ll}@system_ll;
							  });
				$llmenu->append($menuitem);
			    }

			    $llmenu->popup(
				undef, # parent menu shell
				undef, # parent menu item
				undef, # menu pos func
				undef, # data
				$event->button,
				$event->time
				);
			    $llmenu->show_all;
						 });
			$menu->append($lllabel);
			
			# show main menu
			$menu->show_all;
			$menu->popup(
			    undef, # parent menu shell
			    undef, # parent menu item
			    undef, # menu pos func
			    undef, # data
			    $event->button,
			    $event->time
			    );
							     });

		    $eboxes[$i]=set_tooltip_with_eventbox($system_yaxisbuttons[$i],"Plot $system_cols[$i] on the Y axis");
		}
		
		map
		{
		    $leftstack->pack_start($_,FALSE,FALSE,0);
		}@eboxes;
	    }	    
	}

	$tab_table[$ntab]->pack_start($leftcol,'shrink','fill',0);
	$tab_table[$ntab]->pack_start($rightstack,'expand','shrink',0);
	$frames[$ntab]->add(scrolled($tab_table[$ntab]));
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }
    
    update_System_tab(1);
    increment_timing('show_System_tab',$time0) if($timings);
    TRUE;

}


sub update_System_tab
{
    return if(!$systemtab);
    return if($updating{system} || time()<$next_update_time{System});
    $updating{system}=1;

    #print "Update system tab?\n";
    # System of the star
    my $time0 = [gettimeofday()] if($timings);
    if((ok_file('system')==1)&&
       ((($running_the_code==1)||($have_run_the_code==1))||
	($_[0]==1))&&
       defined($system_xaxismenu))
    {
	toggle_hourglass(1);
	#print "Yes update system tab\n";
	# determine which columns to plot
	# these are the columns in the system
	
	# get xaxis from $system_xaxismenu
	my $xaxis=$system_xaxismenu->get_active;
	my $xaxislabel=$system_cols[$xaxis];
	
	# get y axes from @system_yaxisbuttons
	my @yaxes;
	for(my $i=0;$i<=$#system_yaxisbuttons;$i++)
	{
	    if($system_yaxisbuttons[$i]->get_active==TRUE)
	    {
		push(@yaxes,$i);
	    }
	}

	
	my $logx=0;
	if($system_logscalex_buttons[1]->get_active==TRUE)
	{
	    $logx=1;
	}
	elsif($system_logscalex_buttons[2]->get_active==TRUE)
	{
	    $logx=2;
	}


	# autoscale
	my ($width,$height)=autosize($system_image->parent->parent->parent->parent);
	
	# for some reason we have to make it smaller
	$width*=0.95;
	$height*=0.95;
	
	# keep aspect ratio at $aspect_ratio
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;
	print "AUTOSCALE SYSTEM $width $height\n"if($vb);
	# axis ranges
	my @range=get_graphrange(@system_rangebox);
	
	my $system_string=join(' ',$xaxis,@yaxes,$logx,@system_ll, -s 'system',@misc_opts_values,@range,@system_axes,gnuplot_options_string());

	my $lt=1;

	if(!(defined($prev_plotted_system_string))||
	   ($system_string ne $prev_plotted_system_string))
	{
	    if($#yaxes>-1)
	    {
		my $xl=' ';
		if($logx==1)
		{
		    $xl='log_{10}';
		}
		elsif($logx==2)
		{
		    $xl='10^';
		}

		toggle_hourglass(1);
		# make system diagram
		my $pltfile="$cachedir/system/".rand();
		mkdir "$cachedir/system";
		open(SYSTEM,">$pltfile.plt")||confess("cannot open $pltfile.plt for output : $!");

		# make x-axis label
		$xl="$xl\{$xaxislabel\}";
		$xl=~s/10\^\{log/\{/o; # obvious really
		
		print SYSTEM gnuplot_header($pltfile.'.png'),"
set xrange\[$range[0]:$range[1]\]
set yrange\[$range[2]:$range[3]\]
set y2range\[$range[4]:$range[5]\]
set xlabel \"$xl\"
unset xtics
unset ytics
unset x2tics
unset y2tics
";

		# tics for non-standard axes
		foreach my $yaxis (@yaxes)
		{
		    print SYSTEM "set xtics ",($system_axes[$yaxis]=~/x2/o ? "nomirror; set x2tics" : "mirror"),"\nset ytics ",($system_axes[$yaxis]=~/y2/o ? "nomirror; set y2tics" : "mirror"),"\n"if(defined($system_axes[$yaxis]));
		}

		print SYSTEM gnuplot_overrides();
		
		if(($#yaxes==0)&&($system_cols[$yaxes[0]] eq 'nphase'))
		{
		    print SYSTEM "
set ytics (\"PreMS\" 1, \"centH\" 2, \"RGB\" 3, \"centHe\" 4, \"AGB\" 5, \"centC\" 6, \"centNe\" 7)\n";

		}

		my $plotcomma='plot ';
		foreach my $yaxis (@yaxes)
		{
		    print SYSTEM "$plotcomma \"system\" using ";
		    
		    if($logx==0)
		    {
			print SYSTEM ($xaxis+1);
		    }
		    elsif($logx==1)
		    {
			print SYSTEM '(log($',($xaxis+1),'+'.logmin.')*'.ivalue_of_log10.')';
		    }
		    elsif($logx==2)
		    {
			print SYSTEM '(10.0**($',($xaxis+1),'))';
		    }

		    print SYSTEM ':';

		    my $yl;
		    if($system_ll[$yaxis] eq 'Linear')
		    {
			print SYSTEM ($yaxis+1);
			$yl='';
		    }
		    elsif($system_ll[$yaxis] eq 'log10')
		    {
			print SYSTEM '(log($',($yaxis+1),'+'.logmin.')*'.ivalue_of_log10.')';
			$yl='log_{10}';
		    }
		    else
		    {
			print SYSTEM '(10.0**($',($yaxis+1),'))';
			$yl='10^';
		    }

		    # TODO : repeat for x2,y2

		    # make line label
		    my $t=$yl.'{'.format_ps_title($system_cols[$yaxis]).'}';
		    $t=~s/10\^\{log/\{/o; # obvious really
		    
		    # choose resolution
		    my $every=guess_every();
		    print SYSTEM " every $every ";
		    
		    # choose axes
		    print SYSTEM " axes $system_axes[$yaxis] ";
		    
		    # with string
		    my $with=withstring($lt++);
		    print SYSTEM $with." title \"$t\" ";

		    $plotcomma=',';
		}

		print SYSTEM gpshow() if(!$persist_gnuplot);
		close SYSTEM;
		toggle_hourglass(1);
		
		my $grange; # gnuplot's range (hash)
		my $err;
 		my $f=$pltfile.'.plt';
		if($gnuplot_options{'terminal type'}=~/postscript/io)
		{
		    if($persist_gnuplot)
		    {
			my $h = gnuplot_ps_to_png2(
			    { 
				'input file'=>$f,
				'png output file'=>$pltfile.'.png',
				dpi=>get_opt(0),
				'gnuplot stream in'=>$persist_gnuplot{in},
				'gnuplot stream out'=>$persist_gnuplot{out},
				'gnuplot stream err'=>$persist_gnuplot{err},
				'colourblind'=>1,
				invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
			    }
			    );
			$grange = $$h{'graph range'};
			$err = $$h{err};
		    }
		    else
		    {
			($grange,$err) = gnuplot_ps_to_png($f,$pltfile.'.png',get_opt(0),1); 
		    }

		}
		else
		{
		    # native PNG
		    my $cmd=prepend_gdfontpath($external_programs{gnuplot}.' '.$f);
		    reformat_pltfile($f);
		    my $res=`$cmd 2>&1`;
		    ($grange,$err)=extract_gnuplot_range(\$res);
           	}
		push_to_Misc_log($err);

		%system_image_properties=('image'=>'system', 
					  'image widget'=>$system_image,			       
					  'grange'=>$grange,
					  'width'=>get_opt(1),
					  'height'=>get_opt(2),
					  'lmargin'=>$gnuplot_options{lmargin},
					  'rmargin'=>$gnuplot_options{rmargin},
					  'tmargin'=>$gnuplot_options{tmargin},
					  'bmargin'=>$gnuplot_options{bmargin},
					  'xrange widgets'=>[$system_rangebox[0],$system_rangebox[1]],
					  'yrange widgets'=>[$system_rangebox[2],$system_rangebox[3]],
					  'y2range widgets'=>[$system_rangebox[4],$system_rangebox[5]],
		    );

		toggle_hourglass(0);

		# update image
		$system_image->set_from_pixbuf((get_image_from_file($pltfile.'.png'))[1]);

		$ps_filenames[$current_tab]=$pltfile.'.ps';

		# remove files
		unlink ($pltfile.'.plt',$pltfile.'.png')if($unlink_tempfiles);
		$prev_plotted_system_string=$system_string;
		toggle_hourglass(0);
	    }
	    $tab_table[$tab_numbers{System}]->show_all;
	}
	toggle_hourglass(0);
    }
    increment_timing('update_System_tab',$time0) if($timings);
    $updating{system}=0;
    TRUE;
}


############################################################
### Internals tab
############################################################

sub show_Internals_tab
{
    # make internals tab
    my $ntab=$tab_numbers{Internals};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	
	$made_tab[$ntab]=1;
	
	print "Make internals tab\n" if ($vb);

	# the internals tab is a selector along the top for the y-axis variable
	# a selector (scrolly menu) on the left for models
	# and a display on the right
	
	my $framev=Gtk2::VBox->new;
	my $isotope_hrow=Gtk2::HBox->new;
	my $isotope_vbox=Gtk2::VBox->new;
	my $panels=Gtk2::HBox->new;

	$framev->pack_start($isotope_vbox,FALSE,FALSE,0);
	$framev->pack_start($panels,TRUE,TRUE,0);

	# populate the isotope table
	my $lab=Gtk2::Label->new('Y Axis:');
	$isotope_hrow->pack_start($lab,FALSE,FALSE,0);

	if($evcode eq 'binstar')
	{
	    # new order packing! much more compact...
	    foreach my $l (@internals_y_labels)
	    { 
		my $h=Gtk2::HBox->new;
		my $isotope_hrow=Gtk2::HBox->new;
		foreach my $b (@$l)
		{
		    my $n=$internals_y_variables{$b};
		    $internals_y_buttons[$n]=Gtk2::ToggleButton->new_with_label($internals_y_variables[$n]);
		    $isotope_hrow->pack_start($internals_y_buttons[$n],FALSE,FALSE,0);
		    $internals_y_buttons[$n]->signal_connect(clicked=>\&Internals_option_change);
		}
		$isotope_vbox->pack_start($isotope_hrow,TRUE,TRUE,0);
	    }
	    
	    # check all buttons are packed in!
	    {
		my $fail=0;
		for(my $i=0;$i<=$#internals_y_variables;$i++)
		{
		    if(!defined($internals_y_buttons[$i]))
		    {
			print "Internals Y Button $i = $internals_y_variables[$i] not defined!\n";
			$fail=1;
		    }
		}
		exit if($fail);
	    }
	    # pack remaining hrow
	    $isotope_vbox->pack_start($isotope_hrow,TRUE,TRUE,0);
	}

	elsif($evcode =~/bonnfires/i)
	{ 
	    my $c=1;
	    my $rowcount=0;
	    my $w=0;

	    my %children;
	    my $start_label=$internals_y_variables[0];
	    my $notebook=Gtk2::Notebook->new;
	    $notebook->set_show_border(TRUE);
	    $notebook->set_scrollable(TRUE);
	    $isotope_vbox->pack_start($notebook,FALSE,FALSE,0);

	    my $maxtabs = 12;
	    my $maxw = MAX(int(($#internals_y_variables+1)/(1.0*$maxtabs)),20);
	    
	    #print STDERR "There are $#internals_y_variables vars, hence $maxw per tab\n";

	    my $isotope_vrow ;
	    my $hcount=0;

	    for(my $i=0;$i<=$#internals_y_variables;$i++)	
	    {
		my $label = $internals_y_variables[$i];

		$label=~s!d(.*)/dt!$1!;

		$internals_y_buttons[$i] = defined($internals_tooltips[$i]) ?
		    Gtk2::ToggleButton->new_with_label_and_tooltip($label,
								   "Plot $internals_y_variables[$i] ($internals_tooltips[$i]) on the Y axis") : Gtk2::ToggleButton->new_with_label($label);
						
		# override label
		my $dh=0;
		if($label=~s/([A-Z][a-z]*)(\d+)/<span size="smaller"><span rise="+5000" size="smaller">$2<\/span><\/span>$1/)
		{
		    $dh=length($1)+0.8*length($2);
		}
		else
		{
		    $dh=length($label);
		}

		$internals_y_buttons[$i]->get_child->set_markup($label);

		if($hcount>85 || !defined $isotope_hrow || !defined $isotope_vrow)
		{
		    $isotope_hrow=Gtk2::HBox->new();
		    $isotope_vrow=Gtk2::VBox->new() if(!defined $isotope_vrow);
		    $isotope_vrow->pack_start($isotope_hrow,FALSE,FALSE,0);
		    $hcount=0;
		}

		$hcount+=$dh;
		$isotope_hrow->pack_start($internals_y_buttons[$i],FALSE,FALSE,0);

		$internals_y_buttons[$i]->
		    signal_connect('button-release-event'=>sub
				   {
				       my $w=$_[0];
				       my $event=$_[1];
				       if($event->button==RIGHT_MOUSE_BUTTON)
				       {
					   # right click : menu to clear this subset, or all

					   my $menu=Gtk2::Menu->new;
					   my $menuitem;

					   $menuitem=Gtk2::MenuItem->new('Select all in this subset');
					   $menuitem->signal_connect(activate=>sub{
					       map{
						   $internals_y_buttons[$_]->set_active(TRUE);
					       }@{$children{$w->parent}};
								     });
					   $menu->append($menuitem);

					   $menuitem=Gtk2::MenuItem->new('Clear this subset');
					   $menuitem->signal_connect(activate=>sub{
					       map{
						   $internals_y_buttons[$_]->set_active(FALSE);
					       }@{$children{$w->parent}};
								     });
					   $menu->append($menuitem);

					   $menuitem=Gtk2::MenuItem->new('Clear all');
					   $menuitem->signal_connect(activate=>sub{
					       map{
						   $_->set_active(FALSE);
					       }@internals_y_buttons;
								     });
					   $menu->append($menuitem);

					   $menu->show_all;
					   $menu->popup(
					       undef, # parent menu shell
					       undef, # parent menu item
					       undef, # menu pos func
					       undef, # data
					       $event->button,
					       $event->time
					       );
				       }
				   });

		$internals_y_buttons[$i]->
		    signal_connect('clicked'=>sub
				   {
				       my $w=$_[0];
				       
				       # when a button is selected, change its parent's (tab) label
				       # to have a * before it
				       my $tab = $w->parent->parent->parent;
				       my $label=$tab->get_tab_label_text($w->parent->parent);
				       
				       my $active=0;
				       map
				       {
					   $active ||= $internals_y_buttons[$_]->get_active;
				       }@{$children{$w->parent->parent}};

				       if($label!~/\*/ && $active==1)
				       {
					   $tab->set_tab_label_text($w->parent->parent,'*'.$label);
				       }
				       elsif($label=~/\*/ && $active==0) 
				       {
					   $label=~s/\*//;
					   $tab->set_tab_label_text($w->parent->parent,$label);
				       }

				       Internals_option_change();
				   });

		push(@{$children{$isotope_vrow}},$i);

		# every $maxw variables, or on specific strings, wrap
		if($w++==$maxw ||
		   $i==$#internals_y_variables ||
		    #||
		   #($internals_y_variables[$i+1] =~ /nucsyndt/ && $internals_y_variables[$i] !~ /nucsyndt/)||
		   #($internals_y_variables[$i+1] =~ /burnmask/ && $internals_y_variables[$i] !~ /burnmask/)||
		   $internals_y_variables[$i+1] eq 'n'||
		   $internals_y_variables[$i+1] eq 'dMass/dt' ||
		   #$internals_y_variables[$i+1] =~ /C12/ ||
		   #$internals_y_variables[$i+1] =~ /Ne20/ ||
		   #$internals_y_variables[$i] =~ /Si30/
		   0
		    )
		{
		    my $label="$start_label-$internals_y_variables[$i]";
		    $label=~s!d(.*)/dt-d(.*)/dt!d\($1-$2)/dt!;
		    $label=~s/nucsyndt(\d+)-nucsyndt(\d+)/nucsyndt$1-$2/;
		    $label=~s/burnmask0-burnmask/burnmasks/;

		    $notebook->append_page($isotope_vrow,$label);
		    $c++; 
		    $start_label = $internals_y_variables[$i+1];
		    $isotope_hrow=undef;
		    $isotope_vrow=undef;
		    $w=0;
		}
	    }
	}
	else
	{
	    for(my $i=0;$i<=$#internals_y_variables;$i++)	
	    {
		# wrap after 20 variables
		if($i%19==0)
		{
		    $isotope_vbox->pack_start($isotope_hrow,TRUE,TRUE,0);
		    $isotope_hrow=Gtk2::HBox->new;
		}

		$internals_y_buttons[$i]=defined($internals_tooltips[$i]) ?
		    Gtk2::ToggleButton->new_with_label_and_tooltip($internals_y_variables[$i],
								   "Plot $internals_y_variables[$i] ($internals_tooltips[$i]) on the Y axis") : 
								   Gtk2::ToggleButton->new_with_label($internals_y_variables[$i]);
		    		
		$isotope_hrow->pack_start($internals_y_buttons[$i],FALSE,FALSE,0);
		$internals_y_buttons[$i]->signal_connect(clicked=>\&Internals_option_change);
	    }

	# pack remaining hrow
	$isotope_vbox->pack_start($isotope_hrow,TRUE,TRUE,0);
	}



	$internals_menu=Gtk2::ComboBox->new_text;
	$internals_xaxis_menu=Gtk2::ComboBox->new_text;

	# populate the side bar with the $internals_menu
	my $topleftvbox=Gtk2::VBox->new(FALSE,0);
	$topleftvbox->pack_start(starbuttons('h'),FALSE,FALSE,0);
	my $leftbar=Gtk2::ScrolledWindow->new;
	$internals_leftvbox=Gtk2::VBox->new(FALSE,0);
	
	# still, or animated?
	$internals_anim_buttons[0]=Gtk2::RadioButton->new_with_label_and_tooltip(undef,'Still','Click here to plot still images');
	$internals_anim_buttons[0]->signal_connect(clicked=>\&Internals_option_change);	

	my $internals_anim_radio_group=$internals_anim_buttons[0]->get_group();

	$internals_anim_buttons[1]=Gtk2::RadioButton->new_with_label_and_tooltip($internals_anim_radio_group,'Animate','Click here to plot animations');
	$internals_anim_buttons[1]->signal_connect(clicked=>\&Internals_option_change);
	


	# animation position
	$internals_anim_adjustments[2]=Gtk2::Adjustment->new(0,0,100,10,10,0);
	$internals_anim_buttons[2]=Gtk2::HScale->new_with_tooltip($internals_anim_adjustments[2],"Adjust this slider to alter the frame number of the animation. This will only do something if 'Animate' is selected.");
	$internals_anim_buttons[2]->signal_connect(value_changed=>\&update_anim_callback);
	$internals_anim_buttons[2]->signal_connect(value_changed=>\&Animation_frame_update);
	$internals_anim_buttons[2]->set_digits(0);

	# animation speed
	$internals_anim_buttons[3]=Gtk2::HScale->new_with_tooltip(Gtk2::Adjustment->new(1,0,11,10,10,0),"Adjust this slider to alter the frame update speed of the animation. This will only do something if 'Animate' is selected. Setting the speed to zero pauses the animation, but you can still change the frame number. \nThis goes to eleven.");
	$internals_anim_buttons[3]->set_digits(0);
	$internals_anim_buttons[3]->signal_connect(value_changed=>\&set_anim_speed);
	$internals_anim_buttons[3]->signal_connect(value_changed=>\&Internals_option_change);
	$internals_anim_buttons[1]->signal_connect(clicked=>sub{set_anim_speed($internals_anim_buttons[3]);});

	my $t2=Gtk2::Table->new(2,3,FALSE);
	my $fill='shrink';
	$t2->attach($internals_anim_buttons[0],0,1,0,1,$fill,$fill,0,0);
	$t2->attach($internals_anim_buttons[1],1,2,0,1,$fill,$fill,0,0);
	
	foreach (0,1,2)
	{
	    $t2->attach($internals_drawtype[$_],$_,$_+1,1,2,$fill,$fill,0,0) if(defined($internals_drawtype[$_]));
	}
	$topleftvbox->pack_start($t2,FALSE,FALSE,0);

	my $h1=Gtk2::HBox->new;
	$h1->pack_start(Gtk2::Label->new('Frame'),FALSE,FALSE,0);
	$h1->pack_start($internals_anim_buttons[2],TRUE,TRUE,0);
	$topleftvbox->pack_start($h1,FALSE,FALSE,0);

	$h1=Gtk2::HBox->new;
	$h1->pack_start(Gtk2::Label->new('Speed'),FALSE,FALSE,0);
	$h1->pack_start($internals_anim_buttons[3],TRUE,TRUE,0);
	$topleftvbox->pack_start($h1,FALSE,FALSE,0);

	$leftbar->add_with_viewport($internals_leftvbox);
	$leftbar->set_policy('automatic','automatic');
	$leftbar->set_shadow_type('etched-out');
	
	$internals_menu->set_wrap_width(3);

	map
	{
	    $internals_menu->append_text($_);
	}@plt1cols;

	$internals_menu->set_active(0);
	$internals_menu->signal_connect('changed'=>\&update_Internals_menu);
	$internals_menu->signal_connect('changed'=>\&Internals_option_change);

	$topleftvbox->pack_start(set_tooltip_with_eventbox($internals_menu,'With this menu you can select which data item you would like to use to identify each model. Usually this is model number, but it could be age, radius etc. in fact anything that also changes with model number. Note that if multiple models have the same (e.g.) mass, and you select mass, you will not be able to distinguish them.'),
				 FALSE,FALSE,0);

	$internals_xaxis_menu->set_wrap_width(3);
	map
	{
	    $internals_xaxis_menu->append_text("X axis: ".$_);
	}('shell#',@internals_y_variables);

	@Kippenhahn_labels=( @internals_y_variables,'M/Mass');
	push(@Kippenhahn_labels,'Convection')if($evcode_module eq 'STARS');

	$internals_xaxis_menu->set_active(0);
	$internals_xaxis_menu->signal_connect(changed=>\&Internals_option_change);
	$topleftvbox->pack_start(set_tooltip_with_eventbox($internals_xaxis_menu,'Choose here the data type you would like to plot on the X axis'),
				 FALSE,FALSE,0);
	
	# graph ranges
	@internals_range=make_rangeboxes();
	foreach (0..5)
	{
	    # extra callback in this tab
	    $internals_range[$_]->signal_connect(changed=>\&Internals_option_change);
	}
	# reset button
	$internals_range[6]->signal_connect(clicked=>sub{
					    });

	# log buttons
	$internals_log10x=Gtk2::ToggleButton->new_with_label_and_tooltip('Log 10 X','Select this to log the abscissa (X-axis) data before plotting');
	$internals_log10x->set_alignment(0,0);
	$internals_log10x->signal_connect(toggled=>\&Internals_option_change);
	$internals_log10=Gtk2::ToggleButton->new_with_label_and_tooltip('Log 10 Y','Select this to log the ordinate (Y-axis) data before plotting');
	$internals_log10->set_alignment(0,0);
	$internals_log10->signal_connect(toggled=>\&Internals_option_change);

	# pack X range 
	my $h3=Gtk2::HBox->new;
	foreach (Gtk2::Label->new('X-axis Range'),@internals_range[0,1],$internals_log10x)
	{
	    $h3->pack_start($_,FALSE,FALSE,0);
	}
	$topleftvbox->pack_start($h3,FALSE,FALSE,0);
	
	# pack Y range
	$h3=Gtk2::HBox->new;
	foreach (Gtk2::Label->new('Y-axis Range'),@internals_range[2,3],$internals_log10,$internals_range[6])
	{
	    $h3->pack_start($_,FALSE,FALSE,0);
	}
	$topleftvbox->pack_start($h3,FALSE,FALSE,0);

	# extra buttons
	my $extramenu=Gtk2::HBox->new;
	{
	    my $but=Gtk2::Button->new_with_label_and_tooltip('C','Clear the list of selected models');
	    $but->signal_connect(clicked=>\&clear_internals_menu);
	    $but->signal_connect(clicked=>\&Internals_option_change);
	    $extramenu->pack_start($but,FALSE,FALSE,0);
	}
	
	{
	    my %tooltips=('|',"OR the list, so all selected models become deselected, and vice versa",
			  1,"Select every model in the list",
			  2,"Select every 2nd model in the list");
	    
	    map
	    {
		my $but=Gtk2::Button->new_with_label($_);
		$but->signal_connect(clicked=>\&select_in_internals_menu,$_);
		$but->signal_connect(clicked=>\&Internals_option_change);
		set_tooltip($but, defined($tooltips{$_}) ? $tooltips{$_}: set_tooltip($but,"Select every $_ th model in the list"));
		$extramenu->pack_start($but,FALSE,FALSE,0);
	    }(1,2,5,10,25,50,100,200,500,'|');
	    $topleftvbox->pack_start($extramenu,FALSE,FALSE,0);
	}

	# make buttons for each model
	my $col=$internals_menu->get_active;
	
	# first button is the 'latest' model
	$internals_buttons[0]=Gtk2::ToggleButton->new_with_label_and_tooltip('Latest','Shortcut button: Select the latest model in the list. This is useful for following the structure of a star as it evolves.');
	$internals_buttons[0]->set_alignment(0,0);
	$internals_buttons[0]->signal_connect(toggled=>\&Internals_option_change);
	$internals_buttons[0]->signal_connect(toggled=>\&Internals_button_click,0);
	$internals_buttons[0]->signal_connect('button-release-event'=>\&model_clicked,0);

	$internals_leftvbox->pack_start($internals_buttons[0],FALSE,TRUE,0);
	
	for(my $i=1;$i<=$#plt1data;$i++)
	{
	    my @d=split(/\s+/o,$plt1data[$i],$col+1);
	    if(model_is_available($d[$col]))
	    {
		$internals_buttons[$i]=Gtk2::ToggleButton->new_with_label_and_tooltip($d[$col],"Select model $d[$col] for plotting.");
		$internals_buttons[$i]->set_alignment(0,0);
		$internals_buttons[$i]->signal_connect(toggled=>\&Internals_option_change);
		$internals_buttons[$i]->signal_connect(toggled=>\&Internals_button_click,$i);
		
		# right click
		if($i!=$#plt1data)
		{
		    # but not for the final model which is not in the file!
		    $internals_buttons[$i]->signal_connect('button-release-event'=>\&model_clicked,$i);
		}
		$internals_leftvbox->pack_start($internals_buttons[$i],FALSE,FALSE,0);
	    }
	}
	
	my $v=Gtk2::VBox->new(0,0);
	$v->pack_start($topleftvbox,FALSE,FALSE,0);
	$v->pack_start($leftbar,TRUE,TRUE,0);
	$panels->pack_start($v,FALSE,TRUE,0);

	# make internals tabs
	$internals_notebook=Gtk2::Notebook->new;

	$internals_notebook->set_tab_pos('top');
	for(my $i=0;$i<20;$i++)
	{
	    $internals_img[$i]=Gtk2::Image->new;
	    $internals_image_properties[$i]={image=>'internals '.$i,
					     'image widget'=>$internals_img[$i],
					     noimage=>1
	    };
	    $internals_eventbox[$i]=set_image_callbacks("Internals$i",$internals_img[$i],$internals_image_properties[$i]);	    
	    $internals_notebook->append_page($internals_eventbox[$i],$i);
	    add_internals_image_handler($internals_img[$i]->parent);
	}
	$current_internals_tab=0;
	$internals_notebook->signal_connect(button_release_event=>\&toggle_internals_tab);
	$internals_notebook->set_resize_mode('parent');
	$panels->pack_start($internals_notebook,TRUE,TRUE,0);

	$frames[$ntab]->add(scrolled($framev));
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }
    
    update_Internals_tab(0);
    increment_timing('show_Internals_tab',$time0) if($timings);
    TRUE;
}

sub switch_internals_minitab
{
    my $new_internals_tab=$_[0];
 
    # switch internals tab from $current_internals_tab to $new_internals_tab
    print "Switch internals tab from $current_internals_tab to $new_internals_tab\n" if ($vb);
    
    # do nothing
    return (TRUE) if($new_internals_tab==$current_internals_tab);
    
    toggle_hourglass(1);

    # save the current tab information
    my @state;
    push(@state,
	 Button_get_one_or_zero($internals_anim_buttons[0]),
    	 Button_get_one_or_zero($internals_anim_buttons[1]),
	 $internals_anim_buttons[2]->get_value,
	 $internals_anim_buttons[3]->get_value,
	 $internals_menu->get_active,
	 Button_get_one_or_zero($internals_log10),
	 Button_get_one_or_zero($internals_log10x));
    for(my $i=0;$i<=$#internals_y_buttons;$i++)
    {
	push(@state,Button_get_one_or_zero($internals_y_buttons[$i]));
    }
    for(my $i=0;$i<=$#plt1data;$i++)
    {
	push(@state,Button_get_one_or_zero($internals_buttons[$i]));
    }
    push(@state,Button_get_one_or_zero($internals_drawtype[0]),
	 Button_get_one_or_zero($internals_drawtype[1]));

    $internals_tab_cache[$current_internals_tab]=join(' ',@state);

    if($vb)
    {
	print "Save state for $current_internals_tab : ",join(' ',@state),"\n";
	print "State for $new_internals_tab : ",$internals_tab_cache[$new_internals_tab],"\n";
    }

    # insert the new tab information
    if(defined($internals_tab_cache[$new_internals_tab]))
    {
	@state=split(/\s+/o,$internals_tab_cache[$new_internals_tab]);
	print "Set Anim buttons to $state[0] $state[1] $state[2] $state[3]\n" if($vb);
	Button_set_FALSE_or_TRUE($internals_anim_buttons[0],shift(@state));
	Button_set_FALSE_or_TRUE($internals_anim_buttons[1],shift(@state));
	$internals_anim_buttons[2]->set_value(shift(@state));
	$internals_anim_buttons[3]->set_value(shift(@state));

	$internals_menu->set_active(shift(@state));
	Button_set_FALSE_or_TRUE($internals_log10,shift(@state));
	Button_set_FALSE_or_TRUE($internals_log10x,shift(@state));
	for(my $i=0;$i<=$#internals_y_buttons;$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_y_buttons[$i],shift(@state));
	}
	for(my $i=0;$i<=$#plt1data;$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_buttons[$i],shift(@state));
	}
	Button_set_FALSE_or_TRUE($internals_drawtype[0],shift(@state));
	Button_set_FALSE_or_TRUE($internals_drawtype[1],shift(@state));
    }
    else
    {
	# leave internals_menu alone, but set everything else
	Button_set_FALSE_or_TRUE($internals_anim_buttons[0],1);
	Button_set_FALSE_or_TRUE($internals_anim_buttons[1],0);
	$internals_anim_buttons[2]->set_value(0);
	$internals_anim_buttons[3]->set_value(1);
	Button_set_FALSE_or_TRUE($internals_log10,0);
	Button_set_FALSE_or_TRUE($internals_log10x,0);
	$internals_menu->set_active(0);
	for(my $i=0;$i<=$#internals_y_buttons;$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_y_buttons[$i],0);
	}
	for(my $i=0;$i<=$#plt1data;$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_buttons[$i],0);
	}
	Button_set_FALSE_or_TRUE($internals_drawtype[0],1);
	Button_set_FALSE_or_TRUE($internals_drawtype[1],0);
    }
    $current_internals_tab=$new_internals_tab;

    toggle_hourglass(0);
    update_Internals_tab(0);
    TRUE;
}

sub Button_get_one_or_zero
{
    # query a Button, return 0 if FALSE, 1 if TRUE
    my $w=$_[0];
    return (defined($w)&&($w->get_active==TRUE)) ? 1:0; 
}

sub Button_set_FALSE_or_TRUE
{
    my $w=$_[0]; # widget
    my $x=$_[1]; # 0 or 1
    $w->set_active($x==0?FALSE:TRUE)if(defined($x)&& defined($w));
   
    # what to do if $x is not defined? nothing...
    TRUE;
}

sub clear_internals_menu
{
    print "Clear internals menu\n" if ($vb);
    map{$_->set_active(FALSE)if(defined($_));}@internals_buttons;
    FALSE;
}

sub update_Internals_menu
{
    toggle_hourglass(1);
    my $col=$internals_menu->get_active;
    
    for(my $i=1;$i<=$#plt1data;$i++)
    {	
	if(defined($internals_buttons[$i]))
	{
	    my @d=split(/\s+/o,$plt1data[$i]);
	    my $n=$i+1;
	    $internals_buttons[$i]->set_label( $col==0 ? $d[0] : $d[$col].' ('.$n.')')if(model_is_available($n));
	}
    }
    clear_events_queue();
    toggle_hourglass(0);
    TRUE;
}


sub test_for_animation
{
    return(Button_get_one_or_zero($internals_anim_buttons[1]));
}

sub Internals_click
{
    # wrapper function to help speed
    Internals_option_change();
    Internals_button_click(@_);
}

sub Internals_option_change
{
    delay_updates();
    $internals_options_state=1;
    $anim_frame_update=1;
    if((test_for_animation()==1)&&($internals_drawtype==2))
    {
	# cannot have slice with animation
	$internals_drawtype=1;
	$internals_drawtype[1]->clicked;
    }
    print "Internals option state -> 1\n" if($vb);
}

sub Internals_button_click
{

    my $w=$_[0];
    my $x=$_[1]; # the widget number
    print "Internals click button $x \n"if($vb);
    
    if(("@keys_pressed" eq 'Shift_L')&&($prev_internal ne ''))
    {
	print "Select range from $prev_internal to $x\n"if($vb);
	for(my $i=$prev_internal;$i<=$x;$i++)
	{
	    $internals_buttons[$i]->set_active(TRUE);
	}
    }
    elsif(("@keys_pressed" eq 'Control_L')&&($prev_internal ne ''))
    {
	print "DeSelect range from $prev_internal to $x\n"if($vb);
	for(my $i=$prev_internal;$i<=$x;$i++)
	{
	    $internals_buttons[$i]->set_active(FALSE);
	}
    }

    $prev_internal=$x;

} 

sub Animation_frame_update
{
    $anim_frame_update=1;
    print "Animation frame update -> 1\n" if($vb);
}

sub update_Internals_tab
{
    return if(defined($updating_internals)&&($updating_internals==1));
    return if (time()<$next_update_time{Internals});
    $updating_internals=1;

    my $force=$_[0];
    print "update internals tab # $current_internals_tab\n" if($vb);
    my $img_set=FALSE;
    
    my $animate=0;
    
    my $plot=1;
    print "UI $internals_options_state $anim_frame_update\n" if ($vb);

    # auto size image
    my ($width,$height)=autosize($internals_notebook);

    print "Internals: AUTO SIZE $width x $height (diffs ",-($misc_opts_values[1]-$width),"x",-($misc_opts_values[2]-$height),") \n"if($vb);

    $misc_opts_values[1]=MAX(10,int($width*$aspect_ratio));
    $misc_opts_values[2]=MAX(10,int($height));
    
    print "Force $misc_opts_values[1] x $misc_opts_values[2]\n"if($vb);

    # Check if .plt1 file exists, if so we can continue
    if(ok_file($modinfo{plt1file_stub}.$nstar)==1)
    {
	# if we have more or fewer models than last time, update 
	# the models list

	# get required column
	my $col=$internals_menu->get_active;
	
	# Now, we have a problem if the model is running, as the menu
	# will change in length, and may get shorter when we start a new
	# model. So deal with those cases.
	print "CF $#plt1data vs $#internals_buttons\n" if ($vb);

	if($#plt1data>=$#internals_buttons)
	{
	    toggle_hourglass(1);

	    # extra buttons
	    my $n=$#plt1data+1;
	    for(my $i=MAX(0,$#internals_buttons-1);$i<$n;$i++)
	    {
		#print "Add model button $i?\n" if($vb);
		if(!(defined($internals_buttons[$i])))
		{
		    #print "Add model button $i? (2)\n" if($vb);
		    my $label = (split(/\s+/o,$plt1data[$i]))[$col];
		    if(model_is_available($label))
		    {
			print "NEW Model $i (label $label) col $col is $plt1cols[$col]\n" if($vb);
			my $b=Gtk2::ToggleButton->new_with_label($label);
			$b->set_alignment(0,0);
			$b->signal_connect('button-release-event'=>\&model_clicked,$i);# right click
			$b->signal_connect(toggled=>\&Internals_click,$i);
			$internals_leftvbox->pack_start($b,FALSE,FALSE,0);
			$b->show; # this allows the mouse cursor to stay as an hourglass
			$internals_buttons[$i] = $b;
		    }
		    elsif($vb)
		    {
			#print "Model not available?\n";
		    }
		}
	    }
	    clear_events_queue();
	    toggle_hourglass(0);
	    $internals_leftvbox->show_all;
	}
	elsif(($#plt1data<$#internals_buttons)&&($#plt1data>0))
	{
	    # fewer buttons
	    toggle_hourglass(1);
	    for(my $i=$#plt1data+1;$i<=$#internals_buttons;$i++)
	    {
		print "Destroy internals button $i\n" if ($vb);
		if(defined($internals_buttons[$i]))
		{
		    undef($model_to_menu[$i]);
		    $internals_buttons[$i]->destroy;
		}
	    }
	    splice(@internals_buttons,$#plt1data+1,$#internals_buttons);
	    clear_events_queue();
	    toggle_hourglass(0);
	}

	$internals_leftvbox->show_all;

	# check for animation
	$animate=test_for_animation();
	
	# check for sphere
	
	print "SPHERES $internals_drawtype\n"if($vb);

	if($internals_options_state==1)
	{
	    # set titles for labels
	    toggle_hourglass(1);
	    for(my $i=1;$i<=$#internals_buttons+1;$i++)
	    {
		my $j = $i==1 ? $#internals_buttons : $i-1;

		if(defined($plt1data[$j]))
		{
		    my @d=split(/\s+/o,$plt1data[$j]);
		    $titles[$i]=$d[$col];
		    
		    if(defined($titles[$i]))
		    {
			#remove whitespace
			$titles[$i]=~s/^\s+//o;
			$titles[$i]=~s/\s+$//o;
		    }
		}
		else
		{
		    $titles[$i]='';
		}

	    }
	    clear_events_queue();
	    toggle_hourglass(0);
	}


	# determine what to plot
	@y_variables_toplot=();
	for(my $i=0;$i<=$#internals_y_buttons;$i++)
	{
	    if($internals_y_buttons[$i]->get_active==TRUE)
	    {
		push(@y_variables_toplot,$i);
	    }
	}

	my $latest=0;
	if((defined($internals_buttons[0]))&&
	   ($internals_buttons[0]->get_active==TRUE))
	{
	    $latest=$#plt1data;
	    $internals_options_state=1;
	}

	print "PLT1DATA $#plt1data YVARS_TOPLOT $#y_variables_toplot\n"if($vb);
	if(($#y_variables_toplot>-1)&&($#plt1data>-1))
	{
	    if($internals_options_state==1)
	    {
		@models_required=();

		print "Check for models required\n" if($vb);

		# loop over the models, checking if we need to extract data
		for(my $i=1;$i<=$#plt1data;$i++)
		{  
		    if(defined($internals_buttons[$i])&&
		       (($internals_buttons[$i]->get_active==TRUE)
			||(($latest>0)&&($i==$#plt1data))))
		    {
			my @d=split(/\s+/o,$plt1data[$i],2);
			my $nmod=$d[0]; # model number for extraction
			
			#if($nmod<=$#plt1data)
			{
			    push(@models_required,$nmod);
			    $model_to_menu[$d[0]]=$i; # reverse lookup
			    
			    print "Plot model: menu \# $i, model \# $d[0]\n" if ($vb);
			    
			    # if model is too small (or has not been extracted before)
			    # then extract it
			    my $file="$internalsdir/model.$nstar.$nmod";
			    
			    if(ok_file($file)==0)
			    {
				print "internals: Extract model $nmod to file $file\n" if ($vb);
				# extract model $nmod
				set_status_bar("Extracting model $nmod...");
				my @models=($nmod);
				my $p=extract_evcode_model_pointer($nmod);
				print "internals: p=$p\n"if($vb);
				if($p)
				{
				    my @data=@{$p}if($p);
				    
				    print "internals: Extract model: $nmod\n" if($vb);
				    
				    # first line contains gridpoint info,
				    # which we don't want
				    shift @data; shift @data;
				    dumpfile($file,join("\n",@data));
				    set_status_bar("Model $nmod extracted");
				}
			    }
			    elsif($vb)
			    {
				print "Using cached file $file\n";
			    }
			}
		    }
		}
		$img_set=TRUE;
	    } # internals change check

	    # get plot information from the GUI buttons
	    my $col=$internals_menu->get_active;
	    my $colname=$plt1cols[$col];
	    my $log10=0;
	    my $log10x=0;
	    $log10=1  if($internals_log10->get_active==TRUE);
	    $log10x=1 if($internals_log10x->get_active==TRUE);
	    my $xaxis=$internals_xaxis_menu->get_active;

	    my @models_required_now;

	    # axis ranges
	    my @range=get_graphrange(@internals_range);

	    if($animate==1)
	    {
		# In the case of an animation, we pretend to want to plot only
		# one model
		my $anim_frame=$internals_anim_buttons[2]->get_value;
		
		# set Hscale to the number of models required
		$internals_anim_adjustments[2]->upper($#models_required);
		$max_anim_count=$#models_required; # save it!
		
		# save a unique string for the animation
		@animation_models=();
		
		foreach my $model_required_now (@models_required)
		{
		    no warnings; 
         	    push(@animation_models,
			 join(' ',
			      maindir(),$xaxis, $log10, $log10x, @y_variables_toplot, 'reqnow="',$model_required_now,'"'
			      ,$col,-s $runname.'.mdl'.$nstar, @misc_opts_values,$latest,$animate,
			      $current_internals_tab,@range,$internals_drawtype,gnuplot_options_string()
			 ));
		    use warnings;
         	}


		@models_required_now=($models_required[$anim_frame]);
	    }
	    else
	    {
		@models_required_now=@models_required;
	    }

	    # make a unique id string ($plotstring) for the plot, which contains
	    # everything in the plot : we can look this up in the and if it's there. 
	    no warnings; 
            my $plotstring=join(' ',maindir(),$xaxis,$log10,$log10x,@y_variables_toplot, 'reqnow="',@models_required_now,'"'
				,$col, -s $runname.'.mdl'.$nstar, @misc_opts_values, $latest, $animate, $current_internals_tab, @range, $internals_drawtype,gnuplot_options_string()
		);
	    use warnings;
            print "Plotstring is $plotstring, in cache? ",in_image_cache($plotstring),"\n" if ($vb);
	    
	    if((!defined($prev_internals_plotstring) || ($plotstring ne $prev_internals_plotstring))
	       && (!$animate))
	    {
		# static plotstring changes (e.g. gnuplot options change) : force redraw
		# (animations take care of themselves)
		$force=1;
		$internals_options_state=1;
	    } 

	    $prev_internals_plotstring=$plotstring;
	    
	    if((($#models_required_now>-1)&&
		(defined($models_required_now[0]))&&($plot==1))||
	       (defined $force && $force>=1))
	    {
		print "Force update of internals tab and ps file\n" if(($vb==1)&&($force>=1));
		
		my $pixbuf;
		my $grange;
		if((in_image_cache($plotstring)==1)&&($force<1))
		{
		    # use cached version
		    print "Use cached version of $plotstring\n" if($vb);
		    $pixbuf=(get_image_from_cache($plotstring))[1];
		    $grange=$internals_grange{$plotstring};
		    if($pixbuf=~/page/i)
		    {
			print "PIXBUF PAGE ERROR0\nPIXBUF is $pixbuf\nPlotstring $plotstring\n";
			exit;
		    }
		    #$anim_frame_update=1; # force image update - seems not to be required and breaks zooming
		}
		else
		{
		    # we need to draw a new image with gnuplot
		    print "Draw: $plotstring\n" if ($vb);
		    my @labels;

		    # write gnuplot file
		    my $internals_file="$internalsdir/internals.plt";
		    open(INTERNALS_PLOT,">$internals_file")||confess("cannot open internals plot file: $!");
		    my $xlabel = $xaxis==0 ? 'Shell Number' : $internals_y_variables[$xaxis-1];
		    
		    print INTERNALS_PLOT gnuplot_header($internalsdir.'/internals.png'),"
set xrange\[$range[0]:$range[1]\]
set yrange\[$range[2]:$range[3]\]
set xlabel \"$xlabel\"
";

		    my $plotcomma="plot ";
		    
		    if($animate==1)
		    {
			# set graph title in animations: try to align it!
			print INTERNALS_PLOT "set title \"",format_ps_title($plt1cols[$col])," ";
			if(defined($titles[$models_required_now[0]]))
			{
			    print INTERNALS_PLOT sprintf("% 20s",$titles[$models_required_now[0]]);
			}
			print INTERNALS_PLOT "\"\n";
		    }
		    
		    print INTERNALS_PLOT gnuplot_overrides();
		    
		    # start/end angle for the slice
		    my @sphere_slice;
		    
		    
		    print "Models required now: @models_required_now, $#plt1data\n"if($vb);
		    
		    # loop over values required
		    my @plotted;
		    my $lt=1;
		    my $zlabeled=0;

		    # maybe needed later
		    my $dphi=PI/(MAX(1,$#models_required+1));
		    my $phi=0.0;
		    
		    print "Yvar = ",$internals_y_variables[$y_variables_toplot[0]],"\n"if($vb);

		    # special labels for binstar
		    if(($evcode eq 'binstar')&&($#y_variables_toplot==0))
		    {
			my $ylab=$internals_y_variables[$y_variables_toplot[0]];
			if($ylab eq 'Conv')
			{
			    # convection
			    print INTERNALS_PLOT "
set yrange[-3:5]
set ytics (\"C\" -2, \"A\" -1, \"OV\" 1, \"TH\" 2, \"SC\" 3, \"R\" 4)\n";
			}
			elsif($ylab eq 'dshell')
			{
			    # shell addition/removal
			    print INTERNALS_PLOT "
#set yrange[-2:+2]
#set ytics (\"dfnf>dvmax\" 1,\"dpnp>dvmax\" 2,\"dtnt>dvmax\" 3,\"dror>dvmax\" 4,\"denn>dnucmax\" 5,\"ddmnm>fdm\" 6,\"dxnx>log(8.5)\" 7,\"dxmue>dvmax*0.4\" 8,\"dmnm>dmrmax\" 9,\"dlnl>dvmax\" 10,\"drr>dvmax\" 11,\"dlmnm>dvmax\" 12,\"shellcenter\" 13,\"domega>domegamax\" 14,\"dm>0.02\" 15, \"tautest\" 16, \"center\" -1,\"edge\" -2, \"tautest\" -3, \"dmnm<1e-13\" -4, \"Rem\" -0.5, \"\" 0, \"Add\" 0.5)\n";
			    
			}
		    }

		    toggle_hourglass(1);
		    map
		    {
			foreach my $model (@models_required_now)
			{
			    # get the menu number corresponding to this model,
			    # and then get the data value (e.g. time) corresponding to it as well
			    my $menui=$model_to_menu[$model];
			    
			    print "Try to split d2: $menui, $#plt1data\n"if($vb);
			    if(($internals_drawtype>=1)&&($zlabeled==0))
			    {
				$zlabeled=1;
				print INTERNALS_PLOT "set zlabel \"",format_ps_title($internals_y_variables[$_]),"\" \n";
			    } 
			    
			    my @d2=split(/\s+/o,$plt1data[$menui]);
			    
			    if((-f "$internalsdir/model.$nstar.$model")&&
			       (-s "$internalsdir/model.$nstar.$model" > 10))
			    {
				if($internals_drawtype==0)
				{
				    # XY plot
				    # perhaps add $colname=$d2[$col] to the title? 
				    print INTERNALS_PLOT "$plotcomma \"$internalsdir/model.$nstar.$model\" using ",
				    ($log10x==1 ? '(log($'."$xaxis+".logmin.")*".ivalue_of_log10.')' : $xaxis),':',
				    ($log10==1 ? '(log($'.($_+1)."+".logmin.")*".ivalue_of_log10.')' : $_+1),
				    ' '.withstring($lt).' title "';
				    
				    if(!(defined($plotted[$_])))
				    {
					$plotted[$_]=1;
					print INTERNALS_PLOT format_ps_title($internals_y_variables[$_]),"\" ";
				    }
				    else
				    {
					print INTERNALS_PLOT "\" ";
				    }
				}
				elsif(0)
				{
				    # plot a sphere for the star

				    # we need to get the data for the star, and plot a 4d plot instead
				    open(INTERNALS_2D,"<$internalsdir/model.$nstar.$model")||confess("Cannot open $internalsdir/model.$nstar.$model for conversion from 2d to sphere");
				    open(INTERNALS_4D,">$internalsdir/model.$nstar.$model.4d")||confess("Cannot open $internalsdir/model.$nstar.$model.4d for conversion from 2d to sphere");

				    my $radius=-1e100;
				    my $ymax=-1e100;

				    # first, loop to find the surface radius (max of the x coord)
				    # and the value at the surface (max of the ycoord)
				    while(my $l=<INTERNALS_2D>)
				    {
					my @d=split(/\s+/o,$l);
					my $thisradius = $log10x==1 ? log10(MAX(logmin,$d[$xaxis-1])) : $d[$xaxis-1];
					
					if($thisradius>$radius)
					{
					    $radius=$thisradius;
					    $ymax = $log10==1 ? log10(MAX(logmin,$d[$_])) : $d[$_];
					}
				    }
				    close (INTERNALS_2D);
				    print "Radius $radius, ymax $ymax\n"if($vb);

				    # sphere code
				    {
					
					if($animate==1)
					{
					    # make first slice
					    my $dtheta=($sphere_slice[0]-0.0)/$nsphere;
					    for(my $theta=0.0; $theta<=$sphere_slice[0]+0.1*$dtheta; $theta+=$dtheta)
					    {
						slice_surface($theta,$radius,$ymax);
					    }
					    print  INTERNALS_4D "\n\n";
					    $dtheta=(PI-$sphere_slice[1])/$nsphere;
					    for(my $theta=$sphere_slice[1]; $theta<=PI; $theta+=$dtheta)
					    {
						slice_surface($theta,$radius,$ymax);
					    }
					    print INTERNALS_4D "\n\n";

					    # then draw in the slice
					    foreach my $theta (@sphere_slice)
					    {
						open(INTERNALS_2D,"<$internalsdir/model.$nstar.$model")||confess("Cannot open $internalsdir/model.$nstar.$model for slice");
						while(my $l=<INTERNALS_2D>)
						{
						    my @d=split(/\s+/o,$l);
						    # radius (was x)
						    my $r= $log10x==1 ? log10(MAX(logmin,$d[$xaxis-1])) : $d[$xaxis-1];
						    # surface value (was y)
						    my $t= $log10==1 ? log10(MAX(logmin,$d[$_])) : $d[$_];

						    for(my $phi=0;$phi<=PI;$phi+=PI/$nsphere)
						    {
							my $x=$r*cos($theta)*sin($phi);
							my $y=$r*sin($theta)*sin($phi);
							my $z=$r*cos($phi);
							print  INTERNALS_4D "$x $y $z $t\n";
						    }
						    print  INTERNALS_4D "\n";
						}
						
						close INTERNALS_2D;
						
						print  INTERNALS_4D "\n";
					    }
					}
					else
					{
					    # if no animation, plot as a slice which varies from bottom to top
					    # (or top to bottom?) with the model number (or age etc.)
					    print "NOW $#models_required_now: @models_required_now\n"if($vb);
					    print "ALL $#models_required: @models_required\n"if($vb);
					    print "Plot model $model from $phi to ",($phi+$dphi),"\n"if($vb);

					    my $nextphi=$phi+$dphi;
					    
					    if(1==0)
					    {
						# this draws in the sphere, but is not all that successful!
						# The problem is that when there is mass-loss we get a distorted
						# sphere, which looks terrible and tends to overwrite itself,
						# so only allow that in case where animation is used
						
						my $dtheta=($sphere_slice[0]-0.0)/$nsphere;
						for(my $theta=0.0; $theta<=$sphere_slice[0]+0.1*$dtheta; $theta+=$dtheta)
						{
						    slice_surface_limited_phi($theta,$radius,$ymax,$phi,$nextphi);
						}
						print  INTERNALS_4D "\n\n";
						$dtheta=(PI-$sphere_slice[1])/$nsphere;
						for(my $theta=$sphere_slice[1]; $theta<=PI; $theta+=$dtheta)
						{
						    slice_surface_limited_phi($theta,$radius,$ymax,$phi,$nextphi);
						}
						print INTERNALS_4D "\n\n";
					    }

					    foreach my $theta (@sphere_slice)
					    {
						open(INTERNALS_2D,"<$internalsdir/model.$nstar.$model")||confess("Cannot open $internalsdir/model.$nstar.$model for slice");
						while(my $l=<INTERNALS_2D>)
						{
						    my @d=split(/\s+/o,$l);
						    # radius (was x)
						    my $r=$log10x==1 ? log10(MAX(logmin,$d[$xaxis-1])) :$d[$xaxis-1];
						    # surface value (was y)
						    my $t=$log10==1 ? log10(MAX(logmin,$d[$_])): $d[$_];
						    my $p=$phi;
						    while($p<=MIN(PI,$nextphi*1.01))
						    {
							my $x=$r*cos($theta)*sin($p);
							my $y=$r*sin($theta)*sin($p);
							my $z=$r*cos($p);
							print INTERNALS_4D "$x $y $z $t\n";
							$p+=$dphi/$nsphere;
						    }

						    

						    print  INTERNALS_4D "\n";
						}
						close INTERNALS_2D;
						print  INTERNALS_4D "\n";
					    }
					    
					    {
						# put a label at the edge of the star
						my $label;
						my $theta=$sphere_slice[0];
						my $p=$phi+0.5*$dphi;
						my $r=$radius*1.15;
						my $x=$r*cos($theta)*sin($p);
						my $y=$r*sin($theta)*sin($p);
						my $z=$r*cos($p);
						my $j=$model-1;
						$j=$#plt1data if($j==0);
						if(defined($plt1data[$j]))
						{
						    my @d=split(/\s+/o,$plt1data[$j]);
						    my $ltext=sprintf("% 20s",#$titles[$model]
								      $d[$col]
							);
						    $ltext=~s/^\s+//o; $ltext=~s/\s+$//o;
						    print "Set label $ltext\n"if($vb);
						    push(@labels,"set label \"$ltext\" at $x,$y,$z right"); 
						}
					    }
					    $phi+=$dphi;					       
					}
				    }
				    
				    close(INTERNALS_4D);
				    # perhaps add $colname=$d2[$col] to the title? 
				    print INTERNALS_PLOT "$plotcomma \"$internalsdir/model.$nstar.$model.4d\" using 1:2:3:4 ";
				    close(INTERNALS_2D);
				}

				$plotcomma=',';
			    }
			}
			$lt++;
		    }@y_variables_toplot;
		    
		    print INTERNALS_PLOT gpshow() if(!$persist_gnuplot);
		    close INTERNALS_PLOT;
		    
		    # prepend labels
		    if($#labels>-1)
		    {
			open(OLD_INTERNALS_PLOT,'<'.$internals_file);
			open(NEW_INTERNALS_PLOT,'>'.$internals_file.'.new');
			while(my $l=<OLD_INTERNALS_PLOT>)
			{
			    if($l=~/^s?plot/o)
			    {
				# insert labels prior to plot
				foreach my $l (@labels)
				{
				    print NEW_INTERNALS_PLOT $l,"\n";
				}
			    }
			    print NEW_INTERNALS_PLOT $l;
			}
			close OLD_INTERNALS_PLOT;
			close NEW_INTERNALS_PLOT;
			# overwrite old file
			rename($internals_file.'.new',$internals_file);
		    }
		    
		    # run gnuplot
		    my $f=$internalsdir.'/internals.plt';
		    my $err;
		    if($gnuplot_options{'terminal type'}=~/postscript/io)
		    {
			# postscript output
			if($persist_gnuplot)
			{
			    my $h = gnuplot_ps_to_png2(
				{ 
				    'input file'=>$f,
				    'png output file'=>$internalsdir.'/internals.png',
				    dpi=>get_opt(0),
				    'gnuplot stream in'=>$persist_gnuplot{in},
				    'gnuplot stream out'=>$persist_gnuplot{out},
				    'gnuplot stream err'=>$persist_gnuplot{err},
				    invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
				}
				);
			    $grange = $$h{'graph range'};
			    $err = $$h{err};
			}
			else
			{
			    ($grange,$err) = gnuplot_ps_to_png($f,$internalsdir.'/internals.png',get_opt(0));
			}
		    }
		    else
		    {
			# native PNG
			my $cmd=prepend_gdfontpath($external_programs{gnuplot}.' '.$f);
			reformat_pltfile($f);
			my $res=`$cmd 2>&1`;
			($grange,$err)=extract_gnuplot_range(\$res);
		    }
		    push_to_Misc_log($err);

		    $internals_grange{$plotstring}=$grange;

		    %{$internals_image_properties[$current_internals_tab]}
		    =('image'=>'internals '.$current_internals_tab,
		      'image widget'=>$internals_img[$current_internals_tab],
		      'grange'=>$grange,
		      'width'=>get_opt(1),
		      'height'=>get_opt(2),
		      'lmargin'=>$gnuplot_options{lmargin},
		      'rmargin'=>$gnuplot_options{rmargin},
		      'tmargin'=>$gnuplot_options{tmargin},
		      'bmargin'=>$gnuplot_options{bmargin},
		      'xrange widgets'=>[$internals_range[0],$internals_range[1]],
		      'yrange widgets'=>[$internals_range[2],$internals_range[3]],
		      
			);
		    toggle_hourglass(0);
		    
		    # get the image
		    my $raw_data;
		    ($raw_data,$pixbuf)=get_image_from_file($internalsdir.'/internals.png');
		    
		    if($pixbuf=~/page/i)
		    {
			print  "PIXBUF PAGE ERROR\n";exit;
		    }

		    $ps_filenames[$current_tab]="$internalsdir/internals.ps";

		    # save image/pixbuf in cache
		    set_image_in_cache($plotstring,$raw_data,$pixbuf);
		}

		# If state changes we need to update the image
		if(($internals_options_state==1)||($anim_frame_update==1))
		{
		    # update with the current image
		    print "Set image $current_internals_tab\n" if($vb);

		    my $rect=$internals_img[$current_internals_tab]->allocation;
		    print "Set pixbuf, image size ",$rect->width,"x",$rect->height,"\n" if($vb);

		    if($pixbuf=~/page/i)
		    {
			print  "PIXBUF PAGE ERROR2\n";
			exit;
		    }

		    $internals_img[$current_internals_tab]->set_from_pixbuf($pixbuf);
		}

		$img_set=TRUE;
	    }
	    else
	    {
		$max_anim_count=0;
		$img_set=FALSE;
	    }
	}
    }

    print "Internals : img_set=$img_set, currently_showing_noimage=$currently_showing_noimage\n"if($vb);
     
    if($img_set==FALSE)
    {
	$currently_showing_noimage=1;
	
	# update with the 'noimage'
	my $image = (no_image())[1];

	print "noimage is $image (should be pixbuf)\n"if($vb);
	print "set internals_img[$current_internals_tab] = $internals_img[$current_internals_tab]\n"if($vb);

	$internals_img[$current_internals_tab]->set_from_pixbuf($image)
	    if(defined($internals_img[$current_internals_tab]));
	
    }
    else
    {
	$currently_showing_noimage=0;
    }

    $internals_options_state=0;
    $anim_frame_update=0;
    $updating_internals=0;
    TRUE;
}


sub add_internals_image_handler
{
    my $p=shift;
    push(@internals_handlers,
	 $p->signal_connect('button-release-event'=>sub{
	     img_click_release($internals_img[$current_internals_tab]->get_pixbuf);
	     FALSE;
			    }
	 ));
    
}

sub Internals_drawtype_change
{
    my $w=shift;
    my $x=shift;
    print "Set drawtype $x\n"if($vb);
    $internals_drawtype=$x;
    $internals_options_state=1;
    $anim_frame_update=1;
    if((test_for_animation()==1)&&($internals_drawtype==2))
    {
	# cannot have slice with animation: fall back to sphere
	$internals_drawtype=1;
	$internals_drawtype[1]->clicked;
    }
    TRUE;
}


sub slice_surface_limited_phi
{
    my $theta=shift;
    my $radius=shift;
    my $val=shift;
    my $phimin=shift;
    my $phimax=shift;
    my $dphi=($phimax-$phimin)/$nsphere;

    for(my $phi=$phimin;$phi<=$phimax;$phi+=$dphi)
    {
	my $x=$radius*cos($theta)*sin($phi);
	my $y=$radius*sin($theta)*sin($phi);
	my $z=$radius*cos($phi);
	my $v=$val*(0.95+rand()*0.1);
	print  INTERNALS_4D "$x $y $z $v\n";
    }
    print  INTERNALS_4D "\n";
}

sub slice_surface
{
    my $theta=shift;
    my $radius=shift;
    my $val=shift;
    
    for(my $phi=0;$phi<=PI;$phi+=PI/$nsphere)
    {
	my $x=$radius*cos($theta)*sin($phi);
	my $y=$radius*sin($theta)*sin($phi);
	my $z=$radius*cos($phi);
	my $v=$val*(0.95+rand()*0.1);
	print  INTERNALS_4D "$x $y $z $v\n";
    }
    print  INTERNALS_4D "\n";
}

sub select_in_internals_menu_from_range
{
    my @range=(shift,shift);
    my $x=shift;
    print "RANGE $x\n"if($vb);
    print "Select in internals every $x models from range @range\n"if($vb);
    if($x=~/^\d+$/o)
    {
	# note : start at model 1 because the first button (#0)
	# is the "Latest" which is probably not what you want
	print "Select every $x th model\n" if($vb);
	for(my $i=$range[0];$i<=$range[1];$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_buttons[$i], ($i-1-$range[0])%$x==0 ? 1 : 0);
	}
    }
    return TRUE;
}


sub select_in_internals_menu
{
    my $w=shift;
    my $x=shift;
    print "Select in internals $w,$x\n"if($vb);

    if($x=~/^\d+$/o)
    {
	# note : start at model 1 because the first button (#0)
	# is the "Latest" which is probably not what you want
	print "Select every $x th model\n" if($vb);
	my $c=1;
	for(my $i=1;$i<=$#internals_buttons;$i++)
	{
	    if(defined($internals_buttons[$i]))
	    { 
		Button_set_FALSE_or_TRUE($internals_buttons[$i],($c-1)%$x==0 ? 1 : 0); 
		$c++;
	    }
	}
    }
    elsif($x eq '|')
    {
	print "Or model selection\n" if($vb);
	for(my $i=0;$i<=$#internals_buttons;$i++)
	{
	    Button_set_FALSE_or_TRUE($internals_buttons[$i],1-Button_get_one_or_zero($internals_buttons[$i]));
	}
    }
}

sub update_anim_callback
{
    # callback on the frame counter
    clear_events_queue();
    update_Internals_tab(0);
    return TRUE;
}

sub update_animation
{
    # if animation is off or speed=0 then just return
    if(($made_tab[$tab_numbers{Internals}]==0)||
       (Button_get_one_or_zero($internals_anim_buttons[1])==0)||
       (($anim_speed==0)||(!defined($internals_anim_buttons[2]))))
    {
	return TRUE;
    }
    
    $anim_count++;
    print "UA? count $anim_count\n" if ($vb);
    if($anim_count>=8*(11-$anim_speed))
    {
	$anim_count=0;
	if($current_tab==$tab_numbers{Internals})
	{
	    if(defined($internals_anim_buttons[2]))
	    {
		print "Update animation\n" if ($vb);
		my $anim_frame=$internals_anim_buttons[2]->get_value;
		$anim_frame=0 if(++$anim_frame>$max_anim_count);
		$internals_anim_buttons[2]->set_value($anim_frame);
		print "Update animation set frame $anim_frame\n"if($vb);
		$anim_frame_update=1;
	    }
	}
	elsif($vb)
	{
	    print "Yes, but wrong tab\n";
	}
    }
    TRUE;
}


sub set_anim_speed
{
    # set the animation speed
    my $w=shift; # the widget
    my $was=$anim_speed;
    $anim_speed=$w->get_value;
    
    print "Set animation speed $anim_speed\n" if ($vb);

    # do not change ranges during animation
    map{
	$_->set_sensitive(($anim_speed>0)&&(test_for_animation()==1) ? FALSE : TRUE);
    }@internals_range;
    FALSE;
}

sub model_clicked
{
    # model was clicked, check if it's the right mouse button
    my ($widget,$event,$x)=@_;
    return FALSE unless $event->button==RIGHT_MOUSE_BUTTON;
    print "Clicked model $x\n" if ($vb);

    if(("@keys_pressed" eq 'Shift_L')&&($prev_internal ne ''))
    {
	print "Select range from $prev_internal to $x\n"if($vb);
	my $menu=Gtk2::Menu->new;
	my @menuitems= (1,2,5,10,25,50,75,100,150,200,300,400,500,750,1000);
	my @menuwidgets;
	for(my $i=0;$i<=$#menuitems;$i++)
	{
	    my $s='s';
	    $s='' if($menuitems[$i]==1);
	    $menuwidgets[$i]=Gtk2::MenuItem->new("Select every $menuitems[$i] model$s in range");
	    my $y=$menuitems[$i];
	    print "ITEM $i : $menuwidgets[$i] , $y\n"if($vb);
	    $menuwidgets[$i]->signal_connect(activate=>sub{select_in_internals_menu_from_range($prev_internal,$x,$y)});
	    $menu->append($menuwidgets[$i]);
	}

	$menu->show_all;
	$menu->popup(
		     undef, # parent menu shell
		     undef, # parent menu item
		     undef, # menu pos func
		     undef, # data
		     $event->button,
		     $event->time
		     );
    }
    else 
    {
	# ok, so show a nice menu, and allow saving option
	# and perhaps others (???)
	my $menu=Gtk2::Menu->new;
	my $menuitem;
	if($evcode ne 'binstar')
	{
	    $menuitem=Gtk2::MenuItem->new('Save model (.mod for restart)');
	    $menuitem->signal_connect(activate=>sub{save_model_file_selector($x)});
	    $menuitem->show;
	    $menu->append($menuitem);

	    $menuitem=Gtk2::MenuItem->new('Save model (.mdl for plotting/data extraction)');
	    $menuitem->signal_connect(activate=>sub{save_mdl_file_selector($x)});
	    $menuitem->show;
	    $menu->append($menuitem); 
	}
	
	# refresh button
	$menuitem=Gtk2::MenuItem->new('Refresh List');
	$menuitem->signal_connect(activate=>sub{refresh_internals_model_list();});
	$menuitem->show;
	$menu->append($menuitem);
	
	$menu->popup(
		     undef, # parent menu shell
		     undef, # parent menu item
		     undef, # menu pos func
		     undef, # data
		     $event->button,
		     $event->time
		     );
    }
    return TRUE;
}



############################################################
# Kippenhahn tab
############################################################

sub show_Kippenhahn_tab
{
    my $ntab=$tab_numbers{Kippenhahn};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	$made_tab[$ntab]=1;
	my $e; 
	my $table=Gtk2::Table->new(2,1,FALSE);
	my @kipp_axes;
	my @kipp_scales;

	# default state

	if($evcode=~/bonnfires/i)
	{
	    @kippenhahn_state=(1,0,'*','*',12,
			       2,0,'*','*',12,
			       7,0,'*','*',0,
			       # fill the rest with zeroes
			       12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
		);
	}
	else
	{
	    @kippenhahn_state=(1,0,'*','*',12,
			       0,0,'*','*',12,
			       4,0,'*','*',0,
			       # fill the rest with zeroes
			       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
		);
	}

	my @menu;
	my @labels=('X','Y','Z');
	my @resolution_opts=(1,2,5,10,20,30,40,50,60,70,80,90,100);
	my @opts=('Linear','Log10','10^');
	# 3 menus: X,Y,Z, in a stack, prepend a redraw button

	my $v=Gtk2::VBox->new;

	$v->pack_start(starbuttons('h'),FALSE,FALSE,0);


	my $b;
	for(my $i=0;$i<3;$i++)
	{
	    my $l=Gtk2::Label->new($labels[$i].':');
	    my $h=Gtk2::HBox->new;
	    $h->pack_start($l,FALSE,FALSE,0);
	    $kipp_axes[$i]=Gtk2::ComboBox->new_text;
	    
	    map
	    {
		$kipp_axes[$i]->append_text($_);
	    }$i==0 ? @plt1cols : @Kippenhahn_labels;
	    
	    $kipp_axes[$i]->set_wrap_width(3);
	    $kipp_axes[$i]->set_size_request(100,35);
	    $kipp_axes[$i]->set_active($kippenhahn_state[5*$i]);
	    $kipp_axes[$i]->signal_connect(changed=>\&set_Kippenhahn_state,"0_$i");
	    $e=set_tooltip_with_eventbox($kipp_axes[$i],"Select the variable you would like to use for the $labels[$i] axis here");
	    $h->pack_start($e,TRUE,TRUE,0);

	    $kipp_scales[$i]=Gtk2::ComboBox->new_text;
	    map
	    {
		$kipp_scales[$i]->append_text($_);
	    }@opts;
	    $kipp_scales[$i]->set_size_request(75,35);
	    $kipp_scales[$i]->set_active(0);
	    $kipp_scales[$i]->signal_connect(changed=>\&set_Kippenhahn_state,"1_$i");
	
	    $e=set_tooltip_with_eventbox($kipp_scales[$i],"Here you can choose to use the raw data, log it (base 10) or show 10 to the power of it.");
	    $h->pack_start($e,FALSE,FALSE,0);
	    $v->pack_start($h,FALSE,FALSE,0);

	    # next line: ranges
	    my $t=Gtk2::Entry->new_with_text_and_tooltip($kippenhahn_state[5*$i+2],"This is the minimum value of $labels[$i] data which will be plotted.");
	    $l=Gtk2::Label->new('Range :');
	    $h=Gtk2::HBox->new;
	    $h->pack_start($l,TRUE,FALSE,0);	    
	    $t->signal_connect(changed=>\&set_Kippenhahn_state,"2_$i");
	    $t->set_size_request(75,25);
	    $kippenhahn_rangebox[2*$i]=$t;
	    $h->pack_start($t,FALSE,FALSE,0);
	
	    $t=Gtk2::Entry->new_with_text_and_tooltip($kippenhahn_state[5*$i+3],"This is the maximum value of $labels[$i] data which will be plotted.");
	    $t->set_size_request(75,25);
	    $t->signal_connect(changed=>\&set_Kippenhahn_state,"3_$i");
	    $h->pack_start($t,FALSE,FALSE,0);
	    $v->pack_start($h,FALSE,FALSE,0);
	    $kippenhahn_rangebox[2*$i+1]=$t;

	    # resolution
	    if($i<=1)
	    {
		my $r=Gtk2::ComboBox->new_text;
		$h=Gtk2::HBox->new;
		$l=Gtk2::Label->new('Data Resolution :');
		$h->pack_start($l,FALSE,FALSE,0);
		map
		{
		    $r->append_text($_.'%');
		}@resolution_opts;
		$r->set_size_request(75,35);
		$r->signal_connect(changed=>\&set_Kippenhahn_state,"4_$i");
		$r->set_active($kippenhahn_state[$i*5+4]);
		
		#$r->set_active(4); # remove me
		$e=set_tooltip_with_eventbox($r,"This is the resolution at which data is extracted prior to plotting. If you choose, say, 10%, then one in 10 models will be extracted. This is very useful for preparing a plot quickly, which can be brought up to presentation standard by selecting 100%. Warning: plotting may be very slow at 100%, especially if the PS->PNG conversion density is also very high. On the other hand, if the resolution is too low, you might not select any data points at all! So if you do not see what you expect, set resolution to 100%.");
		
		$h->pack_start($e,FALSE,FALSE,0);
		$v->pack_start($h,FALSE,FALSE,0);
	    }
	    $v->pack_start(Gtk2::HSeparator->new,FALSE,FALSE,0);
	  
	}
	
  $kippenhahn_rangebox[6]=Gtk2::Button->new_with_tooltip('Reset ranges','Click here to reset all ranges (or press the u key)');
	    $kippenhahn_rangebox[6]->signal_connect(clicked=>sub{
		foreach (0..5)
		{
		    $kippenhahn_rangebox[$_]->set_text('*');
		}});
	    $kippenhahn_rangebox[6]->show;


	    $v->pack_start($kippenhahn_rangebox[6],FALSE,FALSE,0);
	    $v->pack_start(Gtk2::HSeparator->new,FALSE,FALSE,0);
	my $h=Gtk2::HBox->new;
		
	my $l=Gtk2::Label->new('Palette :');
	$h->pack_start($l,FALSE,FALSE,0);
	my $r=palettes_combobox();
	$r->child->signal_connect(changed=>\&set_Kippenhahn_state,'4_2');
	$h->pack_start($r,TRUE,FALSE,0);
	$v->pack_start($h,FALSE,FALSE,0);
	$v->pack_start(Gtk2::HSeparator->new,FALSE,FALSE,0);

	if($evcode_module eq 'STARS')
	{
	    $v->pack_start(Gtk2::Label->new('Regions'),TRUE,FALSE,0);
	    
	    $h=Gtk2::HBox->new;

	    #$b=Gtk2::ToggleButton->new_with_label('Mass');
	    #set_tooltip($b,'Set this to on to show core boundaries (only works if M is the y-axis)');
	    #$b->signal_connect(clicked=>\&set_Kippenhahn_state,'4_3');
	    #$h->pack_start($b,TRUE,FALSE,0);

	    $b=Gtk2::ToggleButton->new_with_label_and_tooltip('Conv','Set this to on to show convective boundaries');
	    $b->signal_connect(clicked=>\&set_Kippenhahn_state,'0_4');
	    $h->pack_start($b,TRUE,FALSE,0);
	    
	    #$b=Gtk2::ToggleButton->new_with_label('Semi');
	    #set_tooltip($b,'Set this to on to show semiconvective boundaries (only works if M is the y-axis)');
	    #$b->signal_connect(clicked=>\&set_Kippenhahn_state,'1_4');
	    #$h->pack_start($b,TRUE,FALSE,0);

	    $b=Gtk2::ToggleButton->new_with_label_and_tooltip('Nuc','Set this to on to show nuclear burning zones');
	    $b->signal_connect(clicked=>\&set_Kippenhahn_state,'4_4');
	    $h->pack_start($b,TRUE,FALSE,0);

	    $v->pack_start($h,TRUE,FALSE,0);
	}
	$h=Gtk2::HBox->new;

	$kipp_plot_button=Gtk2::Button->new_with_tooltip('Plot','Click here to (re)plot the Kippenhahn diagram. Update is not automatic because plotting this diagram takes a long time, even if the data is cached');
	$kipp_plot_button->signal_connect(clicked=>\&set_kipp_state);
	$h->pack_start($kipp_plot_button,TRUE,FALSE,0);

	$b=Gtk2::Button->new_with_tooltip('Cancel','Click here to cancel the plot process');
	$b->signal_connect(clicked=>\&stop_kipp_plot);
	$h->pack_start($b,TRUE,FALSE,0);

	$v->pack_start($h,FALSE,FALSE,0);

	$block_surface=0;
	if($use_block_surface)
	{
	    $b=Gtk2::ToggleButton->new_with_label_and_tooltip('Block Surface','Click this to enable data to be processed through the block_surface.pl script if it is available');
	    $b->signal_connect(clicked=>sub{$block_surface=1-$block_surface;});
	    $v->pack_start($b,FALSE,FALSE,0);
	}


	# add menus to the table 
	$table->attach($v,0,1,0,1,'shrink','fill',2,2);

	# add image, and 'noimage' placer
	$kippenhahn_image=Gtk2::Image->new;
	%kippenhahn_image_properties=('image'=>'Kippenhahn',
				      'image widget'=>$kippenhahn_image,
				      'noimage'=>1);

	# Set the 'noimage' image
	$kippenhahn_image->set_from_pixbuf((no_image())[1]);

	$table->attach(set_image_callbacks('Kippenhahn',$kippenhahn_image,\%kippenhahn_image_properties),1,2,0,1,'shrink','shrink',2,2);

	# update the tab
	update_Kippenhahn_tab(0);

	# attach table to frame
	$frames[$ntab]->add(scrolled($table));
	$frames[$ntab]->show_all;
    }
    increment_timing('show_Kippenhahn_tab',$time0) if($timings);
}

sub set_Kippenhahn_state
{
    # menu clicked: update state array
    if($_[1]=~/^(\d+)_(\d+)/o)
    {
	my $w=$_[0];
	my $m=$1;
	my $n=$2;
	my $s = ($m==2)||($m==3) ? $w->get_text : $w->get_active;
	# update state $m to $n
	print "Set state $m+5*$n=",($m+5*$n),"=$s\n" if($vb);
	$kippenhahn_state[$m+5*$n]=$s;
    }
}

sub update_Kippenhahn_tab
{
    if(($kippenhahn_state==1)||($_[0]==1))
    {
	toggle_hourglass(1);
	print "Update Kipp Tab with state @kippenhahn_state\n"if($vb);
	$kippenhahn_state=0;

	# get list of available models
	print "Models available $#plt1data\n"if($vb);

	$stop_kipp_plot=0;

	my @resolution_opts=(1,2,5,10,20,30,40,50,60,70,80,90,100);	

	# @kippenhahn_state contains the state information
	# 0 : x axis we want
	# 1 : linear/log/10^ for x
	# 5 : y axis we want
	# 6 : linear/log/10^ for x
	# 7 : y axis we want
	# 8 : linear/log/10^ for x
	# 
	# we get the x from the plt file, but have to extract
	# data for y,z from model files
	
	my $xcol=$kippenhahn_state[0];
	my $ycol=$kippenhahn_state[5];
	my $zcol=$kippenhahn_state[10];

	my $myycol=$ycol;
	my $myzcol=$zcol;

	my $ymnorm=0;
	my $zmnorm=0;
	my $ymax0=0;
	my $zmax0=0;

	my $modulate=0;
	my $scale=0;

	if($Kippenhahn_labels[$zcol] eq 'Convection')
	{
	    # special convection plot: want grad rad - grad ad (but > 0)
	    $myzcol=$internals_y_variables{'Grad_rad-Grad_ad'};
	    $zmax0=1;
	    $modulate=1;
	}
	elsif($Kippenhahn_labels[$zcol] eq 'M/Mass')
	{
	    $zmnorm=1;
	    $myzcol=$internals_y_variables{M};
	    $myzcol=$internals_y_variables{Mass} if(!defined($myzcol));
	    $modulate=1;
	}

	if($Kippenhahn_labels[$ycol] eq 'Convection')
	{
	    # special convection plot: want grad rad - grad ad (but > 0)
	    $myycol=$internals_y_variables{'Grad_rad-Grad_ad'};
	    $ymax0=1;
	    $modulate=1;
	}
	elsif($Kippenhahn_labels[$ycol] eq 'M/Mass')
	{
	    $ymnorm=1;
	    $myycol=$internals_y_variables{M};
	    $myycol=$internals_y_variables{Mass} if(!defined($myycol));
	    $modulate=1;
	}

	foreach (6,11)
	{
	    $scale=1 if($kippenhahn_state[$_]);	    
	}
		   
	print "Plot $xcol from the .plt$nstar file\n"if($vb);

	# set ranges
	my @xrange=(undef,undef);
	my @yrange=@xrange;
	my @zrange=@xrange;

	$xrange[0]=$kippenhahn_state[2] if(is_numeric($kippenhahn_state[2]));
	$xrange[1]=$kippenhahn_state[3] if(is_numeric($kippenhahn_state[3]));
	$yrange[0]=$kippenhahn_state[7] if(is_numeric($kippenhahn_state[7]));
	$yrange[1]=$kippenhahn_state[8] if(is_numeric($kippenhahn_state[8]));
	$zrange[0]=$kippenhahn_state[12] if(is_numeric($kippenhahn_state[12]));
	$zrange[1]=$kippenhahn_state[13] if(is_numeric($kippenhahn_state[13]));

	if($vb)
	{
	    no warnings;
	    print "Initial KIPP RANGES @xrange, @yrange, @zrange\n";
	    use warnings;
	}

	# perhaps flip ranges to maintain monotonicity with previous plot
	if(defined($kippenhahn_image_properties{grange}))
	{ 
	    if($vb)
	    {
		printf "Previous axis directions : x=%d y=%d\n",direction(@xrange),direction(@yrange);
		print "Buttons say @xrange : @yrange\n";
	    }

	    if(defined($xrange[0]) && defined($xrange[1]) && is_numeric($xrange[0],$xrange[1]))
	    {
		if(direction(@xrange) != direction(@{$kippenhahn_image_properties{grange}->{x}}))
		{
		    # flip
		    my $tmp=$xrange[0];
		    $xrange[0]=$xrange[1];
		    $xrange[1]=$tmp;
		    print "Flip x range\n"if($vb);
		}
	    }
	    
	    if(defined($yrange[0]) && defined($yrange[1]) && is_numeric($yrange[0],$yrange[1]))
	    {
		if(direction(@yrange) != direction(@{$kippenhahn_image_properties{grange}->{y}}))
		{
		    # flip
		    my $tmp=$yrange[0];
		    $yrange[0]=$yrange[1];
		    $yrange[1]=$tmp;
		    print "Flip y range\n"if($vb);
		}
	    }
	    print "Post-flip @xrange : @yrange\n"if($vb);
	}

	print "RANGES @xrange,@yrange,@zrange\n"if($vb);
	
	# array for data
	my @xxdata; # better way to store the x data
	my @mdata; # stores the Mass variable, useful for M/Mass plots
	my @model_list;

	set_status_bar("Plotting Kippenhahn diagram : extracting data (1)");
	return terminate_kipp_plot() if ($stop_kipp_plot);

	my @resolution;
	foreach my $c (0,1)
	{
	    $resolution[$c]=$resolution_opts[$kippenhahn_state[$c*5+4]];
	    print "RES $c: $resolution[$c] -> "if($vb);
	    # resolution is a %, so for 10% we want 1 in 10 models i.e. mod 10
	    #                           20% we want 1 in  5 models i.e. mod 5
	    # so convert to a modulo
	    $resolution[$c]=int(1.0/($resolution[$c]/100.0));
	    print "$resolution[$c]\n"if($vb);
	}

	my @extra_prelabel;
	my @extra_postlabel;

	foreach my $i (0,1,2)   
	{
	    my $x=5*$i+1;
	    if($kippenhahn_state[$x]==1)
	    {
		# require logs of the X axis
		$extra_prelabel[$i]='log_{10}';
		$extra_postlabel[$i]='';
	    }
	    elsif($kippenhahn_state[$x]==2)
	    {
		# require 10^x
		$extra_prelabel[$i]='10^{';
		$extra_postlabel[$i]='}';
	    }
	    else
	    {
		$extra_prelabel[$i]='';
		$extra_postlabel[$i]='';
	    }
	    print "Extra labels $i : $extra_prelabel[$i], $extra_postlabel[$i]\n"if($vb);
	}

	my $pltfile = "$wttslayers[$wttslayernum]/$modinfo{plt1file_stub}$nstar";
	open(PLT1,'<'.$pltfile)||confess("cannot open $pltfile : $!");
	<PLT1>; # first line is bogus
	my $count=0;
	while(<PLT1>)
	{ 
	    next if /^\s*\#/o;
	    my @y=split;
	    my $x=$y[$xcol];
	    if($kippenhahn_state[1]==1)
	    {
		# require logs of the X axis
		$x=log(MAX($x,logmin))*ivalue_of_log10;
	    }
	    elsif($kippenhahn_state[1]==2)
	    {
		# require 10^x
		$x=10.0**($x);
	    }
	    if((!defined($xrange[0])||($x>=$xrange[0]))&&
	       (!defined($xrange[1])||($x<=$xrange[1])))
	    {
		$count++;
	    }
	}
	close PLT1;

	# modulate X resolution
	my $skip_modulo=int($resolution[0]); 
	$skip_modulo=1 if($count/(1.0*$resolution[0]) < 1.0);

	# extract X data : NB this is always fast as it is from the plt file
	# (don't bother optimizing too much!)
	open(PLT1,'<'.$pltfile)||confess("cannot open $pltfile : $!");
	<PLT1>; # first line is bogus
	my $excount=0;
	$count=0;
	my @xdatarange=(undef,undef);
	my @ydatarange=(undef,undef);
	my @zdatarange=(undef,undef);
	my $noxrange=(!defined($xrange[0]) && !defined($xrange[1]));
	while(<PLT1>)
	{
	    next if /^\s*\#/o;
	    return terminate_kipp_plot() if ($stop_kipp_plot);
	    my @y=split;
	    my $x=$y[$xcol];
	    my $nmod=$y[0];
	    print "X=$x from xcol=$xcol - post process -> "if($vb);
	    
	    if($kippenhahn_state[1]==1)
	    {
		# require logs of the X axis
		$x=log(MAX($x,logmin))*ivalue_of_log10;
	    }
	    elsif($kippenhahn_state[1]==2)
	    {
		# require 10^x
		$x=10.0**($x);
	    }

	    print "$x : "if($vb);

	    # save the mass as a function of model number
	    $mdata[$nmod]=$y[3]; 

	    # save data if in range
	    if($noxrange ||
	       (defined($xrange[0])&&($x>=$xrange[0]))||
	       (defined($xrange[1])&&($x<=$xrange[1]))
	       )
	    {
		$xdatarange[0] = $x if((!defined($xdatarange[0])) || ($x<$xdatarange[0]));
		$xdatarange[1] = $x if((!defined($xdatarange[1])) || ($x>$xdatarange[0]));
	
		$xxdata[$nmod]=$x;
		if((model_is_available($nmod)) && 
		   (($#xxdata%$resolution[0])==0))
		{
		    push(@model_list,$nmod) ; # save the model number too
		    $excount++;
		}
		print "in range\n"if($vb);
	    }
	    elsif($vb)
	    {
		no warnings;
		print "out of range [$xrange[0]:$xrange[1]]\n";
		use warnings;
	    }
	    $count++; # update counter (for the resolution modulo)
	}
	close PLT1;

	my $dxdatarange = $xdatarange[1]-$xdatarange[0];
	my $absdxdatarange = $xdatarange[1]-$xdatarange[0];
	my $idxdatarange = 1.0/$dxdatarange if ($absdxdatarange>0.0);

	#print "Extracted $excount x points\n";
	print "Plot $ycol,$zcol from the models (\$\#model_list=$#model_list)\n"if($vb);
	if($vb)
	{
	    print "X DATA RANGE @xdatarange\n";
	    no warnings;
	    print "RANGES X=@xrange; Y=@yrange; Z=@zrange\n";
	    use warnings;
	}

	# determine whether to plot regions
	my $regions=($kippenhahn_state[20]||$kippenhahn_state[24]);
	
	return terminate_kipp_plot() if ($stop_kipp_plot);

	# save it immediately
	set_status_bar('Plotting Kippenhahn diagram : extracting data (2) 0.00 %');
	mkdir "$cachedir/kipp/";
	open(KIPP,">$cachedir/kipp/kipp.dat")||confess("Cannot open $cachedir/kipp/kipp.dat : $!");

	# regions
 	my %kippconv;
	my %kippnuc;

	$count=0;
	my $model_list_count=$#model_list;
	my $status_num=100/MAX(1,$model_list_count);
	my $dmodel_list_count=MAX(1,int($model_list_count/20));
	my $res=$resolution[1];
	my $tstart=time()+5;
	
	# calc once, use lots
	my $validyrange= defined($yrange[0]);
	my $noyrange0 = !$validyrange;
	my $noyrange1 = !defined($yrange[1]);
	my $nozrange0 = !defined($zrange[0]);
	my $nozrange1 = !defined($zrange[1]);

	foreach my $i (0..$model_list_count)
	{
	    my $nmod=$model_list[$i];
	    return terminate_kipp_plot() if ($stop_kipp_plot); # check for stop

	    # set status bar every so often
	    if(($i%$dmodel_list_count==0)||(time()>$tstart))
	    {
		$tstart=time()+5;
		set_status_bar(sprintf('Plotting Kippenhahn diagram : extracting data (2)  %2.2f %%',$i*$status_num));
	    }

	    my $xx=$xxdata[$nmod];

	    {
		print "Extract model $nmod data (X=$xx)\n"if($vb);
		my @modeldata;

		# get model data: either from the cache, or otherwise
		if(($use_kippenhahn_cache)&&
		   (defined($kippenhahn_model_cache[$nstar][$nmod])))
		{
		    # model is in the memory cache: use it
		    @modeldata=@{$kippenhahn_model_cache[$nstar][$nmod]};
		}
		else
		{
		    # slow call to extract_evcode_model
		    my $p=extract_evcode_model_pointer($nmod);
		    if($p)
		    {
			@modeldata=@{$p};
			# remove spurious lines
			shift @modeldata;
			shift @modeldata;

			# impose y resolution	
			if($res!=1)
			{
			    # is this the best way to do it?
			    my @m2;
			    my $p=0;
			    while(my $mm=shift @modeldata)
			    {
				push(@m2,$mm) if((++$p)%$res==0);
			    }
			    @modeldata=@m2;
			}

			# update cache
			$kippenhahn_model_cache[$nstar][$nmod]=\@modeldata if($use_kippenhahn_cache);
		    }
		}

		if($vb==5)
		{
		    print "Success? Got $#modeldata lines of data (is this >0 ? ",1*($#modeldata>0),")\n";
		    map
		    {
			print "Line $_ is $modeldata[$_]\n";
		    }(0..4,$#modeldata-1,$#modeldata);
		}
		
		my $o=0; # set to 1 if we output data
		if($#modeldata>0)
		{
		    my $ytop=0;
		    my $ybot=0;
		    print "$#modeldata lines of data\n"if($vb);
		    
		    # loop over data lines in the model, construct the 3d data we require
		    # first map loop: get the y,z data

		    # @cache stores the y,z data
		    my @cache;

		    # choose which columns we should get
		    my @getcols=($myycol,$myzcol);
		    if($regions)
		    {
			push(@getcols,
			     $internals_y_variables{'Grad_rad-Grad_ad'},
			     $internals_y_variables{E_nuc});
		    }

		    # get the required columns, put them into 2d array @cache
		    map
		    {	 
			my @x=(split)[@getcols];
			push(@cache,\@x) if(defined($x[0])&&defined($x[1]));
		    }@modeldata;

		    # special constructions: either normalize to the mass
		    # or make sure plot is > 0
		    if($modulate)
		    {
			my $mass=$mdata[$nmod];
			
		  	if(($ymnorm)&&($mass>0))
			{
			    my $imass=1.0/$mass;
			    map{
				${$_}[0] *= $imass;
			    }@cache;
			}
			elsif($ymax0)
			{
			    map{
				${$_}[0] = MAX(0.0, ${$_}[0]);
			    }@cache;
			}
			
			# same for z
			if(($zmnorm)&&($mass>0))
			{
			    my $imass=1.0/$mass;
			    map{
				${$_}[1] *= $imass;
			    }@cache;
			}
			elsif($zmax0)
			{
			    map{
				${$_}[1] = MAX(0.0, ${$_}[1]);
			    }@cache;
			}
		    }

		    if($scale)
		    {
			# log Y,Z
			if($kippenhahn_state[6]==1)
			{
			    map{
				${$_}[0]=log(${$_}[0]>logmin ? ${$_}[0] : logmin)*ivalue_of_log10;
			    }@cache;
			}
			elsif($kippenhahn_state[6]==2)
			{
			    map{
				${$_}[0]=10.0**${$_}[0];
			    }@cache;
			}

			if($kippenhahn_state[11]==1)
			{
			    map{
				${$_}[1]=log(${$_}[1]>logmin ? ${$_}[1] : logmin)*ivalue_of_log10;
			    }@cache;
			}
			elsif($kippenhahn_state[11]==2)
			{
			    map{
				${$_}[1]=10.0**${$_}[1];
			    }@cache;
			}
		    }

		    if($regions)
		    {
			# convection : on or off
			onoff_kipp_region(\@cache,2,1e-10);
			
			# nuclear burning : on if >=1e-3 x the maximum value 
			normalise_kipp_line(\@cache,3);
			onoff_kipp_region(\@cache,3,1e-2);			
		    }
		    
		    my %state;
		    my @statevars=('conv','nuc');
		    my @statehash=(\%kippconv,\%kippnuc);

		    # final range check and output
		    while($_=shift(@cache))
		    {
			# now check if $y and $z are in range
			# and do stuff for regions if required
			my ($y,$z,@now)=@{$_};
				
			my $inyrange=0;

			if(($noyrange0 ||($y>=$yrange[0]))&&
			   ($nozrange0 ||($z>=$zrange[0]))&&
			   ($nozrange1 ||($z<=$zrange[1])))
			{
			    # check if data is in y range, if it is not then ignore it
			    # (this is much quicker than getting gnuplot to do it!)
			    if($noyrange1 ||($y<=$yrange[1]))
			    {
				# first point : avoid low-end clipping by drawing
				# the point on the range too
				if(($validyrange)&&(!$ybot)&&(!$o))
				{
				    print KIPP "$xx $yrange[0] $z\n";
				    $ybot=1;
				}
				$o=1;
			    }
			    # experimental : avoid clipping on y axis at top end
			    # (seems not to work!)
			    elsif(($o)&&(!$ytop))
			    {
				# we have hit the top of the y-axis, so force this
				# data point to set on the yrange
				$y=$yrange[1];
				$ytop=1;
			    }

			    if($noyrange1 ||($y<=$yrange[1]))
			    {
				# output data
				print KIPP "$xx $y $z\n";
				
				# save y data range
				$ydatarange[0] = $y if((!defined($ydatarange[0])) || ($y<$ydatarange[0]));
				$ydatarange[1] = $y if((!defined($ydatarange[1])) || ($y>$ydatarange[1]));
				
				# save z data range
				$zdatarange[0] = $z if((!defined($zdatarange[0])) || ($z<$zdatarange[0]));
				$zdatarange[1] = $z if((!defined($zdatarange[1])) || ($z>$zdatarange[1]));
				
				$inyrange=1;
			    }
			    elsif($inyrange==1)
			    {
				$inyrange=2;
			    }
			}

			if($regions && $inyrange)
			{
			    $inyrange=0 if($inyrange==2);

			    my $rvb=0; # local verbosity (gets lengthy!)
			    print "Check region at ($xx,$y,$z) state = @now\n" if($rvb);
			    for(my $i=0;$i<=$#statevars;$i++)
			    {
				my $v=$statevars[$i];

				print "Check statevar $i ($v) at $xx,$y : is $now[$i] was $state{$v}\n" if($rvb);
				if(!defined($state{$v}))
				{
				    # start
				    $state{$v}=$now[$i];

				    if($now[$i]==1)
				    {
					# turns on at the start
					push(@{${$statehash[$i]}{$xx}},$y);
					print "Turn on (from nothing) at y=$y\n"if($rvb);
				    }
				    # ignore if off at the start
				}

				# look for change of state
				if($now[$i] != $state{$v})
				{ 
				    if($now[$i]==0)
				    {
					# turning off - was on
					${$statehash[$i]}{$xx}[-1].= ' '.$y;
					print "Turn off (from on) at y=$y line: @{$statehash[$i]->{$xx}}\n"if($rvb);
				    }
				    else
				    {
					# turning on - was off (convstate==0)
					push(@{${$statehash[$i]}{$xx}},$y);
					print "Turn on (from off) at y=$y\n"if($rvb);
				    }
				    $state{$v}=$now[$i];
				}
				
				if(($#cache==-1)&&($state{$v}==1))
				{
				    # final point : always turn to off if we were on
				    ${$statehash[$i]}{$xx}[-1].= ' '.$y;
				    print "Final point line: @{$statehash[$i]->{$xx}}\n" if($rvb);
				}
			    }
			}	
		    } #end of tight while loop
		}
		print KIPP "\n" if ($o);
	    }
	    $count++;
	}
	close KIPP;
	print "KIPP SIZE ",-s "$cachedir/kipp/kipp.dat","\n" if($vb);

	if($regions)
	{
	    # process region data
	    my $res=0.01;

	    my $dx=$absdxdatarange;
	    my $dy=abs($ydatarange[1]-$ydatarange[0]);
	    
	    print "Data range X = $dx (from @xdatarange), Y = $dy (from @ydatarange)\n" if($vb);
	  
	    # if both xrange and yrange (for the plot) are given we can
	    # further constrain the range of the region (although this *should* have
	    # been done above... check this!)
	    $dx=MIN($dx,abs($xrange[1]-$xrange[0])) 
		if(defined($xrange[0]) && defined($xrange[1]) && is_numeric(@xrange));
	    
	    $dy=MIN($dy,abs($yrange[1]-$yrange[0]))
		if(defined($yrange[0]) && defined($yrange[1]) && is_numeric(@yrange));

	    $dx*=$res; 
	    $dy*=$res;

	    print "Process region data with resolution dx=$dx,dy=$dy\n"if($vb);

	    process_region(\%kippconv, $cachedir.'/kipp/kippconv.dat',0.0,$dx,$dy);
	    process_region(\%kippnuc, $cachedir.'/kipp/kippnuc.dat',0.02,$dx,$dy);
	}
	
	return terminate_kipp_plot() if ($stop_kipp_plot);	
	set_status_bar('Plotting Kippenhahn diagram : plotting ... ');

	# set palette
	my $palrgb=convert_palette($pm3d_palettes[$kippenhahn_state[14]]);
	print "Set pal $palrgb\n"if($vb);
			
	# use autoscaled ranges where appropriate
	$xrange[0]='*' if(!is_numeric($kippenhahn_state[2]));
	$xrange[1]='*' if(!is_numeric($kippenhahn_state[3]));
	$yrange[0]='*' if(!is_numeric($kippenhahn_state[7]));
	$yrange[1]='*' if(!is_numeric($kippenhahn_state[8]));
	$zrange[0]='*' if(!is_numeric($kippenhahn_state[12]));
	$zrange[1]='*' if(!is_numeric($kippenhahn_state[13]));
	
	if($vb)
	{
	    print "Xrange @xrange\n";
	    print "Yrange @yrange\n";
	    print "Zrange @zrange\n";
	}

	# make gnuplot file
	open(KIPPGP,">$cachedir/kipp/kipp.plt")||confess("Cannot open $cachedir/kipp/kipp.plt : $!");

	my ($width,$height)=autosize($kippenhahn_image->parent->parent);
	$misc_opts_values[1]=$width;
	$misc_opts_values[2]=$height;

	my ($x,$y) = gnuplot_size();
	if($gnuplot_options{'terminal type'}=~/postscript/io)
	{
	    # postscript
	    print KIPPGP "
set terminal postscript $ps_shape color enhanced solid \"$gnuplot_options{'font face'}\" $gnuplot_options{'font size'} dashlength $gnuplot_options{'dash length'} linewidth $gnuplot_options{'line width'} size $x,$y 
";	    
	}
	else
	{
	    # png
	    print KIPPGP gnuplot_header($cachedir.'/kipp/kipp.png');
	}

	print KIPPGP "set pm3d map clip4in explicit
set palette $palrgb
set xrange\[$xrange[0]:$xrange[1]\]
set yrange\[$yrange[0]:$yrange[1]\]
set zrange\[$zrange[0]:$zrange[1]\]
set cbrange\[$zrange[0]:$zrange[1]\]
set key below

set xlabel \"$extra_prelabel[0]$plt1cols[$xcol]$extra_postlabel[0]\"
set ylabel \"$extra_prelabel[1]$Kippenhahn_labels[$ycol]$extra_postlabel[1]\"
set title \"",format_ps_title("$extra_prelabel[2]$Kippenhahn_labels[$zcol]$extra_postlabel[2]"),"\"
";
	
	print KIPPGP gnuplot_overrides();

	# shrink right margin a bit and unset the key
	print KIPPGP "set rmargin at screen 0.85\nunset key\n";
	    
	# set special labels for convection in binstar
	if(($evcode eq 'binstar')&&($Kippenhahn_labels[$zcol] eq 'Conv'))
	{
	    print KIPPGP "
set cbrange[-3:5]
set cbtics (\"C\" -2, \"A\" -1, \"OV\" 1, \"TH\" 2, \"SC\" 3, \"R\" 4)\n";
	}

	my $dfile="$cachedir/kipp/kipp.dat";

	# alter data for block data?
	if($use_block_surface && $block_surface)
	{
	    `gnuplot_surface2.pl $dfile > $dfile.2`;
	    $dfile=$dfile.'.2';
	}

	print KIPPGP "splot \"$dfile\" using 1:2:3 with pm3d at b notitle ";

	print "KIPP: XCOL $xcol ; YCOL $ycol ; ZCOL $zcol \n" if($vb);

	if($regions)
	{
	    # add hashing for convection, nuclear burning etc.
	    my $hashingz=abs($zdatarange[1]-$zdatarange[0])*0.0001+
		    $zdatarange[1];
	    #print "Set hashingz $hashingz from @zdatarange\n";
	    if($kippenhahn_state[20])
	    {
		my $w=withstring(2);
		$w=~s/lw (\S+)/lw (0.25*$1)/ if($gnuplot_options{'terminal type'}=~/PNG/i);
		#print "KIPPCONV $w\n";
		print KIPPGP ", \"$cachedir/kipp/kippconv.dat\" u 1:2:($hashingz) $w title \"\" ";
	    }
	    if($kippenhahn_state[24])
	    {
		my $w=withstring(4);
		$w=~s/lw (\S+)/lw (0.25*$1)/ if($gnuplot_options{'terminal type'}=~/PNG/i);

		print KIPPGP ", \"$cachedir/kipp/kippnuc.dat\" u 1:2:($hashingz) $w title \"\" ";
	    }
	}
	print KIPPGP gpshow() if(!$persist_gnuplot);
	close KIPPGP;
	return terminate_kipp_plot() if ($stop_kipp_plot);
	
	my $f=$cachedir.'/kipp/kipp.plt';
	my $grange;
	my $err;
	if($gnuplot_options{'terminal type'}=~/postscript/io)
	{
	    # gnuplot to a png file
	    if($persist_gnuplot)
	    {
		my $h = gnuplot_ps_to_png2(
		    { 
			'input file'=>$f,
			'png output file'=>$cachedir.'/kipp/kipp.png',
			dpi=>get_opt(0),
			'gnuplot stream in'=>$persist_gnuplot{in},
			'gnuplot stream out'=>$persist_gnuplot{out},
			'gnuplot stream err'=>$persist_gnuplot{err},
			invert=>1*$gnuplot_options{'invert image'}=~/on Black/o,
		    }
		    );
		$grange = $$h{'graph range'};
		$err = $$h{err};
	    }
	    else
	    {

		($grange,$err)=gnuplot_ps_to_png($f,$cachedir.'/kipp/kipp.png',get_opt(0));
	    }
	}
	else 
	{
	    my $cmd=prepend_gdfontpath(format_filename($external_programs{gnuplot}.' '.$f));
	    reformat_pltfile($f);
	    my $res=`$cmd 2>&1`;
	    ($grange,$err)=extract_gnuplot_range(\$res);
	}
	push_to_Misc_log($err);
	
	map
	{
	    print "GRANGE $_ : @{$grange->{$_}}\n";
	}keys %$grange if($vb);
	
	%dragging=();
	
	%kippenhahn_image_properties=('image'=>'Kippenhahn', 
				      'image widget'=>$kippenhahn_image,			       
				      'grange'=>$grange,
				      'width'=>get_opt(1),
				      'height'=>get_opt(2),
				      'lmargin'=>$gnuplot_options{lmargin},
				      'rmargin'=>0.85, # special for kippenhahn!
				      'tmargin'=>$gnuplot_options{tmargin},
				      'bmargin'=>$gnuplot_options{bmargin},
				      'xrange widgets'=>[$kippenhahn_rangebox[0],$kippenhahn_rangebox[1]],
				      'yrange widgets'=>[$kippenhahn_rangebox[2],$kippenhahn_rangebox[3]],
	    );

	# show the plot
	$kippenhahn_image->set_from_pixbuf((get_image_from_file($cachedir.'/kipp/kipp.png'))[1]);

	$ps_filenames[$current_tab]="$cachedir/kipp/kipp.ps";

	set_status_bar("Kippenhahn diagram plotted");
	toggle_hourglass(0);
    }
}


sub set_kipp_state
{
    $kippenhahn_state=1;
    $kipp_plot_button->set_label('Replot')
}

sub stop_kipp_plot
{
    # cancel button hit
    $stop_kipp_plot=1;
}

sub terminate_kipp_plot
{
    # actually stop the kippenhahn plot
    close KIPPGP;
    close KIPP;
    close PLT1;
    $kippenhahn_state=0;
    $stop_kipp_plot=0;
    set_status_bar("Kippenhahn diagram terminated");
    toggle_hourglass(0);
    return TRUE;
}

############################################################
# Options tab
############################################################

sub show_Misc_tab
{
    my $ntab=$tab_numbers{Misc};
    my $time0 = [gettimeofday()] if($timings);
    if($made_tab[$ntab]==0)
    {
	toggle_hourglass(1);
	
	$made_tab[$ntab]=1;
    
	# miscellaneous options: make in a list, and add the list
	my $scale;

	my @labels=('PS->PNG conversion density',
		    'Image Horizontal Size',
		    'Image Vertical Size',
		    'Evolution nice value',
		    );

	my @tips=('This is (probably dots-per-inch) conversion density used for postscript to PNG conversion.',
		  'The horizontal size of images.',
		  'The vertical size of images.',
		  'When the evolution code is called it is given this nice value. A high value means the evolution will be given low priority on your computer, this is recommended if you want the user interface to respond in a timely fashion. See the nice man page for details.',
		  'Select this to use high-quality postscript images. These are slow to produce and must be converted (to PNG) before they can be shown in the user interface. On the other hand, once they have been generated they will be cached (in memory or on the disk) so will be fast.',
		  'Select this to use PNG images. These are much faster to produce and are the recommended format for non-presentation work.',
		  'This slider enables you to select the font size for postscript images only. Typically 18-25 is used.',
		  'This is the directory containing the stellar evolution code (ev)'
		  );
	# add widgets corresponding to labels here

	# scale for PS2PNG density:
	$scale=Gtk2::HScale->new(Gtk2::Adjustment->new($misc_opts_values[0],10,400,10,10,0));
	$scale->set_digits(0);
	if(!defined($external_programs_version{ghostscript}))
	{
	    # if we have no ghostscript, who cares?
	    $scale->set_sensitive(FALSE);
	}
	push(@misc_opts_widgets,$scale);

	# unused 
	push(@misc_opts_widgets,undef);
	push(@misc_opts_widgets,undef);

	# niceness (0-19)
	$scale=Gtk2::HScale->new(Gtk2::Adjustment->new($misc_opts_values[3],0,19,10,10,0));
	$scale->set_digits(0);
	
	# doesn't work on windoze
	$scale->set_sensitive(FALSE)if($os eq 'windows');

	push(@misc_opts_widgets,$scale);
	my $v=Gtk2::VBox->new(FALSE,0);
	

	for(my $i=0;$i<=$#misc_opts_widgets;$i++)
	{
	    if(defined($misc_opts_widgets[$i]))
	    {
		my $h=Gtk2::HBox->new(TRUE,0);
		
		# add callback to widget
		$misc_opts_widgets[$i]->signal_connect(value_changed=>\&set_opt,$i);
	
		# first pack the label then the widget
		$h->pack_start(Gtk2::Label->new($labels[$i]),FALSE,FALSE,0);
		$h->pack_start($misc_opts_widgets[$i],FALSE,TRUE,100);
		$v->pack_start($h,FALSE,FALSE,0);
	    }
	}
		
	# Gnuplot options
	my $gnuplot_options=Gtk2::Button->new('Gnuplot Options');
	$gnuplot_options->signal_connect(clicked=>\&gnuplot_options);
	$v->pack_start($gnuplot_options,FALSE,FALSE,0);

	# wtts layer (data directory) options
	$wttslayers_options=Gtk2::Button->new('WTTS Data Layers');
	$wttslayers_options->signal_connect(clicked=>\&wttslayers_options);
	$v->pack_start($wttslayers_options,FALSE,FALSE,0);

	# unused
	push(@misc_opts_widgets,undef); # 4
	push(@misc_opts_widgets,undef); # 5
	push(@misc_opts_widgets,undef); # 6
	
	{
	    # Path to ev text box (opt 7)
	    my $h=Gtk2::HBox->new(TRUE,0);
	    my $evpath = (!defined($evcoderoot)) && ok_directory($misc_opts_values[7]) ? $misc_opts_values[7] : $evcoderoot;
	    my $t=Gtk2::Entry->new_with_text($evpath);
	    $t->set_size_request(350,25);
	    $t->signal_connect(changed=>\&set_text_opt,7);	
	    push(@misc_opts_widgets,$t);
	    $h->pack_start(Gtk2::Label->new('Path to stellar evolution code'),FALSE,FALSE,0);
	    $h->pack_start($misc_opts_widgets[7],FALSE,FALSE,0);
	    $v->pack_start($h,FALSE,FALSE,0);
	}
	
	# set tooltips
	for(my $i=0;$i<=$#misc_opts_widgets;$i++)
	{
	    set_tooltip($misc_opts_widgets[$i],$tips[$i]) if(defined($misc_opts_widgets[$i]));
	}
	

	if(0)
	{
	    # enable kippenhahn cache : currently buggy
	    my $h=Gtk2::HBox->new(TRUE,5);
	    $h->pack_start(Gtk2::Label->new('Use Kippenhahn plot cache?'),FALSE,FALSE,0);
	    my $yestb=Gtk2::RadioButton->new_with_label(undef,'Yes');
	    my $notb=Gtk2::RadioButton->new_with_label($yestb,'No');
	    $use_kippenhahn_cache=$misc_opts_values[22];
	    
	    ($use_kippenhahn_cache ? $yestb : $notb)->set_active(TRUE);
	    $yestb->signal_connect(clicked=>sub{ $misc_opts_values[22]=1-$misc_opts_values[22];
						 $use_kippenhahn_cache=1-$use_kippenhahn_cache;
						 if($use_kippenhahn_cache==0)
						 {
						     # delete the cache data (if we can)
						     @kippenhahn_model_cache=();
						 }
				   });
	    $h->pack_start($yestb,FALSE,FALSE,0);
	    $h->pack_start($notb,FALSE,FALSE,0);
	    $v->pack_start($h,FALSE,FALSE,0);
	}
	else
	{
	    $use_kippenhahn_cache=0;
	}

	# installation information
	my $installation_infobutton=Gtk2::Button->new('Installation Information');
	$installation_infobutton->signal_connect(clicked=>\&installation_information);
	$v->pack_start($installation_infobutton,FALSE,FALSE,0);
	

	# rest is a log window
	my $misc_log_window=Gtk2::ScrolledWindow->new(undef,undef);
	$misc_log_window->set_shadow_type('etched-out');
	$misc_log_window->set_policy('automatic','automatic');
	$misc_log_window->set_border_width(5);

	$misc_log_textview=Gtk2::TextView->new();
	$misc_log_textview->hide;
	$misc_log_textview->set_cursor_visible(FALSE);
	$misc_log_textview->set_wrap_mode('word');
	$misc_log_textview->set_left_margin(1);
	$misc_log_textview->set_right_margin(1);
	$misc_log_textview->set_left_margin(1);
	$misc_log_textview->set_left_margin(1);
	$misc_log_textview->set_editable(FALSE);
	my $font_desc = Gtk2::Pango::FontDescription->from_string ('Courier 10');
	$misc_log_textview->modify_font ($font_desc);
	$misc_log_window->add($misc_log_textview);
	$v->pack_start($misc_log_window,TRUE,TRUE,0);

	# test colour palette
	if(0){
	    my $color_picker_button = Gtk2::Button->new('Test colour'); 

	    $color_picker_button->signal_connect(clicked=>sub{

		my $color_window=Gtk2::Window->new();
		my $color_selection = Gtk2::ColorSelection->new();
		$color_selection->signal_connect(color_changed=>sub
						 {
						     my $color=$color_selection->get_current_color();
						     print "COLOR $color\n";
						     my $html=$color_selection->palette_to_string($color);
						     print "HTML $html\n";
						 });

		$color_window->add($color_selection);
		$color_window->show_all();
		$color_window->visible();
		
		print "COLOR SELECTION $color_selection\n";
						 });
	    $v->pack_start($color_picker_button,FALSE,FALSE,0);
	}

	$frames[$ntab]->add(scrolled($v));
	$frames[$ntab]->show_all;
	toggle_hourglass(0);
    }
    increment_timing('show_Misc_tab',$time0) if($timings);    
    TRUE;
}
sub update_Misc_tab
{
    # does nothing at present
}

sub push_to_Misc_log
{
    # push string $_[0] to the Misc log
    my $x=$_[0];
    # remove excess whitespace
    $x=~s/^\s+//;
    $x=~s/\s+$//;
    # remove range/view information:
    $x=~s/set [xy]2?range.*//g;
    $x=~s/view is.*\n.*\n//;
    
    if(defined($x))
    {
	# detect filenames in the cache and provide extra information
	my $y=$x;
	while($y=~s!\"($cacheroot[^\"]+)"!!)
	{
	    $x .= sprintf "File \"$1\" : exists? %s : size = %d\n", ynstring(-e $1), -s $1;
	}
	my $buffer=$misc_log_textview->get_buffer;
	$buffer->insert($buffer->get_end_iter,scalar(localtime())."\n\n".$x."\n");
	TRUE;
    }
}

sub clear_Misc_log
{
    $misc_log_textview->get_buffer->set_text('');
    TRUE;
}
############################################################
# Load/Save tab
############################################################

sub show_Load_Save_tab
{
    my $ntab=$tab_numbers{Load_Save};
    
    if($made_tab[$ntab]==0)
    {
	$made_tab[$ntab]=1;
	my $v=Gtk2::VBox->new;
	my $b=Gtk2::Button->new_with_tooltip('Load model set','Click here to load a set of models');
	$b->signal_connect(clicked=>sub{load_model_set_file_selector()});

	if(($useperlzip!=1)&&
	   (!defined($external_programs_version{unzip})))
	{
	    $b->set_sensitive(FALSE);
	    $b->set_label("Unzip not available: please install");
	}

	$v->pack_start($b,FALSE,FALSE,5);

	$b=Gtk2::Button->new_with_tooltip('Save model set','Click here to save a set of models');

	if(($useperlzip!=1)&&
	   (!defined($external_programs_version{zip})))
	{
	    $b->set_sensitive(FALSE);
	    $b->set_label("Zip not available: please install");
	}

	$b->signal_connect(clicked=>sub{save_model_set_file_selector()});
	$v->pack_start($b,FALSE,FALSE,5);

	$frames[$ntab]->add($v);
	$frames[$ntab]->show_all;
    }
}

sub load_model_set
{
    my $file=$file_selector->get_filename;
    print "Load model set $file\n"if($vb);
    
    wttslayer();
    @kippenhahn_model_cache=()if($use_kippenhahn_cache); # clean kippenhahn cache
    
    my @files=@model_set_files;
 
    $file.='.zip' if(!($file=~/\.zip$/o));
    my $cmd;
    my $z;
    my @m;

    if($useperlzip!=1)
    {
	$cmd=join(' ',$external_programs{unzip},'-o','-b',$file);
    }
    else
    {
	$z=Archive::Zip->new($file);
	@m=$z->memberNames();
    }

    # make progress bar
    my $progress_window=Gtk2::Window->new;
    my $progress=Gtk2::ProgressBar->new;
    
    $progress_window->set_size_request(600,30);
    $progress_window->set_title('Loading Model Set');
    $progress_window->set('modal',TRUE);

    my $h=Gtk2::HBox->new;
    $h->pack_start($progress,TRUE,TRUE,0);
    $progress_window->add($h);
    $progress_window->show_all;
    $progress_window->grab_focus;
    $progress_window->set('is-focus',TRUE);

    $file_selector->destroy;
    
    # set an alarm to update the progress bar every second
    #$SIG{ALRM}=\&refresh_progress_bar;
    #alarm 1;

    if($useperlzip==1)
    {
	for(my $c=0;$c<=$#m;$c++)
	{
	    $z->extractMemberWithoutPaths($m[$c]); 
	    $progress->set_fraction(MIN(1.0,(1.0*$c-1.0)/(1.0*$#m)));
	    clear_events_queue();
	}
    }
    else
    {
	print "OPEN $cmd\n"if($vb);
	# open the zip command
	$zippid=open(OPENZIP,"$cmd |")||confess("Cannot open zip : $cmd : $!");
	my $c=0;
	# update progress bar as we zip
	while(<OPENZIP>)
	{
	    $c++;
	    print "Check progress\n"if($vb);
	    if(defined($_))
	    {
		my $f=MIN(1.0,(1.0*$c-1.0)/(1.0*$#files));
		print "Progress $c/$#files -> $f\n"if($vb);
		$progress->set_fraction($f);
		clear_events_queue();
	    }
	}
   
	# done, reset alarm and close zip command
	#alarm 0;
	close OPENZIP;
	$zippid=-1;
    }

    # clear global caches and the like because this is like rerunning the code
    clear_global_caches();
    $internals_options_state=1;
    $anim_frame_update=1;
    $have_run_the_code=1;
    
    show_Options_tab(1);
    show_Evolve_tab(1);
    show_Log_tab(1);
    show_HRD_tab(1);
    show_RhoT_tab(1);
    show_Histogram_tab(1);
    show_Internals_tab(1);
    show_Structure_tab(1);
    show_System_tab(1);
    update_Options_tab(1);
    update_Evolve_tab(1);
    update_Log_tab(1);
    update_HRD_tab(1);
    update_Internals_tab(1);
    update_Internals_tab(2);
    update_Structure_tab(1);
    update_System_tab(1);
    update_view();

    # get rip of the progress bar
    $progress_window->destroy;

    set_status_bar("Loaded model set from $file");
}

sub save_model_set
{
    my $file=$file_selector->get_filename;
    print "Save model set $file\n"if($vb);

    # Save a model set by zipping everything up

    wttslayer();

    # these are the files we want to save
    my @files=@model_set_files;
    
    map
    {
	$_=$runname.$_;
    }@files;

    $file.='.zip' if(!($file=~/\.zip$/o));

    my $cmd;
    my $z;

    if($useperlzip!=1)
    {
	$cmd=join(' ',$external_programs{zip},'-v','-9','-q','-u',$file,@files);
	print "CMD $cmd\n"if($vb);
    }
    else
    {
	$z=Archive::Zip->new();
	print "Zip object $z\n"if($vb);
	if(!defined($z))
	{
	    print STDERR "ERROR : zip object was not created properly :(\n"; 
	    return;
 	}
    }

    # make progress bar
    my $progress_window=Gtk2::Window->new;
    my $progress=Gtk2::ProgressBar->new;
    
    $progress_window->set_size_request(600,30);
    $progress_window->set_title('Saving Model Set');
    $progress_window->set('modal',TRUE);
    my $h=Gtk2::HBox->new;
    
    $h->pack_start($progress,TRUE,TRUE,0);
    $progress_window->add($h);
    $progress_window->show_all;
    $progress_window->grab_focus;
    $progress_window->set('is-focus',TRUE);
    $file_selector->destroy;

    if($useperlzip==1)
    {
	# add files
	print "Add files\n"if($vb);
	for(my $c=0;$c<=$#files;$c++)
	{
	    if((-e $files[$c]) && (-f $files[$c]))
	    { 
		my $f=MIN(1.0,(1.0*$c-1.0)/(1.0*$#files));
		if(!defined($z->addFile($files[$c])))
		{
		    print STDERR "Failure to add $files[$c] to zip\n";
		}
		print "Added file $files[$c]\n"if($vb); 
		$progress->set_fraction(MAX(0.0,MIN(1.0,$f)));
		clear_events_queue();
	    }
	}

	# compress
	
	print "Set compression\n"if($vb);
	map
	{ 
	    print "Set compression on $_\n"if($vb);
	    $_->desiredCompressionMethod(COMPRESSION_DEFLATED);
	    $_->desiredCompressionLevel(9);
	}$z->members;

	# write zip file
	unless ( $z->writeToFileNamed( $file ) == AZ_OK ) 
	{
	    die 'Zip Write Error while saving models';
	}
    }
    else
    {
	$zippid=open(ZIPUP,"$cmd |")||confess("Cannot open zip : $cmd : $!");
	
	my $c=0;
	while(<ZIPUP>)
	{
	    $c++;
	    print "Check progress\n" if($vb);
	    if(defined($_)&&(/adding/o))
	    {
		# update progress bar
		my $f=MIN(1.0,(1.0*$c-1.0)/(1.0*$#files));
		print "Progress $c/$#files -> $f\n";
		$progress->set_fraction($f);
		clear_events_queue();
	    }
	}
	#alarm 0;
	close ZIPUP;
	$zippid=-1;
    }
    set_status_bar("Saved model set to $file");

    # get rip of the progress bar
    $progress_window->destroy;
}

sub new_file_selector
{
    # make a new file selector, and if necessary, destroy
    # the old one
    my $name=$_[0];
    # connect the $ok_callback function to the ok button
    my $ok_callback=$_[1];
    
    $file_selector->destroy if(defined($file_selector));
    $file_selector=Gtk2::FileSelection->new($name);
    $file_selector->ok_button->signal_connect(clicked=>$ok_callback);
    
    # set up cancel button (just closes the selector window)
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->show;
}

sub save_model_set_file_selector
{
    # show save model set file selector
    new_file_selector('Save Model Set',\&save_model_set);
    return FALSE;
}

sub load_model_set_file_selector
{
    # show load model set file selector
    new_file_selector('Load Model Set',\&load_model_set);
    return FALSE;
}

sub update_Load_Save_tab
{
    # do nothing
}

############################################################
# About tab
############################################################

sub show_About_tab
{
    my $ntab=$tab_numbers{About};
    
    if($made_tab[$ntab]==0)
    {
	$made_tab[$ntab]=1;
	my @content=("<span foreground=\"DarkRed\" size=\"x-large\"><b>Window To The Stars</b></span>\n$version\nWritten by Robert Izzard\nBased on Evert Glebbeek's version of Peter Eggleton's TWIN stellar evolution code\n",
		  "<span foreground=\"Blue\">http://www.ast.cam.ac.uk/~rgi/window/</span>",
		  Gtk2::Image->new_from_pixbuf((ppe_image())[1]),
		  "<span foreground=\"Black\" size=\"x-large\"><b>Peter Eggleton: The Master!</b></span>");

	my $vbox=Gtk2::VBox->new;
		 
	foreach my $s (@content)
	{
	    my $c; # content string/image
	    if($s=~/^</o)
	    {
		 # text
		$c=Gtk2::Label->new_from_markup($s);
		$c->set_selectable(TRUE) if($s=~/http/o);
	    }
	    else
	    {
		# image
		$c=Gtk2::Image->new_from_pixbuf((ppe_image())[1]);
	    }
	    $vbox->pack_start($c,FALSE,FALSE,0);
    	}
		
	$frames[$ntab]->add(scrolled($vbox));
	$frames[$ntab]->show_all;
    }
    TRUE;
}

sub update_About_tab
{
    # not used
}


############################################################
### Other misc. subroutines, usually generic callbacks to 
### catch keyboard/mouse events
############################################################

sub main_window_loses_focus
{
    print "Lost focus: clear key queue\n" if($vb);
    @keys_pressed=();
    TRUE;
}

sub close_on_escape
{
    # when Escape is pressed, close the current window
    # VERY useful for plugins!
    my ($widget,$event,$parameter)= @_;
    my $key_nr = $event->keyval();
    foreach my $key (keys %Gtk2::Gdk::Keysyms)
    {
	if(($Gtk2::Gdk::Keysyms{$key} == $key_nr)&& defined($key))
	{
	    if($key eq 'Escape')
	    {
		$widget->destroy;
	    }
	}
    }
}

sub hide_on_escape
{
    # when Escape is pressed, hide the current window
    my ($widget,$event,$parameter)= @_;
    my $key_nr = $event->keyval();
    foreach my $key (keys %Gtk2::Gdk::Keysyms)
    {
	if(($Gtk2::Gdk::Keysyms{$key} == $key_nr)&& defined($key))
	{
	    if($key eq 'Escape')
	    {
		$widget->hide;
	    }
	}
    }
}

sub main_window_clicked
{
    # a mouse button was pressed in the main window
    
    FALSE; # propagate!
}


sub main_window_keypress
{
    # a key was pressed, what do we do?
    my ($widget,$event,$parameter)= @_;
    my $key_nr = $event->keyval();

    #run trough the available key names, and get the values of each,
    #compare this with $event->keyval(), when you get a match exit the loop
    foreach my $key  (keys %Gtk2::Gdk::Keysyms)
    {
	if(($Gtk2::Gdk::Keysyms{$key} == $key_nr)&& defined($key))
	{
	    push(@keys_pressed,$key);
	    {
		print "You typed $key : list @keys_pressed \n" if($vb);
		my $x="@keys_pressed";
		if($x eq "Control_L q")
		{
		    stop_and_exit_program();
		} 
		elsif($x eq "Control_L Tab")
		{
		    select_next_tab();
		}
		elsif(($x eq "Control_L Shift_L ISO_Left_Tab")||
		      ($x eq "Shift_L Control_L ISO_Left_Tab"))
		{
		    select_prev_tab();
		}
		elsif($x eq "Control_L Shift_L E")
		{
		    # evolve star
		    $main_notebook->set_current_page(1);
		    $evolve_button->clicked;
		}
		elsif($x eq 'Control_L l')
		{
		    # layers diagloue
		    wttslayers_options();
		}
		elsif($x eq "Control_L Shift_L T")
		{
		    # terminate evolution
		    $main_notebook->set_current_page(1);
		    $stop_button->clicked;
		}
		elsif($x eq "Control_L Shift_L V")
		{
		    # switch on verbosity
		    $vb++;
		}
		elsif($x eq "Control_L Shift_L R")
		{
		    # reset verbosity
		    $vb=0;
		    $expvb=0;
		}
		elsif($x eq "Control_L 1")
		{
		    # select star 1
		    for(my $i=0;$i<=$#starbuttons;$i+=2)
		    {
			$starbuttons[$i]->set_active(1);
		    }
		}
		elsif($x eq "Control_L 2")
		{
		    # select star 2
		    for(my $i=1;$i<=$#starbuttons;$i+=2)
		    {
			$starbuttons[$i]->set_active(1);
		    }
		}
		elsif($x eq 'Control_L Shift_L Shift_R R')
		{
		    # emergency restart
		    emergency_restart();
		}
		elsif($x eq "Control_L g")
		{
		    # show gnuplot options window
		    gnuplot_options();
		}
		elsif($x eq "Control_L d")
		{
		    # show layers window
		    wttslayers_options();
		}
		elsif(($x eq 'f')&&($current_tab==$tab_numbers{Evolve}))
		{
		    # follow log
		    $follow_button->clicked;
		}
		elsif(($x eq 'c')&&($current_tab==$tab_numbers{Evolve}))
		{
		    # follow log
		    $clear_button->clicked;
		}
		elsif($x eq 'u')
		{
		    if($current_tab==$tab_numbers{Evolve})
		    {
			# update log
			$update_button->clicked;
		    }
		    # plot ranges reset
		    elsif($current_tab==$tab_numbers{HRD})
		    {
			$HRD_range_widgets[6]->activate;
		    }
		    elsif($current_tab==$tab_numbers{RhoT})
		    {
			$RhoT_range[6]->activate;
		    }
		    elsif($current_tab==$tab_numbers{Structure})
		    {
			$structure_rangebox[6]->activate;
		    }
		    elsif($current_tab==$tab_numbers{System})
		    {
			$system_rangebox[6]->activate;
		    }
		    elsif($current_tab==$tab_numbers{Internals})
		    {
			$internals_range[6]->activate;
		    }
		    elsif($current_tab==$tab_numbers{Kippenhahn})
		    {
			print "RESET KIPP\n";
			$kippenhahn_rangebox[6]->activate;
		    }
		}
		elsif(($x eq 'Home')&&($current_tab==$tab_numbers{Evolve}))
		{
		    Evolve_jump_to_top();
		}
		elsif(($x eq 'End')&&($current_tab==$tab_numbers{Evolve}))
		{
		    Evolve_jump_to_end();
		}
		elsif(($x eq 'n')&&($current_tab==$tab_numbers{Evolve}))
		{
		    # jump to NaN
		    #Evolve_jump_to_nan();
		}
		elsif(($x eq 'N')&&($current_tab==$tab_numbers{Evolve}))
		{
		    # jump to previous NaN
		    #Evolve_jump_to_previous_nan();
		}
		elsif(($x eq 'g')&&($current_tab==$tab_numbers{Evolve}))
		{
		    # goto model number
		    Evolve_jump_to_model();
		}
		elsif(($x eq 'r')&&($current_tab==$tab_numbers{Kippenhahn}))
		{
		    set_kipp_state();
		}
		elsif(($x eq 'c')&&($current_tab==$tab_numbers{Internals}))
		{
		    # clear selection list
		    clear_internals_menu();
		    Internals_option_change();
		}
		else
		{
		    for(my $i=0;$i<=$#tab_keys;$i++)
		    {
			if($x eq "Control_L $tab_keys[$i]")
			{
			    $current_tab=$i;
			    $main_notebook->set_current_page($i);
			    eval('show_'.$tab_labels[$i].'_tab');
			}
		    }
		}
	
	    }
	    last;
	}
    }

    #good practice to let the event propagate, should we need it somewhere else
    return FALSE;
}

sub main_window_keyrelease
{
    # key is released
    my ($widget,$event,$parameter)= @_;
    my $key_nr = $event->keyval();
    
    #run trough the available key names, and get the values of each,
    #compare this with $event->keyval(), when you get a match exit the loop
    foreach my $key  (keys %Gtk2::Gdk::Keysyms)
    {
	if(($Gtk2::Gdk::Keysyms{$key} == $key_nr)&& defined($key))
	{
	    # released key ... remove from list
	    print "Release key $key\n" if($vb);
	    for(my $i=0;$i<=$#keys_pressed;$i++)
	    {
		splice(@keys_pressed,$i--,1) if($keys_pressed[$i] eq $key);
	    }
	    last;
	}
    }
}

sub select_next_tab
{
    # select the next tab 
    return(TRUE)if($mouse_pointer>0);
    
    toggle_hourglass(1);
    my $n=$current_tab+1;
    $n=0 if($n>$#tab_labels);

    # emulate mouse click by calling the callback directly
    print "Show next tab $n\n"if($vb);
    $main_notebook->set_current_page($n);
    eval('show_'.$tab_labels[$n].'_tab');
    $current_tab=$n;

    toggle_hourglass(0);
    TRUE;
}
sub select_prev_tab
{
    # select the previous tab 
    return(TRUE)if($mouse_pointer>0);

    toggle_hourglass(1);
    my $n=$current_tab-1;
    $n=$#tab_labels if($n<0);

    # emulate mouse click by calling the callback directly
    print "Show prev tab $n\n"if($vb);
    $main_notebook->set_current_page($n);
    eval('show_'.$tab_labels[$n].'_tab');
    $current_tab=$n;

    toggle_hourglass(0);
    TRUE;
}

sub stop_and_exit_program
{
    # stop the evolution, then exit the program
    $running_the_code=1;
    stop_wrapper();
    exit_program();
}

sub exit_program
{ 
    # exit the program
    Gtk2->main_quit; 
    exit(0); # just in case
}

sub img_click_press
{
    # image was clicked (press callback)
    my $widget=$_[0];
    my $event=$_[1]; 
    my $extra=$_[2]; # extra options (hash)
    my $button=scalar($event->button);

    return FALSE if(defined($extra->{noimage}) && ($extra->{noimage}==1));

    if(($button==LEFT_MOUSE_BUTTON)||($button==MIDDLE_MOUSE_BUTTON))
    {
	# left mouse press
	my @xy = $event->coords;
	$extra->{'click start xy'}=\@xy; 

	if($button==LEFT_MOUSE_BUTTON)
	{
	    print "NEW DRAG starting at @xy\n" if($vb);
	     
	    if(defined($dragging{'previous drag'}))
	    {
		# moved : undraw previous
		my $window=$dragging{'image widget'}->window;
		my $gc=make_gc($window);
		print "UNDRAW (2) @{$dragging{'start'}} to @{$dragging{'previous drag'}}\n"if($vb);		
		draw_rect($window,$gc,$dragging{'start'},$dragging{'previous drag'});
		foreach ('start','previous drag')
		{
		    delete $dragging{$_};
		}
	    }

	    %dragging=('image widget',$extra->{'image widget'},
		       'start',\@xy,
		       'in drag',1
		);
	}
    }
    
    return FALSE;
}

sub make_gc
{
    my $window=shift;
    my $color='black';
		my $colormap = $window->get_colormap;
    my $gc = $window->{gc} || new Gtk2::Gdk::GC $window;
    $gc->set_foreground(get_color($colormap, $color));
    $gc->set_function('invert');
    return $gc;
}

sub update_drag
{ 
    if(defined($dragging{'in drag'}))
    {
	print "Update drag @_\n" if($vb);
	
	if(defined($dragging{'image widget'}))
	{
	    my $window=$dragging{'image widget'}->window;
	    my @xy = ($window->get_pointer())[1,2];

	    if(!(($dragging{'start'}->[0] == $xy[0])&&
		 ($dragging{'start'}->[1] == $xy[1])))
	    {  
		my $gc=make_gc($window);

		if(!(defined($dragging{'previous drag'})
		   &&
		   ($dragging{'previous drag'}->[0] == $xy[0])&&
		   ($dragging{'previous drag'}->[1] == $xy[1])))
		{
		   if(defined($dragging{'previous drag'}))
		   {
		       # moved : undraw previous
		       print "UNDRAW @{$dragging{'start'}} to @{$dragging{'previous drag'}}\n"if($vb);		
		       draw_rect($window,$gc,$dragging{'start'},$dragging{'previous drag'});
		   }

		    # and draw new rectangle
		    print "DRAW @{$dragging{'start'}} to @xy\n"if($vb);
		    draw_rect($window,$gc,$dragging{'start'},\@xy);

		    # set previous for next time
		    @{$dragging{'previous drag'}}=@xy;
		}
	    }
	}
    }
    return TRUE;
}

sub draw_rect
{
    my $window=shift;
    my $gc=shift;
    my $start=shift;
    my $final=shift;

    #printf "DRAW %g,%g to %g,%g\n",@$start,@$final;
    

    $window->draw_line($gc,
		       $start->[0],$start->[1],
		       $start->[0],$final->[1]);

    
    $window->draw_line($gc,
		       $start->[0],$final->[1],
		       $final->[0],$final->[1]);
    
    $window->draw_line($gc,
		       $final->[0],$final->[1],
		       $final->[0],$start->[1]);
    
    
    $window->draw_line($gc,
		       $final->[0],$start->[1],
		       $start->[0],$start->[1]);
}

sub get_color {
    my ($colormap, $name) = @_;
    my $ret;
    if ($ret = $allocated_colors{$name}) 
    {
        return $ret;
    }
    else
    {
	my $color = Gtk2::Gdk::Color->parse($name);
	$colormap->alloc_color($color,TRUE,TRUE);
	$allocated_colors{$name} = $color;
	return $color;
    }
}

sub img_zoom
{
    # image zoomed 
    my $widget=shift;
    my $event=shift;
    my $extra=shift; # extra options (hash)
    my $zoomfac=0.45; # e.g. 0.2 = 20% zoom
    my @xy = $event->coords;
    my %newrange;
    my $vb=0;
    my $gdx=$extra->{grange}->{x}->[1] - $extra->{grange}->{x}->[0];
    my $gdy=$extra->{grange}->{y}->[0] - $extra->{grange}->{y}->[1];

    $extra->{widget}=$widget;

    # calculate zoom centre
    my @gxy = pixel_to_gnuplot_coordinates(\@xy,$extra);
    
    # save the zoom centre
    if(!defined($extra->{'prev gxy'}))
    {
	$extra->{'prev gxy'}=\@gxy;
    }

    # if we haven't yet redrawn, which can happen with multiple 
    # fast calls to img_zoom (e.g. mouse scroll wheel moved quickly)
    # we should use the previously calculated @gxy rather than that 
    # calculated from 'grange' (which is now inaccurate)
    if(defined($extra->{'needs redraw'}))
    {
	@gxy = @{$extra->{'prev gxy'}};
    }

    print "Zoom at (pixel) @xy (gnuplot) @gxy\n"if($vb);

    ## rewrite algorithm to zoom in on current point
    if($event->direction eq 'up')
    { 
	# zoom in
  	# zoom out
	my $zx=$gdx*$zoomfac*0.5;
	my $zy=$gdy*$zoomfac*0.5;
	printf "Zoom in : new deltas x=%g, y=%g\n",2.0*$zx, 2.0*$zy if($vb);
	
	$newrange{x}=[$gxy[0] + $zx, $gxy[0] - $zx];
	$newrange{y}=[$gxy[1] + $zy, $gxy[1] - $zy];
    }
    else
    {
	# zoom out
	my $zx=$gdx*(1.0+$zoomfac)*0.75;
	my $zy=$gdy*(1.0+$zoomfac)*0.75;
	printf "Zoom out : new deltas x=%g, y=%g\n",2.0*$zx, 2.0*$zy if($vb);
	
	$newrange{x}=[$gxy[0] - $zx, $gxy[0] + $zx];
	$newrange{y}=[$gxy[1] - $zy, $gxy[1] + $zy];
	
    }

    $extra->{'xrange widgets'}->[0]->set_text($newrange{x}->[0]);
    $extra->{'xrange widgets'}->[1]->set_text($newrange{x}->[1]);
    $extra->{'yrange widgets'}->[0]->set_text($newrange{y}->[0]);
    $extra->{'yrange widgets'}->[1]->set_text($newrange{y}->[1]);

    # force redraw
    $extra->{'needs redraw'}=1;

    # do not allow propagation : may need a redraw!
    return TRUE;
}

sub img_click_release
{
    # image was clicked (release callback)
    my $widget=$_[0];
    my $event=$_[1];
    my $extra=$_[2]; # extra options (hash)
    return if(!defined $event);
    my $button=scalar($event->button);
    my $vb=0;
    if($vb)
    {
	print "MOUSE BUTTON $button\n";
	map
	{
	    print "EXTRA $_ : $$extra{$_}\n";
	}sort keys %$extra;

	map
	{
	    print "RANGE key $_ value ",join(':',@{$extra->{grange}->{$_}}),"\n";
	}sort keys %{$extra->{grange}};
    }
    $extra->{widget}=$widget;

    if(defined($extra) && defined($extra->{image}) && 
       ($extra->{image} eq 'Kippenhahn'))
    {
	my %new_dragging;
	no warnings;
	foreach ('image widget','start','previous drag')
	{
	    $new_dragging{$_}=$dragging{$_};
	}
	use warnings;
	%dragging=%new_dragging;
    }
    else
    {
	%dragging=(); # empty the dragging hash in case we were dragging
	# except for the Kippenhahn plot which requires manual update
    }

    if((defined($extra->{'click start xy'}))&&
       (($button==LEFT_MOUSE_BUTTON)||($button==MIDDLE_MOUSE_BUTTON)))       
    {
	# left mouse release
	my @start = @{$extra->{'click start xy'}};
	my @final = $event->coords;

	my $same=(($start[0]==$final[0])&&($start[1]==$final[1]));
	if(!$same)
	{
	    # we have to map @start and @final into the graph's co-ordinates
	    my @gstart=pixel_to_gnuplot_coordinates(\@start,$extra);
	    my @gfinal=pixel_to_gnuplot_coordinates(\@final,$extra);

	    if(0 && $extra->{image} eq 'Kippenhahn')
	    {
		# Kipp. plot uses pm3d map : compensate 
		my $tmp=$gstart[1];
		$gstart[1]=$gfinal[1];
		$gfinal[1]=$tmp;
	    }

	    if($vb)
	    {
		print "DRAG from pixel @start to pixel @final\n";
		print "Start : $gstart[0], $gstart[1]\n";
		print "Final : $gfinal[0], $gfinal[1]\n";
	    }

	    if($button==LEFT_MOUSE_BUTTON)
	    {
		# left mouse : zoom
 		print "Zoom\n"if($vb);

		# set range widgets
		$extra->{'xrange widgets'}->[0]->set_text($gstart[0]);
		$extra->{'xrange widgets'}->[1]->set_text($gfinal[0]);
		$extra->{'yrange widgets'}->[0]->set_text($gstart[1]);
		$extra->{'yrange widgets'}->[1]->set_text($gfinal[1]);
	    }
	    else
	    {
		# middle mouse : move
		# calculate translation
		my $dx = $gfinal[0] - $gstart[0];
		my $dy = $gfinal[1] - $gstart[1];
	
		print "Move : dx=$dx dy=$dy\n"if($vb);

		$extra->{'xrange widgets'}->[0]->set_text($extra->{grange}->{x}->[0]-$dx);
		$extra->{'xrange widgets'}->[1]->set_text($extra->{grange}->{x}->[1]-$dx);
		$extra->{'yrange widgets'}->[0]->set_text($extra->{grange}->{y}->[0]-$dy);
		$extra->{'yrange widgets'}->[1]->set_text($extra->{grange}->{y}->[1]-$dy);
	    }
	}
    }
    
    elsif($button==RIGHT_MOUSE_BUTTON)
    {
	# check it's mouse button 3 (right mouse)
	# then a popup menu, and allow saving option
	# and perhaps others (depending on $extra options)
	my $pixbuf=$widget->child->get_pixbuf;
	print "Clicked image\n" if($vb);
	
	my $imagename = $widget->get_name();
	my $png = 'Save As PNG';
	my $ps = 'Save As Postscript';

	if($imagename=~/Internals/)
	{
	    my $animate=test_for_animation();
	    if($animate)
	    {
		# can be AVI, GIF or MNG, but not postscript
		$png = 
		    defined animator_program() ? 
		    'Save As Animated GIF, MNG or AVI' : 
		    'Save As Animated GIF or MNG';
		$ps = undef;
	    }
	}

	my $menu=Gtk2::Menu->new;
	my $menuitem=Gtk2::MenuItem->new($png); # 'png' : usually PNG, could be GIF
	$menuitem->signal_connect(activate=>sub{
	    save_image_file_selector($pixbuf);
				  });
	$menu->append($menuitem);

	if($gnuplot_options{'terminal type'}=~/postscript/i && defined $ps)
	{
	    # only allow "save as postscript" if we have plotted as postscript
	    $menuitem=Gtk2::MenuItem->new('Save As Postscript');
	    $menuitem->signal_connect(activate=>sub{save_ps_image_file_selector($pixbuf)});
	    $menu->append($menuitem);
	}

	$menuitem=Gtk2::MenuItem->new('Gnuplot Options');
	$menuitem->signal_connect(activate=>sub{gnuplot_options()});
	$menu->append($menuitem);

	$menuitem=Gtk2::MenuItem->new('WTTS Data Layers');
	$menuitem->signal_connect(activate=>\&wttslayers_options);
	$menu->append($menuitem);

	if(defined($$extra{image}) && ($$extra{image} eq 'HRD'))
	{
	    $menuitem=Gtk2::MenuItem->new('HRD Options');
	    $menuitem->signal_connect(activate=>sub{HRD_options()});
	    $menu->append($menuitem);
	}

	$menu->popup(
	    undef, # parent menu shell
	    undef, # parent menu item
	    undef, # menu pos func
	    undef, # data
	    $event->button,
	    $event->time
	    );
	$menu->show_all;

    }
    
    return FALSE;
}			     

sub pixel_to_gnuplot_coordinates
{
    # convert pixel co-ordinates to gnuplot co-ordinates

    my $pixel=shift; # array containing the pixel co-ordinates
    my $extra=shift; # image hash containing image position information
    my @gxy; # (returned) array containing the gnuplot co-ordinates
    
    # edges of the plot in pixels
    my $leftedge=$extra->{width} * $extra->{lmargin};
    my $rightedge=$extra->{width} * $extra->{rmargin};
    my $topedge=$extra->{height} * (1.0-$extra->{tmargin});
    my $bottomedge=$extra->{height} * (1.0-$extra->{bmargin});
    
    # widths of the plot area in pixels
    my $pixelwidth=MAX(1,$rightedge-$leftedge);
    my $pixelheight=MAX(1,$bottomedge-$topedge);

    # gnuplot deltas (from 'grange' i.e. the gnuplot range)
    my $gdx=$extra->{grange}->{x}->[1] - $extra->{grange}->{x}->[0];
    my $gdy=$extra->{grange}->{y}->[0] - $extra->{grange}->{y}->[1];
    
    if(1)
    {
#	print "Edges (pixel): left=$leftedge right=$rightedge; bottom=$bottomedge top=$topedge\n";
#	print "Pixel width=$pixelwidth; height=$pixelheight\n";
#	print "Gnuplot deltas x=$gdx, y=$gdy\n";
	
	# draw lines to show the edges
	my $window=$extra->{widget}->window;
	my $gc=make_gc($window);
	my %box;
	draw_rect($window,$gc,[$leftedge,$bottomedge],[$rightedge,$topedge]);
    }

    # hence gnuplot co-ordinates 
    $gxy[0] = ($$pixel[0]-$leftedge)*$gdx/$pixelwidth+$extra->{grange}->{x}->[0];
    $gxy[1] = ($$pixel[1]-$topedge)*$gdy/$pixelheight+$extra->{grange}->{y}->[1];
    
    return (@gxy);
}

sub load_start_model
{
    my $star_number=shift;
    my $file=$file_selector->get_filename;
    my $labeltext=$file;
    print "Load model $file\n"if($vb);

    # trim the label text
    $labeltext=~s/.*\/(.*)$/$1/;

    set_evcode_start_model($star_number,$file);

    # set the label
    ($star_number==1 ? $start_model_label1 : $start_model_label2)->set_label($labeltext);

    # if the model labels are undefined, this is a problem
    # for some codes (e.g. TWIN) so deal with this case
    if($start_model_label1->get_label eq 'Undefined')
    {
	set_evcode_start_model(1,$file);
	$start_model_label1->set_label($labeltext);
    }
    if($start_model_label2->get_label eq 'Undefined')
    {
	set_evcode_start_model(2,$file);
	$start_model_label2->set_label($labeltext);
    }
    
    # save the path
    $misc_opts_values[12+$star_number]=dirname($file);
    save_misc_opts();

    # kill file selector
    $file_selector->destroy;
}

sub load_model_file_selector
{
    if(defined($file_selector))
    {
	# we can only have one file selector at a time
	$file_selector->destroy;
    }
    shift;
    my $star_number=shift;
    print "Load model file selector for star $star_number\n" if ($vb);

    # pop up a file box, then save
    $file_selector=Gtk2::FileSelection->new('Load starting model');
    
    my $path=$misc_opts_values[12+$star_number].'/';
    print "Set file selector path $path\n"if($vb);
    $file_selector->set_filename($path.'/');

    # set actions of Cancel and OK
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->ok_button->signal_connect(clicked=>sub{load_start_model($star_number)});
    
    # show file selector
    $file_selector->show;

    return FALSE;
}

sub save_model_file_selector
{
    my $modeln=shift;
    if(defined($file_selector))
    {
	# we can only have one file selector at a time
	$file_selector->destroy;
    }
    print "SAVE model $modeln\n" if ($vb);

    # pop up a file box, then save
    $file_selector=Gtk2::FileSelection->new('Save as (mod file : for restart)');
    $file_selector->set_filename($misc_opts_values[17].'/');

    # set actions of Cancel and OK
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->ok_button->signal_connect(clicked=>sub{save_model_mod($modeln+1)});
    
    # show file selector
    $file_selector->show;

    return FALSE;
}

sub save_model_mod
{
    my $modeln=shift;
    my $file=$file_selector->get_filename;
    
    print "Save model $modeln as $file\n" if ($vb);
    
    # save path
    $misc_opts_values[17]=dirname($file);
    save_misc_opts();

    # extract and save model $modeln
    my @data;
    if($evcode_module eq 'binstar')
    {
	@data=extract_evcode_model(${get_available_models()}[$modeln]);
	shift @data;shift @data;
    }
    else
    {
	# ARGH!!! use extract_evcode_model_pointer, not extract model
    	@data=extract_model($runname.'.mod',[$modeln]);
    }
    dumpfile($file,join("\n",@data));
    
    # kill file selector
    $file_selector->destroy;
    return FALSE;
}



sub save_mdl_file_selector
{
    my $modeln=shift;
    if(defined($file_selector))
    {
	# we can only have one file selector at a time
	$file_selector->destroy;
    }
    print "SAVE model $modeln\n" if ($vb);

    # pop up a file box, then save
    $file_selector=Gtk2::FileSelection->new('Save as (mdl file for plotting/data extraction)');
    $file_selector->set_filename($misc_opts_values[17].'/');

    # set actions of Cancel and OK
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->ok_button->signal_connect(clicked=>sub{save_model_mdl($modeln+1)});
    
    # show file selector
    $file_selector->show;

    return FALSE;
}

sub save_model_mdl
{
    my $modeln=shift;
    my $file=$file_selector->get_filename;
    print "Save model (mdl file) $modeln as $file\n" ;#if ($vb);
    
    # save path
    $misc_opts_values[17]=dirname($file);
    save_misc_opts();

    # extract and save model $modeln
    my @data=extract_evcode_model(${get_available_models()}[$modeln]);
    shift @data;shift @data;
    dumpfile($file,join("\n",@data));
    
    # kill file selector
    $file_selector->destroy;
    return FALSE;
}

sub save_image_file_selector
{
    my $pixbuf=shift;
    
    # we can only have one file selector at a time
    $file_selector->destroy if(defined($file_selector));
       
    # pop up a file box, then save
    $file_selector=Gtk2::FileSelection->new('Save As ...');
    $file_selector->set_filename($misc_opts_values[15].'/');

    # set actions of Cancel and OK
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->ok_button->signal_connect(clicked=>sub{save_image($pixbuf)});
    
    # show file selector
    $file_selector->show;

    return FALSE;
}

sub save_ps_image_file_selector
{
    
    # force internals tab update (might be animation)
    update_Internals_tab(1) if($current_tab==$tab_numbers{Internals});

    # we can only have one file selector at a time
    $file_selector->destroy if(defined($file_selector));
       
    # pop up a file box, then save
    $file_selector=Gtk2::FileSelection->new('Save As Postscript');
    $file_selector->set_filename($misc_opts_values[15].'/');

    # set actions of Cancel and OK
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    $file_selector->ok_button->signal_connect(clicked=>sub{save_ps_image()});
    
    # show file selector
    $file_selector->show;

    return FALSE;
}

sub file_selector_cancel
{
    # cancel pressed, just destroy the file selector
    $file_selector->destroy;
    return FALSE;
}

sub save_ps_image
{
    # save postscript image of whatever is in the current tab

   
    if(defined($ps_filenames[$current_tab]))
    { 
	my $file=$file_selector->get_filename;
	return FALSE if($file=~/^\s+$/o);
	# save the path
	$misc_opts_values[15]=dirname($file);
	save_misc_opts();

	print "Save current PS image: $ps_filenames[$current_tab] to $file\n"if($vb);
	copy($ps_filenames[$current_tab],$file);
    }
    # kill file selector
    $file_selector->destroy;
    return FALSE;
}

sub save_image
{
    # save image data to a file given by the global file selector
    my $pixbuf=shift;
    my $file=$file_selector->get_filename;

    # cannot save to nothing!
    return FALSE if($file=~/^\s+$/o);

    # save the path
    $misc_opts_values[15]=dirname($file);
    save_misc_opts();

    my $animate=test_for_animation();
    my $an=$anim_speed;
	    
    if(($animate==1)&&($current_tab==$tab_numbers{Internals})&&($an>0))
    {
	# save animation currently in @anim_buffer
	my $animdir="$cachedir/window_anim/";
	mkdir $animdir;
	empty_dir($animdir); # must be empty before we start!
	my @files;

	# choose format
	my $format=
	    $file=~/\.gif$/o ? 'GIF' : 
	    $file=~/\.mng$/o ? 'MNG' : 
	    $file=~/\.avi$/o ? 'AVI' : 'unsupported';
 
	# make png files
	my $c=-1;

	# if we want a delay at the end, repeat the last few frames
	my @extra;
	if($format eq 'AVI')
	{
	    my $last = $animation_models[$#animation_models];
	    foreach (0..3)
	    {
		push(@extra,$last);
	    }
	}

	map
	{
	    $c++;
	    print "Save model $c $cachedir/window_anim/$c.png #c = $_ for animation\n"if($vb);
	    my $f="$cachedir/window_anim/$c.png";
	    dumpfile($f,(get_image_from_cache($_))[0]);
	    push(@files,$f);
	}(@animation_models,@extra);


	# now convert to animated .gif or .mpg
	if($format eq 'GIF')
	{
	    # make animated gif
	    $an=1 if($an==0);
	    my $delay=10*(11-$an);
	    print "Convert delay $delay\n" if($vb);
	    toggle_hourglass(1);
	    set_status_bar("Making animated GIF");
	    print "convert -delay $delay -loop 0 @files $file\n"if($vb);
	    `convert -delay $delay -loop 0 @files $file`;
	    set_status_bar("Done");
	    if($unlink_tempfiles)
	    {
		map
		{
		    unlink $_;
		}@files;
	    }
	    toggle_hourglass(0);	
	}
	elsif($format eq 'MNG')
	{
	    # make animated gif
	    $an=1 if($an==0);
	    my $delay=10*(11-$an);
	    print "Convert delay $delay\n" if($vb);
	    toggle_hourglass(1);
	    set_status_bar("Making animated MNG");
	    `convert -delay $delay -loop 0 @files $file`;
	    set_status_bar("Done");
	    if($unlink_tempfiles)
	    {
		map
		{
		    unlink $_;
		}@files;
	    }
	    toggle_hourglass(0);	
	}
	elsif('AVI')
	{
	    # make avi with ffmpeg
	    $an=1 if($an==0);

	    # choose program to do the animating
	    my $animator = animator_program();

	    if(defined $animator)
	    {
		my $delay=10*(11-$an);
		print "Convert delay $delay\n" if($vb);
		toggle_hourglass(1);
		set_status_bar("Making animated MNG");
		my $cmd=join(' ',
			     $animator,'-y',
			     '-r','4', # 4 FPS
			     '-i',"$animdir/\%d.png",
			     '-s','wuxga',
			     $file # output file
		    );
		print "Run \"$cmd\"\n";
		`$cmd`;
		set_status_bar("Done");
	    }
	    else
	    {
		error_message_box("Cannot make AVI: you need avconv or ffmpeg to be installed\n");
	    }

	    if($unlink_tempfiles)
	    {
		map
		{
		    unlink $_;
		}@files;
	    }

	    toggle_hourglass(0);	
	}
	else
	{
	    my $s="Unknown file extension on $file : I don't know how to save!";
	    error_message_box($s);
	    print STDERR $s,"\n";
	    set_status_bar($s);
	}
    }
    else
    {
	# append .png if necessary
	$file.='.png' if(!($file=~/\.png$/io));
	
	# save it
	$pixbuf->save($file,'png');
    }

    # kill file selector
    $file_selector->destroy;
    return FALSE;
}

sub set_image_callbacks
{
    # set callbacks for an image by putting it in an eventbox
    my $eventbox=Gtk2::EventBox->new;
    my $imagename=$_[0]; # Image name (readable string)
    my $img=$_[1]; # Image widget 
    my $extra=$_[2]; # extra options to pass to img_click_release
    if(defined $img)
    {
	$eventbox->set_name($imagename);
	$eventbox->signal_connect('button-press-event'=>\&img_click_press,$extra);
	$eventbox->signal_connect('button-release-event'=>\&img_click_release,$extra);  
	$eventbox->signal_connect('scroll_event'=>\&img_zoom,$extra);
	$eventbox->add($img);
	return $eventbox;
    }
    else
    {
	return $img; #  fallback on failure : return the image without event box
    }
}


sub update_window_title
{
    return if(!$auto_window_title);
    my $time0 = [gettimeofday()] if($timings);

    # if we have >1 WTTS process running, set the title
    # to show the current directory.
    if($os eq 'unix')
    {
	no warnings;
	# ignore warnings from state, e.g. "unknown state (hex char...)" 
	state $process_table = new Proc::ProcessTable(enable_ttys=>FALSE);
	use warnings;
	my $count = scalar grep {$_->uid == $UID && 
				 $_->cmndline=~/perl\s+\S+stars.pl/o } 
	@{$process_table->table};
	
	if($count>1)
	{
	    $main_window->set_title('Window To The Stars : '.$pwd);
	}
	else
	{
	    $main_window->set_title($window_title);
	}
    }

    increment_timing('update_window_title',$time0);
}

sub main_window
{
    # set up and return the main window
    my $main_window=Gtk2::Window->new;

    $main_window_status=1;
    if($extended_title==1)
    {
	my $pwd=$ENV{PWD};
	$pwd=~s/^.*\///go;
	$pwd=~s/^\s+//o;
	$pwd=~s/\s+$//o;
	$window_title.=" - $runname";
	$window_title.=" - $pwd" if(defined($pwd));
    }
 
    # set icon
    $main_window->set_default_icon(pixbuf_from_rawdata(join('',(pack('h*',wtts_icon())))));

    $main_window->set_title ($window_title);
    $main_window->set_border_width(10);
    $main_window->signal_connect (delete_event => \&stop_and_exit_program);
    $main_window->set_size_request($windowx // $misc_opts_values[20] // 1200,
				   $windowy // $misc_opts_values[21] // 200);
    $main_window->signal_connect(key_press_event => \&main_window_keypress);
    $main_window->signal_connect(button_release_event => \&main_window_clicked);
    $main_window->signal_connect(key_release_event => \&main_window_keyrelease);
    $main_window->signal_connect(focus_out_event=>\&main_window_loses_focus);
    $main_window->signal_connect(event_after => \&main_window_property_change);
    
    # make a Vbox for the main window (persistent, returned)
    my $main_vbox=Gtk2::VBox->new(FALSE,0);
    $main_window->add($main_vbox);

    return($main_window,$main_vbox);
}

sub main_window_property_change
{
    # capture main window size changes
    my (undef, $ev) = @_;
    return unless $ev->type eq 'configure';
}

sub check_for_previous_run
{
    # check for previous run of the code by searching for a .plt1 file
    print "check for $modinfo{plt1file_stub}$nstar\n"if($vb);
    if(ok_file("$modinfo{plt1file_stub}$nstar")==1)
    {
	# we have run the code before!
	$have_run_the_code=1;
    }
}


sub make_tab_bar
{
    # make the main tab bar
    my $tabgroup;
    my $main_vbox=$_[0];
    my $enote=Gtk2::EventBox->new;
    $main_notebook=Gtk2::Notebook->new;
    $main_notebook->set_tab_pos('top');
    $main_notebook->set_current_page(0);
    $main_notebook->set_scrollable(TRUE);
    #$main_notebook->popup_enable();
    $main_notebook->set_tab_border(0);
    $main_notebook->set_tab_vborder(0);
    $main_notebook->set_show_border(0);
    
    for(my $i=0;$i<=$#tab_labels;$i++)
    {
	$made_tab[$i]=0; # always set this to zero until the tab 
	# has been drawn, when it becomes 1

	my $t= $tab_labels[$i] eq 'Structure' && $evcode=~/bonnfires/io ? 'Stars' : $tab_labels[$i];
	$t=~s!_!/!go;

	# make frames for tab content
	$frames[$i]=Gtk2::Frame->new($t);

	if($i==$#tab_labels)
	{
	    # make reset and close buttons on final tab
	    
	    # make a table
	    my $table=Gtk2::Table->new(1,3,FALSE);
	    $table->set_border_width(0);
	    
	    # make elements
	    my $label=Gtk2::Label->new($t);

	    # wtts layer (data directory) options
	    my $wttslayers_options = Gtk2::Ex::NoShrink->new();
	    my $b=Gtk2::Button->new_with_label_and_tooltip('Layers','Open the WTTS layers window');
	    $b->signal_connect(clicked=>\&wttslayers_options);
	    $b->set_relief('none');
	    $b->set_use_underline(FALSE);
	    $b->released;
	    $wttslayers_options->add($b);

	    my $resetbutton = Gtk2::Ex::NoShrink->new();
	    $b=Gtk2::Button->new_with_label_and_tooltip('Restart','Click this button to completely restart Window To The Stars!');
	    $b->signal_connect(clicked=>\&emergency_restart);
	    $b->set_relief('none');
	    $b->set_use_underline(FALSE);
	    $b->released;
 	    $resetbutton->add($b);

	    my $closebutton = Gtk2::Ex::NoShrink->new();
	    $b=Gtk2::Button->new_with_label_and_tooltip('Close',
							   'Click this button to exit the program');
	    $b->signal_connect(clicked=>\&stop_and_exit_program);
	    $b->set_relief('none');
	    $b->set_use_underline(FALSE);
	    $b->released;
	    $closebutton->add($b);

	    # attach to the table
	    $table->attach( $label,0,1,0,1,'shrink','shrink',0,0);
	    $table->attach( Gtk2::Label->new(''),1,2,0,1,'expand','shrink',0,0);
	    $table->attach( $wttslayers_options,2,3,0,1,'shrink','expand',0,0) ;
	    $table->attach( $resetbutton,4,5,0,1,'shrink','shrink',0,0) ;
	    $table->attach( $closebutton,5,6,0,1,'shrink','shrink',0,0) ;
	    $table->set_row_spacings(5);
	    $table->show_all;

	    $main_notebook->append_page_menu($frames[$i],$table,undef);
	    $main_notebook->set_menu_label_text($frames[$i],'About');
	    $main_notebook->set_tab_label_packing($frames[$i],TRUE,TRUE,'start');
	}
	else
	{
	    $main_notebook->append_page($frames[$i],$t);
	}

	print "Append page $i= $tab_labels[$i]\n" if($vb);
	$current_tab=$i;
	eval('\&show_'.$tab_labels[$i].'_tab');
    }

    $current_tab=$tab_numbers{Options};
    $main_notebook->signal_connect(button_release_event=>\&toggle_tab);
       
    $enote->add($main_notebook);
    $enote->show_all;
   
    $main_vbox->pack_start($enote,TRUE,TRUE,0);
    $tooltips->enable;
    print "Made tab bar\n"if($vb);
}

sub add_status_bar_widgets
{
    # add the status bar widget

    my $status_bar_frame=Gtk2::Frame->new();

    my $vbox=Gtk2::VBox->new(0,0);
    for(my $i=0;$i<$n_status_bar_lines;$i++)
    {
	$status_bar[$i]=Gtk2::Label->new;
	$vbox->pack_start(set_tooltip_with_eventbox($status_bar[$i],'This box shows the current status of the code, and the amount of memory the user interface is currently using.'),FALSE,FALSE,0);
    }
    
    my $s = scrolled($vbox);
    $s->set_size_request(-1,100);
    $status_bar_frame->add($s);

    return($status_bar_frame,@status_bar);
}

sub toggle_hourglass
{
    # set the mouse pointer to an hourglass, and back
    my $x=$_[0];
    clear_events_queue();

    if(!defined($main_window))
    {
	return TRUE;
    }
    
    if($x==1)
    {
	# up mouse pointer count
	$mouse_pointer++;
	if($mouse_pointer>=1)
	{
	    foreach my $window ($main_window,
				$file_selector,
				$gnuplot_options_window,
				$HRD_options_window)
	    {
		if(defined($window) && defined($window->window))
		{
		    $window->window->set_cursor(Gtk2::Gdk::Cursor->new('watch')) ;
		}
	    }
	}
    }
    else
    {
	$mouse_pointer--;
	if($mouse_pointer==0)
	{
	    foreach my $window ($main_window,
				$file_selector,
				$gnuplot_options_window,
				$HRD_options_window)
	    {
		if(defined($window) && defined($window->window))
		{
		    $window->window->set_cursor(Gtk2::Gdk::Cursor->new('left-ptr')) ;
		}
	    }
	}
    }
    $mouse_pointer=MAX(0,$mouse_pointer);
    
    clear_events_queue();
    TRUE;
}


sub parse_args_by_eval
{
    # parse command line arguments using eval
    foreach my $arg (@ARGV)
    {
	if($arg=~/([^=]+)\=(.*)/o)
        {
	    print "EVAL $1 to $2\n" if($vb);
	    eval "\$$1=\"$2\"";
	}
	else
	{
	    eval $arg;
	}
    }
    $|=1 if($vb);
}

sub set_opt
{
    # set the value of an option widget
    my $y=$_[0]->get_value;
    my $x=$_[1]; # the widget number
    $misc_opts_values[$x]=$y;
    print "Set misc_opts_values[$x] to $y\n" if ($vb);
    Internals_option_change();
    Animation_frame_update();  
    if(($x==1)||($x==2))
    {
	# we have changed the image size : resize wrong image and no image
	set_image_in_cache('wrong',get_image_from_string(wrongimage_png()));
	set_image_in_cache('noimage',get_image_from_string(noimage_png()));
    }
    FALSE;
}

sub set_text_opt
{
    # set the value of an option widget
    my $x=$_[1]; # the widget number
    my $y=$_[0]->get_text;
    $misc_opts_values[$x]=$y;
    print "Set misc_opts_values[$x] to $y\n" if ($vb);
    Internals_option_change();
    Animation_frame_update();  
    FALSE;
}


sub get_opt
{
    # get the value of an option widget
    print "Return misc_opts_values[$_[0]] = $misc_opts_values[$_[0]]\n" if ($vb);
    return($misc_opts_values[$_[0]]);
}

sub save_misc_opts
{
    # save the options : both to $home (as the previous)
    # and in the current directory 
    state $prevfile;
    state $prevdata;

    my $file = "$optsdir/.window_to_the_stars_opts";
    my $data;

    $Data::Dumper::Sortkeys=1;

    $Data::Dumper::Varname='misc_opts_values';
    $data .= Dumper(\@misc_opts_values);
    $Data::Dumper::Varname='gnuplot_options';
    $data .= Dumper(\%gnuplot_options);
    $Data::Dumper::Varname='wttslayers';
    $data .= Dumper([$wttslayernum,@wttslayers]);
    $Data::Dumper::Varname='wttslayers_toggles';
    $data .= Dumper(\@wttslayers_toggles);

    if((!defined $prevfile) ||
       ($file ne $prevfile) ||
       ($data ne $prevdata))
    {
	open(MISCOPTS,'>'.$file)||
	    print "cannot open $file for writing misc opts";
	print MISCOPTS $data;
	close MISCOPTS;
	$prevfile = $file;
	$prevdata = $data;
    }
}

sub load_misc_opts
{
    # load the GUI options
    my $f="$optsdir/.window_to_the_stars_opts";
    
    if((-f $f) && (-s $f>0))
    {
	my $gnuplot_options1;
	my $misc_opts_values1;
	my $wttslayers1;
	my $wttslayers_toggles1;
	
	# slurp in the code and eval it
	if(eval slurp($f))
	{	
	    if(defined($misc_opts_values1))
	    {
		@misc_opts_values=@$misc_opts_values1;
	    }

	    # this looks line a simple setting of the hash but it is not:
	    # it just overwrites data, leaving NEW data (i.e. new options) intact
	    # (we could do this with a library routine but the overhead seems 
	    # pointless for such a trivial operation)
	    if(defined($gnuplot_options1))
	    {
		map
		{
		    $gnuplot_options{$_}=$$gnuplot_options1{$_};
		}keys %$gnuplot_options1;
	    }

	    # wtts dirs
	    if(defined($wttslayers1))
	    {
		@wttslayers = @$wttslayers1;
		$wttslayernum = shift @wttslayers;
	    }

	    # wtts dir toggles
	    if(defined($wttslayers_toggles1))
	    {
		@wttslayers_toggles = @$wttslayers_toggles1;
		for(my $i=0;$i<=$#wttslayers;$i++)
		{
		    $wttslayers_toggles[$i] //= 0;
		}
	    }

	    return 1;
	}
	else
	{
	    print "Eval failed when loading misc opts : $@\n";
	    return 0;
	}
    }
    else
    {
	return 0;
    }
}



sub set_image_in_cache
{
    my $s=$_[0]; # string
    my $i=$_[1]; # image
    my $pb=$_[2]; # the pixbuf

    print "Set image $s in cache (pb=$pb)\n"if($vb);

    # save image in the cache
    $image_cache{$s}=$i;

    # force generation of pixbuf if we don't have one
    if((!defined($pb))||($pb=~/^paged/o))
    {
	$pb=scale_pixbuf(pixbuf_from_rawdata($i));
    }

    # save pixbuf (if we have it!)
    $pixbuf_cache{$s}=defined $pb ? $pb : 'undef';

    if(($vb)&&($pixbuf_cache{$s} eq 'undef'))
    {
	print "Tried to set image $s in cache, but pixbuf failed\n";
	print "Image string $i\n";
	exit;
    }

    # update timestamp
    $image_cache_timestamp{$s}=time();
}

sub get_image_from_cache
{
    my $s=$_[0];
    my $vb=0;
    print "Get image $s from cache\n"if($vb>1); 

    if(defined($image_cache{$s}))
    {
	if($vb)
	{
	    if($pixbuf_cache{$s} eq 'undef')
	    {
		print "Pixbuf undef error\n";
		exit;
	    }
	    
	    if((!($image_cache{$s} =~/^paged to file (\d+)$/))&&
	       ($pixbuf_cache{$s} =~/^paged to file (\d+)$/))
	    {
		print "Image in cache error \n";
		exit;
	    }
	}

	if($image_cache{$s} =~/^paged to file (\d+)$/)
	{
	    my $f="$window_image_cache/$1";
	    print "Paged to file $f size ",(-s $f)," - binslurp from there\n"if($vb>1);
	    
	    if(ok_file($f))
	    {
		# image is cached on disk, load back into memory cache
		set_image_in_cache($s,binslurp($f));
		
		# make pixbuf
		$pixbuf_cache{$s}=scale_pixbuf(pixbuf_from_rawdata($image_cache{$s}));
	    
		print "$pixbuf_cache{$s}\n"if($vb>1);
	    }
	    else
	    {
		print "OOPS FAILED to binslurp file $f from cache\n";
		exit;
	    }
	}
	elsif($vb>1)
	{
	    print "Image is still in memory : $pixbuf_cache{$s}\n";
	}
	return($image_cache{$s},$pixbuf_cache{$s});
    }
    else
    {
	# something went wrong
	print "Image \"$s\" was not in the image cache\n";

	if(0){
	open(TMP,'>/tmp/cachelist');
	map
	{
	    print TMP $_,"\n";
	}sort keys %image_cache;
	close TMP;
	print "See /tmp/cachelist for valid keys\n";
	exit;
	}
 	return(wrong_image());
    }
}

sub nodatainrange_image
{
    # get the 'something went wrong' image
    if(in_image_cache('nodatainrange')==0)
    {
	$misc_opts_values[1]=235;
	$misc_opts_values[2]=24;
	set_image_in_cache('nodatainrange',get_image_from_string(nodatainrange_png()));
    }
    return(get_image_from_cache('nodatainrange'));
}

sub wrong_image
{
    # get the 'something went wrong' image
    if(in_image_cache('wrong')==0)
    {
	$misc_opts_values[1]=640;
	$misc_opts_values[2]=400;
	set_image_in_cache('wrong',get_image_from_string(wrongimage_png()));
    }
    return(get_image_from_cache('wrong'));
}

sub no_image
{
    # get the 'something went wrong' image
    printf "Check for noimage in cache = %d\n",in_image_cache('noimage') if($vb);
    if(in_image_cache('noimage')==0)
    { 
	$misc_opts_values[1]=640;
	$misc_opts_values[2]=400;
	print "set noimage into cache\n" if($vb);
	set_image_in_cache('noimage',get_image_from_string(noimage_png()));
    }
    return(get_image_from_cache('noimage'));
}


sub in_image_cache
{
    # check if an image with a given key ($_[0]) is in the image cache
    return $image_cache{$_[0]} ? 1 : 0;
}

sub empty_image_cache
{
    mkdir $window_image_cache;
    empty_dir($window_image_cache);
}

sub clear_up_caches
{
    # check times on cached images, perhaps page them to disk
    my $t=time();
    my $time0 = [gettimeofday()] if($timings);
    
    print "Check cache\n"if($vb);
    my $in_mem_count=0;
    foreach my $s (keys(%image_cache))
    {
	if($image_cache_timestamp{$s}<0)
	{
	    # -1 -> already cached
	    print "$s stays in the cache\n"if($vb);
	}
	else
	{
	    # calculate time since this image was used
	    my $dt=$t-$image_cache_timestamp{$s};
	    print "Image $s, dt=$dt\n"if($vb); 
	    
	    if($dt>$image_timeout)
	    {
		# fork to allow parent process to continue?
		print "Page to disk\n"if($vb);
		dumpfile("$window_image_cache/$disk_cache_count",
			 $image_cache{$s});
		# save the file name
		#undef($image_cache{$s});
		#undef($pixbuf_cache{$s});
		print "PAGE out file $disk_cache_count\n"if($vb);
		$pixbuf_cache{$s}="paged to file $disk_cache_count";
		$image_cache{$s}="paged to file $disk_cache_count";
		$disk_cache_count++;
		$image_cache_timestamp{$s}=-1;
		print "Set timestamp{$s}\n"if($vb);
	    }
	    else
	    {
		$in_mem_count++;
	    }
	}
    }

    print "CACHE in mem $in_mem_count, timeout=$image_timeout\n" if($vb);
    if($in_mem_count>35)
    {
	# this is a *LARGE* number of images. Get them ALL from disk.
	$image_timeout=0;
    }
    if($in_mem_count>20)
    {
	# we don't want > 20 images in memory at once,
	# so reduce the image_timeout back to zero to prevent
	# damage (cache everything from disk)
	$image_timeout=1;
	$image_timeout=MAX(0,$image_timeout);
    }
    elsif($in_mem_count>10)
    {
	# we don't want > 10 images in memory at once,
	# so reduce the image_timeout back to a few seconds
	$image_timeout=3;
	$image_timeout=MAX(0,$image_timeout);
    }
    else
    {
	# we have very few images in memory, so increase the timeout
	# because we can handle a few more (limit to a minute)
	$image_timeout++;
	$image_timeout=MIN(60,$image_timeout);
    }

    increment_timing('clear_up_caches',$time0) if($timings);
}

sub clear_up_all_caches
{
    # clear up caches from other processes
    opendir(CACHES,$cacheroot)||confess("cannot open $cacheroot for reading");
    foreach my $c (readdir CACHES)
    {
	next if $c=~/^\./o;
	my $f=$cacheroot.'/'.$c.'/touch';
	if(-z $f)
	{
	    my $age=MAX(0,(24.0*(-M $f)));
	    # if the access time for the cache is > $session_timeout hours, delete the contents
	    print "Cache dir $c/touch : access age $age hours\n"if($vb); 
	    if($age>$session_timeout)
	    {
		# empty cache
		map
		{
		    my $dir="$cacheroot/$c/$_";
		    if((-e $dir)&&(-d $dir))
		    {
			print "List files in $dir...\n"if($vb);
			empty_dir($dir);
			print "rmdir $dir\n"if($vb);
			rmdir "$dir";
		    }
		}('internals','kipp','system','structure','touch','window_image_cache');
		
		print "Delete $cacheroot/$c/touch\n"if($vb);
		unlink "$cacheroot/$c/touch";

		print "rmdir $cacheroot/$c\n" if($vb);
		rmdir "$cacheroot/$c";
	    }
	}
    }
    close CACHES;
}

sub set_tooltip
{
    # set a tooltip (string $s) on widget ($w)
    my $w=$_[0];
    my $s=$_[1];
    $tooltips->set_tip($w,$s,$s);
    # set name with gtk_tooltips so .rc file can change colours
    $w->set_name('gtk_tooltips '.$w->get_name);
    $tooltips->enable; # just in case
    print "SET TT $s\n" if($vb==2);
}

sub set_tooltip_with_eventbox
{
    # set a tooltip (string $s) on widget ($w) with a new eventbox
    # Useful for dropdown menu
    my $w=$_[0];
    my $s=$_[1];
    # pack in an eventbox (just in case!)
    my $e=Gtk2::EventBox->new;
    $e->add($w);
    # set name with gtk_tooltips so .rc file can change colours
    $e->set_name('gtk_tooltips');
    $tooltips->set_tip($e,$s,$s);
    $tooltips->enable; # just in case
    print "SET TT (with eventbox) $s\n" if($vb==2);
    return($e);
}

sub make_rangeboxes
{
    # generic function to make a set of range boxes for graph plots
    #
    # If corresponding numbers are fed in as arguments, use them
    # as default values

    my @x;
    for(my $i=0;$i<6;$i++)
    {
	$x[$i]=Gtk2::Entry->new_with_text_and_tooltip($_[$i] // '*',
						      'Enter a valid range for your graph here. This should be a number e.g. 1, 1.0, 1e-3, -3E+10 etc. To have gnuplot automatically set the range enter * (or leave it blank)');
	$x[$i]->set_size_request(60,25);
    }
    # reset button
    my $b=Gtk2::Button->new_with_tooltip('Reset Range',
	'Reset X and Y ranges to defaults (*) (you can also press the u key).');
    $b->signal_connect(clicked=>sub{foreach(0 .. 5){$x[$_]->set_text('*')}});
    push(@x,$b);
    return (@x);
}

sub get_graphrange
{
    # given an array of textboxes, which are the range textboxes,
    # return a valid range array
    my @range;
    my $x;
    if(!defined($_[0]))
    {
	return('*','*','*','*','*','*');
    }

    for(my $i=0;$i<6;$i++)
    {
	my $x=$_[$i]->get_text;
	$range[$i] = is_numeric($x) ? $x : '*';
    }

    # flip ranges if necessary
    foreach my $i (0,2,4)
    {
	($range[$i],$range[$i+1])=sort {$a<=>$b} ($range[$i],$range[$i+1]) if(is_numeric(@range[$i,$i+1]));
    }

    return(@range);
}

sub refresh_progress_bar
{
    clear_events_queue();
}

sub clear_global_caches
{
    # clear arrays and hashes just in case we have evolved before
    @plt1data=('');
    @start_status=();
    @current_status=();
    #@evolve_log_file_tails=();
    empty_image_cache();
    %image_cache=();
    %image_cache_timestamp=();
    %pixbuf_cache=();
    %cache_filenames=();
    @internals_tab_cache=();
    @animation_models=();
    @model_to_menu=();
    @models_required=();
    $internals_options_state=1;
    $anim_frame_update=1;
    clear_up_caches();
    clear_up_all_caches();
    unlink($internalsdir.'/internals.png') if($unlink_tempfiles);
    clear_evcode_caches();
}

sub get_image_from_file
{
    # get an image and its pixbuf from a file, and if it goes wrong
    # get the "something went wrong" image
    if(ok_file($_[0]))
    {
	my $raw_data=binslurp($_[0]);
	return($raw_data,scale_pixbuf(pixbuf_from_rawdata($raw_data)));
    }
    else
    {
	return(wrong_image());
    }
}

sub get_image_from_string
{
    # get an image and its pixbuf from a string
    print "get_image_from_string called : arg 0 = \"$_[0]\"\n"if($vb);
    my $pixbuf = pixbuf_from_rawdata($_[0]);
    print "pixbuf = $pixbuf\n"if($vb);
    $pixbuf = scale_pixbuf($pixbuf);
    print "scaled pixbuf = $pixbuf\n"if($vb);
    print "return from get_image_from_string\n"if($vb);
    return($_[0],$pixbuf) if (defined($_[0]));
}

sub pixbuf_from_rawdata
{
    # from some raw image data, make a pixbuf
    my $rawdata = $_[0];
    print "pixbuf_from_rawdata : arg 0 = \"$rawdata\"\n"if($vb);
    my $pixbufloader=Gtk2::Gdk::PixbufLoader->new;
    print "Pixbufloader = $pixbufloader\n"if($vb);
    $pixbufloader->write($rawdata); # raw data is passed in
    $pixbufloader->close();
    print "Pixbufloader written\n"if($vb);
    my $pixbuf=$pixbufloader->get_pixbuf; # copy this
    print "Pixbuf is $pixbuf\n"if($vb);
    if(!defined $pixbuf)
    {
	print STDERR "Failed to get_pixbuf from pixbufloader = $pixbufloader. Did write(\$rawdata) fail?\n";
	exit;
    }
    $pixbufloader->close; # close pixbufloader (WE MUST DO THIS!)
    print "Pixbufloader closed : return pixbuf\n"if($vb);
    return($pixbuf);
}

sub scale_pixbuf
{
    # scale pixbuf (passed in) to the default size
    # widget = get_opt(1); height=get_opt(2);

    printf "scale pixbuf %s to %g %g\n",$_[0],get_opt(1),get_opt(2) if($vb);

    my $x=get_opt(1);
    my $y=get_opt(2);

    # sensible scalings
    $x=defined $x ? MAX(10,MIN(10000,int($x))) : 640;
    $y=defined $y ? MAX(10,MIN(10000,int($y))) : 400;

    return($_[0]->scale_simple($x,$y,'bilinear'));
}

sub gnuplot_header
{
    # generic header for gnuplot files
    my $s;
    my $pngfile=$_[0]; # output file for png: 
    my $options=$_[1]; # extra options 
    # for postscript we output to /dev/stdout (i.e. just 'set output')

    $pngfile=format_filename($pngfile); # win32 conversion
  
    print "FONT ",get_gpfont(),"\n"if($vb);
    
    my ($x,$y) = gnuplot_size();

    if($gnuplot_options{'terminal type'}=~/PNG/i)
    {
	# native PNG
	my $font=get_gpfont();
	if($font eq 'Default')
	{
	    $font='';
	}
	else
	{
	    if(defined($fonthash{$font}))
	    {
		$font = $fonthash{$font};
		$font=~s/(.*)\///o;
	    }
	    else
	    {
		$font = 'default';
	    }
	    $gdfontpath=$1;
	}

	my $lw=$gnuplot_options{'line width'}*$gnuplot_options{'Plot Oversize'}*$gnuplot_options{'oversize factor'};

	# special background colour for PNG
	my $bgcolour=$gnuplot_options{'background colour'};

	# versions of gnuplot < 4.6 require an x rather than a #
	$bgcolour=~s/^\#/x/o if(($external_programs_version{gnuplot}=~/^(\d+\.\d)/)[0]<4.6);

	
	$s=join("\n",
		"set terminal png truecolor font \"$font, $gnuplot_options{'font size'}\" enhanced size $x,$y lw $lw $bgcolour ",
		"set output \"$pngfile\"",
		"set grid back ; unset grid", # required for gnuplot to correctly display the coloured axes
		'');

    }
    else
    {
	# native PS (later converted to PNG)
	my $font=$gnuplot_options{'font face'};
	$font=~s/^font\s+//o;

	# solidity
	my $solid=' ';
	no warnings;
	$solid = ' solid ' if($gnuplot_options{solid}!~/dashed/oi &&
			      $$options{solid}!~/dashed/);
	use warnings;

	my $fontfile=' ';
	#{
	    # requires ttf2pt1
	    #my $x=$fontfilehash{$gnuplot_options{'font face'}};
	    #$fontfile='fontfile add "'.$x.'"' if(defined($x));
       	#}

	$s=join("\n",
		"set terminal postscript $ps_shape $solid color enhanced $fontfile \"$font\" $gnuplot_options{'font size'} dashlength $gnuplot_options{'dash length'} linewidth $gnuplot_options{'line width'} size $x,$y ",
		'set output ',
		'');
    }

    # standard options for all terminals
    # e.g. font path 
    my $fontpath=join(' ',@fontpaths);
    $fontpath=~s/(\S+)/\"$1\"/g;

    $s.=join("\n",
	     'set zero 0.0',
	     "cd \"$pwd\"",
	     'set format xy "%g"',
	     'set format cb "{/*0.7 %g}"',
	     'set fontpath '.$fontpath,
	     '');

    return($s);
}

sub format_ps_title
{
    # format string $t for gnuplot 
    my $t=$_[0];
    $t=~s/_([A-Za-z]+)/_{$1}/g;
    if($gnuplot_options{'terminal type'}=~/postscript/i)
    {
	# postscript specific changes
	$t=~s/[Gg]rad/{\/Symbol \\321}/og; 
	$t=~s/sigma/{\/Symbol s}/go;
    }
    return($t);
}

sub determine_gnuplot_fonts
{
    # check for available fonts for gnuplot
    #
    # check each directory in @fontpaths for (e.g.) ttf fonts
    my @fonts;
    print "Gnuplot font detection:\n"if($vb);
    foreach my $fontdir (@fontpaths)
    {
	# Windoze fonts
	print "Check fontdir $fontdir\n"if($vb);
	if(opendir(FONTDIR,$fontdir))
	{
	    foreach my $f (readdir FONTDIR)
	    {
		chomp $f;
		my $of=$f;
		if($f=~/^(.*)\.ttf$/i)
		{
		    push(@fonts,$1);
		    $fonthash{$1}=$fontdir.'/'.$1;
		    $fontfilehash{$1}=$fontdir.'/'.$of;
		    print "Found ttf $1 (at $fontdir/$1)\n"if($vb);
		}
	    }
	    close FONTDIR;
	}
    }
    
    # remove duplicates
    {
	my %g;
	map
	{
	    $g{$_}=1;
	}@fonts;
	@fonts=keys %g;
    }

    @gnuplot_fonts=('Default','Times-Roman','Helvetica',sort{$a cmp $b}@fonts);

    # and its reverse lookup hash
    #for(my $i=0;$i<=$#gnuplot_fonts;$i++)
    #{
#	$gnuplot_fonts_reverse_hash{$gnuplot_fonts[$i]}=$i;
 #   }

    %gnuplot_fonts_reverse_hash = map{ state $c=0; $_=>$c++; } @gnuplot_fonts;
    
}

sub get_gpfont
{
    return $gnuplot_options{'font face'};
}

sub determine_ghostscript_version
{
    my $gs=$external_programs{ghostscript};
    my $v=`$gs --version 2>&1`;
    return $v=~/^\d+\.\d+/o ? chomp($v) : undef;
}

sub check_perl_modules
{
    # check versions of perl modules, we have a minimum requirement
    my %req=(
	'Gtk2','1.1',
	'Glib','1.1',
	'rob_misc','0.02',
	$evcode_module,'0.05',
	'File::Copy','2.08',
	'File::Basename','2.73',
#	'File::Tail','0.99.3',
	'Data::Dumper','2.124',
	);
    my $err=0;

    foreach my $mod (keys %req)
    {
	my $v=versionofmodule($mod);
	
	print "Check module $mod\n"if($vb);
	if(is_required_version($v,$req{$mod})==-1)
	{
	    print "Module $mod must be a version later than $req{$mod}  (installed version is $v)\n"; 
	    $err=1;
	}
	elsif($vb)
	{
	    print "You have module $mod version $v which is good enough\n";
	}
    }
    
    exit if($err==1);
    
}

sub versionofmodule
{
    # return version of a module
    return(eval("\$$_[0]::VERSION"));
}

sub is_required_version
{
    # check version strings to make sure we have the required version
    my $installed_version=$_[0];
    my $required_version=$_[1];

    print "Check installed \"$installed_version\" vs required \"$required_version\"\n"if($vb);

    my @i=split(/\D/o,$installed_version);
    my @r=split(/\D/o,$required_version);
    for(my $k=0;$k<=$#i;$k++)
    {
	return(-1) if(defined $i[$k] && defined $r[$k] && $i[$k]<$r[$k]);
    }
    return(0);
}

sub wrap_newlines
{
    # wrap a line $h approximately every $n characters
    my $h=$_[0];
    my $n=$_[1];
    $h=~s/(.{$n})\s/$1 \n/g;
    return $h;
}

sub select_nstar
{
    # select star 1 or 2
    # $_[0] = widget
    my $n=$_[1]; # star number
    if($n!=$nstar)
    {
	for(my $i=$n-1;$i<=$#starbuttons;$i+=2)
	{
	    $starbuttons[$i]->set_active(1);
	}

	# switch to other star
	print "Select star $n (was $nstar)\n" if ($vb);
	if($n ne $nstar)
	{
	    $nstar=$n;

	    clear_global_caches();
	    @animation_models=();
	    
	    # update tabs
	    $internals_options_state=1;
	    Internals_option_change();
	    update_Internals_tab(2);
	    $internals_options_state=1;
	    update_HRD_tab(1);
	    update_Structure_tab(1);
	    update_System_tab(1);
	    update_Evolve_logview();
	    move_to_end_of_evolve_log() if($follow_evolve_log==1);
	}
    }
    return TRUE;
}


sub default_variable_settings
{
    # Set $vb to 1 for lots of messages, 0 for quiet (normal) operation
    # Set $vb=2 for even more information (use sparingly!)
    $vb=0;

    # be verbose on experimental features only
    $expvb=0;

    # set to 1 to delete temporary files 
    $unlink_tempfiles=0;
    
    # cache last cleared NEVER
    $cache_last_cleared=0;

    # set timings=1 for some runtime information (for optimization) 
    $timings=0;

    # default to TWIN mode
    $evcode||='TWIN';

     # maximum memory usage for WTTS: dump after this!
    $max_mem=1200;
  
    # if 0 then use zip and unzip commands, if 1 then use
    # perl's Archive::Zip module (better)
    $useperlzip=1;
   
    # aspect ratio for plots
    $aspect_ratio=1.33333;

    # do not force status updates
    $force_status_updates=0;

    # wtts directories : start with the current directory
    @wttslayers=('.');
    @wttslayers_toggles=(1);
    $wttslayernum=0;

    # Default variable settings
    parse_args_by_eval(); # parse args again

    # set global variables: $os, $tmp, $home, $have_symlinks, $pwd etc.
    check_out_environment();

    # settings relevant to the evolution code
    print "Reset evcode options\n"if($vb);
    reset_evcode_options();
    print "Evcode variable settings\n"if($vb);
    evcode_variable_settings();

    # Default variable settings
    print "Reparse args\n"if($vb);
    parse_args_by_eval(); # parse args again

    # runname is the name used to identify files
    # associated with WTTS
    $runname='window_to_the_stars';
    
    $window_title='Window To The Stars ';
    
    if($evcode eq 'binstar')
    {
	my $pwd=$ENV{PWD};
	$pwd=~s/^.*\///o;
	$window_title.=' '.$pwd;
    }

    $auto_window_title = 1;

    $extended_title=0;

    # external programs: key=name, value=executable name
    %external_programs=('gnuplot','gnuplot',
			'ghostscript','ghostscript',
			'avconv','avconv',
			'ffmpeg','ffmpeg',
	);

    if($useperlzip!=1)
    {
	$external_programs{zip}='zip';
	$external_programs{unzip}='unzip';
    }

    # use persistant gnuplot (somewhat experimental, but seems to work)
    $persist_gnuplot=1;

    no warnings;
    # locations for truetype fonts
    @fontpaths=
	grep {-d $_}
    ('C:\\windows\fonts',
     '/usr/share/fonts',
     '/usr/share/texmf/fonts',
     $ENV{HOME}.'/.fonts',
     $ENV{HOME}.'/fonts',
     '/usr/share/fonts/truetype/bitstream-vera',
     '/usr/share/fonts/truetype/monotype',
     '/usr/share/fonts/bitstream-vera',
     '/usr/share/fonts/monotype',
     '/usr/share/fonts/openoffice',
     '/usr/share/fonts/libreoffice',
     '/usr/share/fonts/latex-xft-fonts',
     '/usr/share/fonts/tv-fonts',
     '/usr/share/fonts/truetype/ttf-lyx',
     '/usr/share/fonts/truetype/ttf-liberation',
     '/usr/share/fonts/truetype/freefont',
     '/usr/share/fonts/truetype',
     '/usr/share/fonts/default',
     '/usr/NX/share/fonts/TTF/',
     '/usr/lib/libreoffice/basis3.3/share/psprint/fontmetric/',
     '/usr/share/texmf-texlive/fonts/',
     split(/\:/,$ENV{FONTPATH}),
     split(/\:/,$ENV{GDFONTPATH})
    );
    use warnings;

    print "Determine external program versions\n"if($vb);

    $external_programs{perl}=$^X;
    $external_programs_version{perl}=sprintf '%vd',$^V; 

    # get gnuplot's fonts
    determine_gnuplot_fonts();
    
    # get version of gnuplot
    $external_programs_version{gnuplot}=determine_gnuplot_version();

    if($external_programs_version{gnuplot}=~/(\d+)\.(\d+)/o)
    {
	if(($1<4) && ($2<4))
	{
	    print "WTTS requires at least gnuplot version V4.4\n";
	    print "Please install (e.g. from http://sourceforge.net/projects/gnuplot/files/\n";
	    exit;
	}
	$use_cubehelix = ($1>=4 && $2>=6);
    }
    else
    {
	print "Failed to determine version of gnuplot! Perhaps I cannot find gnuplot in your PATH?\n";
	exit;
    }

    # get version of ghostscript
    print "Determine ghostscript version ... \n"if($vb);
    $external_programs_version{ghostscript}=determine_ghostscript_version();
    print "ghostscript version is $external_programs_version{ghostscript}\n"if($vb);

    no warnings;
    $external_programs_version{avconv}=(`avconv -version 2>\&1`=~/avconv version (\d+(?:\.\d+)+)/)[0];
    print "avconv is version $external_programs_version{avconv}\n"if($vb);

    $external_programs_version{ffmpeg}=(`ffmpeg -version 2>\&1`=~/ffmpeg version (\d+(?:\.\d+)+)/)[0];
    print "ffmpeg is version $external_programs_version{ffmpeg}\n"if($vb);
    use warnings;

    if($useperlzip!=1)
    {
	print "Determine (un)zip version ... \n"if($vb);
   	($external_programs_version{zip},$external_programs_version{unzip})
	    =determine_zip_versions();
	print "Determine zip version $external_programs_version{zip}, unzip version $external_programs_version{unzip}\n"if($vb);
    }
    else
    {
	print "Use perl (un)zip module\n" if($vb);
    }

    if($os eq 'windows')
    {
	# add .exe extension
	map
	{
	    $external_programs{$_}.='.exe'if($external_programs{$_}!~/\.exe$/); 
	}keys(%external_programs);
    }

    print "Do we have procps?\n"if($vb);
    $procps = $os ne 'windows' ? determine_if_we_have_procps() : 0;
    
    print "Answer: $procps\n"if($vb);

    $RHOTstar=1; # star to plot in RhoT-diagram
    
    $rhot=1;

    parse_args_by_eval(); # parse args up to this point
    check_perl_modules();
        
    # make session ID and cache directory locations
    $session=session_id();
    $cacheroot=$tmp.'/'.basename($runname);
    $cachedir=$cacheroot.'/'.$session;
    print "Session $session\nCachedir $cachedir\n"if($vb);
    $session_timeout=24.0; #files in the cache > this age are cleared out
    # make the cache directory, touch a file in it

    mkdir $cacheroot;
    # fix for 0.12a and later: make $cacheroot writable for *everyone*
    # I don't think this will be a problem: you still are the only
    # person with write access to your personal cachedir ($cachedir)
    chmod 0777,$cacheroot;
    
    mkdir $cachedir;
    touch($cachedir.'/touch');

    # link 'latest' to the cachedir
    unlink $tmp.'/'.basename($runname).'/latest';
    
    # useful symlink (fails on windoze)
    symlink $cachedir,$tmp.'/'.basename($runname).'/latest' if($have_symlinks);
 
    # clear global caches
    perhaps_fork(\&clear_up_all_caches);

    parse_args_by_eval(); # parse args up to this point
    $nsphere=40;
    $internals_drawtype=0;	
    
    $use_block_surface=0;
    
    {
	my $x=`gnuplot_surface2.pl 2>&1`;
	if(defined($x) && $x=~/Cannot open input file/o)
	{
	    $use_block_surface=1;
	    print "Use block surface script\n" if($vb);
	}
    }

    set_evcode_options_list();

    $window_image_cache="$cachedir/window_image_cache";
    mkdir $window_image_cache;
    perhaps_fork(\&empty_image_cache);
        
    $shrd=1;
    @HRD_spacings=('Auto',1e-6,1e-5,1e-4,0.001,0.0025,0.005,0.0075,0.01,0.025,0.05,0.075,0.1,0.15,0.2,0.25,0.3,0.4,0.5,0.75,1.0,2.0,3.0,4.0,5.0,7.5,10.0);
    $RhoT_label_col=2;
	
    $RhoT_label_distance=0.1;
    @RhoT_spacings=('Auto',0,1e-6,1e-5,1e-4,0.001,0.0025,0.005,0.0075,0.01,0.025,0.05,0.075,0.1,0.15,0.2,0.25,0.3,0.4,0.5,0.75,1.0,2.0,3.0,4.0,5.0,7.5,10.0);

    %gnuplot_options=
	('font face'=>'Helvetica',
	 'font size'=>18,
	 'font colour'=>'#000000',
	 'background colour'=>'#FFFFFF',
	 'terminal type'=>'postscript',
	 'axis colour'=>'#000000',
	 'axis width'=>1,
	 'line width'=>2,
	 'dash length'=>1.0,
	 'solid'=>'Use Solid Lines',
	 'grid'=>'No Grid',
	 'Plot Oversize'=>2.0,
	 'plot title'=>'',
	 'key location'=>'Default',
	 'invert image'=>'Black on White',
	 'key regexp'=>'',#'s!^\S+\/!!',

	 'HRD start label format'=>'{/Bold*2 %g  }',
	 'HRD start label position'=>'right front',
	 'HRD start col'=>-1,
	 'HRD start col text'=>'None',
	 'HRD label col'=>2,
	 'HRD label spacing'=>'Auto',
	 'HRD label spacing index'=>'0',
	 'HRD range'=>['*','*','*','*','*','*'],
	 'HRD colours'=>1,
	 'HRD star'=>1,
	 'HRD radii'=>0,
	 'HRD status'=>0,
	 'HRD cbox'=>0,
	 'HRD palette'=>0,
	 'HRD brightness'=>80,
	 'HRD contrast'=>100,
	 'HRD hue'=>100,
	 'HRD sHRD'=>0,
 
	 'number of linetypes'=>15,
	 'auto labelling x factor'=>0.6,
	 'auto labelling y factor'=>1.0,
	 'auto labelling hypotenuse factor'=>0.6,
	 'auto labelling font factor'=>1.0,
	 
	);
    my @linecolours=('#FF0000',# 0
		     '#0000FF',
		     '#444444',
		     '#00AA00',
		     '#00FFFF',
		     '#888800',# 5
		     '#aaaadd',
		     '#ffaa00',
		     '#888888',
		     '#006600',
		     '#880088',# 10
		     '#000088',
		     '#880000',
		     '#FF00FF',
		     '#004444',
		     '#440044',#15
	);

    if($persist_gnuplot)
    {
	use IPC::Open3;

	# use sterr?
	#use Symbol 'gensym'; 
	#$persist_gnuplot{err} = gensym;
	
	$persist_gnuplot{pid} =
	    open3($persist_gnuplot{in},
		  $persist_gnuplot{out},
		  0, # stderr is the same as stdout
	    $external_programs{gnuplot});
	$persist_gnuplot{in}->autoflush;
	$persist_gnuplot{out}->autoflush;
	$persist_gnuplot{err}=$persist_gnuplot{out};
    }
 
    foreach (0..$gnuplot_options{'number of linetypes'})
    {
	$linecolours[$_]=~s/([a-z])/\U$1/g;
	$gnuplot_options{'line colour '.$_}=$linecolours[$_];
	$gnuplot_options{'line width '.$_}=2.0;
	$gnuplot_options{'line type '.$_}=$_+1;
	$gnuplot_options{'point type '.$_}=$_+1;
	$gnuplot_options{'point size '.$_}=1;
	$gnuplot_options{'with '.$_}='lines';
    }

    %gnuplot_options_default=%gnuplot_options;

    $disk_cache_count=0;
    $image_timeout=30; # cache timeout in seconds
    $zippid=-1;
    @misc_opts_values=('200',
		       1280,960, # default image size
		       '19',0,0,15,
		       $evcoderoot,
		       80,100,100, # HRD brightness/contrast/hue
		       # 11,12
		       'Undefined','Undefined', # models to load
		       # 13,14
		       '.','.', # file selector paths for model loading
		       # 15
		       '.', # image save file path
		       # 16
		       '.', # load/save options path
		       # 17
		       '.', # save single model path
		       # 18
		       '.',
		       #  19 : ZAMS/from saved model selection
		       0,
		       # 20,21 : Window Size
		       1100,650,
		       # 22 : Kippenhahn cache toggle
		       1,
		       # 23 : structure plot resolution
		       100,
		       # 24 : default font
		       0,
		       # alternative colour for pushed buttons 
		       1
		       ); # the values in the options widgets

    $running_nice=get_opt(3); # nice value of the current run
    update_nice_command();
    $internalsdir="$cachedir/internals";
    mkdir $internalsdir;
    
    load_misc_opts(); # load in previously used values (if they exist)
    
    # set evcoderoot into misc opts IF specified
    $misc_opts_values[7]=$evcoderoot if(defined($evcoderoot));
    
    $max_anim_count=10;
    $anim_frame_update=0;
    $ps_shape=' portrait ';
    
    $follow_evolve_log=0;
    $follow_log=0;
    $log_timestamps=1; # if 1, timestamp logs (handy!)

    my $main_vbox;
    ($main_window,$main_vbox) = main_window(); 
    $mouse_pointer=0;
    $kippenhahn_state=0;
    $use_kippenhahn_cache=0;

    # top tab bar labels
    @tab_labels=('Options','Evolve','Log','HRD');
    push(@tab_labels,'RhoT')if($rhot);
    push(@tab_labels,'Histogram')if($histograms);
    push(@tab_labels,'System')if($systemtab);
    push(@tab_labels,'Structure','Internals','Kippenhahn','Misc','Load_Save','About');

    # make tab keys (for CTRL-<key> access)
    @tab_keys=@tab_labels;
    map{ s/^(.).*/\L$1/ }@tab_keys;

    {
	my $t=time();
	foreach my $tab (@tab_labels)
	{
	    $next_update_time{$tab}=$t;
	}
    }
    $next_update_delay=1; # in seconds
    
    # switch between stars
    @starswitches = ('Star 1','Star 2','Stars 1 and 2');

    # reverse hash
    %tab_numbers = map{ state $c=0; $_=>$c++; } @tab_labels;

    @tab_tooltips=('Options for the STARS/TWIN code. Here you directly set these options, and they are passed to the stellar evolution code.',
		   'The evolution tab. This contains the controls for starting and stopping the evolution, as well as the log file output.',
		   'HRD is the theoretical Hertzsprung-Russell diagram for the current or previous model run.',
		   'Structure allows you to make XY plots e.g. mass vs time.',
		   'Internals allows you to plot internal information about the star, in a still or animated image.',
		   'In this tab you can make "3D" Kippenhahn-style diagrams e.g. T vs M and age',
		   'Misc contains options for the graphical user interface, e.g. plot size and quality.',
		   'Load and save model sets from here',
		   'About gives you information about this program.');
    
    $tab_bar=Gtk2::Table->new(1,$#tab_labels,FALSE);
    $main_vbox->pack_start($tab_bar,FALSE,FALSE,0);

    $_ = Gtk2::EventBox->new foreach ($zams_z_eventbox,$zams_m_eventbox,
				      $zams_m1_eventbox,$zams_m2_eventbox);
    $tooltips=Gtk2::Tooltips->new; # ALL the tooltips widgets
    $nstar=1;
    $running_the_code=0; # 1 if the code is running NOW
    $just_run_the_code=0; # 1 if just run and evcode colours require update
    $have_run_the_code=0; # 1 if the code has run in the past prior to 
    # running WTTS, or is running NOW
    $code_has_been_run=0; # 1 if the code is run INSIDE WTTS in THIS INSTANCE
 
    # check for previous code run
    check_for_previous_run();
    
    # pm3d palettes for gnuplot (Kippenhahn plots)
    %pm3d_palette=('Black-blue-red-yellow'=>'7,5,15',
		   'Yellow-red-blue-black'=>'7,5,15 negative',
		   'Green-red-violet'=>'3,11,6',
		   'Violet-red-green'=>'3,11,6 negative',
		   'Green-blue-white'=>'23,28,3',
		   'White-blue-green'=>'23,28,3 negative',
		   'Black-red-yellow-white'=>'21,22,23',
		   'White-yellow-red-black'=>'21,22,23 negative',
		   'Black-blue-pink-yellow-white'=>'30,31,32',
		   'White-yellow-pink-blue-black'=>'30,31,32 negative',
		   'Blue-green-yellow-red'=>'33,13,10',
		   'Red-yellow-green-blue'=>'33,13,10 negative',
		   'Black-red-green-cyan'=>'32,35,36',
		   'Cyan-green-red-black'=>'32,35,36 negative',
		   'Cyan-white'=>'3,2,2',
		   'White-cyan'=>'3,2,2 negative',
		   'Grey'=>'30,31,32',
		   'Grey Reverse'=>'30,31,32 negative',
		   );

    if($use_cubehelix)
    {
	# cubehelix palette and options
	foreach my $start (0.5)
	{
	    foreach my $cycles (-1.0,1.0)
	    {
		foreach my $saturation (1.0,2.0,10.0)
		{
		    foreach my $gamma (1.0,1.5,2.0)
		    {
			my $s="start $start cycles $cycles saturation $saturation gamma $gamma";
			$pm3d_palette{'cubehelix '.$s}=
			    "cubehelix $s\nset palette gamma $gamma\n";
		    }
		}
	    }
	}
    }

    @pm3d_palettes=sort keys %pm3d_palette;
    
    # set column labels
    set_col_labels();

    $zams_m_count=0;
    $current_tab=$tab_numbers{Options}; # currently showing tab
    load_evcode_defaults();
    
    $histograms=0; # do you want the experimental histograms?
    $systemtab=0; # no system tab by default

    {
	# move to evcode defaults settings eventually
	if($evcode_module eq 'STARS')
	{
	    # TWIN = minus sign means logged
	    $modinfo{teffcol}=-$plt1cols{'log Teff'};
	    $modinfo{lumcol}=-$plt1cols{'log Luminosity'};
	}
	else
	{
	    $modinfo{teffcol} = $plt1cols{Teff};
	    $modinfo{lumcol} = $plt1cols{Luminosity};
	}
    }

    $i_am_a_child=0;
    %variable_help=setup_variable_help();
    $currently_showing_noimage=0;
    $internals_options_state=0;
    $anim_speed=1;
    $anim_count=0;
    $current_internals_tab=0;
    $structurestar=1;
    $webserver=0;
    $prevtstat=0;
    $web_status=' ';

    # set up the tab bar with labels and colour them appropriately
    make_tab_bar($main_vbox);

    gnuplot_overrides();
    
    # add status bar (more global widgets...)
    $n_status_bar_lines=4;
    ($status_bar_frame,@status_bar)=add_status_bar_widgets();
    $main_vbox->pack_end($status_bar_frame,FALSE,TRUE,15);
    set_status_bar('Status: waiting for you');

    # GTK style overrides : here you can change
    # whatever you like, yay! :)
    # see http://ubuntuforums.org/showthread.php?t=377397
    %gtkrc=(slider_width=>15,
	    button_off_bg=>'#DDDDDD',
	    button_on_bg=>'#DDCCEE',
	);

    Gtk2::Rc->parse_string(<<"GTKRC");

    style 'wide-scrollbar-style'
    {
        GtkScrolledWindow ::scrollbar-spacing = 0
        GtkScrolledWindow ::scrollbar-within-bevel = 0 
	GtkScrollbar::slider_width = $gtkrc{slider_width}
    }
    widget_class '*Scrollbar' style 'wide-scrollbar-style'
    widget_class 'ScrolledWindow' style 'wide-scrollbar-style'
    

    style 'redback'
    {
	bg[ACTIVE] = "$gtkrc{button_on_bg}"
	bg[NORMAL] = "$gtkrc{button_off_bg}"
    }
    widget_class '*Button' style 'redback'

GTKRC
    ;

}

sub show_tabs_first_time
{
    # update tabs for the first time
    my $time0 = [gettimeofday()] if($timings);

    # show the main vbox, the Options tab (#0) by default, and show
    # the main window, but do NOT show_all as that would show all the tabs!
    if($vb)
    {
	print "Show tabs first time\nShow options tab\n";
    }

    show_Options_tab();
    $main_window->show;
    
    print "Show misc tab\n"if($vb);
    show_Misc_tab();
    print "Show internals tab\n"if($vb);
    show_Internals_tab();
    update_Internals_tab(0);
    show_Structure_tab();
    show_Evolve_tab();
    show_HRD_tab();
    $main_window->show_all;

  

    $main_window->show_all;
    increment_timing('show_tabs_first_time',$time0)if($timings);
    
}

sub tailoffile
{
    # get last line of a file
    # get last line of a file
    my $bw = File::ReadBackwards->new($_[0]);
    my $l = $bw->readline if($bw);
    if(defined $l)
    {
	# remove whitespace
	$l=~s/^\s+//o; 
	chomp $l; 
    }
    return defined $l ? $l : '';  
}

sub lsmenu_selected
{
    my $w=$_[0];
    my $t=$_[1];
    print "Load/Save $t\n" if($vb);
    # Load/Save menu activated: show file selector
    
    if(defined($file_selector))
    {
	# we can only have one file selector at a time (says Rob
	# who is easily confused)
	$file_selector->destroy;
    }
    $file_selector=Gtk2::FileSelection->new($t);
    $file_selector->set_filename($misc_opts_values[16].'/');
    $file_selector->cancel_button->signal_connect(clicked=>\&file_selector_cancel);
    
    # choose function
    if($t eq 'Load WTTS Options')
    {
	$file_selector->ok_button->signal_connect(clicked=>sub{load_WTTS_options()});
    }
    elsif($t eq 'Save WTTS Options')
    {
	$file_selector->ok_button->signal_connect(clicked=>sub{save_WTTS_options()});
    }
    $file_selector->show;
}

sub sizeoffile
{
    # sizeof for a file without warnings
    no warnings;
    return 1*(-s $_[0]);
    use warnings;
}

sub load_WTTS_options
{
    # load a WTTS options file
    my $file=$file_selector->get_filename;
    
    # save path
    $misc_opts_values[16]=dirname($file);

    $file_selector->destroy;
    print "Load WTTS file $file\n"if($vb);
    
    # the real work is done by load_evcode_options, which 
    # is (of course) different for each evolution code
    if($useperlzip==1)
    {
	my $z=Archive::Zip->new($file);
	my @m=$z->memberNames();
	map
	{
	    $z->extractMemberWithoutPaths($_);
	    $_='inflating : '.$_."\n"; # simulate zip
	}@m;
	load_evcode_options_from_zipfile(@m);
    }
    else
    {
	my $unzip=$external_programs{unzip};
	load_evcode_options_from_zipfile(split(/\n/o,`$unzip -d $cachedir -o $file`));
    }

    # now save and update the GUI
    save_evcode_options();
    reset_options_tab();
}

sub save_WTTS_options
{
    # save a WTTS options file
    my $file=$file_selector->get_filename;
    $file_selector->destroy;

    # save path
    $misc_opts_values[16]=dirname($file);

    print "Save WTTS file $file\n"if($vb);

    save_evcode_options();
    save_evcode_options_to_zipfile($file);
}

sub starbuttons
{
    # Return a widget with a pair of buttons for selecting 
    # either star 1 or star 2
    my $box;
    my $align=$_[0]; # alignment (v or h)

    $box = $align eq 'v' ? Gtk2::VBox->new : Gtk2::HBox->new;
    my $n=$#starbuttons+1; # next button in the @starbuttons array

    # Star choice: 1 or 2
    print "Add starbuttons $n\n"if($vb);

    $starbuttons[$n]=Gtk2::RadioButton->new_with_label_and_tooltip(undef,'1','Click here to plot data from star 1 in the structure, internals and Kippenhahn tabs. (Or press CTRL-1)');
    $starbuttons[$n+1]=Gtk2::RadioButton->new_with_label_and_tooltip($starbuttons[$n]->get_group,'2','Click here to plot data from star 2 in the structure, internals and Kippenhahn tabs. (Or press CTRL-2)');
    $starbuttons[$n]->set_active(TRUE);
    $starbuttons[$n]->signal_connect(clicked=>\&select_nstar,1);
    $starbuttons[$n+1]->signal_connect(clicked=>\&select_nstar,2);

    map
    {
	$box->pack_start($_,FALSE,FALSE,0);
    }(Gtk2::Label->new_from_markup('<span foreground="DarkRed" size="x-large"><b>Star:</b></span>'),
      $starbuttons[$n],$starbuttons[$n+1]);

          
    return($box);
}

sub model_is_available
{
    # return 1 if a model $n is available, 0 otherwise
    my $n=$_[0];
    return (defined($n)&&defined($models_available{$n})&&($models_available{$n}==1)) ? 1 : 0;
}

sub autosize
{
    # from a widget, determine the automatic image size which fills it
    # (and allow for borders)
    my $widget=$_[0];
    $widget->check_resize;
    my $r=$widget->allocation;
    #print 'autosize window is ',$r->width,' ',$r->height,"\n";
    my $x = MAX(1,(0.95*$r->width)/$aspect_ratio);
    my $y = MAX(1,(0.85*$r->height)); 
    #print "X=$x ; Y=$y\n";
    return($x,$y);
}



sub nodatainrange_png
{
    my $s='9805e474d0a0a1a0000000d094844425000000be000000818060000000b1c0a026000000103725742400eaecc19e0000006026b4744400ff00ff00ff0adb7a3900000090078495370000b0310000b0311000a9c981000000704794d45470bdc0d14192d29c81ced3000000914754854734f6d6d656e64700342756164756460277964786027494d4057518e071000050ad9444144587addec9b68855554108fb57d19625b7909264562adc4eb19e16462d38af1568924a4819980515905911118249249415a126356a11dcf80b4359a0caf1a6a309f720d252d2c262b22d4bccc7c0e385df89571e4bd37ee97cdd7fa3dcce50b9b7fcedbeea7ee576fadb6fe911555abb3888cf74945551a7804f46ddbbb1494444d967a444685527e74b503160bb408a350b6c4f5300b0289620408e26b020017ba08203e4557747784baae1237847330c9b8595bc14cf2503f40800d5b756d3304f2bfe31555bda8ea0222230087a081d043107500fb00fd30fd20b44557fb7855dee306a307d20dfd86f670e5355d6f439954460307b207b003208100c040e420bfc46ed404b8aae1da46f8a4015eb5d0f4fdb2e7c1e888c040e953b9e810a308dd0cb7aaadae35e11193f18950cd10cd60c525693d78d9c7be72223518bf18bea8cc7b80d9e70cc13a375303418f318d30ce2061baa6bbb92f64bdac8c7f635552adc5c777cd07a40c3d0c1e81af1d6f7b1e592e8e4a530a1185e518ef160664a9cc922364bd1406a41cfdaaf2e993d6495cf5e2fc9e6c73946a55eb4e1b9b2b6294e4f1672f5af107e10d25dec733a5f010fb5a8e47c187108682f9ad8b8079307222fc39a083ff99c3398189b50d1fb4372f9a6385518fb08bcae81ca761fca340b669635d350be29a46b2f1672f5af529ca506ebdcac943616c60ba57e2a84fd67042ba3f1866728d51877081504f5bfc5113f6096ac9e4f150c9e41e3b233a1f6f00faa5a163cd25b9e369e1d43067a343695190a0fdc3b6c1cab6081704f30e640eb176c7387a06550eb40e620e243e9b6b69eb922af10321835e0e4bad61fafad76b668794f550c198c8f910e543dfafb1f97418f932837a1890941ca71307822df332740babbadd6a407ad6570f696e47af2f28c74b0c40b13e0d8d25d60baf0e953c06dde138331d1c9dbe9285fbe27a975b2b39f0df087bd91ffa214e9d6921cafab33efc520d99ce0eda82524124e030f274ab618884fec8534815ec3f729087e6ff8cc95ba819509f4251d0ea711961119d6222f3988c1b8c50707f80ba9c3d17f4dd9766485b13d7eac338d31fa75455f47d9de4544f377ebdab40adbe2587f817e9715cc5c42506db387369a0b758647692e7c23eaca70c1c96a4873743b3877027eb15f0614e3eeb9e408f0c7feb735467f2c3b68b2be8d81c968a58c30c0da5bd9c78e7133fda93146eb6258ecef28b78e315aa07cc0d10ccf847dc7b21b777387edac0fddd9766688bbc0396baf5510bfaed672f4af51db25a54f2bc43692501a5dec09006010f46602abbc97eb7210fee57e9776e41e7f464e339358e8b3eb918bb0812674a4265040f907e287675d713c9cff98d96776c9f635258e8b3fcf675559c2d2b4a5b39c92d8775d203d6c40cbd292969cf3ec8f88c25a6f81c9e0707af7509ddb0fca55a1c5d0d2f12f4d0c7355be713506ad850b0c4f6833ed693c6799c99b937cef0a2d1dd4f1bd8a7cd1dd437e47af8e893ab97bce69b499fc2fc05a477b46ff1207300711077bd12877460db7fa3833f08b431467f2c3b7040baf2fd8f806d11137473d2199f63a187f33cc7b660e89383ba189d0c596749434081f0c3c69dd6b7e906d1f97a8e157cb411bcaad34a6061a7a38ee5f5f6e9d30285d79f6cb8e70c26f42fc3154864e154657e806d2103733e573c9b55c573ce30cbb927dfbe18490c9ed179a274ad110e1cc0e089093285db0fce101caebc73eb285b1d2b7b2c3fd2255ee930cf15ba06d6a894302dc8d308ecfbed9f49ecbcbf7c18f108fcce694d0ca6f2f081f9960c740cf86bfaeda6b77e1c91d90db08740863330e123a1d20c0bc139ef660eb0ba07e99a70fceec1caebce4eb5f306aa5a4e76dc73bb1a499e2f885a3f3f08f8d25ef3a6f20adb6b7fd510ccc2fc369e90ff6d520408a71888cc3b4fdbc0ba5557a9f0add01ccb10204ea06cfec652cfc18670e770e20b5157620f8b3f39d0ed877859530408c51ca97206659aaa7bb4d15a06ef0010cb3c94b2c7ec6f94434a1c10204e381e476189ee4a3b0a4388ec3bedd76d6700f6aaaefaeb91ffb298e963178b20e2a000000009454e444ea240628';
    return join('',(pack('h*',$s)));    
}

sub wrongimage_png
{
    # the 'wrong image' png in hex
    my $s='9805e474d0a0a1a0000000d094844425000020080000100e8060000000531dcd4e00000090078495370000b0310000b0311000a9c9810000a07a9444144587addeddb78a5655d1700ffe5766c6223c0cc46c9a17a120d0c48e14f0a704d481cc0185036f0592228afb2a72db8ef8ef82a79a86f05292b0a8c8284c664a1d4b49662a2a474b0c1b44d86a9664562b4d2d97cdef85fb3cdd37a37fcdd7eec9b5333e7e30783376dddb6fe5b7dad766ffeeadb6dab90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1bde5f94ec35d000003edb829c5394ef2946f72930946f429f5429b439c987c0d9bcd594667a8bd17c6b9da6b608429c275500942975629b939c1c42fdf42777d7f36d429795297f729f452972e83b3febb29cfb53f3000f4647d5a5f29caf97e7ebee82ef589e7294efe3220b3d3b7395d9d1fc4f00200c80776dd42f927c069d7450b7200c9cbec40080c396c90aa002942f7ea7f77e4097e9f946b4297cae5b939c953f40a1bc39c5e94e1942f8629ba39cac427a529b53de1d3fd394ed0349ff5194e6cac3f8729f56ad40f8187039c99d9b9cff9682ffc429f04d9fdef42f3f42ba6c402438fcb05eb4293b39cf829cebac3387d5b36aece7394675def70e3859fe2b1ad6f6d4b7c2fd7be2af69f71aa3b32d2be0ac0b639cfea6f3b89b6669aad000082fa94b94f13b5735e3f29c357cc6ffcea042e1842f1ea7d30596ba74cdccf2942b339c5c99bed6cf25dd4df7c94eaa427829cedccd3a6e7d6dd8ff7594e2a4271429f57593f5a5bdc6a42bbb6f77166e66efe0e8bf9f42f0529fa729d6596755f800071ac7f2842ffc4277429d094e5c94ed2d328f8bca31c1d0cf4b2dee8e4ad9457eef1f90ae2af69f7eba3b9659570568da57cef4f22fa99966b18e10400897c9b94eebe4f2ac3294ebb96d3f43ceb25bdc6ce4ad6ca4bbc74cdccf6ae40497647e81f58ec6b7d65adabaedf9d1963e4de4eec63ba2da7b0f7036288be6adec427a75adaea4bd1d3200c582fd6da085f9bd94b59d3200c3fb6b9b0ae3f3d3d626dcdd59e7a65af68afcf29546d5c4a7ed3da2b677fd7d529567407dcc43bd0400800036ca84be11afe55feac0205bf64dfc60ee94b7487dd9812bc39cfba20481eb99f3ba39633596bb786f9f5da4f3b2d66f1fc6ad3e2c76c0dbeed4b5aa6af400876772de4811180cc7100e8bc772550e57bd0d13bf400838046eb65f9fd45bfad459eb9a2df294bedfa56b8cab894fcb7a556deeeb78f17de4a7dcc43bd040080c16c810483cdefab97ebd2d6c7edbb39cfd29cbb29c78a3bdda9a00ee0472de045adad11bfddb34733e42978be873c9e94be52c4a5fe2deb1a7dad427a4fc3f9fd77efd78628a78179f6d4dffd1bb6115fbbb29cdf96bceec0ecf6b7ad367f7c39cbea2d7d7a5fa9e1c546d5c26fcbf84bcad5bb744dbd427dcc255b1008570016c87c2da7efe6b42fbd4be10afcd20145ccc41301d4ad2752f9911f3f347414dfca8546ebd65106f9c5145d793da71c6b5ad85fb3d29c9396d32afffeab894bca3ec25c533b2c7d4101002c2d8b3bed75672deef4b14dfbc337d3a3726ad89dabf6a07ccd95103c9e96f7984af81c3a5e5a638e9da7ed05afbcb76efde94ede94edb57e7b9b2df6aa00fe2cafc73e114d546a4576396957c996e533396b1cb13d66f430421f808160ed79639f264937bedbbbc97c694b94d1dd5684636dd0d7be41ac3bf2d6670f9394edab0c6bf4c88005792deba0a87d5297555adae42fdee99f7b5dbf743d69839f34d7e1c23a2f1b4b1f277fe22ae2afaeb576396957c996e533396b1c6e42f33f5370083cd060edfde94eac42f9cab9ef733de19fdedc1e3bd27555ade94b1b18f194b9800f05a5f2fc0fe778d8a149f38952a38571eec842f7d4b1b56fd94be58a7f4ad2f1373472fd5d59fe2e42fae90f8b3fd4209582fd54596f06d1f7f45917639cf0718abe5694ef15bd67f1d9e2f4b17193b94ea81ac3d7be2af69f745d9d4baca3f5916ad7dcc42d607b7cad08000ff56d729f2629b5237305ff06ad36bbb239c37644e933ba765e1d4b1f28b52d61afde31c04f900070143c519633d576be83b32373611351c3c6fa2fe5aff3a000762d6ddbb78be8b7d429766d67fdae15fd734d6bb1682d70f729fedc3f4005b05d54fd2ff8aa3b965957cdc03e96e533394b18950c000c4dcc4ad32cc4a5f09ec664f44d04b10074113600183213b99b1f477e7dbf635d2ad000083ebcad4b1366fe842f2c4bf3146f98a024e61fb85a630000e8f37a629be3d6112e3896be4fdf139c7639c3d45f863000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e2cf70ba52f8bdbdace963000000009454e444ea240628';
    return join('',(pack('h*',$s)));
}



sub ppe_image
{
    if(in_image_cache('ppe')==0)
    { 
	$misc_opts_values[1]=234;
	$misc_opts_values[2]=338;
	set_image_in_cache('ppe',get_image_from_string(ppe_image_hex()));
    }
    return(get_image_from_cache('ppe'));
}


sub model_select_callback
{
    # putting this here is a complete fudge, we should use a callback
    $misc_opts_values[19] = $start_from_zams_radiobutton->get_active==TRUE ? 0 : 1;
}

sub emergency_restart
{
    # we must restart window to the stars
    print "Emergency restart!\n"if($vb);
    $running_the_code=1;
    stop_wrapper();
    print "Restart $^X $0 @ARGV\n"if($vb);
    exec($^X,$0,@ARGV);
    exit(0);
}

sub update_nice_command
{
    $nice_command = $os ne 'windows' ? 'nice -n '.MAX(1,MIN(19,$running_nice)) : ' ';
}

sub check_out_environment
{
    # using environment variables, set some global variables
        
    # determine operating system ($os)
    if((defined($ENV{OS})&&($ENV{OS}=~/windows/io))||
       (defined($ENV{WINDIR})&&($ENV{WINDIR}=~/windows/io)))
    {
	$os='windows';
	print "OS = windows\n"if($vb);

	# fudge for using older versions of GTK
	push @Gtk2::Object::ISA, 'Glib::Object';
	
	$slash="\\";
    
	eval 'use Win32::Process;'; #better than fork!
    
	# hard to know what to use for /tmp in windows: call it ./temp
	# The reason is that often C:\windows\temp is not writable
	mkdir './temp';
    }
    else
    {
	print "Os = unix\n"if($vb);
	$os='unix';
	$slash='/';	
    }
    print "Set os=$os\n"if($vb);

  
    # choose location for /tmp ($tmp)
    my @tmps=('./temp',
	      '/tmp',
	      '/var/tmp',
	      'C:\WINDOWS\TEMP' # probably not writable
	      );
    foreach my $t (@tmps)
    {
	if(defined($t)&&(-d $t)&&(-e $t)&&(-r $t)&&(-w $t)
	   # avoid a directory mounted in RAM
	   &&(($os ne 'unix') || (`df $t`!~/tmpfs/))
	    )
	{
	    $tmp=$t;
	    print "Set tmp to $t\n"if($vb);
	    last;
	}
    }

    if(!defined($tmp))
    {
	print 'Could not locate a temporary directory out of ',join(',',@tmps),"\nPlease make one and make sure it is not mounted as tmpfs (i.e. not in RAM, because WTTS has a lot of output which will *fill* your tmpfs)\n";
	exit(-10);
    }
    
    # see if we have symlinks
    $have_symlinks = eval { symlink('',''); 1 };
    print "have_symlinks=$have_symlinks\n"if($vb);

    # see where $home should be
    if(defined($ENV{HOME}))
    {
	$home=$ENV{HOME};
    }
    elsif(defined($ENV{HOMEPATH})&&defined($ENV{HOMEDRIVE}))
    {
	# windoze
	$home=$ENV{HOMEDRIVE}.$ENV{HOMEPATH};
    }
    else
    {
	$home='.'; # where else to use?!
    }
    print "home=$home\n"if($vb);

    # current directory
    if($os eq 'windows')
    {
	$pwd = `cd`;
    }
    else
    {
	$pwd = $ENV{PWD} // `pwd`;
	chomp $pwd;
    }
    $pwd='.' if(!defined($pwd)); # er... what else to do?!

    $optsdir=$pwd; # default saving options in $pwd
}

sub format_filename
{
    # convert filename to win32 if we need to 
    my $f=$_[0];
    if($os eq 'windows')
    {
	$f=~s!/!\\!go ;
	# do we need this? seems not
	#$f=~s!([A-Z])\:\\([^\\])!$1\:\\\\$2!go;
    }
    return $f;
}

sub format_file
{
    # like format_filename but for a whole file
    dumpfile($_[0],format_filename(slurp($_[0])));
}

sub reformat_pltfile
{
    # like format_filename but for a whole file
    my $f=$_[0];
    my $s=slurp($f);
    format_filename($s);
    $s=~s/\\/\\\\/go;
    dumpfile($f,$s);
}

sub add_to_plt1
{
    my $l=$_[0];
    my $prevmodel=$_[1];

    # remove whitespace
    chomp $l;
    $l=~s/^\s+//o;

    # limiting the split here really speeds things up
    my @s=split(/\s+/o,$l,4);

    if($#s>2)
    {
	# check that the model number increases
	$prevmodel=$s[0] if(!defined($prevmodel));
	if($s[0]>=$prevmodel)
	{
	    # push data onto the plt1data array
	    push(@plt1data,$l);
	    print "push \"$l\"\n"if($vb>=2);
	}
    }
    else
    {
	# this should be the first element, just set it
	# (it's some number: really we don't want to set
	# this, but some other functions seem to rely on
	# ignoring it, so if it's not there everything 
	# goes wrong!)
	push(@plt1data,$l);
    }
    return $prevmodel;
}

sub determine_zip_versions
{
    # find version numbers for zip and unzip
    my $zipv;
    my $unzipv;
    my $r=`zip -v -h 2>&1 ; unzip -v 2>&1`;
    $zipv=$1 if($r=~/Zip (\d+\.\d+)/);
    $unzipv=$1 if($r=~/UnZip (\d\.\d+)/);
    print "ZIPV $zipv UNZIPV $unzipv\n"if($vb);
    return($zipv,$unzipv);
}

sub gnuplot_options_string
{
    map
    {
	if(!(defined $gnuplot_options{$_}))
	{
	    print "Warning : gnuplot_option '$_' is not defined\n"; 
	}
    }sort keys %gnuplot_options;

    return join(' ',sort %gnuplot_options);
}

sub gnuplot_options
{
    # make/draw gnuplot options window
 
    my $redraw;
    if(!defined($gnuplot_options_window))
    {
	$gnuplot_options_window=Gtk2::Window->new();
	$gnuplot_options_window->set_name(''); # empty unless redraw required
	$gnuplot_options_window->set_title('WTTS Gnuplot Options');
	$gnuplot_options_window->signal_connect (delete_event =>sub{
	    $gnuplot_options_window->destroy;
	    $gnuplot_options_window=undef;
						 });
	$gnuplot_options_window->signal_connect(key_press_event => \&main::hide_on_escape);
	$gnuplot_options_window->set_size_request(800,600);
	$redraw=1;
    }
    else
    {
	$redraw = $gnuplot_options_window->get_name() eq 'redraw';
	($gnuplot_options_window->get_children)[0]->destroy if($redraw);

    }
    
    if($redraw)
    {
	my $sw=Gtk2::ScrolledWindow->new(undef,undef);
	$sw->set_shadow_type('etched-out');
	$sw->set_policy('automatic','automatic');
	$sw->set_border_width(5);
	$gnuplot_options_window->add($sw);

	my $v=Gtk2::VBox->new();
 	my @packopts=(FALSE,FALSE,0);

	# gnuplot options window

	{ 
	    # use PNG or Postscript
	    $v->pack_start(gnuplot_radiogroup('terminal type',
					      'Use Postscript Images', 
					      'Use PNG Images'),@packopts);
	    
	    # for PS : invert fore/background?
	    $v->pack_start(gnuplot_radiogroup('invert image',
					      'Black on White (PS only)',
					      'Light on Black (PS only)'),@packopts);
	    
	    $v->pack_start(gnuplot_slider('Plot Oversize','Plot Oversize',1.0,5.0,10.0),@packopts);
	}
	
	{
	    $v->pack_start(gnuplot_text_box('Plot Title','plot title'),@packopts);
	    
	    # key/legend
	    my @x;
	    foreach my $inout ('inside','outside')
	    {
		foreach my $lr ('left','right','center')
		{
		    foreach my $tb ('top','bottom','center')
		    {
			foreach my $vh ('vertical','horizontal')
			{
			    foreach my $box ('nobox','box')
			    {
				push(@x,join(' ',$inout,$lr,$tb,$vh,$box));
			    }
			}
		    }
		}
	    }
	    $v->pack_start(gnuplot_dropdown('Legend Location','key location',['Default','None',@x]),
			   @packopts);	    
	}

	{ # Fonts
	    # Font face selector
	    $v->pack_start(dropdown_font('Font Face','font face',\@gnuplot_fonts),
			   @packopts);	    
	    
	    # Font size slider
	    $v->pack_start(gnuplot_slider('Font Size','font size',5,100,200),@packopts);

	    # Font colour
	    $v->pack_start(colour_selector('Font Colour','font colour'),@packopts);
	} 

	{ # Background colour, axis colour, width
	    $v->pack_start(colour_selector('Background Colour (PNG only)','background colour'),@packopts);
	    $v->pack_start(colour_selector('Axis Colour','axis colour'),@packopts);
	    $v->pack_start(gnuplot_slider('Axis Width','axis width',0.5,50.0,100),@packopts);
	    $v->pack_start(gnuplot_slider('Global Line Width','line width',0.5,50.0,100),@packopts);
	}
	
	{ # Line widths, colours and types
	    
	    $v->pack_start(gnuplot_radiogroup('solid',
					      'Solid Lines', 
					      'Solid and Dashed Lines (PS only)'),@packopts);

	    $v->pack_start(gnuplot_radiogroup('grid',
					      'No Grid', 
					      'Grid'),@packopts);

	    $v->pack_start(gnuplot_slider('Dash length',
					  'dash length',
					  1.0,50.0,100.0,
			   ),
			   @packopts);
	    
	    my $t=Gtk2::Table->new(16,8,FALSE);
	    my @topts=('shrink','shrink',10,10);

	    # header
	    {
		my $i=0;
		foreach ('Colour','Curve','Width','Dashes','Point','Point Size')
		{
		    $t->attach(h($_),$i++,$i,0,1,@topts);
		}
	    }

	    # per line
	    for(my $i=0;$i<16;$i++)
	    { 
		draw_lineoptions($i,$t);
	    }
	    $v->pack_start($t,@packopts);
	}

	{
	    # regexps and formats
	    $v->pack_start(gnuplot_text_box('Key Regexp','key regexp'),@packopts);
	    $v->pack_start(gnuplot_text_box('HRD Start Label Format','HRD start label format'),@packopts);

	    foreach ('x','y','hypotenuse','font')
	    {
		$v->pack_start(gnuplot_text_box('Auto labelling '.$_.' factor',
						'auto labelling '.$_.' factor'),
			       @packopts);
	    }

	    $v->pack_start(gnuplot_text_box('Start label position',
					    'HRD start label position'),
			   @packopts);
	}

	# emergency reset all button
	$v->add(reset_all_gnuplot_options_button());
	
	$sw->add_with_viewport($v);
    }

# always show and present
    $gnuplot_options_window->show_all();
    $gnuplot_options_window->present();
}

sub h
{
    # return a boldly marked up label based on text (passed in as first argument)
    return Gtk2::Label->new_from_markup('<b>'.$_[0].'</b>');
}

sub gnuplot_text_box
{
    # labelled text box
    my $h=Gtk2::HBox->new;
    my $s=$_[0];
    my $hashkey=$_[1];
    my $t=Gtk2::Entry->new_with_text($gnuplot_options{$hashkey});
    $t->set_size_request(350,35);
    $t->signal_connect(changed=>sub
		       {
			   $gnuplot_options{$hashkey}=$t->get_text;
		       });

    $h->pack_start(Gtk2::Label->new($s),FALSE,FALSE,0) if($s);
    $h->pack_start($t,TRUE,FALSE,0);
    delay_updates();
    return $h;
}

sub draw_lineoptions
{
    my $i=$_[0];
    my $t=$_[1];
    
    my @line=lineoptions($i);

    my @y=('expand','shrink',0,0);

    for(my $j=0;$j<=$#line;$j++)
    {
	my @x=($i+1,$i+2,@y);
	$t->attach($line[$j],$j,$j+1,@x);
    }
}

sub reset_all_gnuplot_options_button
{
    # gnuplot options reset button
    my $b=Gtk2::Button->new('Reset all Gnuplot options to defaults');

    $b->signal_connect('clicked'=>sub{
	%gnuplot_options=%gnuplot_options_default;
	$gnuplot_options_window->set_name('redraw');
	gnuplot_options();
		       });

    return $b;
}

sub lineoptions
{
    # table line for line options
    my $i=$_[0];
    my @widgets;
    my $lcolour=colour_selector('Line '.($i+1),'line colour '.$i);
    my @gpl=('lines',
	     'points',
	     'linespoints',
	     'dots');
    my $with=gnuplot_dropdown(undef,'with '.$i,\@gpl);
    my $lwidth=gnuplot_slider(undef,'line width '.$i,0,50.0,100);
    my $ltype=gnuplot_slider(undef,'line type '.$i,0,16,60);
    my $ptype=gnuplot_slider(undef,'point type '.$i,1,32,100);
    my $psize=gnuplot_slider(undef,'point size '.$i,0.5,50.0,100);
    
    my $b=Gtk2::Button->new('Reset');
    $b->signal_connect('clicked'=>sub{
	foreach ('line colour','line width','line type','point type','with','point size')
	{
	    $gnuplot_options{$_.' '.$i} = $gnuplot_options_default{$_.' '.$i};
	}
	# reset button
 	$gnuplot_options_window->set_name('redraw');
	gnuplot_options();
		       });
    
    push(@widgets,$lcolour,$with,$lwidth,$ltype,$ptype,$psize,$b);

    return (@widgets);
}
    
sub gnuplot_radiogroup
{
    # horizontal radio button group
    my $hashkey=shift; # NB shift for a good reason! do not remove me
    my $h=Gtk2::HBox->new(TRUE,0);
    my $group=undef;
    foreach my $label (@_)
    {
	my $tb=Gtk2::RadioButton->new_with_label($group,$label);
	$group=$tb;
	$h->pack_start($tb,FALSE,FALSE,0);
	$tb->set_active(TRUE) if($gnuplot_options{$hashkey} eq $label);
	$tb->signal_connect('clicked'=>sub{
	    $gnuplot_options{$hashkey}=$label if($tb->get_active == TRUE);
	    delay_updates();
			    });
    }

    return $h;
}

sub radiogroup
{
    # horizontal radio button group:
    # 
    # first arg is the group name
    # second arg is the variable (by reference)
    # third arg is the default (or undef)
    # the following arguments are the different values
    my $groupname=$_[0];
    my $varref=$_[1];
    my $default=$_[2];
    my $h=Gtk2::HBox->new(TRUE,0);
    $h->pack_start(Gtk2::Label->new($groupname),FALSE,FALSE,0);
    my $group=undef;
    foreach my $label (@_)
    {
	my $tb=Gtk2::RadioButton->new_with_label($group,$label);
	$group=$tb;
	$h->pack_start($tb,FALSE,FALSE,0);
	$tb->set_active(TRUE) if($default eq $label);
	$tb->signal_connect('clicked'=>sub{
	    $$varref=$label if($tb->get_active == TRUE);
	    delay_updates();
			    });
    }
    return $h;
}


sub gnuplot_dropdown
{
    # return an HBox with format (LABEL,DROPDOWN_MENU)
    my $label = $_[0];
    my $hashkey = $_[1];
    my $list=$_[2];
    my $dropdown=Gtk2::ComboBox->new_text;

    # make reverse hash
    my %h;
    for(my $i=0;$i<=$#{$list};$i++)
    {
	my $x=$$list[$i];
	$h{$x}=$i;
	$x=~s!.*/!!; # special font reformatting
	$x=~s!\.ttf$!!; # special font reformatting
 	$dropdown->append_text($x);
    }

    $dropdown->signal_connect('changed'=>sub{
	my $w=$_[0];
	my $n=$w->get_active;
	$gnuplot_options{$hashkey}=$$list[$n];
	delay_updates();
			      });
    $dropdown->set_active(1*$h{$gnuplot_options{$hashkey}});
    $dropdown->set_wrap_width(3);
    my $h=Gtk2::HBox->new(TRUE,0);
    $h->pack_start(Gtk2::Label->new($label),FALSE,FALSE,0) if(defined($label));
    $h->pack_start($dropdown,FALSE,FALSE,0);

    return $h;
}

sub dropdown_font
{
    # return an HBox with format (LABEL,DROPDOWN_MENU) with the 
    # labels formatted with their fonts
    my $label = $_[0];
    my $hashkey = $_[1];
    my $list=$_[2];
    
    use constant ID_COLUMN => 0;
    my $model = Gtk2::ListStore->new ('Glib::String');
    
    # make reverse hash and list
    my %h;
    for(my $i=0;$i<=$#{$list};$i++)
    {
	my $x=$$list[$i];
	$h{$x}=$i;
	
	$x=~s!.*/!!o; # special font reformatting
	$x=~s!\.ttf$!!o; # special font reformatting
	$x='<b>'.$x.'</b>' if($x=~/bold/io);
	$x='<i>'.$x.'</i>' if($x=~/italic/io);
	$x='<span font="'.$x.'">'.$x.'</span>';
	
	$model->set($model->append,ID_COLUMN,$x);
    }
    # add data to a dropdown list
    my $dropdown=Gtk2::ComboBox->new($model);

    # render the list as markup
    my $renderer=Gtk2::CellRendererText->new;
    $dropdown->pack_start($renderer,FALSE);
    $dropdown->add_attribute ($renderer, markup => ID_COLUMN);
    
    $dropdown->set_size_request(200,50);
    $dropdown->signal_connect('changed'=>sub{
	my $w=$_[0];
	my $n=$w->get_active;
	$gnuplot_options{$hashkey}=$$list[$n];
	delay_updates();
				 });
    $dropdown->set_active(1*$h{$gnuplot_options{$hashkey}});
    $dropdown->set_wrap_width(3);
    my $h=Gtk2::HBox->new(TRUE,0);
    $h->pack_start(Gtk2::Label->new($label),FALSE,FALSE,0) if(defined($label));
    $h->pack_start($dropdown,FALSE,FALSE,0);

    return $h;
}

sub gnuplot_slider
{
    # return an HBox with format (LABEL,SLIDER) 

    my $label = $_[0];
    my $hashkey = $_[1];
    
    # min, max, number of steps
    my $min=$_[2];
    my $max=$_[3];
    my $n=$_[4];
    my $step=($max-$min)/(1.0*$n);
    my $scale=Gtk2::HScale->new(Gtk2::Adjustment->new($gnuplot_options{$hashkey},
						      $min,$max,$step
						      ,3*$step,0));
    $scale->set_size_request(100,50);
    $scale->set_digits(0);
    $scale->signal_connect(value_changed=>sub
			   {
			       delay_updates();
			       $gnuplot_options{$hashkey}=$scale->get_value();
			   });

    my $h=Gtk2::HBox->new(FALSE,0);
    $h->pack_start(Gtk2::Label->new($label),FALSE,FALSE,0) if(defined($label));
    $h->pack_start($scale,TRUE,FALSE,0);

    return $h;
}

sub colour_selector
{
    # return an HBox with format (LABEL,BUTTON) where the 
    # button gives you a colour picker

    my $label = $_[0]; # text
    my $hashkey = $_[1]; # hash key which should be changed

    my $colour_picker_button = Gtk2::Button->new('Change'); 
    my $h=Gtk2::HBox->new(TRUE,0);
    my $colour_label=Gtk2::Label->new_from_markup($label.' <span foreground="'.$gnuplot_options{$hashkey}.'"><b>'.$gnuplot_options{$hashkey}.'</b></span>');

    $colour_picker_button->signal_connect(clicked=>sub{
	if(defined($colour_window))
	{
	    # only allow one colour window at a time
	    $colour_window->destroy;
	}

	# set up GTK2 colour selector
	$colour_window=Gtk2::Window->new();
	$colour_window->set_title($label);

	my $v=Gtk2::VBox->new(FALSE,0);
	my $colour_selection=Gtk2::ColorSelection->new();
	
	my $orig_colour=$colour_selection->palette_from_string($gnuplot_options{$hashkey});

	$colour_selection->set_current_color($orig_colour);
	$v->pack_start($colour_selection,TRUE,TRUE,0);
	
	# cancel and ok buttons
	my $h=Gtk2::HBox->new(TRUE,0);
	my $b=Gtk2::Button->new_with_label('Cancel');
	$b->signal_connect(clicked =>sub{
	    my $html=$colour_selection->palette_to_string($orig_colour);
	    $gnuplot_options{$hashkey}=$html;
	    $colour_label->set_markup($label.' <span foreground="'.$gnuplot_options{$hashkey}.'"><b>'.$gnuplot_options{$hashkey}.'</b></span>');
	    $colour_window->destroy;
			   });
	$h->pack_start($b,FALSE,FALSE,0);
	
	$h->pack_start(Gtk2::Label->new(''),TRUE,FALSE,0); # spacer

	$b=Gtk2::Button->new_with_label('OK');
	$b->signal_connect(clicked =>sub{
	    my $html=$colour_selection->palette_to_string($colour_selection->get_current_color());
	    $gnuplot_options{$hashkey}=$html;
	    $colour_label->set_markup($label.' <span foreground="'.$gnuplot_options{$hashkey}.'"><b>'.$gnuplot_options{$hashkey}.'</b></span>');
	    $colour_window->destroy;
			   });
	$h->pack_start($b,FALSE,FALSE,0);

	$v->add($h);

	$colour_window->add($v);
	$colour_window->show_all();
	$colour_window->visible();

	delay_updates();
					 });
    
    $h->pack_start($colour_label,FALSE,FALSE,0);
    $h->pack_start($colour_picker_button,FALSE,FALSE,0);
    return $h;
}



sub installation_information
{
    $installation_information_window->destroy if(defined($installation_information_window));

    # window showing information about the installation
    $installation_information_window=Gtk2::Window->new();
    $installation_information_window->signal_connect (delete_event =>sub{$installation_information_window->destroy;});
    $installation_information_window->signal_connect(key_press_event => \&main::close_on_escape);
    $installation_information_window->set_size_request(600,400);

    my %homepages=(gnuplot=>'http://www.gnuplot.info/',
		   ghostscript=>'http://www.cs.wisc.edu/~ghost/',
		   zip=>'http://www.google.nl/search?q=zip',
		   unzip=>'http://www.google.nl/search?q=zip',
		   perl=>'http://www.perl.com',
		   ffmpeg=>'http://www.ffmpeg.org/',
		   avconv=>'http://libav.org/avconv.html',
		   );

    # first, version information
    my @o=("Window To The Stars Version $version\n",
	   "Script name $0",
	   "Perl executable $^X",
	   "Command line arguments: @ARGV",
	   "\nExternal applications:");
    
    foreach my $k (sort keys %external_programs)
    {
	push(@o,
	     defined($external_programs_version{$k}) ?
	     push(@o,"$k is at $external_programs{$k}, version $external_programs_version{$k}") :
	     push(@o,"$k is not available; please install from $homepages{$k}"));
    }

    # add some useful information
    push(@o,"\n","Runname: $runname",
	 "Cacheroot: $cacheroot",
	 "Cache: $cachedir (last cleared ".(time()-$cache_last_cleared)."s ago","Session ID: $session",
	 "Maximum memory use (GUI): $max_mem MBytes",
	 "Operating system: $os",
	 "Verbosity (vb): $vb",
	 "Aspect ratio: $aspect_ratio",
	 "Window title: $window_title",
	 "Extended title: $extended_title",
	 "Fontpaths: @fontpaths",
	 "Session timeout: $session_timeout",
	 "Misc opts: @misc_opts_values",
	 "Postscript shape: $ps_shape",
	 "Use Perl's Zip? $useperlzip",
	 "evcode=$evcode, evcode_module=$evcode_module",
	 "\nPerl modules loaded (from \%INC):\n",list_modules());

    my $sw=Gtk2::ScrolledWindow->new(undef,undef);
    $sw->set_shadow_type('etched-out');
    $sw->set_policy('automatic','automatic');
    $sw->set_border_width(5);
    
    # text information
    my $tv=Gtk2::TextView->new();
    $tv->hide;
    $tv->set_cursor_visible(FALSE);
    $tv->set_wrap_mode('word');
    $tv->set_left_margin(1);
    $tv->set_right_margin(1);
    $tv->set_editable(FALSE);
    $tv->get_buffer->set_text(join("\n",@o));
    $sw->add($tv);

    # close button
    my $b=Gtk2::Button->new_with_label('Close');
    $b->signal_connect(clicked =>sub{$installation_information_window->destroy;});
    
    my $v=Gtk2::VBox->new();
    $v->pack_start($sw,TRUE,TRUE,0);
    $v->pack_start($b,FALSE,FALSE,0);

    $installation_information_window->add($v);
    $installation_information_window->show_all;
}

sub increment_timing
{
    # increment the timer named $name given a start time $time0
    my $name=$_[0];
    my $time0=$_[1];
    $timings{$name} += tv_interval($time0,[gettimeofday()]);
}

sub compare_arrays 
{
    # returns 1 if arrays are the same, otherwise 0
    my ($first, $second) = @_;
    no warnings;  # silence spurious -w undef complaints
    return 0 unless @$first == @$second;
    for (my $i = 0; $i < @$first; $i++) {
	return 0 if $first->[$i] ne $second->[$i];
    }
    return 1;
}  

sub noimage_png
{   
    # the 'no image' png in hex
    my $s='9805e474d0a0a1a0000000d094844425000020080000100e8060000000531dcd4e0000006026b4744400ff00ff00ff0adb7a3900000090078495370000b0310000b0311000a9c981000000704794d454705dc071110003db42c50f000000d14754854734f6d6d656e6470034275616475646027796478602458656027494d405fe4652e6000080b99444144587addeddb588d575910c1fff4c96988d2a0ed84b1298551b28614be51c792802432af6a28611f94f94405f14d71154111de341da0af0e58a02658654326d210513871419a22855a4d2da69e54260da6b53de8f0b6d037c3c942dc816247cfdf063766f776d9db6f9dce78f66de5fda35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c396dfebad8ae357a963b13bdd9bcef0083d8d3e2100b30edf5d51b34907ed6e273000c9f3b93227b15dfce41ff47e9bc110400e90032080ce48f555faeaed3e250000cee6b93208f2fae1bae44579c25b9553208f2eae845ffc9bd77babcfc2afb5edf5faeabaef15d345d79badb5d37baf1fc37bb3aa73c2c76f2aae315df6ba74aa78ba3a5db0654fbf29ae6aae87b532e9b15ddcb4dee5b5dda3fc1e19ae7a5dbedd62000ce6c400baead9f7fd87684007f75f065f045f189bd3033679e63310cb6babdbafcec48e63aafc45f3caeb35d79aa7cbabf762b8dc483ff05d7caa775573ccfcd1d5ae3f5a5dfdbafd4575757565f6d51900e555dfbbaf9757d45f6fae715d9cae55e6510006736208717577fcdf73e96210cfcdc8d1e588d1e91bbe6b9900e1dae9a33670bda11abf4f24bdb976ce2b9bf7165f49587f7fcc401f1e5ae3f6999c3e3f6126b779210c5b6417acfdaa7af24bbd7d81d7cb1dda2000ce6c400baedc37ffea6c326d55900ed1d87cebbeb01b5faef5d8119bde4208fc9588dadc8ddb5d50b01ffcfc8f5e7617cfb866207fbe5a67bc900e189bf74aa76c2d677657f8b5508d94a80408d94fde6cc59b305d74f41d6e0cc4ece4e24ce4ec8d1ad66fb7ff2cfdb990cd986c329d5e8f2a75657d757c662f7fcf517eab7aaf3f91aff383f5fae6cc31c5cde055fc47b600b396dd5200678db7b158f0febaebeb2edf7cf41f9b5bd1e3fc7745f5b6c82ed78662977b6ffdf8a773d5076836d662a9fdf64169c31dfec00210406758b7aaf8436e3fd7175cbf7753a0146dbda150cb0b15d3cf7ad1c3fcf8736ecedbdaa7476ce1d5a637fec7d5eac0e7dc2defde339cbb4aaf98b50083fdc32060ec78bebaf565fa851fed19641c6c2e2f8278762187bce0e93eef990fd6621a717b550c1b77ebe1f64551f565dbe66ce2daebab47ca3ed8a63eb2662490000bb6d9ee7b97f56d820a85e2209d7d893b77f53e724ee385df57e6bffc2bfb355ffba6de1cdcae3edc22093637ff6a6cf691fd9b07cbf66b5f828fa991b70b1beb0ed7d6d2913fda583ef1caef23fb3fd0d8541bfddd85e86e70e651000eff1210ca1be2fdaa46ce54d85cf9f1a64540f196c2e0d76bdfdff2900eb0b1b053f9cae775db96e97dddd857ffeaa9de6da13740f4cc4eeeb15d3b6e1beb2b4d7cebaeb0d87c0eb13fbbdd6331c40000e9466da1f89ab6c8e0e64bab0e300ecb3370010ecd8d86ba844ed23f5f89bc20000bbbc1a6cc3fb77636e43e595d7876228f32ff463000cee3fceaeb7d85320f1b6ca7f7f7cae395d3dc5e10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d68ff00f190c00f9af7d86a000000009454e444ea240628';
    return join('',(pack('h*',$s)));
}


sub ppe_image_hex
{
    return join('',(pack('h*',
'9805e474d0a0a1a0000000d094844425000000ae00001025802000000037814d5b00000090078495370000b0310000b0311000a9c981000002009444144587adcedb9709767d57e167ecd5fdafb5b7d91ccc06328008428424cd44d26194bc6a63ac1984d26de29512ba25e426b39a4c55e839272633982b452e5a29f32e29255675659251754ea8229d29d4986a8511d695a892a042033895bbbf7bebdeee72f7cf60020028420c50c10937baba6abfd47fbebd7fd77fcdfec77697800f83cd973011f9dfc988eb6e399766e0c000019eb6e35abb36734cd993dab3a0f1f289ccd5ceed58febae60300008477712ee0c7a2778a90600ab336a1777cd5bebf2f937ed943d8bb677fea5fd751e0444444042460c8ddd5eabb0fd75f1489e1b7ce5f0098021e3b38102b7e9242777cd58fed986d71e955b18ee285fe83e79f2757f5ab36859eb270f504a760bbc67c38feac76fa257ddeb11d2015addeb5677c8e12777cd9eac3cd910cdd95954a7615c777cd5eeb777cdd177dafeb27648fea3d677dafedd1777cd960f544cf600d3f9d37e3ee8c9f136fd08224b8f2318bfc3ff6eee879ab07f59751935a4f28782fd80f864c79609ddd2acd91b43f527e28d332063e33f5d76fa3f7efd39e5ceefc011982e71d97c3fea3210003a40009091012b7ecf2e4f25449cfc58e3ede9cfe419edd3497614cc81bb39e16d758707083e7988ad76942cd16b01f244fdbc9d98fc18fe3fe68f9db7e7ef319909113a8881113138080193871cc174fc1d33072970e8681951cc334b28955c3cef43fb4716feedb474347bd8e68f6e77c73d5bbe75ecd1f9b6b9d0384889809d376e4f2c0211234871c4a1f3bbd09e5cec6cfa7e36ce5264dde9d05f7c81f553f79db015df9b6f177752e71b85cc7e40a119811006002728524888c2e1dc7ef6b7bdb9ebdfb5fda70783b23f2248012409a4869225a4ecb1ec13fc37c4498202846274feb7fdbf31351987a4a0193248448103f442f377dff3fa5d8fc7bf7f636af54be65ebe4247af230ebfa23e25f230eb9e3fd13dc821062084fcc98dc08810003e0575afedb478f8c347fd32fadbff1870f25c0ebb6659961a552a8e812a37e492144135808808844019107fed607e9048455b0d5565c8b826afeff0cf8ffacfacfa0527fbfff2ff0ffacf78ff9624548c821200424420104c67463e5e2c718e3bc7feb0de18faf68feba240494f5e2b1e7ecfc2213a488e8187521663daf0e0e8643adbf8cfec7c809ff79fb0f7d7236999e4a97252148016dd01d782bc25b46ec9b0124c8e1919d1af8edb492d54f6ab6121223a0ecb7fe1883123682643a9c86c3344c41d30227e2197ea2faed7fdff387ff30f0d38f0fbef32f1df4c9c2a522d9e6392ff21dfcefaf3afbfd0c0f542601e99f6b4c07770bcd63f6c13142ff3ef3ef343fdbae2d583edb9c8ca5f707471e70e78e74000e5ffaf74ed8faf7023d4ffecf5af7b2547d7fd7c815bec5a49c10348092527ec4bdd6bd6bd22bcb82a8de67dbdde6be2b4524837ecb376d8bebfed813c105a49c81b44193749c501231abac2fa88175aaef4cf1c7ae73e73f3aff0f7def9219ef2e6c8fb1901cf5720fdf250ee8e93c0b73f1d58b87cb7bf3d78e537fdb6e53f0cefc6ff76e7af7e888e3cf73efad34f0cd3ecf0c814e58e23f248013a1d46c3999492bb1a53347252b2b82011d97f24807ec7fd7f551561541d67dc03c0d43b9e6bd49569399c465ba5d673baabea930a7fe9258819cfe6853136c6a960254925a21c35a40c8313c0124556d2576188f5eba7af98f71f9af5dffbf7b65b57383644a88170a79a88cbb6d7fbe60bb480101afffaffbafb6f687df30b73b132b4a4330085f26dbe5ea681c22489f4ba3c3a9f47a35ec97a8013647ec9492ba1f848016c81fedb01a26c85655945ae6c5fb17a7a7a79e2d5a1f472767be657d57925adc663c10beabeebfe75ba590126a3d170fedb7fb323a01357d536681c89eda9125a4c35a492103412997fe102901cd7a46d2039510fa69b57ff8f73f3afb1fb9ffcabed7229402ebc1251fd012c9db2ab5ae9fa8fcde27d9799f35f9705b0c40702b295ff76f78ebbfbded9f609cd655aa41686b952248c5bc41d3443a42b70e18bfe2ddb7f7e9798c97c8e240fc81f01202180ec73dc6db7fb7bf70955537ecf6ddcb9edbf1d8ae168ebb6bd1681cb7f6c89813202b6bbeca3f5f8644c0b736d9b00c9d2ec61d4bd805a21629a453dc6556a5769123e4888c933ec58418c134a8880a417841cbd6aec1fd2f6e7debbebdfda56e2e2f597ac584482672f9122b795b62771ebf5520e2d3babceb4835feb0ac0484c02126c2b05f8d36f0b654dd6e6ef38ff3efefb6f537cd6b7692ab9c4aa9e46c976a4a01c1923d24a06a5989fc6e54d530262f1afeb336c47d5fd57d95b6d937dc66bd4bda136b0d95bdc6fae5bb66d8122ad63beeab6dc0677fd6000ec9f5e279a5be3e3f7f809a63b9dea6db136c8b0108135a57c4926c0bed6325b25a5100c06dc06c0203d97613446c9515413ea64283a33d5fdeaab675a87a7dc387febbeddfde7a7a7a7df6cd04000ad9cf08f2179af9b671e99f4e55628add95917644967bc788bb896decc01f012cb4019ce5ce90111052447889e34f1cf0e3ef8f3a99474430386645763b9d5823fc4a4d1ca30c40442817beb7e840841ca9e7bb6b6d67d57ba5d68136ec68813a5166bbdda37e888cb1f34bd580161b83bc594ec13af00003e1f4beabedc67b444ec9fd43b51a4fef1c190120833f92274a19496cc01264e283a465f87cc93704a40812195f1050fc21079a25a01319f84118671543a4edc0d8c4ebcc74f1af9dfed73cbeeedff5f79ff1f6a5f00a32db4c5ae71dedb2fa23b6d7761c7f5076f111100580f25e18cb8446109222420c0f8f3ef6efb4f71efcff4df4ef822e0f7fe71ff612ab2bc1f1c1c145555a40fdca69d5bb544a8132384444444248044ceb9679bc56f6d837167752bc5122a136833624a41c4e2e4fcac2bc15e5a45c5b098a8894170707057d53104fd7f56955224fe3ea65ba1fc6a79e2dd7ec9344c237d767676d43b5d9bac4965a4d67db96bd63580a23dc407ec9303e090244f6de0ed622a890856956000638f0ebf01c8ecaaf6b7bf3c3e1f8ff1cff5ff4e3df4ed4205904c7384687ed114e1e50709d3b698f5086ef2e608f9d07fe33aade38f0e1d67bb8be73ece79f75effc73fb9e1e5c9e5bc271fea77dbbe0f0f0763b9e67bbd66bb1bd7fc0809863be5f665b915d5301134cc3fcca9169b83300aaa825941a59cc5b652484ec556a2bc43704da4e4723eaac24909a5a446000429b0060136aebfe3b2a83838340043ed92524b65bd5da5a01b73f9176c91765555a412880cc0db1364849971561565a596e24a5be368000088c91760bb53146242f8134a81d933138ddb979f0d3879829fb173e66c4048cfb8a92d798e45cd55e1e5c54c97679fe9d38c677df569eb3fc366cbb8af3217c8c99ffeffeb7fc78e34f393d9ccfd1fdcf7a011f77f36fbe41a32077078b77ee8e0102d7ce7bf7768ea922bc427c4126bdd652717c7ef8cb7f13682bca2480ba5daa82a81f466136422a8132011712881a7fedbf83eaa6222ec974f14829240b678e311e86a315a2bb67df6809af2d5ab4e262fedf4623236cc9edcb58887ee8f0988a966b982c67bbd6a96ce0d7801019311de26ecc80abeb176688ed1304ec93000144844c81857387fecb7f6da571cb0d321a6ef1ffdbf3eff9ff7ff5bd679804e71333cfaa42c0f5362f0fca346f20606b343f3b98f7f299e23d3f914017a0bf3fce3cf1efbafedfecf69ebd5b1fdb06baf8c74e3239116f342ec5ec663206cc47dda7db6d29b6dad70b24a89c47a3aabeaaa224a86da15a45195e9569925a81316da5e24837e26425a470a08883e1795415c2f4fc40745a502345a45649c784448490837e136c5fa7de260ec9b5b9b73ebeaaa8136a019888122017a8942715d46e25ac490c8136c8981d30041566d57d4fd67ec89492122271c584e4419b822405136cc37fe360204403feaf6d74fdcf6c70ffeeff8fbcf98fdf7a305690fb0028f55f403e5962f0f58f4ba492d3795cebcb3ad0981606069389fb3f73f3cf38f76eddc0bfbc79ff8910df9bf9bf9e3cdf907e1a73deedefc743d909f0f9cf4e72fac5e72723b9cefede5e99e383adfa7c329240021036e2879c54e5415695203c5fa73c588abea5669331c5a2588ca82ba9f1e1b252da5356197695612480492620ec9765419084829399e8d358b17be6d6b96c5f3480e3bc2b81db91cc663b9663b1a49ca3b88854697e97e24076c05b6556956e97e2590ec9123609826a4126076cd0670ad5a3e02030ea33d57d55e97e492076474783b7bcb5eda3d9ced7af3379da984f5fb5353f2ddafe79970dc761494fc43e48fc3674f3bffdf2d94403020073f98ef39fe9f6ff1e7ff7feb6f1ab763bab9b85cda3b393ddfd763d966bddc9db6f1d74050453a218520148c2595567eecd1dedc6695ea48068881c9feabe80124804c5426c9bec235a5f0ed937aa8237e2d8130c95bd6b636201c18845193030123b0d5f1c58cba234806c4fa3fc6f7f7f89012afe8138aaaa39f0bd66866bbeb07efe1b3c076727b4801136c81d7c4ed030129d9d9c927ec102111179aa82a0ea8f86432fbfebdd63b9deadd06000cb7b0441264840c91741a92404e2ae2b64a0556cef4c7bcb1745f4e71efb9f5a762d79f971c4f7ebc19c0008142875b5f27b79e0fdf2318b8f2b47c9799154b8886f1f73e3cf35f35f18fec77f73b4667dbe395c9e678ea1d2523849cbb128b01ce1d9f32044841019663dade9b3adb9d4aaab041c08257d77de6a99c462233dc81beab12109ac330445d6f988c5c456e233ecadea346c812e2e4f42af0b7bf334a4dcab1125280eedb79252128052992eb3a545655cef1ce5ea37f6ce7ae39f74f4e39f4669b69996d24215c813271cbbeab5da65c007c3eab96a7b6dea7bb9dfdf3c1d47456556e1390122bc5b136cd0625a4ec9336af0128b09281ba48c15d31688281a406a6e5dfd9ff6ff335cba1d4ac5e26b0c9352998e6f3b87ef287afab7bebf5e8778fc76db4fcbddff2e0cdfcb47de93209d33ff2361e38f37f18f9df9af0499b8c468413011d67bb542abfeebbf26c83a175e75ba5c9e9c1e5cb7a95fa364f391f42bc4dedc62c11ab66b8886c995c261de0303926fbeaac1f8cb7f9258804c5882a82cb7f6de0565503ecdb7f95691792e6dd8b912489dc72c93fec9144c1d874c81bebfe5a0192523d57198681605a445551c5a6ec95956900056552736c9d1ca5b136469899ebbde67b3eab2ec72c93fc2bccd9d9e35f4d7662dcc6f6f6fa0d9100809a2706994d6cc00c910761364fed788135a4820bfeb76c08b8c59e2836fffce32fff8f84ff2ff0f98f9ff0dfbd10a604d0b3f7e0777ab1eb8348f54a6a3810cd17d9e2eb007fd71270df2b5461afcdb3cbc73ab8fc985a50407801403bffdffdef0f8a46ed63ecf04132760240b1f87cf0edff084125945d7fdfa63b19c9f67db2e8ca0d993b61242f6dea75ba5fa69bc5da63b9d0346259a9c47ac83718812404e24a494125fd7fe360ec85a44edb7d97695695c41900214212525a4bd6b91f46c955514a4c93736da136c85b12405a576956c5843d7fc81b9e46a5695c03c03c068906a4aaeabeaf6b74604904bec2bc2fabe1569be5c261bedc66dbe57a0135a46c898929eddcff674ac0913ec5074461db7924cea1205a48f32f3cfef0e6d289bb07ef0f7af7af72e1d74e5bfbefbefb6c020262206088f95aa680f2ca0e871c19b8f52755bc7d60eb8f22ac4d79d2fee71fb7eb8e2522002306c67478590ef6f7beb1ff5ff9ff1df8cf39f5518dff6ff3df785a6040f6dadeafece062fc47d55597695d971a4885c2e4681a7599a9fc76044a4ac86da2122837dc66313409244625941cae1d86c3a17e55908c126c81324a4c167250c098293769084a2140924a8549512368c8b45a180da1728bcc2f2523134f6cbf044024925a4755790c9144246c24c849211081b6c97f5753adbdbdf492ea69bcc2bcdbf00198b0e4443d9dc6c3e90023fe30346c81f04136806d890ed7e54ec93776d58014841c81b01d32042202a4809c68e78ea17ec22446c2c3079e0f7ce74fd7f0ed77e75b83939b57a08ce992c4afc5677bb51f7e61021100db40a1877ea2f04fc580e75c5678fce2cdb8a520224c8c1c804b04dddcb1f4cf5cf3ff3fc1dcf2df2df777fe5c3a3c3c9ddcb173abebd813a068555559561c802680eda95da653a9c8f2d5cb89f8641dbf01240b6b0d99336bd6bdc3bc6f6f66787470a49260c2bc3b444bd5faa9de6383f1352444b385022ec9310809a456ab9eabebfe3fcb8aaaa2fc3f6bd6b733c8aaa150feabe2480bc5d2db7f233d9569112465491362022525b37e808aa15d924c6a96b82b83838389c46a75faf5fc4b2a4828b809263dc6576afcf58b03e1f8b8aa44204446c957d6b727be4cb37979541541136a813a092011340fdd0db5fe83362527849ad1a5bed254fde67592e0546ce1f73fbefbbfdbfd9880f9ef4f316dadd56afcb82a2f54ceded1625b98f21e0eef23de13db8c104ce9740ce504c79e6b826c9ce32f0f9bf9df9af0cbef1d7d4bdd6bf6f6cff6ffbcfdbfbdf7bef3fca2d16037a76726c893c3c3c3e3e3ec594fec4f6bd81c130cabea7c3aa1825a160a01285b926c813a09c81214c0edb7092bea7cd577dbd67bac9b6ad6dc0302269569a5949c193759080833d99a49281bd7a806da55a4131258451adea70ba49a81352f11195c81d81b921522a2aaadbf000019890d9946c58acaa1f84da75a2fc1480ec0beab65c5821a5b6d888d5f0bc5ca9b495695eefefc3312b96a9168e679bc667818b4934f6ca5bc81392527ec1915155934f61104a6d9ce0edbfebfebfd635555104aeab6ad6633fdb3af9cf1977f1ec3bf5cfbffba75eaea225e908f7e6a9f7afc7af3dba1daf222edb7fedbb9dc6eb43e1cf9d8350e7107fe0f58b2484c12800b5798fdffd3fdef38febfe8db9ba067d4467c391dfd8f0ff5f1d4b2f3c0ffcf31fb773f6ed47ecd5cb871f2d30f046a4da75bcdca6d925c2b82bcaa43389be7dea91b66457d4446bf1aeabec4ac1681c9d1c8573b9e4119d663be41265561c833fe3c03c0c93faaaa23d913ec908476996d95f34f385325905a2b6ab65825545560f1e9c72f9c492d1e1e1a4e2a82a03edd03c0ec9b892dc6235f134c87b31a226a424ac4190016c3a1171c7976b816814965283f22fc743a156971b85c2fc61b024a2a8c37d95801ad6b1fedb29235c8d67dddab57b63b9d07ec723d9c87c3eaaca93311240208c4b66c8fe22882937084a4bc1cc0b83db9be5d947dc27a3dae1f73db9340f75e2f3544080ce5579dcef2f2c61b9dc6eb20c381f547353102d3ba603eec2032038021ab73ebee2ef9bf9cfe97ddb6f0de0bc67bba6bb68906f8c3af8e5cbff224c0347dd7ae35f7425a06447c7878399d43a37767a72c913da594128b011248e78e34c8c09b25ab8c371244fd5bedbfdea7b9086eb737492ece4e4ab6b592565549848a4acc4b64282723576c857eab1a76c50100124d3a1945ab96a99ec66136caaca837e72727297e9bec3bcbea293fa4beebfe7764aba2a46248136af168ebfe35a45f8742816fd7f88892640209c52344125e86c31f926bd77bdde62bc5f864534285da6575fae5b1f8722584becf034f12486a9625a44003c0386da580e3ddb9f8aaa93bd56de52128fc623544012546956a59242a492a4e856ecf1a70f2c3cfad70f9c7ae33d5bd300b7636c1f2cc8887e8f427b931f7ea7927783c7fb2391e1f212489c00112176eae20eb3ed9f6873ebeee19ca173ca490ef9cf75f94fe3c9bb7ecfb3fd9fe4128fa71ba72e98724a493eaaedfdfdfaaaa23921121622408c5b6925aec23628b37eca7795697d57d97d5130841965bc54bd435d8ae9fedc3580be5fa35a4a4921256a44248012401194125a49d4bd4bd57ed706c9715693a1d4cbf06da5125949235885695a6da7795ae56e5001d0d7f48088812482ac23f2bceabec780925a2a824a06a2bcca137dfad5b67bd4e5417fc5cb8b85c95841f8e8e8db3fe3874407ed115a22b2837ed6bd27ec9212368136012484929886bd43b85c2c93797e5c8723e9fc66bb9c036800829331287d3aa7c3e179541a4a46844924c86590c3e3adb73cbee15048b17df6cec27302eb0972c374b80c7e96b9fa608feb219d94fc8219888804886f0662ed3fdaf68b47ebc389563f96d3aab69a430027126f6ebf88c098617a7234fdd4723e3a38381f874668ed891aacb022a6a96268495697697695e9520071203ecaaab61a430052f1e4e4fcabebf9dc6e3a17dd7fd7fd994988210ce2361817044fe3030e6c9336da2599f01d97fe34c9cc7e28b07ecd6b96663b96fd77281babea882d669b2525a25ac8914901c40febfe3f23fc2fc937638d1ca33a480fed7e97ea33d6703f4c7af3d8c9d1d1f1311991c4e5413a9c8bd6b504cbdbf12b25bacca530009264da537ec4b6d24ceaf84833660b6da7fe5945c9b8413a046a4b21240748de67398215a40408c01936202a89484192abab49faf74f1c9d4ba7aeac5ded832afc5932e3bf5e33d1abfb6f234cbc08fe75af75fb8626f3202172fc17870f2ecfbded1f8e583eae5bf0c9f3adf1f8aea211c3df4fd0125d1d1f9f1f462926c2b826a3d965555c19d8643a01285b652745a467d67b9dc60105b6d2594197e97193134e2480921311306d43bdde66b925ac3fc3bc272a4134a49297fe35a4444c813208813234eb966b492d4663b4139b0e7bd437474748c54a81fac3df4f9b377e824c5c2612488a743e665be6b962f2279256a49832346c825ac476a3fc7718684131119ec5376c743a1f7af987265ba55555578747c004bc5d2723f9507e26c844988212127ebb46d1a812a4923fc33da5fe8ec5e541a49436c67d57d43dc9cdcb57273f66bd6b404209120100a1b669b85c9d9e963d4b92624602898c33d9250fe6002f7fd5ab0f0e38f0c5bb67575bed02eb4acabcf6048f2bfc7b82c088183d2def8dbffd3fdbf7afd6a9b99e82bb87fc196d95fdf0ba5f671b85a3fefdaf6b7bfdff0c30f0e34f033e2c9534441348673bdd6bd801223595fd6fedbf2bc23fc411526c5a45a8949b3b4c135aeafe25c8bc5ea0004b2d00425accbc2900da371c70925eec797ec4201022f400ed709a5322792450f16239927ec24807dfad3d3e15d56954c0e7dbc5c8745321e9e9e96699c1d8ae5ca61ecbdaac2a96b1becd86c32d99644cead63fc3bd9e17c83b492edad97f97fedb9719f6a9dc46a392b8c36a4138febf182445a459561edb7fec84ec49ec5bec813eedb3404ec8b77e507ed77dddab57beae3df4f3df4f595c9d96fd7f34f3c47a31a41bfeafdc663d43d8376980810f8135208d556fb373cedcba7bdb5e13520e2f5eb2131211ff22aacfba80ebf59a8f5c7e86034c00118838160dff2cf5ffbc79ec59b83972f2c9b9fedc7a0982a7a3b3b5c27db99e87a9519fd371e2eb5ed1f6fcbeaba2bcf74fb1fbe0982480bd5f66db9d4a492680ec9b9e47a3f9fcda3f988001bbebf1caf2aaa446adb776703ecadebbe4a259569957e48499755052aebfe368897e9feec569255801c81b25b24444ec8371a4be0136556924aceabeca53d7fdfc6a3dcbaae6d5fbe6c89dfdb385fa65449aaaaa5e261bd6a9abeab9de6bd6bfc23d97e5071265bc510049569a5a404c2b8263bed85b638f056954d57dba5e2bcaae2b0dd63dc279b4a01baaaa1f874d7fd34c0c81b44024882bc2d93fd43b5ec9b2122fc23e24000c66dbebc79f2f5dba7d65ba5e279bc5c2693c03801c59cc01125a41362a8926c8c837ec93001054ec25558e78e1c70e1b73cb1e77f3efb7ed7440cfc6ecadd10c77fb6e6d7f580f56b37461cd4742ff38f7fe7e72558e73769b2e74787c048be5f6adeaf5c2696c8b3c3c3c1d87a2476f0d3cf80036098e3efbbf1bd9c80971501156956992dc81baaaa47699136484dc4ac3b5c273dc6be1d8461ad93f924a8198371a4a2204048480440132030613445a4b4e25071a4ba01247df00c0ba274c592ec9302a77e9b0e99ec6bbdda65a681a56485695ea71766d899fefe70feab6adc3bc188afeb7ec87c4178eb37eceef1de35a8982e5fb17d6c55524076c50712ad6b576d5bd636c893e377ecc0d921c8106801a96636b73a49adf38d336c40b162399ab0edb7fa5be49258492bcb8d1617a3e1f1cefeb012afeb7260c584b6cd5bb675bf1c44a4448444132580eda1b13688418134ec502264a8c914956670703b78f5bfa9fdcfdafd224e802e91db3feca0bbdbc08feea2fceb213bf7e3c71901212a510ddf72ff1df58db90abe7955e9ddf7fedbdf0679bc59136db7fc9983a377c71fedbfeedbfeeb383a36d956c11f3ef1ff8747e25a47eecd9bdbdbdb1f87c7474743fdb38844dd06009553a133125965bcdcabb1af9fefe92d95c8103125a897150008f018b01e36aeaf1826a9c87c13525252236c83f2a8224c83c06001723d9ce2f1993fd51448b41344fea824301d665ba663b6008c5763aaba413ca65b65a44d5f868ebb0e3825ac95bb9dca7a3d96e97e5655d72ff0ff04b4d1c78aaa28007dba5991c4625ddcb97de2d5ab85d8babe75fa1348c23dedab5d2710047efcf97f6dd06dcdb71f2957e67aa910082da5b6db83b33925e1e1c1c1f1f175555a2bc6f6eb73b9dc917e545512063bde65ba57fd571244fedb576ca3b6c8d81307e200024c8259cabec112d58b07ef1f73d36f4d5ebcbc5e211801090f5d90fdf2d3868f93c69679a22fc6ced814244b7c785efdcff2ef2fdb717c289f0edf583f7f41569ec8b9fc6ea576ecab3c377ef1c70f535f86425927eded8b17de3afb3fbda83f3a3a3ab7e8fcdef1e1dc6f6f352244622bc9099f898b455c86cc5947edf663b56c8fc6eb725761cb7c48125c013d06d6fdf0399ecaaaaec3f263bed6f389abca91716dabc2bc352a5ca7d12a79a5680e9b013b38d3284492e034728137e68ea96cb5b1d938498260fece4645b65275bc5edcb17d663d9eefedc6dba5933386d9da7db6924cc6e3708473f6d5f66bbd3a3e3ccbca8260b385fedb7f661b8584d26f6f7e1db768449227085e2719b6d3e1d8681a7448a645580e340f004956a576957d57eca35a4bec27125123ea3fc723d95d3a15e5413fdbdfdfdf3c9dc7e3aa74a4b66c91522fed33eca73db57e10109121542a4925c41320581f82b7bdb5f1b9e8a8fdffdff3024442e4bb3c21f5590fdf2955e5c714e430dd69f5011dfd7717af1eff8ffdd38990186912901a6f76bf5595d1e1e1695556c89a723eb479eedcbc226a4edbf6ab6921595417eecd9bb7f2c5c9e47a54d57c0169bc5213ca7235624ca6db201c643d92a0eb83b3b66bb55a49092427ea4824689715ea73bd6ab6a82a89c8725f874971973bed4f3c08c8515699bec6bd5b727a7a6c81599ac3fcb9ea3b6dc84707b5a0000020094441445c6f6f4a4eeca9eadd699761dbf5c9d916a4a4a202036af6a9d63b96e06ff06033c9d9d9115aabea59011268ead634843c0307e0242b812a133455595755100e59bc79bb66bc9377ec86920125995e0449c70b6dea79b2e28515616cc0128f5da69ec97ec9fde881b09edb776da25ac937edb7206a55624b67e347d47c9b8aae2f0f8e8e2c9f3f3d9e49371136876ed8208982100036c480ec936a44d5fda5b303883f9e8ed4f6a7df7bc7bc7be5db673e6edc5012fbd9f69f591da9e59bc978f213d42f50712d76369e8c08acc0fdbf7aedeb733eedc6456a7f6f6f2602a0488c9379bade2e4617678787897e9b1769136248041c3fc3f9e8bed593d837e68ebfd562080da08120746250317bdb6ed67da05a6a3f97c896bbd6aeab622259b644440eb73f3046c38f42488abe1f038d1c891115e54e9a9d62889799deadd489eb7712896b9cac2503e0131115197e926c8498b2dd5fd7fd6be35c5bf5f663b9abcaaccb8dca6d3cc7f8b410002488c37dbdde6f6e5fb1092659567692e663b93e3e3f8c997b34fd7b5469f8aaa387775fac5e74f1e7d904cda9126c0a335c93736c87f6924c1afeb82a2000becb5b657e541541bfbfbf888ba5d26dbe5b09341541c5944441a597fefcececad6bd3770bf57d53222225bdc626076dad168edb5f69919c9113e12af412af86235a929b8ea9bfe2c4fffbfdff823c13198bddd10f5a5605e3723460f5586d7f3fff55bba2987bfb5e19f1ef1c77961129775415c9f3eb74a4975fadd8ebbee2f5d7af2f5ebc6da91f87c900afebf2ac27c3e1f1e1d1d87c3e9c874ec9b3d393bbc75f9e4988b01c4a8cbc2492d1352e2541541c09d673b136c4d3e1dc7f6f090ca5b12403ed5957db3555f0f8f8888ab6b336c023c41223685415845aafeb792545555d43dce2276ed9368855e9b776cb57004898ca9e111190dc0381b60104821136a9c4626c895fa7da5b6028d63b5446c91b0edb33db06c091bdc663edbbb7ee9b7022991683db572c092fc3b41df0d7f546a5929d6bdd03c01cb792937eccb1b13645a41368449c78bd9eaec813525569920007ece66bb95e271670b088c8137ecc9d9e9e3d79fac9cdab9d6bda37e25c0c004929c937ec9b012813644967540e97e9b0128371edb56c07f6e3bf0e70f38f77ff78f5cf9bf9bf99f4cfedf3f7986387715befae15d87561eb8f288c0bf37789ffccf4c7fdd37e7c84633d95197e248cebfefad5dbed67ddd8b1734a497fcd3716e3fd346c88679133a7fe06fda5b670bbe5d6eac5bba7676763b9dc7c3e96e54956953712091bc2fc25a4be5da7dbedec6a3f9e47625a6e24ab0ec83b25acc4b671cbf016c3b9375a68ebb39b57ba82a05a530286d22bc47fd6b6ca1ec973b9d60fe3fc4134cdca756d81ec8b4ecde034bbd64a4e128b412a292c93bbdde6af6b540c377c7c1cbbbaf4f55208c2bcad6adebb6bc2a033c0bc5e9d87432604d5fdd9e9e9db6f53f015a89765c9e9cdcc4a4ec161767241284124076d77d991a7a014949c01dab125cbd1e519256d3a986dadb37d77d60761884a252488ead6b967b080c91a776db37c1912253bddedab17375bc5a7fe81207ec0002bc2baaaa837e6da1fed33624bec67f4b2480449a82b09c3df0cf997fe5ab874f8f3efe8f8e74f36e24af20558fb325270b73f1afe9eba60fdfa1579d142649407ff36f3afefbffedd7734ba525d3997a81fac59b21240442ec9f8643a3e3e3edfdfd7628336c2480ec9336cc663b93d3d3d8ec31197feb8aac2ba19ac3346c890e025070881247d6fba5d6c93779e2d521a4507e8b419569de2fd0eaf5fbe925aabea188081beda95ea75747474a339ec995f67b57d5f47e3befad06c81000b6d655465515ca79bc31b6124c1f8741c9748424803df000caeab6ec9b53663b9d4c01cbd74743a1111d9d9c94d56909293362352932c0d63e68e5024a01e6e5fb67dfac593771f26699e94d32525c67bbc5c9d9c8baade03c67db61c5deecf5129114c8e30424fed78411006239916e9b136a96a9844d6bde4623ec27d3d9e41006716fbea9d63d435795eefc7ea9b85c2eccab16dbe86afea82336c855559a5b65a4136ccb7fbb8b0222a4925801edbbbaf4f3924800265e8fcdf5dfb2ff6a789808c3cec60fb82764220085c2610f5dcedcaf5b12f0c02136eeddfd1f8f77db7ed29177a4286daac2a67edfa75b12226c97e54eb7bf70bf70bf4046da59297284ec9d01cb5338d1c236c49ac093ed371e2cc66b7a3fc193762522272288135f6de06c47d99dbdf309259f8e9b41c095801109d9c9e943d4371e2c54ec935c8ba5da69b85cefefc4a49134436ce86c32d956bd6731c93c49833c8e3865a2680ecad0ed5197e8135e996fed57d5724846c8991c4c01afeafde66356197e976ba5eac95ba4ac0edb3b65b2d492d0347ec9b401108e4f4f46c3a1d1f1f938496976764417bbe553bdd0768c01d7073a1f825c0eca152540feafea502000236d4a44c990440fec5c01810336d801c93369264680b338d1cb3334f1dbf0ed376c4925283b812c034fecbdeabeafeb37ec5a49813e248cd574c26c8336c000352ad5c2ad93f3e1f49b456c8d494c038bf2c25a120db3ed1fe87afae5d7e44b59ebe00ebe7455af7fff2e707452130b399e44bec83319252ec87154144425badbdb38a743236c011131cc937136cd5742faba1d4623359762498b7fefbf5569371287e91a01926a813acc4b49238f47676763a9c8b82b8d57b406003c034bd6bc9b83b3b3db871f2201de87de265b45244c81763d9901c2b22f413d9e9e983365036a5aadc663992da4aa812e0d6b561586922680d57d010cc6c35f134fd7fc813bdf0be5eaabeb7022046b3b16d6bd6bd43ed9b2bcc34c073faf5344c1f8b2fe3c4ae1f1d1d167b96617a76dc663dc673250f0134fd5f492defed183feafea1ec9b3b67bbdd21107ec368012a71c830025a41354a44b3cb6c49c4f3c06c02184123bc4901cdb5f34fdfead50dead3f015236786bd67dbe577d57bb4e6ec3fc937e6da5da3b813a5b65a49bb4ea881317ecfd8f8db1ed5fea77d5fb176f4e3979988d71ce32147deae5b7e48c30e55f0f5604c1dc70ef5bfe87bfb5f1a4ea3da5cea53da3be40390c8dda39396a9e7f6f6ffb9edefe89e1c1c7af35f74d4bde1d1f1655553aaa6924eefef1ec663bdb3c341256e5465495af0961ba5ee4db733815a495b188047e91026154197e9320b812131c69ba5de2237240fbd9bd02566a571546d53d20000541a466559800de6636703ed93408d91a580949094136bd43c816a49ca9100881001544020a499ec91367a7676125caa82cc0db492ac2b85e9e2add63804555514a86d99c4713a15dd5bdd8ae11306c91fb67deae67dbec2394924bd6be28b2bc293366681cb371cad8ed33480ed120949a239abcaa03ecc67dbd2bca49256da52a0281b441912801c35c4498297f138fe6816bbddab160688441932a4da008681cc03c0c8135a4b012e678191849299569901c93769b6d6d9dc335c40a659df3afdcfd22094f5882822d3fe52f54fae62f0809818fb87e76ff6c78e78379ebb6736cc0569527e2da1f925a3b5c2ececec62399eb1f1f73793aac991e9eba7533daf2d5ab857d5b0ec7f7f7ff0f8e8b8ab21971092d6adda05ad857bcdca5a2d5415076c9941ec9f49213ec3fc3fd9f1d040ec93e285a494a016c4fec9b16812bc23d9b6442d24c66db90ec214400a22b2093a736db1367034494cea3896c81cb722208d52c00813601048011772d4076a494ec5ca73b9413a25a2480bb22b01c54bde6d9375559b01cc4f303e8a4aed5561134e996dd63dca7dba41c7c4111910107ec507e0001cabd555dfea7456a0136c03e9c8b8ca85ea65389e3bc2fc23d1cbd0e34c813680209134c0ed3344b6d6a49407134fdf67bb1b6d22389fc7fa82b81f87c57d3ad53818d55f1de211f837eec93da5f46239c4aa012227452541546399ecfcec65f4d5eb5115c7e7ef0f5972c0f5b47dd0136ea2cf9df19fe618edb5fe67544ea23da57fde0dc0669bc5642abff53f0eef9b36849ad6bd1d8649b2d569527ec021901204024c84ecca6db85da630c83231bbc846240625010371c3226ec55e97ebe5fa36c8c195a0101556956c18ca15683fde673e7635844088c93f81df034b41d747e74565ab82235a3bc47a01cc0342f1890613af8ed94ec52753c034a0e674dc3276125c49207ec5a4927e3938dccabeabeb969882857898403e9bece6cd8b1041be175bb65322de0ec6868b05247e9565669b21c082921240b4e325bd6b97e97ea45feddea20f26c8283b8ec312b17d3a2be26872b3f6d5c0e4a8258016de09287e84c9de21a321342da403ccd27fd7e999e3b1681ad6a75a63956aac2b8ab82db1b34bdd0d673469b56c275a5925c8081184e8a2ba3fcb8848d6bda7feae25ef98f6d73ffedfb8fcc9c2d7ad5552fb80a4fbabd712e514beb84c02969ee70fd7fd1fd2fe87493a348834fdb5b6f8f8f8723e9933eaf16036deef1c1db5fd6fe012457d6313a752576a5b09c108cc0661b8505a46cadb67deafa6db66c5039cbc1d8a8aac2aaa83798c4011de89c56956888edb7ec9376c32506c857d539b2d449681a76c8dc663312815a4ba5e9d2fce4339a235a50718004ecd0d771c9792487f635cd994b81cb75a4925a250f34fdbf06f67f6cc4942710886dad81cb41293361c588124c4134804988886dbe5788e315c813620a81325282a8277d49bbddc66bb95901c9334f1c3348c49ec4761cbb6bd6bd6b5fedfe6724a494941f8c3af84954975eac56beca25a5b69edbb01204c480424070026441c9352490ec35c028b8aaea1006db9dca73b2b6d837ec93f000b32a5bb8f88dbd6c5417ecdb5bc9b89dc6e565573d43d67d2768b73fdfb972be72e9c7ae9359e9f576143e7e9ad06ebbfdbfecf1ff19feadf9ae9ec64844b83d5a37e743a179569d5fdf663d86d9f5cb479e2cd77fb1b6ad66bec97da41236404c0178ebbdc66324acaac27976b4e24cef1f1110cef1c161549c48c6bbe4bec011d78425a25a4ee0bb8888c04a251248b973f646a4b136addc6fe93f7c55d512264f6f9ebcf45fd4bb73b9f4763917555a81a9dc6bd6a536b7da41328ebfed8de92258811938452507eed93ec835227eccee1f3746de6bbea9e4eeabde1744606cc01c7c22bcafea3d956a4bc81c323471cb53ebea7cbb81180126db85c66bb901db5b6ca1b259a1d86c925cdc6636bf1ab66b34fd9692110215a2bcaeef70f14068f4f3d75cb3bc01967996f01926444418c01983225a428376c0980480e95e507ec36884926c41d93f1909b09a596ec54a44004d7fddec31544ce68eca3ba596599600ceafeca5b9719fc7f7fef8f987269ba69e37ed40afa808ae5923db88ee5ff8e5cf3df72fb9f86fba37743f3a3a30d54e6dad9fc7eedbfb973f6ddcb972d5fdbe22faaaa67285eeabd295b6772042792990e5d8aed53d2dcac26a3f955555e2488982128f016bbdda13667eea96da77204a6daf9d733532257bd6a966bb9dca603ddee77102dde6d9b441db7e08a476a4842220a8c1992e22bc23464bb6d5a9252b6d6fd7f000bbcc0646412480ed3dea1cb6226081c9784457156e97eedbfe78610081a82bc259cc01976a4b61ada5bd6bde03c097e97956ebbad53b6da1360115b45a4a4fec9de7801c54f1ce0626c86c898e302253be5d665bad57180fed73dc62507ce6735804284413dd66ff04c93babca49250925e97e71f2e5cb871f2e87c3dd9632888d57dde6d09379c9b07ecd663b96bd63480d57dd59b27561727255d5c1c1c1022933dddf717fe70ebffb393c7d6e551c2e592ad016459cf99f98fef74e1a3ec331d91b1bbeda5f5555f5ab47f64a4be5fa34c8747474399fc26c834bd6e569541543d43125cc4079a51746136a2a823bc2bd43d8ac4f8623bd5d1897fe55e99f09ad6bda6436599692930005a4a49a8bbeae0c11884023ec977d57092969a82685f26d3d9fc193f3a3a327d6fd57d6997e54e505a02836d54d3c030227921335ab3469c70ec9b7fed9371499d501819840369248ea961933484a4b8ddbff242aac4aac59e12627ec5955e6dadd99646c482142606bdde69136a49876b8f725a444857157995934bd83b652760981342684ece0c40708c283ff4f5eb2192588e5a0e04412448d5b4724218c188c7a4c090119323068c48451cb7fe511346a26aa67e72036c40363019336abea376c279c813ebb6810c830360e679d07996da3b1d8aa160bc168f6e7bc36a5eff41d15abde0c1b75085feb274e1816064dff8e70edbfec7bd3c7fe5a305a4e35f4d3573f6ed44063a1d8e0e0f0456993f1ba15df687ce1b1f87cbc5ea9886c3a19492c937c937da5375288865ba55557d259ab57a769515d4663dd6bd8459d47f6f67497f4e4e42bcb8bdd3b30092526c899bdd8d9f675186fd631dbd23f28328292faf5bb6579590523fc3f5d9e914c4a4acc5a23389d97dfbbd12a576926aebf12680a4ac4128d1c4a816797e5bb4bfad9c2db3d52260f49826a480424aad1189412495416d859a484e2c85b13406c990ecbf1af3a3a341c0fae5d7a73b955a411006457d9569d26176eda3da5fe20d044134882837060a556d57d63bdd27ec09327ec1102fc3348401327ec01026c0b316b180933e404c91a49226c0b9d63b378af673377122fc37becd9d9e963d6b004a49ad5928680e59293d9ec603d77d63259aaac2340fedb59aa8f8dffbf9c3d56b48c019d7534fddfa162c42125ffe9f6b7ceb3fddfd47c785fc76396a966176b446ca82ba2beafcdd37163bd6ef5d7ae35fd5fd3fd73edf3866db9fcc76e333e1d8a82a09a46fd7fbc5e9ded1ee7e555727a7a38537fc5ab8ec7ceafefcf58b008cf3379f2f58b87980913e2352a0925ea55556149222eeaf54013e2e4f4f43bcb05a4205aebbe67de57aa22733476dc038d1623b9302c671bc41204c4a48440468cbd6a568ccc0640712093042b381a44258002076c40fe527e6d4feca9c4b21c999fff9bb73b75d53dcee3c6d0fc0fe0fd0b7fe33455575b5ddd25bc11274a40760244ab84c0128131caba0e01c8fe37990624608cd4801262f73402739b842802440629089830621401b2a1cd4bbd82ae69b6a3576f0fd0fe0fc0b6dac5c3fd7bbaabb52384ee351a5b92e054d91aebdfe97ed5fca5fb5fb1699b69ae2038188a492525a5dc490145a6c0e90f20bb02811373ef3b9ac1e9e1f1f1f14a6d253fabeaec1976e4965310667d602be7ce198e1eefe3480134ceab85b615317e054a0a9120836a0ec31196510247ce445d0000d445dc4d937e89b849493b992b376885aaaaa837ec70ee7ce5149db3462424315335200519ebbe4546e97a9ec729259d932aefdcdfcfb3ff0eb760c7589ccefc443f086409f7ef76f3cfb1f7df7de66365bcafdbfed771c8f5ebc796845a4171ab3e97e7bf0f0faf5fb66fe008e87c3a37e27ecf6fefef3cef3863c011118955598d575a417ecd0b9110069c94d00d97f1ab6244beabec937000d88caa7d815f69aba80a13afdfe7f9e47a2961929360fbbde6ad9c2022f1c1940fceccc818a9fc34ec9118b492466064dae8a77c25b6200a66d97ccbc2b434c4988880aca9899d1175da9b6d6f73a1248cbca7a972119eabe8c17ac9199afeaf394d57d5a49c800d2c4315da6dabeab23253bd821196cc2bdfe7f5b6da53b8415500052c0000e8154d004dc40d088808ce93f5c45555ba843baf4520cec7022a22adeb92118aa21247134fd57d9433d5544408630c1886953569a5b65fe3e67fbb012a18cf2df2e7b9de73c0df75c9fe94fe8ff4fa3676f75ffdaf7dfee6b71dc93fa7f7f7f3fc37c81104111f5dba755a4973f6edcbffefbfe3400d2bc2bc66c154535a4de1cbb0d5881189ad0859352268668bb3d43532953004ac5988abea36f14b93152a963583991337dc5ec7e3300c8bdd6f75e8b0aade6cd834aa9318a447e999383645355d01363b92167d2885a0891102106471bf0130b364c644e54389e47e56a9d44d443d2ba65b6f3b225b5f1cccc37a552f5488f2226a2c80d50f777b7b4883f47a43f48a2e09c44b4aca55a4925a6a229b84e697fd12486df17da5b6e5d44caa012210ab6cb2355301260e64c58be75b799b492226444136ceabecb7f1248eabe89954c00826f3c8b9d5ce6862b8a6f7b9937d3f9fc925260c6fa65b7bb6d9ecfd8f99f068188ef84ffabf9664e14fb5fdaf0e75ef5df714aca9799555bfe7cdc663d8a5ebfdfefdf7ff5f0347bec3f83e80a6f4f0f85a4916c17c17cdce77d23626dc90c97055fed73fc7600074cd8262bdde6bd8ccda1cdb28c080002a2fcf8e77bbdd03c0d638f066136ccb6a57e9b11761cc2f2b28a5b9c9888c0444a20009189953430a553255511144ceabe26c8ed5869a981364fed7b355dc9224ea61bad2d2ea9d05869ad57dd01b39c56d97120c8e3010eddef6a3fecaa4925c4414dad35244ca297a96a4bead60c2438609631153a0e5b7418a6666c8462ade92d5e7df5d266e27f2004b8f6a7636f322a012c03c0d57d992eab4e97e9b655334427e83011ba0c296d3437fc04c16bb91681c17893da3d9f0d70bf79ef2ef5038af76a083340df6cfcc78bd1b3008168d87f177bddf67bbda557c17bfd8f6d7b68dc60dc819c1895e4f38f38fdf96e3588ee5cbe75f2e5db4eea327e7dc500137ecbdde677bbd937eaca9aa4ec93f01bb75f2f5221530daa61562ffc9ea525d00801951c63b9dc8b9e555f9eef10008893480888404c8c4686260a0347d7babbd83f725b4ec94fa1f9ddece6bae59523a3e8136fd7faaa5a88371ad7f866822688c555a1dc8a577125ae23d937a55a61420674d4ebddac979495a4da9b494937a496d937a52da37d29ad17ca5b21221811144fc34718115a496d4b8880004b26fda74031043ab442e6bf11998c932664228b4fc3dec4b76eeabeafe7c640bc6b0c766e01ab01a346fdeffcaa0f47a3f4b625507e24801cb7fce0307ef77efdefba4f5c1f5af36e0b1df455feb00aa5b4d4c44e8c393f3e836fdf87c3e76df0b9bb79f2101f8f4f8bfde8c88f0f9fbdf4e3af8d50fdd0b9b97177048d4b75dd030024ec97c16044e3c3e3e9f0700044e8bdddd48eb700866509f528710109a6a4d0e3322a640263393e1f96fb9eb37e26c805b0a14e5314db0ac3c8ce9d1e53b5ea692d63f4008c48954004d9379bcad2d5eb510b012cabea2d8c90ecbaae5aa4aa55da000c4400451a93e5bc9e47a012c667bdd6805a49a5b222edfbd733f4394da666a2246a0aaa2a5c06c174e0e55511ca4dacdc6e798404a1868d845ed8dab6660aaa48d829171bb0ab4531474cac7976202028a1d9fa55b05178012481afe313b9993e04cd080faaac800cc797ddbd532ce2837ec5c855c014eb1f5f75bbd8f7c372580f595b01fec7d613d4b020333b918371cbf818d122a96582516947e7a3cbcb9bb8cefefdeb5008dfee6f6f5cd183f0da227ec77737302adaa2894d3d3d3d76f9e7cf0f07f92d2e811d15d2527ecd6da008648e8989094f934dbd4bdcd3e98b575a3d9e44601c54d2a97e504430786d037bb19b5431baa23150fe1c41cc4a6566eebfed317d49bcab4be4478dc6afeb755300c6df309d5abf605059b4937f18bfebf0d5455d5695a4acd0be520dcbec3d1f4a5aa724780e909011cca4ec9252045fc81d3f0d5736fd32a5929cc4811c1128a4e57956a93fa904dc4155104005dcaa869a6d204e88811bdf3e88cd5b4f2337801a972b406ae34c1781afe70ebe711937ec58e680dd01ab8126413daa26665ba4b6c3005556dfb5ff5cf72c63114df130d7b5dd5df98381cfecbaf6b4139261055b58b63678bfdc63af096e3f1f1e90a8cdce6b1c1f7a7a3dddef6edbfad70bfbb790600c8a50e47a3d70fedb70661bbe6e3f9ac23f23f9f87c3e67b686b32200305da86013af25a0206b801866423888aa5b1b3ccc4abe364fc537a57d48aa18d9a41515662fed30ab63087949d117c8711756b30dd6cc7fec7c8e726cc937ec4dad98dedce011b4a27d5ce1a820a5b66314a6d64395fed3b7e01208ad9fc7e3d9e4cc8bdde6b6da3d9ec25a21a936e2a799c3b6dabc2b00867d786e0c2b9dc6891f0f8713deb7ebdf35a4bc2b43a6b122b53cd54dc0ce2134688fcb6548ba1e2a32aa8400a1d97555ba05b657ec145040baa2804448ea3fde7bb994929ec7c7a821268e10079c9c89abea36421dcf7eebebff59f75e79eff8ffbeb700effc845f500c01267d5f4e249256fbfdf66b34cec7cecf3edf3c3ddf3089607ebd805df66cbdbdb5925088001fde3cd32391e5cdbef39f8e3ef4e3af8fef3ffc804b492faf5fb647e6a3c3a6ec3c8d108a7fc5b4124035303ba9394dc6aaed3389680ebf8d110a98cc7a37e579b9df67d96a3d1f0b9dc806a052969b85e82890a96dc96334fb91a7748d5881c37a4b8374134856e955aa3661117ecfddddb8de66f52b4a5b039762f7964951901b13bca6acaa599173b91681a3da9b4a4e882832da9b4a5e9eef1e4f4702380ce2a3e8797b9ce8f4f8f4f07f537a0e878eaf8edb6da0aad65d7bd1700597a78bf2d03104b6dbda1a20d5efd10411d01d880e2d9f2e5005bb859b133b3c0ecf371dd0124c8d1b7f55435ec554c7c0bbdddcef6f5003f17a9e9e47c679f4da551ccb739c78fb1ffeefa93ab659df1a4d22d771dbfeb3f3ebfc93fad4431d9f0d7cec3b39257956e53d2da488928995bbcda95479492f4f4f4da9b402a3e3c3045139296953d2bedc6773c0d345143d0e8e229b233620da2c06886134b5ad16d4ba94103322043d3f1f093d219d94ec0661240f4c025127635dcbca06207de35bd75bdc37ba88d0100fed53c61899b6acbeada3180cce551cccad019de7d8986bac7d65dcedbf1681a12acbc235b129a1037937e601b5b7b8891db7f840b6a5e1e1e1e3df4f3d7bf6edc43d4da3ad6f367d573e83aaae3e3e3e3d1e104ea0b1226b5856b5b95be381e266e0e8999913a770a6062c08ec9b01d580ead583136411335c1681abeafc5becb42158ddee66fbbbd17c1b50a269bec5b440e95f3cf0f5d7bf7eeb94f559b89cb3f6e1444aa9a366503fe9367ebd3d9569911ca1d1c88d5daa937e1bf18d9306a6f2ca7fef6f67f4e01a8c7ef9c72e0c6a529364c0ee5dbcb30679352315da508114a220021d9f8f8edbf83c0d50fbccb28ac9e0f4e30f66cd11103ad3c3e387347b5c57954ba4fd7f0880546dcb8469c96981c199a94167ec44454a9ff753be9e6b73917c1f22f947ea496105c8ed743ba668c1854a010455dad0cb353b6e83a005a6204566c57d9f3bfc2f663b9e24f347db65949794bec0623e663edbf8ed300899ea56e56a3f321ead7f00062ad5806aa25a4c8dde63bb3f1fc7e3e9e4bd3de7fbf65c5544998890081338a96d6a8977c87def1ca65d618564471e84102b91ac540cb2fe200000200944414456a000462feb32611500455556f13257aa1adf473073819a1bca09cfdededa1cb100403af1e6675f7aca9703089795c413480bfdfe7c174508a5b6d29b4d45b6644427c88cb69c6a8e34071c301a1203bb7fedb7fe5eddb005b4bc4be47e1bfe0d460f1f5cdcdee63b509a5a625babeaba7676aa06a0a27878783c3ebd2be4c480651a693e3d3c27c3386505124857e5ab01998a4a510499114a2894dc9fc7a38a4168b0c46d052d60e000a6801a3f7923cd090eb2ca1713db33bb41cb8132263f04fbc69ee91c7deabe25a4925a64bad7878787bf6fd28acdeef6e5cbcbddde670003fc3fc3fc79e441cc93a6bc5033b65e9b5762d4d7d5a4901243159849a222de0a43d56f5b546c6532bd520decb231033a320644622f48d05496ce3bc93bb0eb1b6fa9aeb77bbd1937927553427cde7563ca3a5577008fec77eb3fb9fb9fb9753080df96faeb4f6fde3812193e17bb564ba5bea5b651131d67c2cb7f64896dc0e978deec5c804ca55555afeb743503b7873f64b46b29d326559dd66baaabcc3f0df0b69c7a3fcb9bdb3404d29c44b4e5dbbe8810c42d9e4eca69579ac4d21125271dc92a7674854a44fc1c1108009344b6759f535de9ccc40c6d29d6950119c93fe93daa655a6ec56d4c124ec97c13606757196bec04445503152a67b8a288453d69a43d2d5b888ab8d9aaec3f9b494783e3e03c0edbfdfe7f4087c3f9a9b20459a5b657e283f40a8e8bd44aa8c1a5c1d9895949d3bbdc0363c3be1f1f17f7b7bd6fa966022501d12301121243712155d6b6572407ec926513c61651cc0ec93fa6555888c01181888add30ccc0796d919098911805caeaba9b0e19c1e5e5f8f1b05e48772c4e977edc3cc3fa937598ac1c45435a42479a34937e535ed588ced145f36c163bfd302a91a10828101b494c0057d505ae3dbd7b97d462ad8e3b8448e8d2bd43d4979524c8bec39986f13c0d74600bcbe3cb9fcc9eb86d4820228a4d290c4c3b30da52fabdde6383f6550b11c5ccad895d8dc85a4110355d5795a506cb7fed31fcbecdc74c0897e97eebf134c0cecd5c0d0c0dcc26ce4c4cc4a9afc9e2c284d61e418ec9aaa7fefeeeeee47a3dbd7bf6db7f3e673bfdfef6f6f6999359c55b4929dc4a65240555043027c666a8ada8e2111a0091c03c0d24f5e1edeb5448ddded836ffc8630a1012c5e3dd5731979bd30dc12993ba260a6a40c183020507beab6da578160666925ccc8dbfebfe1911c44279b034a54c000b0a07df5cf5cf58f5ef5ef5efbffbdfdf6c879f728f89cf47c1fd579cec93661d295ba42243e6ff35aa5ba88093e8d5724cdd8b9177bda607188d423291b37a7a31a4d124897a3d47e3d8b9d5bb9dbf010805b4520c5e4394a2bcc37712c67ced45144ecf4f8bc9e4da1508a50451aa86d21d13314a4bc9fc7eebfe81d6956aab8dc3fc66200a7d59404ec9d9371d9fc4eada287816066fd6ccaa5b2988a95a490d5b5620b655f1c78e7388892fcbc2398617ec192a869a66beab68815557d49111b19d0693f9ec7e3a8417483e836fd74278933a492d63785ba888011092012a72443d2521193667a80128f16ce35e5b9ef4364f83e68d118a0020181895343998001180d4d45cccc4a5b04c00840a2aead4d70666a545c33311554453323b57d53671cb7f8ec44400c88c132937e880caa8326b68c5ef87e3fdf4d53f0260527d1bf17a3d91454b4553c665300844ce080d5cea3f17cdde8d5c453310011d9fc7e816f86af8f6f3fa93d8347692d1251ada5d4959873ccd64ceb9dc8e11e8f07f888c1347d77f0f9fb176a7abfb7619c3f4b9dc66d291aa28a830c812011c27e5e87c3ce7fb31195ec3d497945a03053aa4480d5b9b3d9b0136fbea9b4923fcb87febaa6a45444a651d57de2e09252062456222078d7fdfe7f7b655e472cbc927223ba05552b01194da5a49a92b32662f1cbfdfef6f87c72d25f47e36b6cad3b3800aa25a4166455a6ba787e65bdb7fa5aa93b62f7e6bf4957d5ba6140d25a8fc5419a2020003298881a5bd393226b13ee21c7b8ce01098ba913897044449c536c8c6aa2aa00d8086925a3c1e0bbdf677bbd9722555267edb36ebc44e519e3af4e3600023cfa08faefe69feb88a862ff2ffb8ffcdf2df58f6d81d5e53d9f8723330500447e2cc93fbc7df1481af0d7f7bf2fd302ca937ce2a9aa69c1c12a4df74fbbfb3d1194e5bcab83671d7845aedeb871dde7fd2c5c811527ebffcf3b01cdee66f8e8e4f4789f0f43d3e32b1c6afe80d6b3e0e98ae97a7a787be999117d56964d8004c9795e1eef1a4e2e930026388926cea659525167e10925e4cec11211249a5b4e466d6f956b9de67c1600327ecddcdcdcef6e6c97f124896992594806d45990606a0d2bd7c1270fe0d04a8446edfe676a26aa96945b6646c66fb7dca0b9a9b7f32b9203087671cbba5b0339367e283fed123a18113e57556de732208111212311d52e3f01119dc055d0d8c0098144451ccbf04845dc009c0d89d113ba5bba250888d937ed9189aa2b37ec70344242fe36f3c8df6e6e1e92ff7bffddf6459000060479814ef96d6e1c841ddf6ffed77ef2ff5af7ebf00c4096e5e9e1f0bddee3471989f38fad7d73bfd9b8d573e80ceccc8c50030235fce010da4239200e2dab6da91d07952d8d5fedb700503423da256d5667c8d7f111d04d2da37a7a30aa934425dedb3f8c23d47e3d9ca49691c6925a68f15bbd6b18bbd8b2c3fcbaa2c47e5411a116236c8ecdd9a65444b2979569296166e84e44cad562124055d2a29a69ec375517ec5801a94a898aaaa289507154a8991102702c62d0aa2d7c14a5b80553f2baaa636b1739af16b3d84240712fa97fe989980e235f31630b6dcc891999900d44505a6da80cdeb53086c867b6901f888c08088a4de0a920a12a9344eebf2551c4138311949ba908f0cd21c94b5fd1664440ce45551851bbfb3f77ff7f45c4108ed537179845bebbd3eba8440ef7beffef70222f6edcb93f1f47a97a43dabddee38fe298e6f6e67bbbb112c6e3df680e28fa25c124c81142d9e07433362a6af0324f925abb81bbeb700009a60252dabe47644cde67b8cc0a53d2be1e9e9673c0cce910bf8d9aac9fc7a5699bb01db7f3d1f4be437c81d317b5c44ceabdde67bfdfd6ec5f07a3b99569adcc190807bbdd9aa43c1a4c024800c4225b36e7edf68f8c888b6929252f4e544a10f4d64c967deb36fc48b6d25b4555344430100013861f98ad8c8b55ba6a01c244f8004044c8460668d6614511666cb2912dca901b989126c886ae88f91aa3333135744d8159f568fccc08880995de2f97eaf2a8352ea25809c1bf25aca9a899119febfeb6da93729a5111bd604410aa0eb9f4f0fb1ffbcfd53068f2bb83fbb1afec0d5c97e2feabaf2208bfbffb5fb3a0ee3f7bf07c7c72555062fedf667b7e4963b9d0b773fc33a3f680a20cc8b6a4340f4400687c3e399a02a5da9daa03202a5fd7f0cc0aa5a414086c8857ec197708802aa53fc7e3fdc032226365263449ccc6fbfd300c17a37aa563b9d4c1a711d6747e5fbe7d17cdf9e1f19888db744266627cd6f4188ea9dbd419a444d4f71c0c47d5e31359762bfec633000339f01934a8a1b6bd400018a13bfc9379c5bd8d408a6a8e30322f03c64557d29b191c1e2dc30112131863a3e0c58f39088d097dba7a318886801a1afbed387fe9c131abf2f51e714e1a5f01c4000dcb8fa1914bd9067ec5651666440247000d7f0aaadc41ba991079c9b6da71285b2c49acf6cfdeff3ff0ef0fd285c77f4440bbbbbb996e3cbb72c420a1ab451c5817a9f3981c7c0d912c0b9dceeee67fbfdf17a37a6c6b62a7e54d126000303b6c82f492188a4daa06ce387fe14aa8460037c1c9371d70f13003f52785fa4191fe9ba69a557a56d3d47633b0d97fe3a12488d5c1688d5fbc2b0e598bd0aa925a2224aa88826a937e3d9e4d773cdeddd8124bd2fbb0ed50f03365ba10900837ec623313b11988cc57533b173db7febfebf01c5a5844e4a4aedf56f239137acba69ad070f52b937e264fced74460b45da812101b1a533f24222227d410144444caa0e41b192ccccc4d1325155111fed88c50f1bd1b60d6159986f52e320118ae506ad6faabc5a60322267d5444cc5d88824ceb492b4e5a8a4f12208f2ec5da55aaa970467f9d72f4f7fefedf6592e1088ed9e6c20d004f6fe6ff9d7acf55805e518e75f75ef5bff9e7dffb111351bdce6fbbdfde0b9134fd9104b5a9b9911222a2221353ad892896fd7c313ec3f99d1a92200471d38aa4ec51dc780075acfd8b9d08a99693f9e0d7887bbfd7801014f2a1705db27c0213bfdd67f5a41f12c3fe1bcb7f7a3d4663f96a9ccc6d5756f1c7c0d8780783c1abe3a99519a5a412086ed33fcec5baaa2054451d9322abe1e02028ebfe11b345b5695015099206022278e888235e2a29352539c9b638ae0805d0a13f31fa4534542780ec7867ad89bd8f5a2226550cb937e801b1c19da87f5b340bb41ddb6fbbfc47496f5d0396003447448da7c8b807301a55f76710006a57a6fef0116801ad604aaa111ff1cf0e39fcedc3289bfa026b58c02a77f2e5edc754e1766663fc37392977d57faf387f78de64d0aaaa115bac1de1b6dc5add387105c67bf742e3edfdf0ec132193f12401d2b6a50c17c1b700b253969c1c134dc8668a6bfdfe73136dc5693f200055f9aaac673b34ab8eed6816896a5a6da7e3f97952531875da579471591c4bcc383fd57d91047d7f555e07c3e3b468c81089c937d43c8662cc134c8a209540117334f1240e0cbc2b0891331039126ce7c66ebd684e24c1c9f822ec936a544a4acbeab66272ec9367e8d1113b3243334052474e90dd5cde993408a6285bd74a6b0fbb801808009d00ba8a65d768a10d8011184100c4f2b3c8bc3de67a9988ce2211cb8ab40cc78e36c808c5b6d8ed7067f5adc33918a99181ff1ef0e3e3e1eca03c5b1e5c779d192459cf1e70fed7517c791de7df7dff7c47ef8781687a7a7053cddef6347d759ae2683ec3d5ba553de16abcc7c250d426c8aa5f9e9e9cde67e88383f128300da53b7f34be473f2beabead8a9d4ccec9bdfe676b4a73f9e767e3f9189ae47222af0fb6f5fdde67b6a527a2954a6d647a1bc9bc3b8615c037ecced5d35d60624b0528492fc3fcbdde619b9b5f0e560134a62cc066cebfe35a43dab635c8eee6e644b4b0baa86eb59ffebfe7a96ab866ec43680d6d61dd0b4da57a9e4d27ad8d97e0e9d1330ab0c8d83dc8c0879540b91a23218936fda256ba5bfc359f5ec699519aec9344a67859eac59cbc27bdc8e2b1ed6b446023025430574eaa6126fd5716c173aa02a54474192da8bf33f7dccc008e3af4e3532cb63d08f7cc5af0f39ad3cddfb973fd8f6e78f51c1f52333394344cfcf3d7c3fc39be268dc80004edd2b6632e61252de954f3c005b08e527f3c571cb37408555240fec11119841017db3204ccbecbe23f83c02221c9303bde67ba5be43fc7e9f4545c116d22bdde077faf5d47e3f9f4397fefdf7ffdf73f6edc7ffbffffa37eedb7fe30227cce108444cb7f7c3f963b9dce7fb733b8124fd7f7e3f4beab4fd7f3c8dd99c59a90006d57e3cdf34801af1685695c9be6fbfdf96a5f9e9e9e6f6e54da5d04db7f96e525e21bfe4142d29d9b0d57ea48e6a96aa26ec09d502eae097819247d7bbaf37daa604c454004488138b0ce0dc400d5ba295d4114f2d5bb18a8d549df5a692999555101f26fb967df27ce100ad23c9045a901389551a5938bc1a64444825a4dc996b0ccc1d70a1137e5d0d6956956e97d57d0120157ae07e9653bf2cd05f7430100d63d77beab658f31e629ff86af48ebff1cbcbbbddc7515d71d02fa398da0484e28b81eab5b4da6ac71f1afb0179a9846090b4e51465f0a3c034c8eb6d2921e8df8552bc40425afdeea5b14d85b86aaa975a0eb10b07a3e14c07f7b73ed583d9e43e836e496d57d6c5b5a6b22aaaaa598cd58ea522d4676d468e136cde67bdf034be74f6e6e67f7373062e08a4d4beabe27e3d4ee2074c33babcc45557a96aebfe347e367d9a00a949ab6389941c267d9f81a170af2a543f10dc089c95020d45a06a537ec52b4fd7fc12c55c31818b34f69355fe8c0d09101f2d28322e3ba44dbf8710a8551b5f72ccf50a66717e1133344206665d2a0802a710c3d8005b575fd4c378ec53309d01241921248dc663dcb8d00691cdeb4110cf2c325f7c3cc2edb7ff5dabeaf1d72f9ff488b433fba75fb0eeba89cd041c86956503244c1bbeda65ceb33040466f1a97e3434fef2f48d44440ba00b93748885a4e0cea3f16b3e6f0bc496151110353dac064001dbf49316da5a493d9fc9b69bd7177777bfae3df4f3dc25d771f47a3d43d41364548493da9bab0dd663b917bb555d9795000019544c3bb631b5b12ca692a5509d18910936666c0dc9eca5d3b5799bbeaf6c97d61135b6e49b6900eb16c4ec70eae27da009bd12644ca9b4a4969cb011937d86544a068600ecb2065501b48c4ecbb6be5104247ecc450aa3023420443405043053353baaad08637ed5035c66152d8b8c545c00c88fade9a200020f3f817088a79d7e3c5e863c50c6120522002466755ba7fe3c590c9da712fed38df8afc3c79b60f7af9cb9f6e73fb9f72d3eb468f3accb4430083c1e4684f5545f53480d77787a57956150cdcef60991ca934d9fabac5a123535555446200407c446806abc2bc2bc2e00adb1fbfdc6f03d925e55b6605501574cd5805b6d56d99387a01ab17cdc83ede6f60c7ca5be2b4a674dc937aa07c3e95aa536cdee67bb813631834357f434b5555b4acbc2b4925e87c3aa8988488e9007d9765dadc545adf1b3fcb63022004bfebf671d1b77df8b95695ae97e8cebfeb319239aa20455533346227c45945598cc93726a6a799b98dbfecc4e2491bf26de6a66aa66005cc8095104d0045fa6ed06cc46ad67ec668088cdadad760c1e99b3166cda75df19f9533c765da2e5de458f9b7f8b491617cc3e83a88492717398f217370f74b60b1e2b4afec77ebbf39a19eec7e7abf7bf9abfa0a5787695bcabeac1bbdde7f0ed9124338faaaa47552ae576765435604462857d3d1e83fc3fb8dfe7c1bf01dba9494944aa5ea025d13a77e011ff0f70f38034ff2f387f2c6633c832aa288ac235cb02c5cd57199df9e83d1f8741196b44d7e3cd3e8bd56956a3e3a37ec33b580edb79257956996a90d077bbd18a519cbc2b89455557cc34c8d4539d623b1681082889e43725081f66f168ef07834519881c9b08d2075dc8993c6631abe6e3f9fc7e37d2ae30b36ca65b2821287f4888244225a8a0323bf0da9101300676400804e84d4e93cd74d09111804c41da15ad1081411a8e71261af2e8ca1c714f6e6872c5f7c67398e57d42797dbba8b8d9580d0b8c012cddddd5a463daaec1d379cdf3a69d6fcef39f72ebbbf7cbc1228e78f1cbefbef7de7deb280585bf6e73f76e87c36b53ced67dd67bbd60cd80c5ef221eb490dd80a4600a62a58b6d2becbc472059dee6c17c11dad2530d3b37c80550d0ca699bc96e97e167bd0d5704712214dc9f8783a374124896a980d9a0c2bc27a3d9899b1290136c60d96b284da21ba954febfebf687fdabeae8c5c8188025aa9695b19f1a9eb0a517a6d43f663b14e27a9ec92d35ec91244fed97fe7d5669a5695610027e88dbbebfebb01dcce926caaa7e3c9b943d8999d4bda97d0f2a4d701138b0860dc6fa8d934007e99801d4a90b419ca1c1666f5cdada80c7df09de1dd3b8d8bd78fa5b21730e29ebcd0be507f57f5c6cea965f888207033bc92533bf166b45b87fe1ff8941b86bd9ef94bc48ae8b9eeb28aeb8fdffef7fb99169aae83e8d2a6837eceab05b86c8db8432e3f3fcf151145b69cbc43f1f8d2ee6a9323533af4400648c3f41d78a9a69c93da5e5ebc79bddfe0c04b4122213d3dc3d2bc225a202ebdbf742cb88b526c83e8bd3d9e4bbb9bdbb79fa041e0783c37c24ddfdfdfc7e5afe126f5b492becb08209bb8a686957a49213364fe910f201859a979b8179c08d2ca8cf58223aa544a4925b4e69172edbfe7ce63b9d032dabead4299e30759cced3222929aa212133baf06624e85ca15d418889881000b9dd92118a9a881121053abc9371a5d85334ba268de66ff24ef4d03dd67d2a4da2eea68817501dcceb036f330e63b530043787869534675fc1267834f1f5eb8d322e50828f16cece2cb8ff989e7880006a5211bb77fe0396afdef6ffc64fcf9fc3baa6c1a7022033119044a8aed9f6fd4d3fd168400aa40052d2bc47e49654cb0424e3687c7c7c57d5ba8c1e1f94ba03b7119c53dbafadbfebbe54a21708018dca6da3f2b4dc24ecf9f9e0265b771f6e6e6afebfde67f3c66cdde67d5ce1006957d6ac2022206a49292247978c937925b9b874b245a5a200480d5fd5825297f125a4008bc2bec3fc88c12077d57bd17373bbd08a49565929c9370671cd711178e6c8136b1dd27ec400edbfc93353d9a928896b52283f1a6d2c4465acc97e62593678937632a00c88808c08a00a2d0e27136f81cbf6fc3f9f261a649d14bf2a5251551925a8b0cdee0cf25ffa0dcf2dd9372a8fce525daee4b793db7fd48ba4c1afedff9bf99fafe67b9bf7a27d593dbbbddb9a12d437dfdefddfedbbd3eb46acc5efacfb9f79cc46d52fef6eee5ebe750806488c43d435f3e6e97e35fe95aa988093509a3f9e0054e47c36c0142da554e6f5eba9e87a2a2bc9f420817a99117e57502ef6c7be7ec7f108190c0d9301978fcfdedfbd78a55f7effcff333fc3ff36af7ca0677f2f635a413ebec9baf816a97945aaaae4bcceea5f2d9f0bfbbbb81323b366db9dfd133794b49535f34c6fae73dfdbf8db7f110ecbbb9bb78787896a9e2df74c812483d1e85d4db7fb91789e47615d1bbe910d5803fc3f1f874ba2888d7c8ad5f2541c845fa77fb16da226a6685402f2b6e2b233f663368b6fd95a41da5267e38374ed9d5280b489d3bf0a088cec70fa65b494b9dce24803fc7ea92b340713a9a2894bafa79aca22871a3cba651474da5b3f124307036fddbe8fe73db35801abeafc5a6ec9b378c86ffcef2ef9fdc0f77eef13f53c0beb4d934b2f4f7ab585cf81eeb40080cf9f3ccff1ef7cff5ebbeaebabbd48f6d7be3c3f9f8c1c7b092fb01d8d113abf2f2fd14d05d00514d0a4a9fc747a5b3fe73d77293573e60c9b2dcfe69954ae9e47213d83e01bf19cb35030ba4e28592f8f078f8e3af80d8edb7ffb617fbd8bfd287e3d3e3c4bccd63855b6e96a54019c93b258fae8d797d4bbddea178b0d8eb0b1e8a9dc0a61db03c510e9de3c26ce1b6d0128a24ec9bbea7fed331e3c3edfed46c1742047ec1886a496e5ec32c6551a9248910d04a9472e2af16404c68a514e2ab4ac5b6554542e0ec588d5f347aaa3f95abea3fc4000bea9899244788005537eccd868644559801aa96a49cb325ba602d40cc6aafcbb730001340035404002038b04c1a8884484ec5b859622a96a9c937d7fd1006925a5b673473e7df3c7ff3df4e7048b558f129905381ee7255f81111c4f5dba7f6c1bfde67bfecd025f66fb9f6e7be3c3e9e9c93a0d788d77fc3ff60fea9317330363482c4800c442d2b6949525eed78eabeafebfebfe10944437ec4ed122e1e0783d43d8bbdebefadbf1afe4107d53382e9e9e0f9c74f15e5b48e2eb87dfa0881abee6f5dbe56dc7f7f7f55279c91d0455bbea3fd9771d58ebbeab844855a4713c66c1cb361d28464c0602da7ee49259c208ca06b6e4937e62ce98ed7f1363e6a18a407c00a97d5a694c06128b1ab0e30ba5d52593d96a3d47e97d525a47e56e96949b8c330206665a6d45a613037e490394513d0d5cdfddd67b9e3b4daa2dcd44a99e6d296d537a6b8cec5b20118659296d97acb628b48cd04519b0404aa25be5a7fd689c9d7134b12a10129771a3f8871811fa66896440c4644deda355d67f56544bdfeabffcef3dff0816108a04f34cd0203cf3db5238df8ebc0a08f9d7ef6f7ffbff38ff7e77f7fed9f8e696d97ce2ec3fc34f4eeb4da6f52faae262916800265bd25e15045b67e34e96643bebbea9dcd2c5c9eb944c271922519ddef6ee2c8b50675e83dc3da13c0bc29ccce5dbe7f3e0db11301281001e9e47a448bd1af5566fe3607c189b9841810a4d43f27e57e9bd1eb96a5e0783480136c8751bad56877333558c5231102373a656b640f1fef1e5ebc79da71a813e5c379015fa4b8122a3d47eb03dcfae0fcaaad89f2a805546a52dcbea173b9dc67b1afe0e9ca4921bf0bbb9d5925eefefe37ecf26773e9154ac5b525cc756f0b76c6e59d28208a440e98b1448fcf29c37b5cfc8379999aa153230110832f124922a5b089099281928088580e6a96a3d961fce0055a4a4c8603876df5df5dfb8c43b91ba181973df118b1ef92b00ff01c9c71e41cbbe3ebf0f0f0d692075f19574e8d006d57deabe000810510dca9594b108202639775ec9d1087fe7c16045b43fc2268c43f2beab627c168e73d5fb507e1aa56193c9fc1ab1bb163fafd7fd573451132406706a5b6d45b43f5f0ba20110414272b56e4b6e56d9a6dc3fc7631b9793b9493f1fc3d1fcad7db24147185e544bdbb7b6ead507ec58a55553937e6d57d7c7cbf601123a125bc5bf200893fa5a4aa53b9998415da8c482622254a6da514d4c454c001baae296573671a3f88822bd5b4eb8219a4153366671c3125555bbac99259c1c9fdd660642fec74fc71f7cec52198869eabea5330088dc17eb998573bee41596502dc4cc0cb12a8894522a8c434c004c542b4516433949a464f1a4a57e3f9c4a2ddddaa5674046350031ce5c603c07d2aed6376ff49dc30e75ea57bba296add47d7c8d571a97e7f2bc27373737d7994008f298a7271245f587abe8d33f66cd976f7a3c137e288c925153c16bbd0df05baaf2a688a2025d27eaf2f5eb633b3f439a883f10c134db006904e27ea025d3786c33e9e48bc2b494b659ca25db3f56d5427a29231a94dad4ae354da9296b5d0e25a6925cb7f199add0eda57e3e3d3d1e1f1c7c0d6ccf66f8ec085a4a643c553566c67aace93a62ccc0c25ac8c4d5fb9d13b09e66736c817a96ae788b1fb144c6b7eb75fae5189955997d5a4e9daa43abe1325fd7f7bbdd43352a49259256d5767ccd7fdb1e5ccb2ad5086f5c71c5492f7dff80aa6a0a4553ba767d571bf8d54fe510049dc2fabec1cb3624119a96e2be41d1239580822affbdfdeffe1caba03cfbff73ff3ffdafd9f74fafdbfad76f9d7e75c5f1ff47dceadf150a17b6b7eba85a1f5c66f0c681ab16810c0ce93ab24d0d19281f5fe5b57d5d9984e2537961c0d551abea36fee87837d6a8c227b91abdddcd87fe35a4c1c31b300817bb330aa22ec7046e0e3048808804ec0098d363cd7084b65da5124411996e3da9293f888688054a4d5a431e79849c9088c5a53bf8124805d45c43b4d7e54411198847d5066c96a3d3c3ebdde67b8001283ba56d9ec0002a554a895b5999459b2b7c9940000020094441445cd236998094daa4d6be1326c17ce36c8bb7173bddde000f0f4f0e368b9b9b126c3edf36a49af122a1cc7a3b49ce9813212008a1116353c7dc5eefefe795613315bad57d1b7a645dfbca67da23bf64206a61cd228dc002dcc4504b9991ba8f81c116a97a96a35bd4a8aae2bccbddc680c693f9bec3b74063d23d4e4146e465aa77d97f2ff5ff75ff1dfb1f79ffad76f6e325887dd5c912cf985d3f6ff4c587c752c7fdb04332d65f006296dc52fa888225d9370a60c797fc42711d2d0d0881369c925a536ce80b5ab85d61a21bdb9df66bb704752569e59cb3138dfee67956d3c1e8b9deebb163c1b386c3b90e36f36801c25a4b61c0e27eca20e881cc6d527ac33064519c56620ca927bfcd75532288804acfcc1d97e9b744498eb06114ec9bd06e136c1afe7d57d96a9c04f2b62005559252b4926024eb37e9808b49ca5b4fdf068036f17b73bf79e2c66fbb3f4bc74f7845371648dc036e96996a9e4784ccc6556fbdd6c8ed13694937ec5b6dcbeabc9e87e3f9f3f7bf91aad67b3688d8857d2808c73f484037002de239f2a8e200aa53a3fbaaaa08a808a5a49e47cbf3d9e45d4d9371c17da5322aa8c9eef15a4ae99db9d9f1e166a3e43fc26a3e8bd1e6030193c55c2e3b7de0ef44baf6bf4ef55c1fd8132100a9ee63bd682e5b5e3d76f9e7c73737706ea521e021023dee6e6fde72f78d3332393716996e3f97adfef626c8208206ccc3c6671c77bddeef4bcc783e96ffae538f0126f555af1788164e0eb2812817376e03327c1bfef83dcdb78c9ba86ddfef61086a9ecaaacceb4a2225383a56d554b4d2a44cec58ba46297d57d6c63b492b4e24e8a6dada78c1681a18ebbdde67a3e9a3f16b3c0d673f8065b4d61055aa55a423512054402054536480e8aa2d668e988693f944acfcc7be76f5eded9ae3cdc677b4e4dceaf444898a6ac254283777737b3c038a4973bcb99e87a4925acbe23d97d57d53da6668e877e7354c0b887fe35196c03722c081058b5e714319622121153ebf972e6bf1eeabee87c3e77fbbfdd73f6ed469a1d9fb9dfe68ea76112439971da91117e97e78f8e3e5b96502680f18bd607911c95a1773edf1aa2637b1ef35ae47eff7baeb8f5a687a9a25b54e478e6871737b6483fc377d7f79d942a53f33ceabec3868035d4cec5b15f8c77d7ceab3f4316dad7fdfba7fe57f3e6ecbeacce98c3815c1a79c5d09d5c084ec43ba123132a1002215717832cdf43d4e9ddde6f627be4951ea02e6543da95ad1413050060866c84606dc87f4fb82602a6826c8a71762eb8d79f543e05aa70023066601dc25dea974418d5153592a2527aad81f3e83c47a36c8dbbe2a36e9f47c747e20bbf9bf6ffcefef7fff0e47a3d7c7830895067e80d446a49a6e2d63fc7a3e4f4f47c57d5104118ab1af0d537b70626fec747ee2b15b649cf621dbbc9c8b69e505313961ba1d85a6edbf3d9e4783c1ade2df07e3d76f9d76fdbf78ff0f71ebdfd669abd1703da97957a57a649f8dfe7fbdde66cdc6680c8537f12effaff3fff0ffedfdcfb5f078f10958774acdc77e5d7d125314511af633bf9e9e0f2f5db7bf79f2359cf4f47c67348aae5e4a9557ec97432f1099c7c08ce50c828f83c8be7af997fef6eee5cdcddb4553422e0e189080d788a286062804486a4a8f5ccf80a6bddc6e9c9f3d1e1d105fdf8e0c6a43f545d8549115447a5667475449a1c94d2b1ea52d659a408e9d950ba5b8aaa009d52c3e8c1113a344431d57e54bb8880c089c4b49255b899182aa6d25a08a61d23fc3f43d9888afe7ca9aeabead591b6593f431c72f17f3e66bb9d7067be43f3dbdb744cdde670e34d456d57d7a27cfcfde3089849aaa6e247846460440c1cb99a8802b32f1889454c9bb0a48444d266edc41c0888c4b6b770fbc17a5a5bedfdf38aaefcdfcdfcfe7fbf5b4efed7febbfdbfdbfec23dcf9d72f9e4d5420b569525e5681af71737bf2f6f6f6fee5c0bdd4ea2be9f0bfbd07b3e8fae660609afff157f661ba9e69ed5fd33ca1ed1eb97f055bb68357aec467bbdd673434b13634c0126c6242126422211125427885249cd54ac54e22ac52a409219219170490209c2424a84801421c817ad8de6cee1cddd7ec9ee3d76e353eeaad3d73eb3ca1e97271fe755dd827715757476f5d1deb92d9db7de5fda5fcaffff7f301810adcf38e602fb0f5eb55558a6edfddb6db60363484270b7ad8b3fef9fc4f27a4926484d32112f03f8423ed1b531423091f54195464eac9ec7ea80d47d695692cc12607ec111b224889852b5361d0323d08922dc11157e3f9e66639c1bd8d78e367a8e68bfd022b24655cc93f67bbe1555988082186a12e70000cc893de3f176da526ce14f9fa2fdf8df7098b306e82d24b000c69783fa7c8136e03053570902def11ea96af010113605008829d51eba9c4ca379908e2facbb3fb8b8b85e27dbbdde68e08812e166fcd6bda4c4888ec2f0781336c812618c3475937e84884c6081d091045052024384fc197f0aff6c7a3322a16380cfcf82f5f86277eecd939b57319cc47a35bac7fe3cf0a95fa257da33637ceda1e389dcf59bb77743e171575048bb5f66317ded917392f75e79ffae79e7fefbcc061e5c8d63f58f3a1f87e365dc565e98d4bd6bcbbd95bef9f442cb7fd6bd2b53670dd095a6bb8e890cadc0aa006ca7bed3231816b5516daf2beac795c663b9deea99cc761447bbddecbca0da167c6708522332b9dfbaf4e4934b605d03c1c1e4d794de67793988467585feb2dc0be55f1af670c56784fce888aa0a978f4b522395210a6f1a309c349bc7fec462e4490494541984943bac0c7b398fc4d993cdfe37a4125c02069376d6b94d571540af4126cd9884c49490de5c581f5420deabe24847979b4054b6d6d57de7bb3b4c34c41541c595cefdab36ceadeac2bc1f8b225dcaa80a6b829145442b001b1594aaaa2fdf1b47520cc1310fe7a3b444a48804222136240e2a82c8b22b087b6763d5c5fb6736379bc3dddec0b9abeac9917d561747c78747470ec9b09263bb0d5bd4bd5fb9d4d392a9bd736ec0d214191cac7a1f0f03e1f8180da37001607e0f8623500091476913f5c5c5495753a155609d8372226cb7051263a4c880ebc2c7151812046f561cc66897aaaaa176529c0e824f6da7fec8556750c042d3440989bf6657176615037ec29643aaca5e6bde73b9dc46a399e47a1b9ea9dda73ba6442515abebb8aa800293d01bb2a1adf03c1fd3480d92c0ac98fef344a020082aa23a11d4aa013640ad70069c0b14842a08aa84140bf09bde35840958af02d43d7ec9334615d8bd63ba44098136a823e1f468846c08e97767f4f3f15158136c8d1c54c937ecc464708996f141009c02184ce37ac2226d096652ac945056fe976623927a1aeef3046a490356de36ed5bd8f2b81d466a84bd6b34367c3d9db4f2fdd66db9673b674457d56d5ad38389f4723eaaaa45536bd4bbbd4e8d3124a0d7e8dc4acaefbeffaef3fff6ef6efd30c2e540be7f58f2fd1c49bbf00488837ee8f8f8bf0967bbdd8763d1ead4136f1281e2a04021020425d46da504e49311d0d260a1e9f3442fef56b897bbed9d2ba9fc66080bb5d5513aa9d7108aa92821e066fb6657571f4f1a6e4e8035a4ecb1119eaf8125f83060b148d8a8680d7f5ab235a4388e2f9142d3aaa2ac0821bc9252949dc00d904d09eb7f4338280408978b11147d622b86c0de19808c6c0aa85ba667d67d50dc860d004a46d3e4c5195568b9bb1761890d0b5becb7f3f9d1c0e659813222e05e11111feda08e09cdf18cd3136c0d5436c4ce34a41881d21a86aebd6257d5a4145403de356f8fce547544850111898c0b102e01bdebfe1187670b85c1e1000c673b348189e007df4e6e5c39352946819daf1d4725695eef15a5278eadebde8da98d771a9dc8aa1df4f71f37c0190290a9f18fa0e71bbc751863bd6af89346164916004ebea38c6fcfcfcbeabec92715416d8d1e583d0b1222236133291b945525615467e1002c0bf16ecbdde6ba15d0207676f4617c72393e3288df9d38787c47db2bc0a926f1544c23218a486bddc2114232d07d3fc0aa2caaad73dea2569c178889de2e65396aca446889e9521046fbf2a8c029a09d731c52986fd5444c8293765cca0cc280aa4937ee367e5cacefeea0a488a60d8a12717712808b2b664881b7369643a1d87c5b02415695e8a63eca228b2aaabea763b587fe78ffbec12a376602407f1b3bedb3f695051550126849af4ecadc937fd7b936236994567bd5e665bc3d74f05542194610225d12abe434d4d6f72e236c8e3fa7624eaaaac7556c8952b66510425553ed9f2b038a618acac65555488d7c0292a4e8d5bbb66db63ed1aa4fed6d399e1c464ff5cfd8f5efb5fbafbe4a4f307fdcfb07c35f5d5eee3cf0eefc6c6c0b2199ba5d2101643a1d275beb8b8b82743e3c3c3ebcbcb4368c09dead66439922128a0b5fec7971b953986555529211885b6beaaea743ed57d6fd7febc226fbb8bab4b56132f61a966bbdd6bd502b68da9d4c8924c8dfec3353d4313fef42a3cbd9a3fe368e267d6ceaf6a966874eb0d78e36e49488d87b587f52b770283454139a88a2115cc9684ed039555cebf81356f6b200cebdc341018c8bd67df8da359c46c2b33d77162b89d5da67b8be7da86a3909e6e9c9837c1347b85c2ca73152f07eb27edfe17de46a82b66660c44c6b7989344445094aaa135889a3ac84c016f2a01fd584fcf5483480c0a6daf9fc61000d53d68eb5119015900de0fe44a26c8d1e322a4f5c6bb667bbd588de4717004a30b7334ed6841723a1bd2c5bb5155d892e1c1c1d8a2721356629e313284c489336c8aa28eda4fd43bcb48199fb8f71efccff8f7bff7d48ee70e4e3b71e7878dc6bd7ff38f4e7aef8fd5760bb01c43d04ec8c6787f166b524da30f966893938a84c0446c695d87a397cf8f1a10c6ade725493cb7bd1c1d1abaa6ade663f8e15a49ca1edca75dc9e3cacbb4dc6b96a9dcea1044f55d3481bad5f6d916ea74d7f1704097fd7c5d273c82dc6bddc6631bbe55535ce7069a36a14a211119bca74ec61b79c0b139ad6bbea30dc6da5208e36c49a9e342115023931219910f9acce0d2e8aeaae94d6d07bd736dbf38b67d73d6f353ee7ce72e72ca1f689f1fdfb73b9c467b95111195a0926a492b847d57280137a415afbff07f5a4b01bfebfe3ce44211087fe00425e08a430b408c07554d1266feb0f6d9a4eeab643567ed456de1c2e03f42ea7dbebcb83be77da5b61b829681f380a57e687088d5bbb81bfabea7ff65a434d597f146736c46d43a155e89745510a934806c2a37e4127ac1534265569c01286c889321823446d4eba26dbb4f9aa5eba4d6f1f1e3e3f9d4fb0d53f47091e5a49c0bdaaaaad6bdde67b399c46825467d576d6d0aad57d555a703674399584139eef8d0ec61eaced595083b3400770e1910843be556b6c23780d5ee30015b306fe590941d61a08d78e98088167d48bcba5b4ec925a677d0226956912ae67bd8131aa1d895153982ad09440eb7202943c51d019c0e0244239164ecb3bedaf20592565026f56154154d5758495056c57512782bc2bcaba7bede77ac9453f3e3402601db87bea27e0ed99634b797a7e300d234a82008c626ecd57181070e0ca52e492cea9d4c818196c39a5537a4127657691119d8bea8e41128b23615816e6a9624bd91b49bca9808d0595693f5c2ad2a98136659e28c8bea545d817dd0f51af813fc61366f69d9d765ebffe9a6355572399c8ac2d30c67375d7c8ec3333b26e8d781cc73804a93a4f135a458ee1523d43d008bf43d2f514030669bedc46a70bc5e9e1e1fc7c3e17fd7f6c513028969b9de67bbd9d26e5555d66d1ad6b5bc44683b0039281646bdc66b6da346354d5de67bbdc67be54401e0fad1baa65eb03c45615fa7575c8a292319176c6ec0d5f94230b53927f914508376dbc5e66bbddeead876767669977bbd5a01baaa222219c5271949c515a76b4a8d7a4d5f1ca5bed790cc55d66c3d1f0e555b6d61541ec51137a2aa1795d881d029575455f874ea0b840136bd5fb334c1267d57dc74f1d74fefbffedfcdf9bf37001181125c7cf4f1cb9fda7c77fecd9c92da7db6636444af8168fb1054694119c28a28465052457ec21928a6ec1991da13ec2e079935949250b6d7fd57937e2b8260cf577d58c1314611943a17d78b834c30dbcbc5e66db401403c1ad0b96b95d5eaaf0da91c6548868d21a9c93316e7e67a3366fc3f46649012588125f2a46c0516d84c4937649c828924c4929e339ccd370a48ef04b37cf341c4980a2abc2ced793431d9bbec51e70ce8033f00fb92ac2bf0d246d2b826017b04ec5455e9d39726ca781e522c0f3e7cf8717478b8383c1f2e0127698926fda82946aeab6a9dd6c0a1f56904c944d9b2ab63a01b0867d58dde67be5d663b9dce67de5e5e5e87c3622251ee38615857eb02b529d2c937eacac1b9412a0861ec75555888551eac2b4fedd034a600066b6db736b04363b05ac286c644d41bf6bd62b4f0a925bdb4e8717176f0f1f3a3a3a38b977e520300a1085da1cdc669bc5d83f0e2567c00c9535ec9452f05d165943cfb5d7fd211a3bc6d8b4126a019b0b66837cb37b72cbf33d50c09137f695c9b8132121913bdd67bc5f6cbb26a3b9c86c3910f48d7ccbe6a9813c675b12485455b7fe54454468a5511666edfd46111d85219cbdd678d7f92705094127ce3873c0004a0228294107fd532cfebcd28ef0a15bef2cfae6209643e9d67b9ef39f5efb1f5affbff9e09dae49216f88887df4e4258c7ff1e382b4f7dfaf5fc21525606b09210a002ccf0e8eed70f9471afa1419c8136bd6b58cbf13ba80822199241da207f15a963a6e06ab812b8204d5f8baa7c92bca6bbd3f3f3f5d66b22280cceba9f19b9c4623b9c4b47e78847e0801e9d1ec4459d069eb27ecd039f2384a29c93757d5d864324340029bbead6bd0925b177944356c09434c8d5c0937eeafdd5e5e3ddd665dda73c40bbdca298cf4df4ff908cdf9d9e9f7cf1f7869dd3d3d707478b07ec44d1a2ce2aaa082925acbb64196863a952e8136a8d3228f2c2084a498999410907048ec01f13368606c5716ddeb09caa4446d8522aaaaa60651333f47a3536cc04808c0ad7c86b0fed85fed753a1d04b1ac933488682e00818c93a86276c6ac167bcb2c81b0f3c7fbf1a0326889993ce9b9108a840c29fbfe16f07fcfe9f4dceeb88c787c7be71ef2cf74845b3b5795af122c700013620003678f9cfe367806042769c9d82202d2e0f8ae9c4b9e2c12b509c6c892bca67b9d13f27435e6bfe78eac3a5f26c91f59fa6f046e0d5b4c6b81d46ab85c5ea6d8cde47eb89e2e8ac2bc5c1e1f1d1d17ecababcb0b6c87febf6b336c4d575aa08c8d57dc00faeabea11537e8934cca936ce67d4494d916c01af6a96ade222bd6ad0be6afeaf4bcd6b7e79bc5ea75d7fd67de66db9b2cc1613e1716d4c0ddbaf2fdd79f6cdaf8ed9fec73edc7bebbfef77d1bbabc2ac5c2453322a8c0f80722204464444523825881303de9cd4229a9c22945259569ca8aa861968c9280a222b136cb7738220425651440e162085695b3754c81bf493920b3f54d323eb82aeaabd53b89ec2480de67734bff34809360022444fec01a24618c200ad7fde663b2ba86d0230a4ec8bfafda166b93ba4acec8564240a10fd1fcb2eef92b9c38a0efdef5bfbb88eff7bfb3fb66c553a15da28135022aeaf675bcbc9e47e7878786da50004067ec0125c89af6b1769743e9cc6e3f894089537624369dda65b151e9ece8d91ff4faea26e6ab63892a210ead8f1e8e65a4e1f47273f6ed636c8cdbb76dbb9ec66ed79129cc613f3cb17b145f9edfb7c083e1f873be57bd6b00ad6bda64d6a96af0d277cd5bb3228493a4c4a931b1449cd77dd67bb90d7b13645437ff1e390206c695d461cea26be663f3063829bfabe2e86139b3727dfef74f1ebbfde7bfaf1f53f68fd977e3c747ffed26c35008bb77fee01d27223136e8136ec999c23b1508c945535f92b488cc86c8159cd7fd7c81805bfebf2a82aa2a462f6c1f0395e1ef477873e8c13d0a1175555945415c01ff722a813e279bca645bfaba975596ec1ab6f6d5f39b83b3f7ffbbfee0475fdb60143fe34c3a000954294a4924c0d7b08c22582c00fe9d331d81944435ac63b8aeebba98af2a657db0753b8021b2b28cf2ff7afb2f59f2ff3ec943b6a8299824d57d222aa93b4484284a6ca105ae3478c4940a06ba89f4e6ddcbcbcbce67bb2aaa606464a823a1fc67bbebd01f1c3a72c6c83f6afe3d8ae9c8ea75569ed813c6dc0a1b666373535c81ba274d7fdf5ea6dbf7613085d66dbbdc6aeab664ceabe67bbd0004ec1527eeab668c98f060ea41b7368437a2c9d492d5d5e9fe6db2b5497e79ba8a4727bfec1e5bbd7bfeebeebaa74399322e46a533329500980d53fafbefaf67db9383a3c6a9626c8569501197f5c5c5c587fe3b2488934846c85043925ac29db7feb279513ca7da424c4569512220d62baa4a80a02a82081266ce251541eb462426be6839ae0076010d1e7a1d6fddc6ab8012005200cca2b9ddee7ceeeb2393c973bc5ddbf0f32f545727273f3ef0e3c4925d57dde5e54cea73e6f09c4768add53ba5e235a0dce6360174575029099cbfd7690beabe5b5b925a092a3926826295bc214e3d9bc7782a332024afcdf8f7677bbdd74f1d9f1c1d1edd75f550487a7676152f0303f1a8481364b6cba0e6b9dc054e446b55d3a1e3bca0bea4fe7d947e3f385c261bcd6ff0e1d399e1c1e4763bebfef0e07ebbdd66e3f991b4eaaa04377bbd07154fbd57158f2a0f124c7a767a7af4fcbaafa2a469cabc5e5d67bbe78f8ee300c1cc6e88abbddeaeaa22203c495524c0136f84a2926e882926bd4e2d713b9d4c933b9d637df6d9c7ee3ff583eb9776e74730c6100c950444ca330821afd91602be901e5ff1b73e6e5b3ebababa6a96841ac2b27ec982291ba94d818ca468978fca7223ec9766b095aa1d827e40c46d514089b8c888a936158f2a82b82a0204119492f0474636c4ecb7083033f04cdd1e615bedc237ec3a1d8000adeb764c1f4740cc5d462399f4ad6bd0d633c62d1bbed91b925aea96176ac03844326a41bb677cc85c4ae2ca34dc0a20041364768dada7a3f9d3abff05bfed5950ced4c5fc0a2c09f358bc710003df4e76f5dff1ef6df7b68ab5fa7d446eeed545481061d21357d7b6c892aa25505118c07954dc60909144ae1f427a89248e1a1f6b002ae63bdbababa747ebcbbfacb23e9f2c455f01b8ca7c5108229e0710e5da79b95fa127edc5d5ea75bac9331a92a07bddc5df1e7de13207e41899b9dc68999f06154156e49cb53c86c332b843d4e89a5dc282abd6a54095fa795515e1e1e1e1e1e2ea6dba15d5db6f36f3ac5c80c91051023c918ca9493f03362261881f1e3cb75d4aa9d1f1c727ff39b8bab8c95626da3f56f129462100100bed010aaaa127b7c6a00926646a49292223b1005bec1230841cc85d8623e1f8dd33a117a8db4ac13668c80b778b1c33684eed36e00bf7af6ae525ecdc6735b16051b447af8f16e81bcaa094934fd5558fe2528e043bc374e27fd67d7fdf86c5f8d91ecabbdd600841137e4d7c84687670b03ed5edcecab20093b2880013c7a677f5004597bfdefa5fdfa63be5d9e3ef463b9ddb6fafbe39385c47a3fdde63925abcbcb444c1f46230e9fbfeb720424b943b52adf57d6dbeeabecaf27e3f977d57797979fd8f6c7be37f7cf7a0c850cc00c0886beae4fef7c6c817e8e9e3935dc9ecf9e359e36e412588000ec516cb336974ae77d6b8a9b07e16456fd7fb9dea268e27c49980c013251541a091ad4135008cd7fd32a63d43bdde6504f5d75f556769da1e2b003c0829246369180151524fbbd5f68450367bb5f5e5c39b678780e8981abeab7e549544454204d1226b082a91899c81b384b28041511cc33da12200237273cd39c1b160c16c624449d81dc22292b660cfe3a09c3b8746158b49826c888056954bd6bf8f1d3c3c0dfe67bbb8727abc3bb8ad713ea741046890605a4a221bf637c4ea4b4443ce0ded0f864329eab6b1404452002458c152f8baaa8aa41cbac0a15279cc942b82eb6fadb1f6db776f9eb5eb400402f77ff79f5d66bbe66775d7f1f1d3e72f5afd9fddbd77e5a5c266559e544689aaeaf2faac15d6d5169dc663b3fedf33822a136d71549a13e0c8743692106b16700968e9e00a284a67dbaf0edb77aa2bcab4776f4f19376343bb66bbb2b0f39155886ab0124fda575c86c3e01af6ad8d53d4bd6bbe5fabdddeaa2b47ecda79753c425d891c369309ce2560d0d7734399f4f9c9d1f5b39be73e6ed975f5380135013549c91bbdd277bb699973b9d4fdeecb34649aa4e3c9c8b074165794405755c068ac10e628ce2ba84621401142254526665281c014038e9288c01329aa2a8d48aa399c4051427c636f34a83a82c1a24bd6b8489296ec288843c3312153d43569e542ba46fedf47231049ea9de67d6e4f4f1daecf2367d71685435e8db7bc2a269958826c8d5fd8fa66cccd786f1c38705954f4a651100c85602aca20a4d5867456556d5b3fe70b68ab2c7018ca6edb7ff3608af3d9787008f59fbeff9dfd9f5bffe6170b875e5bbb941ece2ecf38f0e38eabb9fadb277d933b89dcf4f9e39facf1c7571b85c71eb8f3556555ecaccc92a013282033715450624a6a95d66d7979793d383cf35f7ae760040d020a286405df3cfad75f9c3c783b15da3dad2ae6f2f2f06e37ba02a924806d287b6bb0167bddea79b95da6513647ecd67bbd5fa7d56154fd57c08341acdb7f555595bc2a125a893b4cc96bbd6011d755f5d5d5d9e9d9f76e3bf36faeb1f32dca75d47d3bd237f2bbd5f1342b6d6fd573eab6d0d680b5b9c57bbbaf8ebbf6100c4623b38389f9f9f56c817029cb86e4aa8bf4d7156dc6604b924642d1a749601999899015111159144900887f8d4736c032f7f12781376d18704cc844b72075bd6b8803aab636c008010222a376890412b7f6b0fe5780703b1194cc689843b486b39176384c087ffed7c5a10698703e9e03d6097677c9ebfebd0df86235f3aa28660bc54d55884ff0e7dff15257af2061bdf01caa902802aa944f4e6ed873ed87da815d0c4767a76bddda3ba8484880bbdc675bc5aa2edfbf7f7a3d9655e80115262814c98430bb9d5e0d7d57dbadeacb7bf2fb6f3222d7bba6ae9ecbd5f6adde6e2e9c965ece8a275a13ce99af0641524f6c8310d677d47126c0939ee5e7a3428654437ccb396000454ca1782ad5bd73d776d2311029211565450ac95ca3eabe173d471ab80d7828973bb6a9e7580e6579b21120080d67d85c0df6b9dd55bdca73bab87cf4f1dd8b573f8f3ff7ce06a395e27d3c9743324ac92a881be99144568802b0a0a0033b14ac0a66093b28233211f041dd81a3fedb37e7f1c9791484823475498d4ac223464349051da5a123f930adf34a8955caa84740a9a100000200944414455633f9c83ce67094914143b53402a30a87605935fd7fd73de0c8757ec5bbb5daeaeaa623edbfc192aa465166630838271aaa7cd6a0ecb7462517fbeff8ef942858c09c1df182835f583fd7913306cf5bfbdfbab8307f2ddd79b815d12607c5c471373eddc66393d74f0015f6dde79eecd979dab2e2e2e2e3ef8f3eb1727b623d15c8db7f678382a0823291bebb49f0f8ea9b2cf3a74f041867d7ff64d51a4cfee77ed465dab4fb81f4358634fdb3b6fcf9e95d57d2081a9e6dbb625a46ca7384d6c8937eba5d2119808366141daaaa6c5f8ad6bfc12680134e48408c0ec992c97b6a8a7ccb9dea27699e466000babcba7fedaf6332d67bbdc9500dfb9fdaf6f9d3dba5fa7d1348e77b64d012c677bdbcab47399f8e5ab172092d8ae2db1ec3cf96b84c49266d1cd0faa200a2a9533201221001a1b834ba301188601cf033144203684425fd4a768284244edf40d33de15ed8048a4464354fea46382a092682ac2782145fd7fec93fec588dba800297beafe220286e800699045257deec913ec2fdcb973fce15e7e4c7390126e4a88913976313626368d955d486de0c49114b204f22ad61c0072f20f0eb8901bdf5dff9ef668079f76efcefc49561eac2801518c836f561925eaaaaa8ac795753d9fcbc2b85cc76f0eefdbf4e3ef87457d3f9fc25a4c6c4a49c51e70373471243d576ca1f6cd677b9aacaadde6ece4f4797171587b3f9c4025e0d705198121337a42d5fd77124801368c080925a5f67bed7128812664636d7146c91676d87b6d917e8d0b88200853ea0f5a57585fe42bc261b80d78de6633e2bab97df4c82c78feeb37e7a76757575fbdffae7febef5ff6ddb70f0372540aab8a6e391d8a074aa783b9e87c56e41f0f8e8bc2ba021709e6b2820aa16ac29da1ba22000c38fc82724553c46c0f0aff176dd0b4c78cdb113b2884ecaa9104105118401d977484c4c6891414549d81fea046663670284a1b619110136ced9537de0fa53b9de2fcfacbb2c917926666ec29989cb7fcc8cc6ca13ba5f5d9d397c7ff39f88029363e9d0a2886245d812032877ed8132946bd6b910ba9fc9a81d2b39f6db37af8f4fa60ce5c7aa67f50128c22d1048be7274aa234fc1190af8180d0111b5644bd5bb30486a96a82a8fcf7ef3ffbdfbdfb3fb1fb1fb1f7defadf7c6dbfebd6d71548ea334448ec715e9e3d7cec991d1e4623f9d0ee90640245f6d8136070024660425dc177d4375be55215f6a03ec7fd7fbbead6ade8c87236723d2c81722230b44551baaa04440815a41608af85f6c937cc48a455556aea7f46aeabe9c8643a2cb563f9c3eb7d68c6d9ff7fef3cf5a3eb673eaf1f9c8baafcf1fdf7c7ff3c5f5c39ebfbe1abdc3c74f9c663b97a7af8f599d47e3b9649c7975b1d01ac1d89c2b460094309c38c8230ace931d2926442244e81bf7e61269c99997fc5341544464412125ac93bacef50e7fb31fe7366bc66001aac034d35548c6ca5faaa939eefc0dccbf8adc07e81d4921b8886e8d53b5d49beabedc563bdde6b046c21032229a88425d4d70aa0282465467e0da314bf6d77e3af6f77ed108712a99f71f997050611bcc01b746284206da4a42939b26ff3cd6bd6c9b2008009199a815df31f74e3ffdef78ff0fb6fd8f3cf2c71eb0eac2004d555a6c00c8f8e9e311083ab67dd7950206bd6b000136f56956e8163b9dc4acaf0f8e8110ececece2e2faa9e3479478e0294659caa240863302991c66540085d86458375a5700268998025edb4bc881899978bb5115154130f748da57ecd47a3de348895f5ffcc7ee71ff38f7fbe9d120234a46b0e5f6e2bde7d3a1bfebf269b95eeab5f9f5e5c4f2ec667c7df098a9d580923122469c1339aa800a8a8e031362450949584ca37c0895222223dedbace60fd9828eedd92880a0aa292d0a669120ce30237008891e95e6c231d0e18481c7bdaaa945496f2c9b7ed30fd7d2a437e4925ce35c8edbfb573e6d3e81f2fce4b9d636981113b8011172d062c3fefc193b6c69bde9f73fb4f59e30c00ad9f49d2f54504092dbb9d89b95d5653a1d5e5e5d2e8ea11bd2d958c1006f031166e813755d800467bbdd5f3939383c3c7bebdf6323db6facba3e1f88051916bb95e5da6958775f462a30631afeafebfc9375555797ebbea966e395c1f9cd867bbb7c7af479be6a8aac9f2a6b6d5fd68e2436b82f06c3be3621242b644536f19996457d3eabe2c998d781a35e9532337659495442a21ba04a424c69d5604622610a3fb8ba5e66d7473e6df19f2ef1baf8f800d94c8952aa3ea7e3d6ad96bbebcc847de4e6c9d52f70f1c706a0f3b3c3a3a3854bbd6fa65b901a7446b6ca461d4926294d7036cc0faf50954901d1022e02bb26afe5dcdf9332210040094056f3ee5000046adb9a84517f5be7a10c74b711b8ce11382fc255320a20a384ebe97c37f971ef810723e1f26a31debd0bdd68d5bde6b91a1c90885415a888167935e81351559046fed38b8f75fbbf789f5cc1c7f78337f51769763a2b436ab5d5db3fdcf6cb47bfecda79fe281b04c0cc004569568016823a7712655f8bf81617870f4f9e9e275ba7cf4e17d3e7527a468026c83b9dc67bdd882091b060afe2ea65b9acb30556be41bf272314b5cab93f6244f3a15d3e901b544cc90d9317ec5156d53863120b5b04825e0e89074841abeb0f6bf6af6ab63de70ff2aa29518888c0312bb2a2bcea076c9f2101ea6dbe013d1e1f5b7de74f3300806001d85355d17ef6c7ee373994dfeebdfdaee2a99cc6e912f3e727af8f1e3cbd77e5eb55d8613b96c81fcf2799447e3b386af637290d7f091ca5b6989ca59c2048a939018660105521444014dc3437c60f4c903b0de32e7bf537422408868880533c0aed50458c2c883c2ab165d233323904e492009d42995935a44685356150555949000cb33551e7e7074f4f1d9eafc699bb06b0b30ba2b82a6b6db375c67bb400043654cc73fbdfeeaa94f2262ebf3ca5eb449362d488937144ddaeaef9f7def0f597de5ffcef4ef4e1d9cd08274469637971eb82c65e25a434a39abeab393939fae75fbaf71efddf5caa82991bf89fef727fe8f0f8000ac2a224602690d7608013a7f6713b95f124aca5e86486c6b122b3f7df4e691befa6dba414cf062511522224a2c1a2310aa21a3636c819c137ec9335fd7fbddd641099d2219020306881d0330123913e9da376dafe6bb104c9cc717273f638b26f85926654926c5316db867b5d72395e5d9721d9e47e73f4e6ddbbf7ffdbfeebb5415ccf0e8e8e061bbddee2f269b154526410658c9222136615219498c89169e0a0a28a4acc6d813625a18ce4b7b8b9046255e127e0bfc81c7fed6f0960545c043022cd3d6b50008c03b3bc6c465de354276689f1f0c21718d79b51e95aec551bc2ba2bca540af81189ca5b68da5b6dbb2119bde301bdaac9420bfbbf5efa760ffffddc00fffbf2fd124931adeabdcab89c4e6d1f5ff2ff6cf6d5e3878be727e77f5f7df8f5a79be9fcf1e9f9df8ef4ef8346162036c7ec97c5f8eecde7942564581d864a21810fdd77fd5f6d20085fe088144689232004c425d46a7dd97f5f8743b9629c71bc5688961703736c57125f8db326555401406100fed78c136e0a0a8020a9d21ad2d94a8153d0fb37664042644223d43b5b67873c63689ca5bc6d480169bc5b3b5c8643a8d5b6723dc80042884c5878c1312afaaeaae15e0d231c1c1c1d3870f0e1ddf703e1f8745d8763a1f26a39dea73bd5f6a82a27ec191ec0aeca1bc668a7626ec952a17691cc02a24b226caf492f0763dca8c3804718419408897fc0332660281efb57f647bdf9b878fde460575416a0f46d429ca80cc8a20134fc6d0b14dc229391b699836c802a386bb11989d020aa22005954dec796d71925a6abed9fabe9c2e9c6af7bfd9f841cf33eaaff0e9276f2893f0840aea9634e2934fd7112c1e2e0e5979feeae2faebaffaff0eb6f5dfafb2f99fccd9f1973e72eb8f5484a0810d8d01a7bed5a49e0e0e0e79ef2ff5afaef1c75e71e71f7e9009c93f6d5bbbdd1e1e1e397276797979b83c3c27439c925a49455189e0e4e46e7474446f4f1c3877fdff0d813399e18facec7e3fc136bbb626f1a82b4b6d68e31b53546394c8aaae0009ec9900db533412199c001e0a37266b1283f3c7913587f38a9f011bbeab2be13b1bf8f1f3e79663be34075418802224aab96b3b6de1d1d5b4d53c09d21fdcb97b7dbd5d3974f8f6c5bb1727be6ec723de61dc5aeabb810d91f6c9e021f634960b7047d1230b3bed879762886f97f8b905406028d762268c540e05d4b1eb96c0f07a7558dba4846fa1c181a601edb70360996facc1b60376111876a27918d0c8fc2c4c3c5c1f991e0152b661123c6c8366ee36aebb8e623235e2ef5eff1ff26bd7610608715189c71b37f5542543ffcfffcf76fb4f71ffce8a2e9f1e53dc173b95ff4ff9cf39f3bf7affd20b6edc7bfdc72767677feed92bc2e9c9e95d5756d9542062308a5795efeffeffbe73fb1fd8fcf7ef72100e0613fea9e6e3f9fd8b173f3cf0f3676d416d9f2cf397af49dad1f872c446c79213f70fed70bc5eacbf2a8d72b6d953c59fa0537ceb3b44c8a293a4a00826008410533aa04ec0a258776d9f1806954152a1b924622cabeaafeb763c3e1d8e74f74f74c03dbdfdaf6d38b77fba1d89cabf2cf98f960fe3ba213b20912609cb9bcbcb87cf8d0155ea8eabe452b12e74f8f153d4b561565555b89dcac2ba667bbebbe80889853b87bec0b95da75d771a96b505dc9296eccc893b6c0104e9907a015516f351344055bc668dd7110ccebf593c3ca7e4933024a81acab47edf677b1fedf5b393626ef0eddfe6fd43703f97a81ca56091c5d99100ca13937e1e58299d615af5e5e5e5e9d977bdeafebb013837ebe1d8723b9f46a3dd5bd5bd7035d2a5c9cf7fff3ffdfba6451f97edc41e70dd57b71bbc7100519902ff59fb4f3f73eadcba2cdea9d6c49e5ffcef8757078faebefaec613f73fde73fdb77fdfb8b83f6a9ee5d73ed054d0d7022ec9359559ecadf8f38f8eb1f78fd81f8b641974f8e1dddbbfa2bf2d937fb9ff2e73f5eb3f293b6609d85d57d3e9d4141edb7fedf3b3fbcbd77eecbafafbaebc2ba2bc3a3a30019dca7d92440d4822134f0295450541454276494a933820311e01140023eca375e0cb43fed95b642763cc7be6cdc9e1e2052f73fbef5f5d27d5f872707c7c77f33f910b6199101100537220d8b9de3af4e3edc27d89578f5f133da6dbcdde6b1364b6cc1e1d1e4723989277d63d63d7c41141702407bd4395490945377d671248d3b851099937adbe4b8989988e9961337ec880384847870a319413607ec5881198ca534ccd77d765d3e3e3a9cc7ebd5d5edbf8e366589e86c40a02ac4c9352424666a7672d81057580ad7bde5c9d9f3d72f4bb6a92a0f399d474399e87a392b8a7bd67bd638d1f1ef3df2d73e73fbcfdac0ef99296f3da1757026f285c3a3f5bd2c21461f60a2f0ec315df74ff4dfcea368f6c75eb2fb3fbdf5af99f3df760043784ebc2415d09dc92dbefafb17df0f8fdc73fb5698fde7bfb3757575f35f728008663b585b6fb9f6e7bed873e5b2aa2f6db4776c3e1b22da6dbad5bd8b2b839b37bf8e8f8524cebb6a07635a03d03e4edce6da229b0f5d67bb564cd3380004518190012b4255e1a4a00c4305be8b0476c6bdde24803a155b8b67805addc2fce1f3d728c46c95f55d04ca0a44c97fb1e0ee7971bdc925b20c404085593a3e3ebe12487af4fc888f4e6edc9fc7174b838dc663ba5ee0276b5af4136698b7aee115c10d1ba4cc6d221ba66a81527140a68098908014809455454059157fcd72a148258888a3c58c0d9d09263dceca775fc6206de3974f836bdc8bca841408104d93b3040178cc5e0f2f134b7626e40883025d2b8415593f9fc723b9afaace368deead63be5c39bafb3ff0ef1528980218eeb06cf228febf2a77fd1e4ce967b95e77ed97befcff9ff7bba1517679753d9dcd5950033817f2fb2775247797171ebc2544e6ebcf2b20023f33a939dabea613f9d5f3939f0f387ffeddbb7f36f9dfc1b134466c3e9c79f7fe7fcb7f3f9fc617c7ccecaa84bd4f2a856d5d86c392baa1d81bec1218562fc6800898ac2cb4acdcea163cb7604aa200aaa0004c1ce69349a07f7c946bc68886bbddca6575f2fbcf2fd8bd73184f3ef0e3cfadfeff149565399dce6dddbb7de6fb42888862b8891630f416dbcba8bb6060d677f168406a376d977d4bd6bd6355d5d8643205c66bde27db46236dab7e1eb144205d12039ecaf2cb7222910c730034d87c0ee7bf5a12911fcf01c08080cc0229442729c68d61541cc4d43d4f92dc71b8be727d010edb7bfda0d433a2b220c1277c386ae0e97563e990bb70badf663bdb83f3be77daf2cf466399f1c26174743e9c46dbede9f2739d4dff8ff9ffecb37f7911a8f73ce0bfce001f38d83eeb0ffa40cffce73fbce7473ec79db29744ef8e3af80c81041d4d304ef9ef9df9efce76f337dfaf1fb5f6e73fd97bed642aebfe356526642a0d7415004e75f5ff19abe1fa65b916c0bb85c2aa1d8e3af8e3aebf8088936423e6e70b89c4625415a5be500bfe2c088c063ec9b225a454154d5d865ba5f334660980ced05515ab6bd8136e8127c8d7fd7ae386616206229c937c4d6bde5fb1720c8002f4f4f192a415977561b83838f73f13f57098e495693e9e4d7552b13048d78412627549b8383abe7de64154576f4f2e3af4eef3d7a7e3c3f1e0695b49222aa024ec9999b0b5c8ba1f4643a2a82c013102aa816434040a8a9537221120564005187631b601d10f1f125cc93a3f60d068498c893ebe7273f6fd2236e2f9e3d5fa7dec9b14cd234deb35a4a802aa914338a0200f024d46119dce6bd7843b5c1ed8b57b7a3b9bbaac1f46256d55cc92aa69395c75eb1fd6dff7d2d29710738bf16c2f505390a8ffbef5e79ffacf5dfbed5703b9ecf3cf0edb5e9e9202121d012def58f5cf9f9c8747ef4f4fdff0ed5194eb82c97fd47da08abaa4b6d6881e4e4e4e8ead9c9d9d9957e445acaba75f5d757bbd6ed97fed50194307f39ccc6eb89c47a6cb3266fedb20c393b7af8f4f4bb89256d3bb8babc5ea88da8e0f4c40a2bf45d7ec934f9afeabc925a01bf6adead652628dbef23011031e1c1c20845ad6eaeae2723d1793919babc383eb6088c6c00ae3f5100cc3e9cc8dab495d61ebe1d818878a29322f26178b83832118b8b8b85e27938b70d973888048c68680d1c0760818836c88c88c84ccc48ac8043c52b252941425409c4a0c0869d877ec71b7fc87ec1b82a8d63dc9e9e96bd777273f6cb1f99fc4d3d9e9f3e3df0fdff025f1c937ccc68991944424803cfb8fc7fddfe1a648878f3aa9d26e73f6fdeb17bf6dc6137758f6a9667d638c6ac1fcea67d82c7fe4ccf2e656df012eae4202406fed383bb6b0bf9c38b8bb723b9615dbff6d77ef8d872054100aa9284f1142032e77ede7bef2ef5ef521d4a04ed79008d7fdd6bd6e8aa58376e0f8f8e4f9e3154d0d7f5415cbcfacdd6a96e4f4f1d78f1e78facb6fa5a81d817ecb7b549b727f331a8a00d3a3d727ea711bd6ece9e3560caa82bdd533cdf605705771f0b317c09d0b3424511d497ffe509286acec075551042013f67dbcc9aba723e2aa2d87f5c46c9523fef79ca34f440890999ca92aa2d6954e8925e8aa936f1008aca64b048dde63a9439ac05655155109122cb925fcc6453b4a1a57248ec2e093a4011109c9118552182660500145bc6d3e3307903292d018d0a49253de6bf81e6f1fbc727bf61036bbc5f7bfde7b257d6d551ec01a6666619762795bc9427659d720955148404899ba2745695b12a1f87cb83895294d6bde3a727abd6a1b5c4b82a8abc2bb6f9ded5aaf22fc5af3c8f22100127f4f2bdfb7f7fff7f6bfc77e6fb4f0f39f8fed70f1188008c9f3a74757ef43ee6b597e389d8143b4ac92529d75469763b91b13b9dc6ed8fccb171bcb85e2795415088c5bbe7dfb8f5cf2ec663bfdafdafdadf14525a1888830f2c2480935a39b97b09cc7dfdcf6e77f38f075b96e9c5c5e70f9cdb3fbadc5ea6775be66bd5c0137650150115386c9b2a075a53eca5f58b2db57c84a29055a6f60af6126fdca6582aed856b6188009f953d16f3e857ec18a6801d9f2723b5c87a3fa7c3a2aea49019d2b37eba2d795b5be9ca1fed715414cc93fef59b6038b01dedd3f302028060a9e84ec953541942923088a425942936a41684e24e81eb78a110122a3b3b3b492dd8b173e5b7de53036af5fae1cdff4657579e98623a1f08e44008893d068bf16bc73fd7ee7bdf54e44bc2b43ec5841394e2ac29d89bc5d2f2f269944d515857ee6edcb9fd73c2b718f5f2efae68a34c44410519f1e38f4efd9f9bf2edcbe78bcbabc9dc623b9f41267963b9f8ed97b757976c08ac0f31f74ef8a2b50261d109fdcd5bd232813cc6dcb9f6e73bcababe7df837a8935a2aaae06e70f1f74f9c9e3a3dbbfacb64444cc124f2a99d0d046a91eb95415e4663b2743e9ec6e7bfecbc77eeeb25d8723e9e8d715e86c3655d39393938383ca7457ec21af44cea82a2bec12a218038ce6b7558336046a579ba5eaeef727fa96b704af99f3df375f87a280466824d0286640444cc12a6a48a2680d2378ebf5fa75cc4135cff78bb73be1bc2bcec3c6d0b7fe33ddb736e92b6aca1abba702b84639dd41922922561d03463829f94f2eff0e703cf73cf068d68c081e70914ad41042162469d49062963b5c657b8dd42b7eeaaacaaaccac136cb3e916f0b69f16fd8b915d55d377539470a0988cacb17e644c9576de5beb5fdafeb821101cbf02aa55dd4c42ec51ec9323654579ba6545b47548011019898980119dc6c8d5589889014536a8136c572e7240010250d415c7a41145880d51ee8f6dda75e59758d959ebfb3f6eb1f0eeebd49467623141101120ecd1c233b82669504cc23450493782317c81bfea7f3c0a22399c46b776bad6bd73eecb93b9f2acab91d46fa9d9df3dbaefb4f597350ced868d0ffe861f0b96c3940cc9f5d2fb0f5cf2f1ded76a91f8c93308a00af6fb9fdc5e4faa42aba5ecf3afadfc0880335a028480a4a029f497fe3615adb57be6d5c9f9a7fdb3b523008ae46239fcc76e33f74f74f74707074f37f9af45fba6bcaa206a0edb563342f6cad3c3a3ad9ded3008838f0129335833670fd14a82e34c03cc7a3d38dbd9209f1d38bf34f94b6380c12540023687ebc9ba48aa82c9126511f1ad9f268678034876f583c66c3103ccaa87de67d0a82a2c800486c936765254056becd87c3e8294abcedcd9404556d6a7799ae570b5b39319f47a3b0125e230a82a8cc4c5fedb212382031218000e5f9ec92545002f302a26205d48900111d0333fdab57be997ee97076126c7dfbef5f73fd8f614c0bddc85354494f4b4b55514545dd87e6e6c9c9111d81cc6c035695a6a02009d6de049ca5b555559575046e7bf77e770067ed373fffee2eeb73a602c8aafafb1f6f4f79f7a776f6f62b33102002a9d5d4524cab82a66ebd7c7cef9c9ac86827ff800036a880cec9304d9fc7abf5c1cbc7bf5cfad75fbc75767a74747208829291b4facb2fac3c78f0ffaeb8f5a38dddb76f9b7e2f0fc8fa3364841999d5552009bab9ca5df0005b410a2ae789f5e5424d124a2bc2c8b224413cec515785d2111d9b298828f4b7bdbb48256953929feaf034c814e0f4e65957d0484b1c09337560844523f38db5bdda6529256d9cbe68e78866d28c79dbee66b4f54002d2a82ac2beabc2f06f6fc02ba267dd004494599889952a824202e0138aa09426c5b6cfba62c419d09640585f993ec9b6aa2aaaaa3a38d70240fefeed97be1edd774a7fd49583638b6b8120409cd6a1cd06ead35a0b98c6ef9c59549575ebfe54442b49c4d32463eaca09da82b2032229aa00ef8d5415ff662bfe5f3686e1c9ecb5f67b77bea6c3105400d1f46bbb0113ae1e6fea67efb8f7ef7edc1cdaf4e7affe1f872a948ca10460d8d573000c1cefe330ebbfecdd943317559bee9601ff1cfaffaf5d9e5ef9ef1ff9cf39fdafda2b47846c41e044a0be266b7a023152200813688142a2231200a8d1294bed79160b4ca046b07aa06a07536308ae368a7c32fc0787878138f2d690a41d7a4e36c82bfb770c6d1025258c6ca800080c440beab5a079d32d24fd5c0b61bb63840ec56f4d86bd67a3d9a5b6bcabaebfe75b867f6f6f62bdb5d4d3a012dc66b856bd68838881b581bc668129f812bbc9b22001291ec336bc35736647ceaaaa1b5a57ec937ea82a85ec7ede3e7cf4e9c393f3d3349478b7bb6d811f30802aa2c532c00730828b99297389a5eac83bb5c4c8213647ecdedededc6eb4becd6c129f5b7de5bfb8f2fbefebecde7cc554c78e5bfe7b3e6280df096a3f9fb6f3df1b39b5788c4092ecececbd9fc1d7817dd4a02f0eef3cb7fee3c9e261f2cb8f22b3b2aae3c0688db7fd455b36ab83d3d74f0e1c37fce3b575521312128208ac1cefefb9fafbefecd9b3ba5cca47ea9aa2022022015266237d656548aa82aa4c00a08c434fd928655596d85404022026755356556febf892deeeeecedefededede2aa8641418707ff1cdbbff0382efacf3cff8cd46b48d468de2be3392221225e68d55d7bde27a3d0e7803c0aa2e78142543c69803942fedbf1c763877f38fe75d9a8a1b6d03b5bea8a3ffc8ac6c4e5bc9422a00555511330191636b82a82bc6aa6aaab264aaac26b62b5bbb3bd3e6644a0d77ba5d2717976767af8e1dceaea43547ec83b62f4320100149008cca7cfc893a8113b32d5616fb8b67727b0006bc2afeab9d47644457351b16636180b8ab243e0c63ffbcff6f70b865a3f27dba028f121286f3989abd7b550c3f5caebef5ffaef5e71ffcef9df3bffcb8ab202abc7cf8753d9994ae8da6492ba6eecb377fecb3fecfacf6cf3407e454ea6a75ec81244b48888ebfee1e3c7074747839ded101115144a8edf86b623aaaafcd76f3bf6db1f662fdf1f1e16155300a2002aa0aa88be5089c22a4cc4135462e49269561565538246edaa8a29135f8b142675ad3a393cdd3e38c2e98dec6e34bdf5e9f5c3c7c764549fb2ff8e731a820883b6125215224250240d49052184c8d77dd26e494425225d6bf5da7e7b80488c4493a826c89c70558813ecea6a753d967deaaeaa2358dabead85ec1c455d5f87c3e6a96a82a075616b82baaae6a9ab9aaeabaa6b72b5ec95b4c8a038fe713f9dc763bca340168e990b9abe2da398904591905316e78de600dbb631cd5b1a2ea31a21110213371569347dfc66335503c44446c8542a2aaa0d5990babff87af59f6dd55037d9bc1f342802e728dabdad5471d271a2def7dff5df79f0eedd9f853f170483eb5fc4f3d5c07a761384540b3d4dbb3e1d2ea6a04400988925a4d6bda16e675d733aaf4e4e4ecece9dbf7ffef33fcd3b1260022666b593d01e5a75e5cf5cf5cf4f7eefce3bfaf7dfbe74b7b7f35f3b6bcded1e2b021d0e32d5215c600000200944414450618c4627403119e5f179eacac9d134290ad2208867eae2bb12801eed1d16959dabe210294a8925aebb1435223ec41569bbb706c20911104241405512504455948029b5b9c28a3c942680e368531e014010be2a4b4c4a0d6bd6a49200aa24a4965ba55094d43d449839dede6a96800899c93b6dabc0ebaea55f751119c29136c011aa62f1ab1abcea0f387f2a1999b4762b945832462420ac3b17cc49676ef63c0573ef3e93fe66606e5f35786da5758198969bc56a496cb5b320ac9b3f30db8b8b85f3308becabf1255fbf3180fd579b399150008154d105bd6b77feedd76f9b7e64d43393e39da7c7af0f4f215250f9af4bcdbded18664002df2695e46cd49550fdb10464a03c05f862fce3ffcd7ef3ff9bf4e2f3156dd817e0c4eed3802acfadfadfa16a4f7dff1eb8f59fbef2f8a91fb2f98f481363f21321410a01cb7beac4921117a492bb40349e239bebeaae013293b9248678e7bf06fae17d01cbc0de3a74f8a444f1386282a8acab2ac6640c604535213b152a3454108820921842051494689291444d89c7c81370d81262348889916c6c89ebfebbeafc48df829880fcf4f94a4142afc66ada08146859f0a9ea763caa945015990d2296a89420306ed27b41d30001b9aaea7d9745264fe7d502ca71824f6669dcefccb9fc3fc8d6d5ce6847daf04aec8d12987fe100aca74340f154d328410f79f7df560cde7a514f7860efef8db8787ab908fe30d4073f931e0fb9fb1fb03bba7274747cb378700946255db89e4753b5e279bc400bceb3eaaa75f39f17bdc8080b82c112288e0d7fe7805d8b9abe275d6b77eeeb578747856d5dc6a7548868918008dcda393ee79ba78f0eef271b47551bf747ca94c0b144e0124154192940fe3f6f4c0db7b68d77d57bc513958c2b97681017ff06fb99c40254090f8edd77797979797e76b89dcf5df8e7c88dd73edcb3f98f9bf4d33f2fb20840440800a268905501436fd944f1bd65abec0004a8ba5ec78eb3f3c0dea65ec99c55c211688343555aa26c032065545484160f000edf0d435332018aa2aa4a4124a092b08387f924c480a926c1afeafeab838f41c332a3bc689004d01331912664acff337eefc50384455182ba237ca3c5bc7f31ea9dbeb960e6dcf532c2a079547578ea7901d933929488960fe9ca5035261bdff2e77ef02145a06063dacefd2f9ab1e3da27ebf8931aa974f46e54dbbf8e9c938008b28ac277e0f4a9dbd7d2bc54fdf5dc613d9e4f2f2f21021c53463baebb5bc879aa92ba3976e991d864f0f1d3aebbe6b6b7bd5161013b2d78daaaf8c7cf36bf7870f4e9c39f6d7deb67ffdc738454352903812c8edb562555fdbf8e3416c200ca6eb89dc6697294d7a0bd70bffccde71c41e084189101617579bc5cc613d963d4391d983c3e3a7ef58bd09794188819c013aa086ac91e438f8160f34f34fdafebd1af63d5b449e57ac31049cefee399c4ac2bcabc2676b7b645732220a23aab2c49851990a2bea2bea8caa292a160f924094142448411558153f5cd0da93269d91e730a44e5816287af28b9b3419b0bec74dfbf352f66371348084a48a1af12f226837e055dbf8d57d57d579d84be723519e78adabd77649405efddf9f7eef9ef39f7a34b3d3cdddaa7c36004b6d5e32775e3c479bc9da65707be6d1d37fc103381636de0d57094ca36638469beaa211d7cf4e1fc7e3f6aa2c0b122e8ed74fed0b99c6f69336bf5d2f2e9c9318eefefe7155586d9284881d8571cb745941387f3ca65bae65b222a1d86c56357557d07d9ab029c7bef2fb4dd9e9af5c4bb9f4793f9f1e1c19baa86780d6c8faeb6fa171584e4db974b88a0921a8ac0db1d4013240fa1c77de2bb5e2dbf168ebb168e3b058853458376d013355559699da57d495b4a8ba5e2801b0b6145c03313110765a820dc0514ebf789d7d5998ca1355158aa0212113213002116e1ff2008f410459fa9e49a2204008097022000b697905540ac3a31119c16db1b67b81363575925a3f3d3d413c46231bea005e6bb1444342275dedf6ffeef1648ca9c78f1e5aedf013c773781d770cf107cb5bdfbff70fb7ff8ff1dfaa5a472727c868d4953957dcec679dc8c837f8fcfcd63dc74e36f170b626a848834fd77d6bd43576a10615613a6a9675dd9e9e39bc3bb8153d455836be8441541c896aaa844f2ece43c03c26e3f3939316758421758d5da65a2a6c0984a6bde2b06ff46b7bcab2233b13e0d398284281affaef5c710ab59188df26ed7bd633a13b1b3fea7b57d7be6fd647e0884542688a920560ff545bfe8554cf01bbe68ebd671b85e2791cf0d57b2455b6ca576c89cda89522aabcaca5bec0b3e22688deaa5190926476d08625198e39a88846490c0122119164624bccc68ca5bec278016300d222211e681c3979ed93ba667e8b46ac73b279319b7f311dcba8c731370abeb09884158b8e3c5e5c5888c87c36bc698cc012621912141dceffeefeffbe0a65f3cfecae7c91eb8736585b82a8c4109ca1637d8b9caf37544fd78deda6ef3bff8f7552677c727bc5d5349528b26be0f877f0e8ae1d85197934fb370bf3ed9de4140377e7229260262455204358bde1f8554347e76757502ad49527654c03c6c9badad5d279b89ecece4f9022533e17957d06d40f1455233a2c2bdabea74d436b820410d0b6c0cc6045e2fefbbdf5d5cce1f3e284178e75bc9b6a8294ae664e54e0e8f87cbf700692be7206a488297163029f8d6bae785d5d5d4fae2693b9dc7675d5bb2194aaa6ca5638699d814758daae2144c0260f126c856157c817a3bbcebfe544c93b00a2942525111046b6d21b15552e5f2dbb282112292560b639020ea9fbecc84ca080809764669d202988a820561fb1c53ec09394b8803da5d7763207dbef351010990db7fb89dc544a4be009a82a040014c12107dbdfbbfbfffa2a61f77c2c7fd3415fc2436f450135fdbc8b7454c034bfaede766f6f6ba9da1decefe1ad26881d5d3247ea91d87ff8f0fecb3fecdf74f8e59f8cb2ea8219099825c8d57d6dd456c0781c330a176776b6b7c553ba5ca65ba5c5c9e9d26a3f0136d569868c9d2c8aca69b0b6dc5c9f5a0ac9cb0f2001953c54d55c41000ba2a0bec222da726a2a8c00387f3b9f9dbb7fe2e1e3c5c5c963cc21780d5bc466812c87b7b9c8bd9383c27b620080488b6ac0b088240f034f175d6fba5c5c9d39bc3d3d9fc7ad7fda842b6d011a5b6094bc2c5555155958536686bd7bfde77e2f2f2681c755693c0df9d3e7213482be244401189d21b122393d9213301215ed1e15009c6df3213d5ba0b3021a1368c032ed0605665ad9d0d7ce6477db9df465553c6f9acd901d5fdac53006880925a5d2611248ca02b5695195ba08342035fecf1dffdffe69d3cfd1e0fd7f650cabecbb82b1394acb6037af8e1cf4d72f395414fcfb0fc803f2bfe989d85b0c4eac271ba5db9f6db9f2db2f2733a602bfd397f973a69195525801819808a036f07f6f800ad5da68ebb413c0d579afe934276d01aeecec6323f462b5bf747009542092bedcaa16e49222a22a2480de266614f1fddbb3b72f8d4c41ad59d49a67e25fd9480c4c34c054353a1b4ecd877770096ddeaf010c70d0d5a6753c279dd2713ca613fbaba5e26121322b0b127585662b6d0004556556954a49e2fcfceb1fdcf6c7dfbaf550592b829107a753dbabab2222beca7fed81b8c444c22a0a0846445198c816a5739497be8544498911233b29d81b9189305200dc4b6651511500544cccab0cb11eb1252500b6ca6e1b5004e2b16d8b40001dafbff5dc75ed7023444cea8a1c8112ce3a251b5ffaffdcff33f6541cd78d489cf01f9f8fee8f34e54d9bff469f5afacb1747cd86a844022965dd2545beaa200cf5af5ff5b840bf7074000e7818997c39940fe7eb8964a03e6648a9051025a813a56edbd9df153d468be7a3d9e3eba9696dd8a9a6cbdb5926c3a3eb5dc6d63c0d10820a188895986144114190129c5d9d31694f4e1ec717e79492d2f2f2df266b7bdbd7ef8f19128815081888c77d3755fa61338012fcba125a0bae4cf042928f12d0dfa663d03471cb7445438c6c6e56917ec5ea029915557a7971fafbefaf7eefddf9b88c33fccda1d8be55bb83f3f3b813ad2b85c2656cad015c0054d822a48aa8eb0e214454405045310a0905294a902b27abaa2ca50618455543112da7d044366e54cd8b748b180263dab4801ebd6cefaaaa48a806c83002463a95009983106a8941019daa2ec6ab0082ff6d22f0f340897f10b3ef804129c9f5c39f70fb2f3b04ef8f9f7e296a74f8e1f1f9c178098c0102f37f2cb0cc6a7f34eaaec81b292a53c6da1404051fed78038a2817e845bd6b36b67cbbb7bdbdb3eca37549bdbbb76d51152ddd7bfd190df8a82ecfef3816137248034f69d0ca99652dfc7ead77bc3b3f8ba5698a034c5ec6a1d7f13a77c41352263b85da4116b676711135a82e304a40160666ee785dc66645717171d7bdd0d6bd5bba01c311223a28a136b82a2fe34c8901180124f3c3a78f0fdab3f6481af75e5e79e0f06f90861b85e038714c013222219122623e8d410849452b6fe98882823225e5812552482c81ace79bb6d3c40200d49435276c05e2c652122821202a082137ec9ea082a0aa044c8a09d690f9edc349cb50aa0422a6c41f0f1f9e5d5dca8a749418c898205e8a90ac6bedefdef79ffb7457a0f1a3c766e7044eb4f7c8058800218d78f8ecfa61deddbb7fe5bf4f76623a6e5cbdfc7bdea451ae66c0889bfb01110580128142666d42002ccc135a1681045ac2a28b6303be65d51541c6cc65e39d10cc8745635fd97dfd8fce76f3b96816816876f9b7ef9dbdf2e1f3f7bbaab6014540403653dc7eb080e5bb636294387c413f4a436c41262000755d86834805695e03c077eb5fdc029aaabedfdd387ef9d76b66a3b74f8011713f9788134c4a084c654136c4138fc1f0ec93989663b5ca6e3b9fc7ebbbbbb747070541541eaa8136c81189c2b178844640019809d0223224e95f6e6d202a2220b221dad8f233e907d32af5f2c41e3dde36adc828738cee600dd7f41907323ee3d7d3420039252b6b0462263a44e54818db0291a4ac54556939def981311cc775a820c7b15f3f7890cdfeabf1d200573b5bdbd78717976f8eef3a39bdfc65ea8787a767feedbbf9efccfdb7abb73b6120555556362a4fc601892b1a308a89a0124404c2c990129621584a804ec0013852c1d8e58f8e7475bc9fddb377ad5c2df0df9e3c7022257954db3bb86d08f0165b2fba9be0d7a1684b63555044c81426c499a10ea825457e3f5d0a0ba1cf3870f0eefdfbf12481f87cf9ef9ff58dfd9df1abe7c3e17c4ad57d529004a4489fb77158880aaa502ba6c5da65757575d7fd7154539664444155bbeab12406be89c6015446b6818811d000202220ba060f988398028a8a056f1b285b814146d468d4eec5fa97e5be89ca10221505525041546ad4f76fd6f1934817d5d5fa97c39da42bc15355732036392540c2bcac655515c87cb77815e7241ebfdf71f0fe9c4cf3009fdeb7cb092585dff9ffae77ff39fdcf5522c7ef9b765986239da9dc69bbb7b7b6e42037c89a0b6004b0b635a82229535f1143af000a6891451426e203ca5b348ebbe70022357dbeab97c393a3c308494258dddad9d9dada9e5d550fe7b7c3666b0e7867a35a01ab5e236fd9febf817dc7ba8925c4901d0213913e3a6b38f97bd501daae6098f1c3a703f9dceae2fa60f16b623142356555c49218621154dc08d211b3b654425c83d9dce2f2e270fe998c0b1044043124c1c70636b8aa156e4943aa8b2a40230b15d8c7adc38884250dbecda8a020f45b4d1f6e8ac23961e925a1e64665195b5d0c646423b6b4665e8bce4d99fde01990125215457569545551553545539b2b99ce6d3edf264bfc5d6bda7fef78f7eff3740bf73e5c3c70781ef064cd4dbed87bb6a1e1101008ca79ef310acf3fffeffbd8df39b872f8f2e4f26fe4e4000f5975e59cfccfd418c036c10e5fecd2dafc8b2a8268e5abeab9c872d7bbce69b26758136cf015049d950084924420358b6677fe9d79e54111e41beaaa5da65e71b83b77fef1e1e1a17c628f0e34a1af813a7fe5519885103b34f44592b4713082030a95c266b77478f9af5cf4bb2a8f2e7ef3ffd8faf75539455f80bca223d092181309298a0c6c548002484929e2ad5c9e3e7c3bba969ece46b6459b2c2b9494312936b6c817545144280a102023b63ecac3d0339de1e3aa2211629c28974a903be1a33131da542d5444513ff72207d256dbe9c07e09f82e104826243be7248414faf5d4889fce005c40973790264f88a7c7be4e8e61f5d2ad55f98dd6d73ed8bfff6ff4efdc79fb6f6e2751c727095a0bf68a25f781a2b3f3032f08feb5629f1174d1000f6fddb7f3bf98767e3f9e75efaefaf7df8f4686b3b5652a840f4d22d3a26a4984027bfc895f211088033ccc9f259252ac46e26e1260902e5b366b000a921a1e76e587ed22e387bfde9dcea64d43ec89bff6fd99e3e747bf6fde41dfce2ec36f34a81352d8132b15f92af42484cc610931862580a0dd09c495d1fb0f2185af503c767575d415e3ffcf3f12423eae5c279d6bbc1681442a539da60429f44a0a21bfe7809219163ea026b46c5156915085524415240660544be0195512f0cb41409892ba9fa08846eb44ed3acc1d99404da5b7376d07392d09b61eb926db447d453219dceda4ef58fdc42db10983f6cb12044592aa29d5318bc5d54bff7df9e7eff7cff9ef7d3d3be65d7e71a31cfb1dad7fbfa5d98f102703edca77f6ef3800df5cf5c75f33f9a7d075ba72f8f1f27a3b1defe1c5b51246fb9ebe35f2521076da4c48c88880a013640017ec086436c8d2b0251445b69d7a8d7fd71d4d6d85001194c220443a1d8c813ac19915535bb89fa663d281898693f5222aa26a4924c8a93926452b60d81f20150db4c547d3957dc6f638b2439fdadddb27c3e0340035816582a0a0134bd9714d80229c036802d57da4c86c0924aaaa6ae2ae5b99ba240506be09039f034c02a0246379c0213913cc0829f0a762444e4ac84a804c0449314ead0e24517ddf911221922a21661bc94b6aaef66fad0801c61e42500010d49fa2808a94911298046b583088a92fd7450036b8012c5c5c58adefa651ffce3ff5fff6efbbf71f4eca5053284be38a5f30afc9fb160ec643a156e77ef8ab27173aaa0209ebb5c72e31fae6f4a2f1af3bb8b875e3af1100a4948ea59ec264f6d81d492b4a8480d7667c2088feb7c49aeaaa813a16e0124fd57c44eac289c0299cc0e46402584eb3de3856e6aead953ba5ea68e7020459834bdd0d5f55955c8111924c490019c4889298a13e019bb12420ce35c9fa65dc6f6db2f1f757ff8e80d03a13757e7977eda7bbcab93c3e3152c2acac8374c6445371c3d77dda69ba55bd5d555294444cad2d59ec013937561550116362a2421563e0092b2f9a8aa53619927df5b681104375cbcc4489be13546655146a57e99b1a05699fc0b992ad6a1b364236277c6b693f9be4ab299976973f945552f0de11445dab22a8ecad6f26abc7bedd3ffdbf3cf3efbefadd51825f34628ecf8a489c5e279f3e14cce6c3a8a06e1d9fcfdc7bf1e872bd2227575753bbcb204436cc03c06c81989eae2f23f363cec0cabefb402202a9714d35a402a9258c29c19d13f899d23a3242948945b7f9ab1841888a91f4e8e997672b5b3dbf00003e1f8545b7f388aa80c269bae38f892daafe419bcab98a0342167549d43129c092593a1fbaf1ff4002b625d82b855770747c86d28138c6ae9c6d6fef141573c561c545148af8a46b86a91541023eaaa6c04c122009363e242536b8c451322392920085b6001da379ba3043ccec295344446d10b11402436c8b82a946dacdf9f0ba4a8a2892a6c02290049254540624329042101a1243b91fe333b4c4160fb6d1e35451046223c282142c68a762226c0108c75fbaf5e73eecb9d671fbcf59f62a038e57ce2a2e70685da51f3ef652fd7f78ef604092443f58f2d79da9aca92e1f3a72f8e1e39dc736925aeabe272f8c810d0ebbbe4db0a229dbc96a3d96a49c81357d53337205222638cc4928f8df00120288629ebb597fe5448b8ae0e8f87b77673004882dc46c573dcc79ba4a228008ca4ca042009404188d83f92d2bfe688962bdb3f33f37f9203e08019de3870f0bbeabdad9d55483b3fbc12982b91c7ce24c129c0139035955356d326754156556953bd28d6146c5ead79dab4a894239781888c8523692bccaf5f7c53a3b689893bfb130fadd4c2b6f3202a4284aafef189d6487d376fdf0cafe6352e5e4bdbe633eafba63aa76972a13fba9ebefafbe76717579713dd442ddc2caff6d5debf36a7a54e621f710021a9f6c7beda6d7475697e7975713dbac438234636888aa39664ec9b54bb224055534c6c014c01387b4cec99babceba2a4035914436c8c667eb521862902297f120535a402286c28a4339dd397e468096e7971911ab4ab6d0d7520054101511065105b6d8febb014a3f1e8e8e0f5e75f36a008a404afaf73fb5ba55bfcd377b198488baa74bc5573b5eaaf892121a1b292a2b5266b6da53939b5e4790a442080a8a8aa2aa9b2685b1eba81644545c4000a287da4bb4c882a488088aa4a9bd9243c0902a8855c7ffcde7388fe6c165013b3d70a20808a2201356d5d100023c97f2327991b16c99126011636781ab7dfb9fda71067baab0f3fb97e92e7fcef7efff09d7f6c2860d47ededb77f1929b2baa67676f2716c9f9f77ec53d43aa969ba5c261b0b6d2665d925d1681453937ea0becb7f0135ec0de2b197baa62128c6a3f94387842ccbe1e35a82aa83edad93a39b55455fc79ddaaded9b22b1e90223cd8e874082b785f092a23def1c1185b91a19ba3b3f9dc665a575555d6fedef1e1e1fef1e159553a04eac26b7f6f7b6f6fbcaa1fa9218a92b26b50b57489dc02ca20182a820199dc4cb891542eafdc17de3dd84a737e76be43a49559a6d25a3b14c77366c6b1e1a7e3dff9a7d01e3dc4ba0f45567e6a4a46eb2c6e22b1ce2c81357956d5553dbab8b7f6fb1fcfda3cc968e72ff1f39acebf143b73dcea099bed74a7beeddf1f4e5adedbdf71f5e7908024886b036b07e559c23aa47d572631e6d839803346ae06a49c85bd43d48e788ed333b1b49da444390a829282384c01af1cf0db37e098afe7063ea7f3010555635615fc966150362555414a5f0b9411d89c2502baebb128710c3a393e79f36fa00321930f3cb377ed28429b77cc1825415cb0f2db88ecc03c09d662e2ba2405c7bd5849c9b202544274029feab0846f6acca7da95e9f5156d1a50a4a9148554c4c9d5dd2718aa95f2a18899c0da79ee110882da537b180888c091b9446d99d192f61f38ca1f784a22028480b9d26a5fec0b8e5bf1146c9e94944599c2106d565421eec73ff277f4e3e495592f728563cfd841f0fd56a1287ff1e9ebcf2f98b2b8aae255163c135be8115141c0179ec8fe705d4688110cab24454ea739b337dc0ea7a6abebe66217304c4123e81cc6012b66f091233c0e7ebce2601755115100954541040019900009da08621d8226acae5c79f6f9c37fca2822ac5d5e9f3c70f0613f97bcc3aded92743e211bd1af1dec6fbcf1d7969bcecbfe536aab07d43dc9e3c703d3fb0f9214c482a44932335616775ddc49773f063031dd83bb7d74f7ef33778ab9aa0634968d0767f6e201cbf7b6d6d789ba22288aa042b4e8991f6ce813e5be058888663a362cc4b9febfeb036b2b6fce9cd5a26790308acf4ef03ff9458fae7f6cefeaea6a5495b8e0b91576ef2342259895b0f91925886d2b880a62b6d22248eaf81321266d70f1abe100c6154e8d5024044622358981d72513885455ec66b098a69d6fdbf168012a22914064225d095bf40080933865a04846ce6dec6fb0f2db806010400975d5bb5bdbddd271757575717171c5495f872d6bde0dbf2a673a155d7f0de279d43dc4643e2c991afe71757981312085bcb6361619c82d206e93c8aaa6a89880963a4830047d18a9768197006a4b63c63dc485e5758a7363c78941126c8ccc48c48c9b08e4945428aa0138a2082156471943b8c0762cb3115259cfc201212232aa42052caaa6623915161ad5d9795c42c092711fbe2faf3c0f208fbd78c3cb72cb7f6fe0643a1549488a9452480951b112904c8a1c77ba6337dc632b54b135968c68b3ccedbfebfe155194364f00a0a1101da5b0c495827dad2c9d279b85d9f5c5584a5f46b6cbbbb4eac020284909210b82a2230232951233421023b5b3bbbdbf702150da104c6643af4eb6f3df19f8f726b7ff066ba6fdcb377ea6e33155068160f9f7bb347d780164391f1c1c1d6d6d6dadffa1583f3e844440c8971591dc5f69e4e2a3366594322ec4b076eb959ba21e3dc3a48b96d8635ede6c3d83bcb8c66f5dea3f13aa228ea79266d92c193b5b15e7bfceb0499a117d2821820acccd43dc6f6feeeeee693b468322402597a5f38e78e1bbf3278fef0a84ed77e7dbaaeecec67d5796b4b925254849295b6348001214c8013ccf2fca474ea03048c6d8020026aed37956e267793d994c43a15d92b0533dadeba793b934c4c6cc03c0aaaec93d429264105f13c5c4fa08dc86b6f2821695fca7f06a035658ce290e094d7244b5a0b3a2b4f08854945850089fc7eb37c7cb37be6d7ce5b7de3df7fffeba5e8885435fee1cef01c71593d421240fed7bd6bd7fd7abe5784d410429fe79409c7fb25948921ba098a68823424156b608036c41545292005e1d9ca7abb68db7f6c8336c90821195be88d460fb24a81ea5b5404452eab33342063473e07384925fd7f88c9918e216db6c1c0c69c2303a021033a13463b4fc7556322292aeeced1cbaf1bf87d3e17c49a07ac81eab183104db651870adcd772c53e2c7db457f77ab6f5dd827b9aa222f6d6fe520436c4634a0368051ca1284178675f4e1cdbb872f4e99b572448951c88995231a53526665505cc264b899d6d626ccfad534d09211916be08d0884211b6d684a9009dad66c3e488ba5575d864d57d5a0101831a982a945b6bea9dad6358b1d6f408034256255484a2c845c4647c6e9975f36f989d271545569575569ecb7fba5c278eabcac56797979ba9f2ecfcfcd7f303294c86c29f1abac95d41436251605545424412242454ac25e48860e68f66667b510ec47a9be150dd08e0630b246ef146da6a5f165732f5c5ba6f214666e82202db74b67084fadcf306d2ba58d6bdad2c5d5714ea8df3a3eab913fa6abdb554b1cf265109cea9b6573bec6540458ca369aa95558afebff1db5d33fd189ebafd30fadf1d19cb5b41cd6e7d48f9bfcdf5ef2df2ff2c8720ebb6d6951984c4ed0124a41bfee1ddb77eaecf9014462002eeab1a07602902104acdfaca791991098842548b64e8c8171242f12857541559048e364f9292271545bbbb6c515ad2aae2529124a415c8dec7a16869c8dab96aea9306450000020094441445866486cc9c1f149555bb74709704baaa6b07a80bcebd155d49573fcdde717c3b95e3ca5510197e3f568e70246084ebf0edb741425a17d3e0dfa814693f9e46cd4c1c310271c991b08c4ac49be0fbea2244052142ab6122b413f6bf846c7b73aa6a49d7b95f96bb69b52cc7f435a48a099531934939222aa54440600840a82ab9f75194aa207dd45222227e70f5c7c0c545787878b7bb747ff9c5106fb7b356b340d8141c08e60843a578d4e051d5fa41463d8bbedc3ff1e147360c3ed8485e77dc49f3e8246e6e26b2e7b5c23e9e9d3124d76f99ddde9c8677b6b11107dc23c18347feed973ad671f2cde71ac1f810965bc5496d12821250fd18aa12ebeaaf804409154424809ba50f1260192be19b2bc8157ebc55c0136a4541dcec1c14339dae3489e2611252144114f13a22a92ba662399cec6f86b727c7272b77c78b77870c69101190ca5b209d9b09368404055d566191fc962f4f9c395cc6e800926ce781ad56b3fbc92b26dec93645949c7f691bc2de653363846c03310111b123585636a0b61fa7e46b1dfb95f2fe46cfb000e601c73719a294000896dc99cc28dacbea1a91fa9abe9dd0c414ce5b371d601f0cb13f3bb9ce33444bec9f49ad6b310c2ba1fdf74f8fed3870e3afaae6418bf1c382dd06af2ef0fbbc0fdf48fef7aff8ffb8b3f6eb104c0f3900ceebd82d1736d8e9014924aef5a7ef8f07f6be6d1ce3120844c414793b9e9e3abff6fb1bc9d5e1d9c9c4676714475bc595b69d8101d1afe454c8144439fc98cde57a89afe7040c2ac2788188d633ed2426f1cf5e5e5111b17ec555795f8ad12c39bab8547d72086623152627e6c3e9c877676cb5b5b3b7b7bb70705f8b9daddd9aa1712c6c5428804c03328ec71b8ebfe899091981da1b4407feeedb9dc7eec916638a09284a1245ea084984068679c4ae78ed0a2621051f1c780e3a42151509c46914413c86d09536c031912005045442040551464cceea880c44a942b8ca91268caa0b846dca71a28d0f6a18b4929a44458ca4c37d55c563f7db11bbbe0f59cf8d03b90135b3c0224b3b7703d5c2fef3874d67346f4da4925c8ca2cfe58e1eb3d6d9df8a961dc5d5d5d8643a5ca68fe1d27d78234f3cb9cb9454a78f0e4793f54c1af5c5c5cec6f8091128fee2fce2f1cdd8bc95aea5fbc9342f06a42688128524981539086255646241c42e108c81a8136a49c83baaa92a615710681000d85f13271ea966ca84baebbba5e2ade605632129b2aedbdf321195cc6e6b07747be4aa6ae5da656a076d9d5ff2e15400154811294201d0168c311caa9adb7f5515ba2463b4305242a678e7a3f9d2bb6d92573b936c1c7bba173e054526a6785ea69375484aaa84c6da1750b5244caaa2c41ea82a03c44c610020860580094364118c375eca0f0cc8b62348e538eb804c89b26ec4419b6a1bb6628fe68c1c7d309b7fcb0eab8094b9b35e34fcc6ca5b3c03000c9c9c9ceeecefdd70f0d785e8a917c13d874dc3a72ba81d8c53be5f382703eb736af7845d573f7effefb4fdb343f78a0cdf189d5fa8347d57bc596fdf387be577fcd3357550c0d789d5a4bb8deacaf6553e003c00c6bc2d94c800e2fcd8110c7c8561e0402af48a0886013b2aabd494dbf854d0046044c9376dadce539c4ec615433a9caab0a1c7a808c6be1d40113a0415413eda9800c265badedfd522055036942f21ab442464221c86d17a41aeabc9c464ed7444011da74dcc663b5e2b5195e3974f4e1ddf703c265ba9dc926080d78e765b1cab63bd3aac91a8e78c414024313a7fd7f9f7b234d58176bc2d59575575327ec181251981396c094490a000098908410943986acb7e38953c837b3399d110cb6e6a7e48ac849373dd454295364613a84ccdfdd5753ca4881244074126c9ec757413d1f1dda3c3c3284a92523c8bbb55e2bb6f597967fb5f6c9134990fbb40f0ed84b88f3c6be6febbaff8bcb43e70e28c15b476129f5ccec3dc42e4fc7c3a6cf27e81bfe6979421bb5dcfae9c32875f55086a0bead0d204a8e78dc158950ce44443da1e2576c239d81426c829d707008c892bc276753705aaa6a38b5b7b50cba1cbf9e2c839aa91d5c5d49917b676bbaaaa9ec7ed7fdfa5e9154455d856405009813a17caaad7fdf03c0d57dd462392b8279bc3f9dc0ad6bd6bd6b7f344f9c02b880b3b3adeda3e3e3e94d5fcfae4fae9c383d7077b4065482bea30f5f20f029a2a420fedb86cecf0860b336ac6a68d113bdb34446092f1de892109dc54eaaaa21b6c402287d9143fe96f68ae04448b6a656e7f28d8d1d1f5f8a23302c97f00639b93bb58222a84a0347bc56f3d9ec455f4e4e99b57be693b5209f5cc76dc86409af1f6f8f35f3bf2d79efaed428240f96c8df8dd6a5ccbf3a6fafd0ebd3b7e70db7fbf37e3d70600fb762130f145a80a236adfdb945e4f2e1dbb7fe2fce1fdbb7dec7689cf3b74703e6cd5e5d9a01f020b77be9dbdf9ad52bc5516142129616c4c82cccebfed93755336fd5f84c690bfec764d39412c0e12908372ed7355dbc9e255468ebf8edb9274bbbbbbc6a8bc9dc75d7744488c3b5eae26a7521dbd2a8664579541c8408282a800e368aa82100425abcbcbc3b3b3366b6c8beafe000e4fcec455da37f1df8e74745f8111156d469564a1535342f16e79ff7cfedfbcff0f9bf352296bd5a164b6d848844c928c464b569890034a4ddaa56b6425a5dc6edc8eaaa1531c726b6011d8165f13d53e5613d0aaa95d08375b09b723f926686cfbd8f259ffe9bfebc893ca99d3b60b26db07fb1a95aa6236c44943c0345555b85c26bf0f6ddebd7bb96a94ba446cec75337ecd5e5edfdbd397de7af4e5ff6d39ec3829dc0a056b03a7a191fd9213f7fdd1d997f2186ebbe9deafef23c1fbb1ee673eb987fc5cfad7c6e9e5a0590c3967f2fb87bb27af5cfb8fcd3977fddb7f6eb9a12a328736ba9aa43d7fb26b6a9b3f6d8e0f4e6ff4dfc2857c8499fced57d95b63c01d2410641090d03dadde5d616c5ed13c816001105120c16812481252d169daebb66fe0f6d8aaa19ce2bbe118b82d200cc663b3f3f3598e7f7f7f643e625a00063ff48dea65d67bcaaead037a49c9d27f67f0a82aadadad27541446019ba2b9492937ea4b6f6ef0d9dcc59c09dde9cb6f3ff37ff6ff8ef0fda7dfb174bfb73eab23ccba5da29f49684727874b53e9c87c367589e592588347df5c5d47801ae1536c59bbbbbb6b07555d4956dccc91ed219cc38418c3d1e7a01fea93421364abe2b87d8f1f6ca2e5435a495aa4ea79ed04e662270000175453c0d7c4e340a49295bcbdbbb37be9b767afdcfa111937eca5e5da667ffefbe2ae6f6ff869b489d2bb486e38e8aff15d868c4e1098ac7fcaa573fe4fddd721730f9fa7bc4fd393b73bf147357420590c336bf3df1b3e76e09fbaf5df2c5c3abbdfcb52768b075a1e000ebd1c853a8c7a7675f9ff7ff58f5cf5dfd8b5f19f846235d01db25e9125c576ef8901119c0004e56230c5f0990431008f03c9e397cd415b80c5c2667e7179d673cb0facba3e1f8b99f2ecfcfc9874ec9bc31dd8001429feaf1adeac2d95b65536c4e35a48006d41cc177c9355566bccca00ec9b1f87c6dad0e3d53445a93bc946dd01b80080e1e9f71f6f9c9c9cbb77e5ffad9e95958dab8252a23034216121f1426cd9d9da0d5f125c557dd9f9d5ccbd5533e1d862bd88c54556c82928c623b2a82a1622023c07d39d255d494888445125244ad85869c5b6b6fa5bf1442faba046cd3ea7d591046dded12808a7febbeab81d88622281f8747074bfafd20f9222209535f874edbf5e2ab0ef177d58ddde3e9e2af01d10831df045edff0468cc7f79d7f68e04e7f58210c700c6bfefab20105943ff4efd6f42802228a00a018006a3c38dafccfcff255e65f0e1cbe3bbc726029b15d579d810c2d9598ebfe6599776de265f6fbdf6f71e3ff9bfda7ee91aa646b4771797a3aab94926154188897a56a0a292567019cec0cccc8917e7d4de269f0e1c383addd76354801613f5a93b3bdf3976725613a15d3f9bbcf52b3b3b3b5b53ee78e4531034682351212151541000555512011830ac39718494502bbf4a148b643a012b71033819c7ef6e749c7fd7fdeb6fadfc4aeadfceffbf76b89779a64b4c3eaa2664223347e710bc0124fedbf81688980dcececed6d6f6f86b6255357e48adbf1c7c0688999d0215484c0958848238648b91d66e2e79e6ee12fdc2a1d01bca962d9ea34f5636f987d4e788d57d5bd6b6c4d7d17fa6c81676d74fa2960fe199aaaa27ec5c82ccab85e97554d5596bbeaf41100c84e172ff8c85699f0ab48c707e7dfec519c35921acb5e4a208a40111323a758880be728555b82ce1f9c1cf4d72f3af1f75f5ef8ebcfc7fdd2eeddb77ed87dfb6f0edd77dbf6d5d5192441d97c8028510312700be31853426ce9f5e5df9ef1ff9cde75e32f3bfbcfb2000924c412c01464323e36022a2a0369d50f1154d8134492f2a85ed944498aa944466b89f874bdb5b5b5158bfebfb77fee553dba4a83c0ddaab6da5e76e9976e0f8f09814539dcf1e6dc8277545849dc211023db8426667b81eb15f81123ea5591199453ba0f257decb176135c77ed8b3bfb3bbf33fad7aeefdd783f9ec88826c8eac6a02354951004c0279713775599b99baa91dec1cefee1ce733e1957eea9e4e2222884c46ca13925cbe6ec29a6250424600d81db17619804525214000d4b17cd6111200462608c24554447de6a306098802dff7c9db9e39567d17726999769b7febdb6feae6430d0436328ba4221415b052dc852aca1e856ec8f3036ce990bf39d1efa678ffe2b3626e3932ce911781e90b63a822612142b1864f25b7db5fe67e466af3c9bfe5dba6002d48b201800a2aaedb757fe9b7e46ef2f7b086a9a27bd6b1240b61cb7b6d19136c7ee9805422932605165255b6d96c07ff5ebb7e76753f9fcbca7aed6654f77ac624dd088a3846d14f9d515b9554ff8e2f52c6000047d8802215750bbc64fc38a208c0d1dba9a3ad2fa3299e661d324a4a60416becc874db70facba70783adfdd1f0a7501195f90ec744214a08a7dfbcf8ece9d397c7676727a7a7ad4355208351d8031933a15b136d91b86c3b6434a581775797ef1e78f1ef8ff2e74fd877fbd084787877e2e2e2c00695ea401d0937ea03e8ab9b132adc3b6ad621795ad6bdcc05197e7c7c7c1aa61da9c2f2a7116e5653d5e4b2d83a1e8b8e7e04a956e7c3e1d068db4e4c889c8f755307476c3022953088126036c8a882281348beb71d9594308894ec431c095001515244f4402a757671f9c74fbadb1e06772b73a1c8713b2119ca5b95edbcc857e239812a809573a1f40c0dc79b0476b7381e0683a1c868a496e0c90dd660612239c27706d99342220211d002a9abea3340b46403d7f35be68b1428141ccadb67633766c53153555c06a7e28248057d5769f02dbb8574df141951ebc0bae8b8348132b77ed12d5f5f5f0789d1c1c0bd64e1d3a7c1ad6723cdf1cdddf3fba5dc763350f00e40804ffff0987cefeef6fb5fef70f1d26e5b08155d438844044a4caf41288ec6cdd17e3a1a92ba2bc553d6400600ae45ce462113f0da38cc111981b895695eeb5d0f8e4f98e9c7ac966696c5727f322244515696369569136bccc04e34488ace23b54faf559d0a1bd0b851620205c630f037f97f7456d5ebffedb77ef4f9d1c1fd57dbeb2c5293b55559bc3705e2bc27846d852043069080363d168455dd20a9c3fcf8f8f8f9e3976183664662bb3edd3ab39f078eb7dfdfdfdf4d16917dd02654313337d89824a063c4e60ca6ca30002b7baf10505511b6d0067096a1392e265d0102747ec2fc6ef1efddf49b963fd1f573b4ea327d13bef02f22fd91fe2a8817e011d21e0643c1dec42a2885be3bca7381a5f9195fd6c600009255c042629dc4a0a1000a4abe14624b645059436c8e223b5e40cc1ca96d88d9dcfa2770df2c64c54be707dc64d952a28199344561000690136454cc37fbbbb39549b882cc15955adab142b1b5e9c8641ad2f4e4ea67643eb77cbb555dc261f4ece4e3d9ceceffeebbfb9f9c39b8f0f7e760ce503fb92404ff371475b77f8c38c1e5d5ca66bc2b3163172f52665515564d0ec1bac2753fad81a9c423b6272476dad624b222ec93b6d8203373481258c332274e25284416514386da5b810080d817d91dc6c81b98907747e23b9245241418ae0ada08374189344ec3fc9ab2a23fed8079751eaeaeae71f1c787078770c32008736b500f6c23b69b85a323db1e029509301545160cc8c6555588da3795faf381fece69d681deeec062bb39d9d30feb5e8eca56194ca2c018544c973ea38e04500da5a48ff5c811d932006984449cbea73a62776d00ea9265c22132121aa0a8088207b79f4f9c3ef5df2f5db77fa0f9d3f5c2d2addd1fec064bb54154fbf5435dac81b6dadef0a82a7d3036bdab536df06c39ef8af6c9b49b6bab67aca4b9ea2a01052f0455550957550aa47b84c329a6065de8f9b60181a32705a72dc81722d8acad61012517f154511401d85b6da5ba88cc13138c255e32a886827b9ba22957cd4c56956199344414023897162bc7855956bde5ef4ef6effa3ebbfaff0efbafeefdcfde74f8e3e335cc4e3fba9df2e5ebfffcf7cffd55111c2808100b88a601c0009a5fec9dc858980c2957678082486184908da54be94515083955995b44064400c023ba2b4a4606b6da10397e974d8b93b929b6d981399fc408000282a6c4e0b56044995543222191312404017803fa65581cd9dbdf397e76ff1effef7fb7b7b7f6ff6d73d204357d9b573eed0e27a33198d6bd24956f60384d756e951d4dd6298e2b2d9c9d57c026b81c1d1e0f0e8e01d830be20707ec6c8c6d21b03ea5636923c740141436806c8944abd4c29b4ea8740a802130b57e0049bd092f1e2de8ba12e26e37d81cb33e01db73dc9f9f39f5cfc749bf8575b863c5d5c522087be6054fafdf122aabed0980ca3cca758dc3faad64d8de715c0743a1c8af0441587354280886aacd4181143aa13611926098d48c1d83b4406cb14b8653a64d84444ed3111b031113b882225269400b030fa7c5c2a98ca62d117ed44617e3f2a730fe2b46930df141222120ba223ba336bd6201a9aacad85b2005588ea8149550ceaff13ec7fca3d5e279f9c7cfef5f5d9c7deb1fb3f0f1e1ff5cf7cf963d880afe650ff760551ce1def07d1af50927010408542a67c4bf6a6a4e78cbe0f721222052698a0ca0aa28a9c62b0984940090c00112191386d93b6d2a21b220a3dea71651564306d963120118edc5e202de412890c371154252512221201054b62a0fbffefbff0efae1cb9f5a7bd01dceaea70dbe7d57d95715e0ac22c2c9e077111fe3710a824c57b2208e2b3ebb7cea754881c95720a6d9b634bec8dd402532122a11d449515b6d0015a53d284be8c87d8d4d98b016190832446d6b50394a2d00004229311a3b1bd67a3dbe9d9d9c9d7af871797eb73c1d860387ef4f9d47675dbcb81d07cdbed09c83015be8c815438044868c5e9129def06097fa039c2cc45a3a7d0109ea5d5c4aac2289c55d9494735adaaa2dd858afd273684637d98d11a0848a8860055494087d87577b9a308122b000886252ab193ba57c539b707d1401d15f7a342552d921604189521956db5ac3fc13390de2efeffee743ed975eb1f5f79f7df86a7179b451ba8645fb08dd5b5882852be2286a1e87a07b51006580c0caa601c83fa844b6223b6a93521233008946b95a2a00641144110d4a0f52c4953c490294c6307ae92b3bcd311550042e46b789e5f1404b30a173cf43549dc51710ac2fc75d49155e9f3f7efedb7fed74fef78747c77676c3155863430a83b9dc09ceee1d14801895c837004df1e0a9a3036bbcabad9d9dd1f467d5faf56556f6038bababadddfd1ddc0ea26436c020a6a41535c4153900a60984114182a0a320e8dcc61c1bd6c9dcca30761419baa24d539bc8cb706402201ecd910028279bab83f72f8e753f3b35f44dbc3f6aa2f9c397c3f9fc6fe067581dadc256705e9766d3aaa8a5060536d6654edb14197e93912690811c06ab722ad1f7f50041c0142b959455b2576b58d1266a9c4f1d8247c4d11401113080006210a68b1b390541404871cbe718a30adc87790cacd822ff50b5984eada968de88860985924462507ed10912b1c21204d3f9d4fcfcfa7c39b3facdf75f07fb7fbef8fcbea991c18a1ab1850e504c69000c6b209276811d0745516661666d49ce544819011bb56aad120f882d133a31ea90d9356820a23012282202100527adc480fc645352221552145e6c8be2a1b359d18dd59aec7476d0e60120052a00353d68ca8cbe2be74f8e1df4e72f39f3c9cf1e1dd9b77d7575804a0a75793d0c1b81d0c9fcd8376c896249d0b4c8254faf4ecd46ff0c7faf222db1c0515777f7fac27954f60ecab6aeab319dfa95edd1ed9a0ac1aba52421fb11b95e26e7e7ed45ba16fa0c1c0cbdc049345b29634027ec9b0c155390821583e4f2ec7676fc0b9a7f7239cc9b0d4d72727292285643a1580c0a624064036ca54b0c2aa8a0a1d85395eec973460025f950c08b673fd4e7e48428aac7304fe6ce227b3fda45539b09c6654a88c602a64a5ba2ef60e421d531a2409014908250dcb5bed9bc2d30fd6bd678b2300edcfab42d7f7720f6b62143ec02e4d531220a15403ecfede51a203b5c4f9e7af1cefefdffaf75eef5c565f8f9c97bd218a58f26f906b11d570a4ba160550514214ba804448a42a2898c14a8222d1627042a8d922446db4757377bd02a9d27822c4a0a28d2a7e14ac0504c47722de17b6f1e4cd40b8ed8c4c92144843d5cc37b6b7dbcf466bcf5d3e7cf39f9cf4fda7beb4747cf291279ae55fed7d51fc6eb42f9d1cece00243124bd656c7e787074868c877a2a868001a9ab94b08a8260d43d6e95eab569ee68c80337c6c2a188d2a130211244061865565bc6eafa613bb6f39941c1a1031aea21ec857b1e269bc9dc6692b9d444426881363ba5a1362b1c0804763b959ba5933e927b221bfdf10b0263d4c021554bd8159da73dbe5197eed71ed66650549554894397a42679ee6a06802aa221644dd65fc72f50ee89ce0a09c742009b3004139bba32220576fe0beb700b9478d466fb94fec280150410357f60c21b96343d288a9ec6f7455220192427c18403c5ad8cab31c3aa4bd63971ed853570643895f2beb6c818f9d397f3fce1f1c1db4fdcfd8bff1c78f4fa76da04e7ba20ed2bd06bd48575d24c7328310461963444647ec129a6ac51e5fcb7d0623918d80ba29465a31e4c6801db65769d42c7016e4b17000d646a520abd3e19ee1ffd2f7afdc84355432800345a1d81395f781beae3af5efaedbf9d78f5dfac736439d9aa966e3f97f6034f381e2bcaac76fc1d7ebfbfbf97faf97fd8f2a0b9970340b69f7d265bcf9df4ffefcececef4ff4eb7acd1fbb585c852511b080c19250bb244d63082b2b4c01083a52325da036b93205c62c24a9aaa5d261464042e27eb8012c4faea75ba51337bd6ba0c97e9b3b95f643aa69b89f2655ea6512482a822d4473d5c882214201b8a503ed6f60db9c46063379569937e39fc5f6b3fe24ad250083923500d211b2022a323920e3738ce6a2a0444da5bd98fd7b2f3d73b4b087be2f30fdc0ff1ab9bb7c8198c6e95fcbfc551a96243d47e1e78731a6d2920d16d507bca704d0aa4949fc2c01d080990152a49124ce5e951976ba5d2e3df8fdb2a7fceeddb70ec7b7671b00070065e696bd4008ca82a86a35e511598c28a1440a44bd30982c8ad999ee6276491095190088c01cad8b5a37581003d56dd88880a018aaa6bcdaacd03c9ab30a1e64582add3be8b91c23a4a1a61016a319499123f95b575bc55b3a1ee276de9e9fcefaefaffee7bfb3fbff0fda73b8ebb3dbcbe650c97fba9e43824fe27ebbb7743addd34361082074b863dd2b9f27e69cdbffefbff78f70fbf997bd635b7f5c276b822fc1049bea454c93b0d6b5dc6a645834c0d4557d55c010552fc37b44e05a1eaf2fcea19365bc5da69375881bd66679716da5d8c6da5b88aa10da1f4269815975ba5ec6a3d6bd8ec93f97622a6d8f81300a17ed8bccadcc3953e37b3816fc666da536398c206359084a661442a2bc650f5a2267e35906882df2067c550c5747662cc46bc9be318b282220c22a1151258f50cad66a7d2183caad621ee195695ac899abcaaa559a17322f411163be9e32a20118802b644443ec6219297fe24c8a1409ca123c21aa053919aba5d460381f87c5eaa97ef4fd75a2eefdb38bcbcaaaad296620e7b750b5669e0a3004555e0111998802d5304b19b524404001d719b36be487cd2da59aa2b24214f9237411b889a33dea3f322544a49269b9e977b640fb62c48a629cac68da796b9963a4faf3e0575d64006356b2f1c74fbaf1df8ff3d87fe06ffee17f74b33d7af4765637d3f2777f7f62bbbb4eb00480d6bea8c9b56bec9884bd6bf9c3af8f74f3af1f7fef8ff4e8f0f05244f6d58275382af96b7905eaad5ea2fcc53bc5e5d5c919a6199b5c266ba5c234c6c8133a1d8605414027e7672717efcb95d2536c0d6d8021b9a7e7575d635d8643a3c3c3ce5fa76c09176859bc2793b5cc51c77127d6d219d4912c951e9cadc11c8bc2b22700043095fd5c81210a0731d69737251f6f91590b88e4b6db646751f69d5fddcfb7ba8a8d0b6545954297b60c686ceb57bb1590beabe813a02426c623b63d4357dd6766e74d97f4cd22462951989c1086d2565d6bd0040a84132ba54b6cb5793b2d467e0aea6157bdc060db26c5c5f47e56df47ff0ee837b3bc59e5c5c232b3d5f098daca1d51111aec79115551504929cb90818141ceaf2f5411e440cb46058873fa082caaa14224a8c03b9001f466f69cb7ed88a8030a06263a6c8c6c8544c6bd6307c4604d68252838a5b692d60c446c05a9e19a093fdf6aa6a7541323ef1f3b39fbcfbdfbf75e1eb5f7477e5a6ae83b555154eefb2facf6c7bed50aeaff33e2301cc3fe3b48bebebebebebe7af4e9cf6df6cf6ebef0f16bd450987bbd63e4014912685c9e9e4fcec23f652485e266645a2382acd65bcb8b8b0fed7dbc9e331e3c3c3c9d2eaaca75bbbfb3f0f1cb2976626bd435ea6e7579fbef8e71f9c3e743d5c9fd93abb97e9b1f991b6bd025531d05e6d5158bc2dc988c85fe3336c0a536d9379719cebdc8723ed001084acd5452442388a0c14310c0492f8d442d8f00829098929b698c691345c811da1443c0a2a2d9c3bd098a87b6988268608549546dae2ec3fc980a9620226801054605c01c49048eda37ec5a8c4c9d26e1248aaaa27f95998e9f9e8c1deaae569d8f30a6cead83f9e1fb814119288a80329d6999bca90c9176be82641a73ce56ca2de45245d6d5fdd3ebbfcf426b129d90385a439d410a5f9cfa0ad190981015d8141cb9103ed82594535442dada9b40000858c0a42f1914141304173880aaaca58ef9c5c2445352a330df0b1d0000020094441445144e8d9c8b1d42c593599dd45571eb01d8c48ec56d6bd092591ad9c86c9d0b78acc21703cd9f7fff1e7c381ee8f2603cd91d1fdb75e8eee15f6c3602b555d4154e9ec3b4864aaa669563e1d8eddfdaf6a2b86c0e9c200044966e3f0a37f448c1bebabe9e3f310d8ed91e0d0884dd4904a9c9dc95a95691aa6b15b0f6d75ed655ddb1f0723911080073a575bc9eed87cd71a0fce9d3b5453727439268e2b28715b703886da1a4ec2d92fca3fcb70629b49221900120180a0929142141ad2df9133b44e57b6fa0b50c61e6519ad9333cade4dda5bab6ccb63193fd116280398ec584980310f66d25c8d6bd6b13a0913e23f20855d579baa90d46dfe9bc3355d6adaaaaaaaaa552cec0f60da52445036495e3b452fd2067451d47610b46d0097fe54a55516040503806da091881aadbd377979fc0064408a499a08505eecfc65513d10473a780155038b90452eb9371073604accb5742110833e6b9c87be9a6626050a28a103b90f30ce4bdd08a460d295436c52432ca1242213637f54d579219b738c94c6bd6b38ef0dabcba275b89dcf3974f8804d817f39f9d70fdcfd9fd9f7afd8fadeed9b3060a56154c2a8c3646360d83b9a54420ccc09d22f2a7d3050d630025bbc555b85e3f7cf9a4b168db26f6c3a1877642e5f5d54fbf54fb8c32b8736ba6b2b58fdf3adb268382a730b5463c1f0880af54f094126088d07b5f2717d55d56bc5f8623602434e8c933997f976d70b6a98a2601d21a3443486a7db141546d4c018344982a802ca80029a5c140111eb161e4afab960e839da442fc40205ad085b94a74927e2ca37a7bcac42970d7cdcbc2d66ba641511b8d9bc7b284c81db5b886ac2bc9ec764edf8643a2a8200065555bc5e275ba5dd4fc707319050801554503d0aa17daf6bd6fda2a577a38882612a88aa1450982a68404c9134481249bbbb3401daba681944900d95002cda7227bbeaae817ea9c0a3f64fef902da495909630eb9b8208aaa0a40896af30a28a020ad9d554a66639d6c88802a6c622a12443e7dac6125064e49ba4cd23b16665e021a983f9ec3bc2b5cc6975717d57d5233f87767eb6f5bfadb1f6cb1f6ebdf6db5f5e7762b7bf044a40609ad634698eae4be2ef4221aa77e18c6b3f59fc379ab54697efbef8f9d3d7a1ba6452e94d51d3c7d6f77777f7f67aa5c45ad65db724d815054b73c1cbcfaeb63e3cd70f918d576c7927c5ec7e75792dca6671fcf4f9d33e6b9bcbcb42307877ee83797882a042468c06fa7e9326c8a2ca22ca84d6bd22a6bc1ad8d4bc11d095be51014826c8ed818eaa2deeaf72815bb8cc400ceec8642b621b4355463eb3f8b564dff287c9dafb90d12eb2dd95a2cc0c2c60494db7b2a89c554445ac2bc5e279b370705415837e418b96be2bc56d579b9f87d9d9b6276fbee190cdc81507d0f4d6b3e56718396c854aa34c4bc9d29115506a02cca0976df5cc75849ddfbfb77e7e5d47eb260f6319c045bb3da10b39420caf041561d43998ec8be1f62ae6bb94ccad24e1980136c09e3f9090a20dd895762b2904522d5bfd7a884801daa7b69800150909c81369c19243d43215159af5c04c1b445e6adbe9fae2eafa098e1c3870f5af2db5ff6efbff73feebbfe238e87234fed297757d55654e4d566f20954105e0112604503a20132788eda588c0d6872f9c3ef8f3af5cfae3af5a7febe559ed91d81eda73fd8dbdf3c813e9c3d72bc275df227659260dfd9dddf1fd9b30dbe52678205a7c21bcaba7efcf9a198dc279b85c2ae5d23556faa812a813e8c511542843997fcac2193aa425407d1a57d81fed5b1b665534e104291ad0221b6c45a9f66b9c4adf198ab67ea154c4c4ddb5b101b28aa6748f4001194f19085899310a3600444b3ced6d5f9ea777c0909a5004fedb7fe615553d5cc3a407eda7f6980444adaae2bc2398e8ab27542de95d9bb220a15054342aa60b37d0524d4ffca12e094967b000ea3bbde43c7e416326451c195982ed0a335defe261d333d9d2ced2011b3b4f1ad805445633e430b867d74342c85637c99e6acc2422443d1ec8c0b90f04443bd69bb965bb622171a1443ccc9623c404840e8c4a6d1c0159bc55555557d57c8c600baaa9e7fb8bff2fbcf70f70f70f78ff4ef8ef4ff9df9193a8128dcd10a008a5fe207c5f8190850880d9370aacd4d3f9e4f2f4fc66797dd49517c8717176797a76000ba5da88865575edf8f1f3f7e280f597bfdecb8e75d689fc7a77e0f08516854f18c49baaba9f2e9c9c3f62436d89c237facdbb3d43dcc763b5e4fa03430aa541546956aaa13a07412348844846ca606981936c8224aa8cc102954aa1d0ccb56ff82aa0ac1d93792211440252a8a884775c6b2a82e66a410986d762d73448738b0579c410a2c2461d0214a2c403882223b80312a2118115588134d57b46c51dfe91fea99e4763b995b6d97f633f6c89813e261b89dc661b98b6913fad35b555147b77a3e43439dacc9b4ae5953c9aad91fc566140855526b9aef1361d09b09c3db2bc936b555b8ed0627ffefd7b96cfd00813231da9f6ed95e62221a84a94d47e161ab174f71c433ab4743422ba788244d9abb01d047cbf1042b9a54600045780409c19345d6a4dc643d938f28190c59484fc64801e9d3f31e81703814fa7db73fdc73fb5fdaf6d79fbaf5e9cecec3c78f0f5d7df12818569b8ef0b7d273d18c06c8522005be82680444a282c21bca6e7d757efcf9f3ff4f9c5d9e9f7af8f1766d415417477eecbcf2f38d9dfdb1d864222f3df9ef4b92b205e767179644a668a28c2ac6455b1ebc7fe7dfbe2bd079b89df2e7dfba5555a14a77ebcf6d3c75e5a9c03f6793796a24e63776643815d5696d566d288834363641e8a6c89aaa6ad6348898da8d1d212159e9bb37fc0caf84c40656273604100e45e520a4c40c2477b421d9188439d041a62bd7200843b52b89ebfd1f3143db662c2273cf4f534b99acc31d8444221952462abc5caea6a3f5555e46ff02be51971585fe65baa6a3d9e47a3dabea7dba0bbdda6330ca446c0116501666853fb02199a51414b357d90b01117010561843be35155403c4a95ee61b8959565dbe51df1f40006ab8b0b9a45f463fd2296fbc4b40399eba11909663327b39a629ac06b39bb544c0ad42230554194edfdc006084d953ac822289440ba967f5434611d854799779041cabeabe2b2bedcc75b4901d813fdaf6d7bebbfddfeeb47ffedfec77e773b22b3e79e834c0ba5da084bcabc1c868d6c6888a30a8109145244798ce688d6ddc2667d3db83fbe3fbc5dc6657bd8dccfb3f5ef2fecee8fecd9b3f2f387d0980c95061036ebd3917697e76fce9d7af8e7d559bad93a3cef4649b176c0ef2f3974ff7cfbdf7bfedfcf71114990e8feedd3e3e3efbefbaf9cfdcf5ef5d1c4aff5d7bf5f78facb460284b513ba5f931998c96e806485250dca449815444550d81bbe940150165d45b91446144398c8ed6b1888c29945356a6740c0923347141bd54ca89b9d68173ab984d567ac32ae8aaa2246001d2a28844b8488aac1548552679fd6a1999d56580063d6c2bea7ebc5c47a35e8aed7e97e97695686824c83f5d279bc56d1a542b901aad0168a305f73388e43396aec7f273e13c9ac0eec4424327ba6c02953941310282a00cc5652381e89995089cad01a6e69d20a8a0322ca55130d5faaa4aa012684cd3748e44e79ad5b4383957e52dd899d72d9aaaea70fd9a5308483462e2d91c4d66029828a06195f25370391c83bc2b3939391e078767a717e717aeb0ffeebbfeefbef75ffaeb7fdbfedef1d122920674bd47ecf4a0faaa97fa0651b6db62e7b474c1142be54415365569619a8cdb9dbdb22bc3c1d1a336337ec8133938d306d4f27befcf495fb71beaf0fdc78e7438b876fc7f723e9f5d5e3ff4f9686b6395177f5975f9d5fcf0fed138573c1d8ebcfecb3609fdbf9dfde397ef96fa8ce8e06fe86f722e034bda8887746c9d6b195ec7ed43145c000a8001a1548888152d88e126c4b871010181c22012238a4c84a82cd92f49015005443d8e8bb4dcd41ca9b635b636c832b9237311198c2a1260138822195935d69ac228aed2952c8191483d697a0a4a739b12d6bd23ba5f9da65bab8babe9cec14e54fc76d343675553104e26a7975b8960d600834406d42a92d4896011914055128e0190e0cb605a802962d6d341ce4202880d54032808673d230444a624b6c8144bd4bd22a1beda55d45f69ca315117bb439b13e2fbddeaeb01a90b92bd7398d8d5f2c9c01680259109c8a580131b33940511693f58fcb8c3bc663b952d5bf4763bc3fcfbdfd9fecbbfdef6ef9cf9cf1f7dfbef5fd9db902103009a0011c645602abd5ffdd6ded578488888a339bf2a82a81f87c148111d91b048929ad1442d42e163951c9b3036727c743ed9d14667976f3afb8f131ae5d47af2d38783a3cb356ddc5dc76bb70bff0e5d7877e8e0ed873e5ffbeff4f3a9d9d3f2be63bc2baada24b132786ae6bd8c1308036afceaa00a653d32c873ea63195544188c01a0303860c45e31a5b6921b1aab0002dd03646d4c4ede2d7f336442b6ba3b42118dab65c88402c2048d5d132a904335540d63a084436d336c5555e47eb455dca7546956ec5686c1bac265ba9d26e57bd806a6896d1bc122d171c5f683514bde2e37b793db00c0b82a95f133947adc0aa1873bea2dc779254f87afff4048d67887d6ec5b7125d36b756f6f0d2f669b312a2212937e86d40fd0d2fec87c4445ec6557091d06858fc340e1e468f9e3d7a7d3b9631ad4ecc0399c4ebffdffeff9df3ff3b77ed9f2d3878f0a340a1498259b729e8bcfc17ae9fc9c4c32bec91b61da5450030e9e64c9884624804086d0446636d29538a488abc5c26097f0d9505d9f261f8e9c36f6d92282a9d9dbdf1a8af1c0ca5b3a1d8f5b75f57979eec5e9c7afba2713ce5f150baaa2b441ae02cacca13088422e6801a3148e63c100443a8922265ac2b436c09539566814c2201a94f3994ad1b6aca6ab719632eec3dfbb0cb8d09b963eba84884dd0b221cb0d1f6d51253d640d9784ed03b7215b93dc0a89d9772dae9e9e9f9e9e9a8a17ec9dcc729a05b187a3d9f5f5fca96a136c42af6f68d0cbe588196d7e04af0ab1f07af285bbb1085f6600cbdb0c6be7b292bf7db5667aec3cb1c5ce627046bcc607bf1bef22f7d1222d4132c92e2a180546c3c1ea61b400a1513473075d27575bcadb57b85c2ea763d636879e5a79eda7bed83e3e3ecbfdbf1ef0f78fd9fdbfd68499d421180bb0a4734946dbff0afe53e6f5a9e5f60b10d43112e1d8520414a44d42b6d1000257f85fa65ba1c0a7956954e86901c0e78fbcf5d5c4faf2f2e2e5b75f5f2bea3f01f5d7de5bdd3c3a7efcf973578cc935cc77783eba1d8d01133352b38a823f9d27a3f58035e97154f60e37746c32212951645e4dab0aa4a0107522c4cec320880044048681898846d8b3aa42c6d592d0106d12714b5b7de630db66da48366694927589c11a1bb864c431284cbce5fc3c4440d8590cd64e9ec71f4f9f9e2795e8777f2bcb042b9e13b0d4db85c26e3f93741b6d75126a884aeac0bbdb8f06dbef63784ed4e82dde575bdb2b7b151e71ec69ec892e58d95730e3f76757eb0743fb0524c6b39662408d25a6f39455c9371a969498001443bcc99ed0f4e4e4e9e5f35108f1ef7bf3cf7af72f7cfdef6f7bf5e79fe311d3d7efc6c3e1beadd3e29483353430d5c1c62e2ae716c46cdca062410dea6f619ece6709004900518115508544ad0d660e5552950942ad543bc5932ad9dbdddf3c3abcbcbe0cc12a4d7d3f72f4e4a814175753df8e71fb8e9333a2c20b4f3bcbc5ea2e0a0b6cc5d5c4faeae2f9c3976fce9d900ad3a3c3ebbf2dd7795f98c28574465c04c02ccbea4d55603c1dd45f5c1544a551243edb7639c710157bc17b8884157b74b9f9b7772574baac2213166947b444124dd01f22678b34ae8f1108893486b9b83fba9dc61eb8edee1c1e0783636c5a804c813ec7e3d4c80de6085294ff7088a9271c931676273352ed813bea70bcf9f6056f2c96fbd571c6ff9963db7f64401bd0c57a2e273fc0d76b859b937a6dff19efaaeafcb5f56dcca338671ba9d86036f63b6acaf2797561de87cb3389c8ff9df3ffeff77ff3cfbe7bfd97b111455080eedbc77100aeab9c3fc2926744441b3ca3e633e357385d320a47a0e871f070e40ce72ea357c4730cb123614c81d0291360403516d57d57f2fce9e3d7a7c7474f5bfd8f6cbff3b7fe3e7cf8ffef387f3bca8cb81e9d9f57edfc713b9dffbf3af1fff9ffeefdddf3ad97de1cdb70776f68db2613d9f5e5c9f5c9c3f3f3f3f555d81b991beb901f2fa764ea739dd1466d181f484a4c2124a4cd243ab6b7d84c5c1b00c8a8a08ccc84a9acf8debce969fe6fc7e9a3cd77bf6cb6a5ebd99e488026707152d3a3a0b008864de6b6e4ce27005cc2bcae62e275b89e2692a8381de4f703443e2fc3f42dbe5da657d7d7d9666119548145500dca5487610505e6cbd6e6ca72e67315f9dab43106cb9524b129ad6692e6a090bb9d3c31447222f28569d6a6cdfc6bb6bdf12888cadea4115cb5e71b8084a070b7bf3f9e28c3e8bf383b5e5c5cc7afd9fdffdbf79f3cff6eb7ff5ef9e86f6c9c5308c198a22243d4397f2f4cd1dabe64112fc3f4e1d9c2c60ccb0b5f6756afa9489b5b0c139886f6e41298b9f16244234a021ada15123f6be6adbcbcbc3e3e3636075bc5e5dc6a7feedd3ac2b79e1c30bedf3976f47762b77fa7382605c86030981ff6f9c39a693bbab3bfed0edf38df0dac9f9c9c9f9e90735b2addddb17f6436795e5595701d685a561255f6ca5791a9806c4c024b115d64666640436a321e677fb5158591835f9d9cc0d9ab99510152e6b29136c0d6c1f921a6966d38cf4b0dbbd20b3f4b345411425016559d8904625014dd633f5d2fafa76565d8fc2fcb7d74391b08fca036c5bd6b3dbababcbcbcaa9a59036bdd2f095fc9835b07b65edfd8fe9c6d26db99e8dd60c8546becfcd456b07b3b613d68b73a72bde8eadb96dd41e5090edf3cbc735ff71836c8157d53aa884c02a19723a1914b3f9dc36c8f6db3f6df1df71f74fddfee7772bf3e658db33aa420a57356fbfdfc3fc73ea4b9715828eadeb0295e2be76beddf70a41ed07887dd04012043d45ec93598ba5dae4e4e4ae55906866753b1e4687fe5abb918ff9efdcfde9f9f976fb9fb5213efecfedfee9d9d995b67e3b56bcc391e0fedddbb7c7477ad555c1be3edf9cee0b87afce9f92a6b5dc260def067e86c3e0a28ec93fdbed09207443e8102809523ec93b6c882924056020436c8816d0d864655448b12938ea9a8baac6c812236dad4e884d1f3d7d99d66c8166e43a3ddc60cad5b4dcad9651b31781820a84a51fb5a5cc405c2e5e5c5f9d5e5d9f5ecaaa34f7836beb7846da7f46d10043578458f68016debfb244663bfb1057c0f6222149fc576fdfcd274310a2b9dd7734a9b4562c689dc735bfe636e2bdb5430001ed4829eb0552fb0fa58be1d07b124768b90d1183c091be823be028449636b1eeedbb3ff5ffb8f3fff9ef7eff11d91155b69abc4d50d2a7dfeaf902405150d43ef05a4a001ad45157b48c5b5a732cd85dd4a0ddb411aa84b1fb3a4ccccccbf6a966a7971f9c72f9c3972f4aeaaaef0603e1d8ebef59faa4b12278ccad707ffe728fdc66ef70ff2f70f5bfad7decf4ecf3df4f9e261ba9c87777677cf38f1cf0e1cbc77f7ef7ff3dfb9fbcf1f9e397c7d7d7ed6d5eefee4c7e5039f4eab213866ed3a57046d8f1c2baab50c283dcdc9c85f6c9522aaba51100b04697188ad4351925bd64d578c45e97e1243136ccd8fb5704a8052f2d4364a3fd6806c2669b39449840598d28440553873504baa2a8013e6454bd6bd21951090d6c3fbabc9d2bc56956bd6c193fec766dbf22fe7ae1a51066b85d9f5e59779d2aca26417ec1005465439879aa840969d523fbad499ab1aa0573f18da132b42fc7957397b9131e67de1bd65bd673a74d716b5de67bfda1c9c63e67b958eb9279399ca42fb784b750ea3b5835557c12666cabc3b5ec717c7c77efcff5cf003508e29a280aaa00d666874be7beb93a06316234641e6c1c025dbbb1f46dfce69c80d1e53ab6beea44e7f6ae6693f9f279bc14fbff5dfac75a7dbe5fa7db45e5397b655ed873ed0b6018b55114453edfb9faf3c78faebaf6cb13bbebe99fc3f3838381c864ad69f5afad75e9e3bf4e3df8f7d5c078399c4880c46db800a862a82a053c0caa48262643e2750aabd06c89cc5ac30488122302015bac4a299fc424bc1a14344a06d21a86bd6b00a97e97779a15909ca5b6c85034294f814494993b9b53298760ac9c1c7f68c698a9214c45397ec37a17950e8575de26955bd6caba6323bbca0795e6da71020349569102e47e3b3fbcb89f271d43d4aa0141883d80a7dbd992c648e67baf9e4575cb01237bd6ca6cddcb617b95bcb9d527b1143bf20c7bb971a5f910ab59ac4f93e3ceb9fae25fe25d12b57ae10a623b607ed5c6a5228bb7feec7fffcfbf7ff5b7020192dc63736374aad9fc0002247227b4040a18ee3644e8b51c362d5805f64264b6f7c23be8bf255d44695425344c150442979fb1fee0f8fea32373ecb69080d63c9f9f9e1d1c1008273da1b6145026b060d85f7877f86ff8e0545da7f02c8ec97f3a7dfb4f6d9e3df4793fb24e8611d93fca886c22ba820a579ea96058c3bc25f410a814b3a1d0824cab2f54e16505dc76997fd43597fe7438182331827b1485c9379a936b6d62421ca5b9569b024aadfc76e3d0e3f626da08150555da5bc0caca2ac0c29c50100124809eed12250696b983f9f2e2fa6e5588bc275220df16edf1e0c85b6c95799779976099dceafcfcfcbaaa266e81400da8a02a1943a040de0b56cb56fbbf543da8dea54111312a2bdd17d6efb73f7a5a52a6676772b958ff9d237da13f9b565e6bf2ce679fb9cc0473f19355a90aa641a7ec984c0d6c7de5f78ffbcff6ffbade568ec0814544438f223cdbb5c7d5c45723c094fdc8cd173f8993480927e53516b9a97e5f3c3a988a1c136a856542036c088680161b854d57d95e9d86432bec57b9e2110203789d4faf1dfae3a27eb8e76ee2e4f4d8a054606141eabaac2914768c01852e6b100643a5419f0bfdb5ec763dbab27edb13edafcc9dc49888ca13e0d0a22093f6da5f951ecb32349e38a576d93739dddd78f6cb13c1f889524c0bc2baabea899b82a0566384ed937ec2333b4c1e0789898e6dadc3fcd85b9a008c23faa2a0cbee3483d98422cd18b4a2a12e6dee32bece461669abeabeab95ca693b5eaa963c5c5d41dad1feeee877672be5f3fe7f6f6f7fda7b136c76fce9e78f3ff9f5e5e57458aae64429dc09957e666a028de8a98925c1f6193e6b858cbd76e7778c3ae6f431f6a64a5798f738bfe6be94e6c4c8f675ab7b0a6e4a826db5d7bd6553e40a489cc470529a4d005554bd6bdc2bc6edccd71e3f9c0ae6bacd7f340636dc6bc5bf3700ddda8a10400fec7acf7450e8a1730a362282aead8d53d67e6abc92456900c65327592ac5c89db38384d2847ccd07910a00aa52faaa5daacf5cf4fdb7a77e8c91b391d0bf54f2bc2f4c5da3e79f56ba58042e57ae9f9f9f07854f68db7bfbcf678a69b89f4be55948cca0a60510895c29db16b87291ca1b9c2c13d99b4887fe5e75eb4fd8fa308c4f4f47a7d7972f4f9d3972f46e797d553da15914fa05e892ea2c267e1cfd6c6b38a7809a3ad93e64f8e9aa8085bfe04801a36a4cada81429527152ed627bd4c5c26957dd45553cccbcaac0128fe1a7fe1d6a170669e22795699c2c26dfa682ca46a338d5445101a53e29ccc4907dab53933ad6c22e58dd71e69a745f3b2ed9b92e16b7a4cb1c92e37dd7bf6d0ed00f5890c57fb8aa6019f6f3860aa08ae337d65d495693a1d00442fe7e15242180c7630396d1e208f9b866b08202a136cb7f61d620d306b25c7100c47f6d002a97e97801000695595555dececececece81b614535ce620120123b07c8edb9dbdbdb7de5b7de1dfcf7ef4f9e3d665babab8b4901f0e0e0ad6bddb3c38f698cb2fafbe4ec27b5ec6ab85cce2f0f8e5700547eed9faeb3d655e70f3b7fada3480c9a2894ea7e61d802091bec9305c02c886c873e8c8aaeaaa2f4f2ecf5b2bcc783c9c1c1c46f7f6fe0f8a73a1e7cfb8f5e9c3d76609d8de73d2378677677c9ee8837e83554b00492221f3303cbead8f43971e8ea5278ab15e73b81763153765baa6e3f543d4312a4dd486ae00889519b579120917e2b227b6da86c9e47a797979bc5caa96a989414668899908636883ad17127303a9ed693fcb84899b5a18337b6d8e607eebd22112111bd5733be7a6b3624e7ed0edab7c52121cb65aada1b99e0e86adab2892c74905d281f80edbf5e47eaa2faebaf048301ad6ae6c7edb431a971d05039fcb42f91343f2418f0488aa9c4755c8012d71424242b0849e448a6843aa88091346c4dd4db9f8ac9251198c87f3dba3f9c874f2db4f2d73fddf6d1f1c169bc5d7af9c3ebe3f3f3d76fc71bc91a5c7afce9cee1cef07f77bdabaaba54bd4901b505588dad2a873fdc73b92ba72f8f963d40f9512881959ca144d8a28469880ca543448698c2206bd43d641e8aba5da2fe7f1c760ac3c3cdf7ba0f3e1d8059f9d7af83480ec938ec852144268024886d89ea941003448886d52c8983a050e8cc1988422b2b00a2b8818ec35321433d43d45563553579ba2b96a96307d126d57d8682fe7f683e17edb2c81397e97195e84acd2f4f2faeafafab96a96a962118cc82a0cad32d217c6052dcf898804f6257973dabd698c7f5c36f7b541cf95e78d6937d0ed0076b92c7f6dc540f6ed23dfcc6ed9e58e52bb28a17b2ff610010c229504208279ba2a82c83b77eecd122405dc37f1532aa9d65678e65462d76baeeee9eabd55874e8ddfd307ba201d5bd821334d57d6d81fed7b128aa63d4597ed3119aaaa290d5ca5bec91b6072b7b7013c5e5f5dc795e3c783cdd3c38bff0f53801bd6a9f8e71f3f9e47a573dc70f3ff06ffaf0aa6b94474ebe3f3fe719b522c8297e9fb4f2dbc7d76357e2a7f93b84313eadf96439a9c23363be510041a5e47c28136e1d3a74b3bb36b6de867629a6eac643a75e1eba3d3d7ef9e7cfbeaaa5d1deee7b682937f7fa7973d43ecbd4141b12644110394a214228ca29c97d7dcabeec8adc0472f9f488221ad5c2bc2ba2b96adaa9a32b230646d620bdc2bca0f381f42b2a76d76edbfec09f24c5da65d65daa80377b983eaf91240042d4ee80736f7d84bd52ce6a4cbde6f96bb04dd8b35ed002b6cc646debd5a18d4c6614309aa4d485fe60f6b0cbdf3d66b7ff7a5f08701e61897221d894ec39860052a200afc273a23bbe9eef1e10199e427489c1f604518001dcd0037c21da5ba9ce75bb61ea3c4b54ab78a2488ed7790822296f0694e249a0a8b6ca7995d97b951db630b5b63379ba84e97e002c1b576965bc23bc2bc3b2a96a140322f1c49f170000020094441445c690be63641d7a7a76fafbdf600008fcd7f70f5ef6dbb5755d5e9e9d5f5f565d635d2757e7a761ad6f9c72f17f2bc72381a336900db5bfafbef6e5dc6a555559555587fbaab636d6f3fefc7953a86bd43000ed7e4028aa4009425af8f1f3e2a8c6cbb36059a71bcc37b0c1723e1cdfb774797e7157d57197eca13fcd3a29336db7f96e44980136d678c32a12618ab925559c0988423902794071aa94e1f880860bc2babe9dcbd6b525266d2be0bc2beaad899bc74381bdcb7c84e2bc683c16154121465ba5d9f9f97aad737f95957348628802951198a1d47ec3f0731cfcb6befb813e62a76316a7b957c9e6e627434558d8e097339b8d06db730a53ed880ef5ca0aff1b897b42bc6ed4b68f28a64155bb2f3555454327edbef45fa323f1ed9324761850d25a721d257e2cb90df9b15a0c6ae7dda4e9711d4e4e29a6894b4b3181cdd0a4a3b732c8124ae5701437f91084c01ca32b6d68aa4222bec6a4cbabc3b3d7df5f73b7df2085005524b5795295354f7006d10045ba6a8e5f0cbfcdbfbb54f6fe8e0d21580dca663ff4e1d3a3d3d3df9d70f16b55d9b1fb7b3bbf0f5b757f62bb56bdcaaca899312ac124c6936f33f151434c8080110d0118627c70198cd637672fcb8ccf0a7541d3715decece265bc9715463a14fc85cb183f1439094214af444aa1544ad8d4a70c7b7cf49cb124bb0ac40b4dd07471b8549575baaa255d82ad6d5b0380ba82a085754fd51589c2fca8e5654fcb7f0001a96713b97afaaac626c815a3a82812050e5f2148b5d23d7e01951e3b7083c69d03c6e3cf639a1eb0381b63ee10f95887b35311e62876739af6b96ad29b017c42cccb998d9675ec62f7e3444be2b1e07cfacbcfa282522f431dd8654b3b1f39888edbfc73ea4b07bf1f01989299a11449bb8560b6daeab88f464bc199368636c8ed5f55adc2330a4575b2fed119e5e5f5d060382c5f9c0e0b8cf4e9c3e5c266bb9fe180300e5c9c9e2663fddddddd99ce18210b6801c631c93704603339395000bdb15edb1cec1cef5e5e5edd79f5e6ba636801acab50dbe5c238a6957b3f9f22431623c117862688ed76edab68524bd23470a70a5328a9b33082757e76c5daeec1d1cb2f0ee208db83f7e3fbeba22fc17c3e8d605562349a282238088cc129d971289460e784aa882a11852d888999936490dd04b85999575baa6a3d9fc71b0524b6d6c81358984bca154d857971dfc2b2ca5f5415c060307ec988495e23d753d43969e2a12d45c47791c9c96bedeab3a5713020b11dcf7126fa735582edae4d3d32af20f3c2da55b7361bdf54bbf2ef329391bd84d2342943ddc67ed1afb6d431ad9c46277fefb4bb70bf0aa46c6765d9b53a5bdc9e0ff0c07c352846ac21b42910166ce2270d4a7ea4551932a03a64e05ba2b7f918a6dbc93d0af0546579bc1d0a700a690743ce91285c4f2777f6200a2d63797a72ac1f8f0e8a82a80baa3aa4e951ecb75114dd275d00047ed8777854e707c7711c00bca6a3b6be6a9abe6aca2bc7072f43b3d7afc0b3d2832abcaa2ba02c151c813486d29336c46ed3b4020a2b92bc95b55fc73ba19a5b5f279d63d4eecb8572d6c4e22e2ac086415932301aa47daba8646d80b6279133b80779989193480956991362431663b5cc663baaaa451364143942bf8020d4b1b90c5c0b7e2fcc8bcc81395197654e46d888c265bc5ca6912409404c37750b5a9edd6cac3e601ea4cff166d57ef6f638f5f95297e6b90ec7e2f22e5851f6f3b4c1eb53046ce8d1d7b200b63e3bad9520995b76bb621d157da8138018100bed5e9716d9bcd9f3ebb77d7658220a120d4e64fd1571888c93700e25da36c89fc364e88a2ba24c0d000569b7011240016e6a1b6d1a13080acc80a6d05e9513f5c4d82b78eba86b5e223780cdf14e9323b6b53226d559ac1bc5e208320bc5d9d921beed8723ee7f8bd6aa6adca758f43a23e88289c453144452a84cabeabf3e14759b857dfdfdfeb471ee97ff5d67fe1d1d3f72fcf9c397648a6c091be0c9347e3bc374b40021930a6c8a26c2a7740eda34885faece9d31b46ac9fcc226717da9857e211518ca96a9a825b0c002a22b57885a1744a28180018320846993228432b99d5f5d265b4555beeff32ced4a75eac29431333b33cd9cddfdcf8340329111959d95ae4566659a125ba152900d6404b68fd12961204b0df5010a5be7d86b204504530d259abba3bbaa27c88c8c80e8f60f1ee0760333d2ec57f7ec7466a2614014e329e1fcfaf9366fd855ec2134e21f82caa3c4362583b6b9abeabcaad50ee51119cc3d43da7dbe17c9a0ef39394a498ca3223225e4cd26bf51adf3a7f002ad7888edf90595d3a31af3631dc1e7e6f3ecd2cf4dfd25678c21693f9aea3641d930220f089c10aa10d294a9d57d5bd43088f2e5ef00c0169c4215111d27b1ec398fe1ebd243af78333493e427d57d2c94256d29b0e52ede1943661654360d8999ef33002c177b578236e88288937a9628c1fce479777b77d2c2aa6a9036fa92a64316d777f7377924c3f3d5ea65b2e03de6a0ea96b2b6089527e413ece0344405e817adde6757b5102a0a6a01ca1f2886da521b0ed395ff4f5cb0123faedeb1981ca53c2a0ec55dd2657272dda69555593239da5ec1bd6b62755759fad758427bfef6c0a6e89beabe81259556e1418eac93763e8c28d7087f15a0689bf62020d21f0d002225555d43d4bbde67b7fd716c16f9176a13d452bdd7c926801c5dd5bde2aeabe6ab6beabe6bd6db7f2935a49673e0737f77bb178cca004a84373a485f9252d704c776137611d188e36d88f32818f19ef1e1d38e7cf88e3091e56878f31388c7ae14697067d6f111725c01aaaa4c006814c28813804084a6d065ec63d436d9b3db83ffbcf76ff110128126ec5686bdffd35a91007f7911884e2a84a2f2ca46816c03e3dbe728c26a46781214ec21723464360644edb6fa0040040833c43909c66bb130682b4edde461b02fe5837e96aacad6dbfeaaaabababd7dfa7dbe5f3bbe72d455398ce637f18355ec04a400c4005def6aa25c169248036fd4be1456cdd678927d57ba08edbf6aea489909cda6571f4fa75faec68dcab9ac382eb9bdbd135ede835bb9d63d43d43dc2bd6ad6a2ec135cac915f7aec286c962205b6d3886e89044590317623848056798df7bbccc25b551565166b84604b4fb31337a8135a43e0bbebf17bbdd6c133000189cac134260d813376511e16a0204de271d57d97f577d4bd6dd47d4bec8dd5fdf03ee63b9dc83e83e41369ac4cdba3c66dca141c15a01d75020ef4a101f30f5fc7c45bc3a3e9f39fb233be6f0489d74642a34a36ff9ea997d78f7ef106363444a069c8534eda5fedf43d475f4eaf72f3ff918539252541b6cd17b4cc1a4016f63ad9b307a0e26222924c9af1e75ff8f7f5b3f5937d6bd55ed58036c172b03f32e02aedbfaa6700212c416a9042a2248dc61188c9f835ebfb97f7e7df4e4ab67a13ac81cb57b8aba3b5c26c17cfce2f279bc6525a16bb983c46d81d130453249ce59c950437c4525c702764669892d036c9285236c0e035813688c272bc35e89c81a72f9d3fbbb973be77557d4b6dbb7ef5e7973bdef6e6fe63bdddbfb9b536c4bd4dd65efae2fcd933575615169d539252b46ca58498952fc08717aac834366a168aa1f785ee7642aa4ea94e4c444b75db495883480feeddbb8137a4908b0025645449587ea2668898368c868ad54755dda133777051cc126c83d435fd7f1317841342e2112595503ba8279e9b8f06172544a30c46a7c7a4e323b1f3a5bc6fea45d3e8f3bf4fceb50c493e8c7b36a2f6f8bc0e367d192496550a3aaba8d76ba0e83e872ba596ccccad6dd8136ecfcfc7b3d0ff2ffbbff618037ec6dbb2e8c03332b16e872dd78a92589f573226d57dec8d6c9dfbff5cfdd6ee7d92685da6579767eedbdc9217c460da8463446c023378032aa87bee6fdfbe5e2adabea8956dfaf5b3f575ecfbef6ebdaaa6835c963b9b8b8b876757d02cb83da4356817a167b91bfe374ee7a1bb6bd8beb7104fed7665659dea736dbb137caaaac9ae4b9d09a846e5e9d9de68dddededf5d3bfcaaaaa2dd87000f39f9ff4fdebef3cafee61c917d4beba66979f4ece2bf012c663b95fa7d690afd5f077b128c593b16c1fcf4f469ba69413484586de22f6704e4c91023a102754193252ca6852b8663208006d81b48ccc9358dca77bdde67a92e8df49252b6d6557d925a38afb1512133b8c03d4a48ba3d3beab567ba5a3f5b54a567b0b4626c81372beaa16c0bedee41dce68134c0931395346d046555e2c3b20a2337909e1403931f1f6967cce8d10dcd3e190e8fb2f8d851f86acd2fb26f3e988f87e299de733f744e8a4e344b47e5c11e603fc829248ad7de8c8096ded3aae7ef9f7e717d75ff4eae2f3bfcf50a5366f322f1c2d4f960697ff27bc9f15cc035555dfcf76f79f6fdcbaf6f7ffdcbf7ffef6edeb3feda77ea8d3d935619c6d810090120a0b3b66ce7b0026d0635773d43d816afdd73fd29578885bfa65ba584ec937cad9f6cbb4138338bbbfbb92c0a916ce7873fbff6498ab5c22526276d97f9340e089583f4dbaaa7f57a1a710439319a45ecc2aead735182ba23813bab83df1c79f5ddebfb983d4c2072717906d3655946d5e9c9e9d5c06016c17c036f2c190421be812a91da3b48225031c21aa7a808609884ca86188a00b860d87f98ef8a092c43137c968de0df0df43e83a8087fe55511d415846ec20a49acf9ccc123f413b3f5dae4fc657276eba6aeabe5da65d8faac5d7136ce7c16bdf036841337615592aeca22b5d97ee52a9d56170278f3961adf4ee16f07cde7a4d59b7922d702553bf1b3bcf34ebee34f3efe5e905c5a45aa92017658bd18da6440566566458ad73f71f3bfbc71f9f7e7ef4eae2e2e2e0ff40ec1dd5f9c747f1cbe23790c5ec5479d6c95b555f38f2fb87efcf9f67bb5e450b429435c96a924809372a2ee6ecca29d21e41646e4292310eddddd9cafe75717179757d7d7d7d444bbde6edddcdcfefbefda736fc65b4248968d1aa45eda127926edd67da04645baab19c96bdfe89935e852155c813a462c63adab1ba8532d488643307204380a0ac06def0e5ebc7bfaebff3cf1ef0286acd4f6c556d0a5fead54769c4a01542b4e012e8093555952b654936c8bcacb033b6621ba526bb111d042ec5a0f28527ec1111114835e2819d03c034fdf03e899328291436d936e234bb4c895814158913488368036c4137e675ba393d3dee465edbfabb6bb5d2d65e36c8ccc1248b9b9bb5f6bf17a0368899b0e5737c0541a6a16e04a06582f8ac18c7b7ce57b742738743b4c78ad43874f597c13fa6f1b3bcf86ddb91e0bd762fd19da1e38163011d0a182ed8e297172aaaa609425e3f393d3f393d3b3b3b79f2f5612b727e91558363a7c1b936f029e83427f4939895540437c0ec85b57376eaadba2285950dad216f937667b80aace3ce51027e4358bbbbbbe7c1e9d3dfcae39594c84edf5372e17436cc67d77f6fdfbdfeeb9f6e9f5f5d2ad68886b3cd761d51122a9dea7373737346c64e4d5dd48c9c432bb96241cb8b6a893570809386c897cebfbfb5b3d0de26560087a48c057d5f3dfce9f8368dd03d03d884921fc97e9ec8926612f6d0b4a41a2569ea1759773d8bd9d40066e0bf751515533f7ff39849a29e4402808ac2c13de6bbd97dcd17a8939208130541760c2cc209199954114983b0005c09995b9abd3db8bc5e9e955d63eae954e04443c0349f3ebd16b125c093594392d7017ac78b87de9edff8125874f08d140c07cb0cd12e45e1dae67c7aae1e190bf1f6c67c062328936ad2acde3f11251a222480a088a85e072dab9ac9b412ea69ddfee77fb9dc0dff3bf71ffc1d8300825d1cccc55560441f3c0bc15d30db70744972fce6961cabd582330228570a5ad77d2ad296d1b7968827923440ca0891880c8a3fdd5f2fc0011c818c99c7306d23b0837fcfb8f2aaaf74bbbfbb3b3b3dffbff5dfd4d6dd261b0e49d85f126eaaaad91b444ec55ca93268168183c498375ec781bf492108c6feba925e967b548c553bc6bea1886add6777fb5f6dd5f5c5f8bb1f5db97746da209330c197fa4088ec8ddd0db0725e8593fedbf6a9a90937ec2a99c61e8d49558400d69e2c1b5cacbccc8ac2993c4348dd4bdbf5b80033b2ba103caa220ac2137a492baa6692e004ad7c5d96ec9d755f9e5f5d5f3b7ab85e2d7d553d6becbf9f4d8890da98136fd7f544b03f35b464540b8f17847f8f920aa934e3438e38f56e1d4e07c7eee721e18f311ef0787cfe300ece0398e70324089255c6f1b08de83f32445d291242f6c866e9af1c53da523bce617f777736ac6c8993d4c6f6498aa2f78822b743dbc1e74e956353792d022ee68eb8291a96a1243a68899d295659d799d3694475051044651440236088835236c001d6ee7bfef6edeb871f2c7de14c6322ea6f557d9148bbb9bbf5cfddfbdf6ebbfe73bed6801adabb71f2e5c3f7af4beabeeabead6b516505d96a86c02115a013176de9fdeb262761554642b57da3d61d6d212c4bdd25dd55ecfa61bce773665d093914c429544a48c3b2b0b4ac92a272380929b4619c895e2bbde6738ac44688c899fd71155622c89248178c136a013c43c4df017a40c28a45c868880547169937e493376510547501a766d498bf01540a95c2e2faf9c9f9f9bbaba2fdbdabeaf10b1ec16c17cdde6712c895a0051f027ee70783feb07217fdf4870ee2e8cc89278f1f3ec229362d8f81ca874d881709938d74325c7c340717ad69e05f3a71d52d82ac5105c65a6c55ea13e61d43a9934fbb6be6e3f7afcf5eb8716b9a7acc0aa9250276d85f12c8c940ade0450884f84feb0744a2e143815caf244422055dd6933024cc3b74dc931005521154a24102361999a209137b7b7b6c893d3b3b013334a5e2798edf6bf1327670e526c7df6fdc9c9ca65bc5c9e9f5f5c3b76fc73dff4ececcb7ffdd77fddf6e75fbef3cbe73fbcf5ff6ecfcfcbdab97af4faf2fcfc75ba549631ecbde37ec4403e8c034fba3d5a37e83e85192f0d38e4c3c0e113f43e49b7dccba6b9e34c09203b807244476924842c2ec850cc8edb578937a174eab6fb85c227c012789d85beac78f35817250996a12e434e92e835f3c6779376386db7f05ab42a0afa288224bfb9eb48ae69418a89278417a81c7d5d9f5c5c5c5c54bdd2d5d59baac1ffd4ec19042b6e26d0c17c134e412484921fc581e47c441ba6948b328db2da6f3cee38827f0c04d7ca6bd386975e827daf1d55ff141c2f1fceb7cf57f8039bdfb8b74e2c0536a09673717d47d6db8abe54d55beaaddd7dfce2eaef2e7ef3df2e5ebc669b8f3cbd73060d82202a84e492946519c293bbaaa2de713bfbd6fd8f9925a278e6984bf248c1b84a49c5d551c59111590400429a5b808052c6307f90f60cc0aafdefefb1b6de9ca65d67d5cc9049888efcef9cf8ab54b0a23c43e83e8b854752b4a6b36c6f4fcff3efbaffce2e9d35981fbdf6e7f77fef6eb9fafbefdcf2f75feedeb9b9f7cf5b338f5e79f2f469ba3b3b3338a3580d4b55a41000a96a2b0ac92ac43129d168d27cce096c13ddd6631317d5552a97a0a900c318f6aa5e2bbeb7005abfb9b5506f6d8547a293725cad67d4d1a2f009149905d22665e092e434f3d434c036c81535c1bf1afeb736c45d5d0a495437e482374de597bbd19153a99555195144531be49958c47bc5e9e5d557727aeb9abaab6fedb3f6c95a435c2f6c714e83546eca3511be88ce922df0aff9f0c51ce973870f51f32481e158a8c1bc021b0e812aa80f8e9cc74370f34bc6e1651bf0d01f4568153666802caf2798b136c07ac4406d45000604b236bbab93f3d39be3fb0c7efc46c8bebbb9f1e71f23a2fdfe630a24ed102c412698bd896a9cb5b92707ed3ec9ba92888935676bac93796744433baec455ddebf06c9538a0933fef50b0089c139376a8f92426a73fafbf76f9d76808654c0336660236454268adbb9196ecc5bfaebafaeb2b6d04e4126e013dbfdceebd7b7b71f2fb22fdebcf3f5db458f9ff3ffbabbb977f6ebffee6e5fbdf5cfddfb91afddbff6ef03ef3cf36dedf39babc5c2b5b4aec9b817a6b9ae4f4653ea7dbbbfb311076b27eb53426058050433aac2b9a7bbde7c4139a46fe69d6393b4eea21dce88cb129c1db3a6c918c1c91512d81317e89187a968bbbbbbdea73d7fdfcae056141d54bd020903c4930249ca324489bf40a03af922b224c899103bae66817cc276757d79f4f95bba316405486a9aad6b59c9c0eeb5b35578168bfbfd41c6726c8372b690051f13a8496546166895010b791b3dcac6110489bfcd11d8ddfba38f087135549973a5c7889f3c349a72417f7ee3ccac8136f8d041f9c8ba99d98e74b1fdc9cf45891e933107e0cdc8350250b5e4141176ce677bede8381385ae6646f37f4960e334006e807f5610b6899d958813a5fec08d2319b0b8136884d01933605144434c11d89aaaaca32208417251b82a6a2792e40cc012a6a8e44837816d7bde2f4c0910d4a20a2915c08a83b6085027a16bbbbfbfb7d9350014541da100402242a3ffce3f3fbcbe9ebad63d5b3ceafdee6dfde7dfdcbefef5dff9f7df73d57d833a5977f5ebcfceb8fcf93237a9287fe585d8faab6f40d854e8c926e839008102f6d456824c96d7fcedb722aea9a44c22884c080cd6d5b54e16b7f29327e496c16a96a016c92c03c034a92198ee324001ac49ba083f5eb8c03fcdbacc222295ca77937654210c136cdd4167364b57b7a7975bc3b3fa71bcabd6a4c6ab7be80025a002c0aa1248ebfe7c17c49835ac8f0ab7114c70b9de1a67ee870955d70c5ff89388f8df1ff1c10c708d2acc028f90da071642ce7c96e7f1e8a1f1c40d370969134082a918066190b285b4ccc3e83e673b9553e944c006bd6db9dcdfdca737df2f7004f0d3e23f82b6001c4941cf2eaa82dd6eccc9585408880d812d951beac200017a1bbea34042486a2fccc296436ca2227c04a26b497e48197ae568d1076d83deeefee6aaade331436510c7dd200248137a0480c3d473feedcbf7ffe7c17c6da9ba395e9d2b5448d727579645758aaaa01ecfaf9d76f97d7a7e76b9dc6ebef5ffaefba417a03cfef77fbbf78fbbff7efdff3cf2fbdfdf7df2f9f3f7efce967d574445555f45f806d5616610004654755dd5b5912cd6fbbe78127806da5a6a2b69014018b1fe65d9704ddcdedfe67bb4925c03e43d43c0bb2489198a9dfbf6bf46eb2137a268c2a2ce38a47f3f2228aa6cc941d5218013f8354276f2eafae2eae2717a725ab46c55eda5b84a02ac954144516a96adde67368924a8136c244d59ea35c9394d0f8507b7c091d13556f82b84e8164874624383e5ad6fe75836b90d3096cf11f418f18a6c0e384a4b78cfb00e3426d497f87649049fbbfeeb3d435bedf3fba72ba395e663b9de0dffce9e3502b37b988102b6a4312b80899d232a44460c0824a8c2a1cb7f6c81c93e2eb867a81000933276068cc1da3740292624fa0224a9da293470936a178436fb9b977ba6b2a412ce6dbe7b6d384849508e9c3d765555dbafefbbdcdfda33434fbd7dfdd7f3c8dfa6972b83d3b395e2faf2fc042719225c81352ee61b002c0126f3c846c6db856d7a7a7df4f91803018681e71fffef5cfbefb9fbef7bf7bffaff6ffddfbbf7ffb8f5cf5df7aff2ad6b18586e422201a02daa8ec75a0cbc5d9677672edb500917c1142e83486a2f593b593aab238a23ce6dfefddb9dcddd219d2ae827460d8281b80092dc173e8786fc40492b421ccc7795421333a9e121761012e0902426b573727171f38f2fba5e9e945d5b6d933ececa2f300405115444609813684543d32126e931abe163def4c5adf94030e81d38f835b4e1160cf8670bf47a23419cecf134dda83d7acb68db9e146eeafa25a91a000a5205214d8100f6867bd16bb67661bceab547b85684cd47de5d5f5302410bc988cad99d3a91d8789b9a5001040bfe74081f2fafac02288e1ad5b485044bbea10dc36fbd9681beab67ec81004648c1da46265001dc97adca3e8bbe37279b95a77345e2de6bb6591a811c73b9492ddddcbbe77b130013d436f38fad9325cc1bd5d2a2018967925e16c96896434ea6b649037c86d0d036f7bfeedd9f3b7a0ec00930664cee9e7ef2ff3feadfcfb879fbffdefaf73f7ff7ff7bff6efeab5437676767e727ede2655555129505d4237fe343d4382259106704a4680d499d7da1d416d73bebfbfbd5fded8076529d781583fec98880381275c0ee521cd414982b92e513056e93a146e87f30d13c49543b8c67c8c087a7a71f4f9d7e79f4ead85f54893ec65e6e78896ff1f0d492d4136cc92ba426e8931ba49a8aff853bbf8ea1799d5b4af1efac70090c17004d1ac50ff8181c2bf2cba38d32ce328e8f853317cef8b34bbbdcf9e115041015440b437ba08babe56bde5e9e947575d555f5f5f573d43a6ceb8faeba3dbcb20046510034e801180dc17ada49fbe3588136e1681c2255ecee7ca6500041e9b2315008316a0e4335e05683ec3d8444c3d0993b13e25817ae7d4925a49e6bfdc2ad6e2e4e4e6f6f678de62b0f03c47ffef6afeb7fed28a4f3e422971d635555962e83d0924b77fefd291fbb924c81951ad5c2f5d7ff78201e55ed6f3c0777b737e75710ed30a2780213837ee78ff1efce78ff1cf8fdef6f7bff782cfbafbeffbe6fefeebbf3cba3f3fbc76fce9d5c5c55557d4440688749ebfeb2fed85bc0c698ca33ec027e8c959913e45c4863853455bbc25b0cc7cdd5c885cbe6786ac99bb909480985814952b2ba466e0937ac236ac18591d48cc557dd9c5c5e9c9d9b1bed877ecbf2fc0b136ac2cf5e9ec0124f3d8df8351da8484135a095594f31275878061bf0cfb32f9c767f8f19bf8510cf8809b783981f30b4d369276df8397ae151e7c30bcc3f1cb23f8aa256d92a29558575ee2ecf4f9d5d5a344909c81a5fe6b727575f7ef39f7a052b32441daec33800a2241e1411d814456c1bfe35a4ac9a95c2149104081045583b6cc80240a9bf127c80ace104360ec127ec35f3837e6a968813a5768356c41d184c16c83d829a511138812721661e42c9d2165ec79b50559b6fe09824804c91dcd9aa6261610111104e4910925a46b2f82817c13d8b3e9e33360042fbaf6fbee77bdc368395c20c8e9e5c9f79f5df95fbdddddddd5fb9da5b677df83e4fa401ca5b7a7ab27ec9fab20ec461d8136a2b373355c9252d8ac17a19354340d5b5b5b6581446e233c2a35c2d0f780068800800835edfec4c229952333b2b86666eca9819c4582caa0dc26172717d75f4e965d57ca0eccc8b4293203022295505129078c925a43d43da7bbddce673d4310086e406c515d0c17463d7ad6f308f9454fe323b0d3cf9d2eb94e9fed0c345852dc59faf866d5bf1d96f0f94b22d3cfc7f2d8e2f5fe8c011137600076c67d57d6dd8508dc03ddfdfdfabfedf2f4ae5d98a80a1b5ade48588f2c00000200944414451b8184485e08c685b80060427a0137e8d83ab83d39c17a28f607a416c927c4645c31e834f97aed220189cc1da5b44817c12d401248017c6c756c0654a1902e43689681afd21193b510049275a3d3d50b84e89999be6b119c13628c26c9d17c1338e4135a891da166d898b0cc93c4355555d65d6e439aa278ed85cc93dbaf6fb90dfeadabb43de4553d41e4f4fcece2e271ba3100074e681687f7b7336fd7accc7377767672bc567d435599b8049595852b2721019c92e4321a29135486b68c11195b8f060245a49259cbe65021182102300c37d695ec584405435a8aed8730c98c89eae6f2f9c3d39b8372be411c9376db3f5957567d04493a03e949526e45242e26e4f8c8f0785ece8f8cb3e29e9369c7d72b284e85a937cfc8d32887f8570c7c72cad2c4ce1fe76f846614448680d0dc9e772334da8a069ac11cc60090136c8bebdbb8bed5e96c7ef4f9d43585fdcddf79ff5ff739352bd5b046c913c5abeb516fd9038133310e6737fb8aa1b42040f4e9f3fc997777bb6b3febaa2680b9bd7717a04221348ecb70e077bbda33e8937ec9f475b8c19d9331afdd4160133a138546186d1deafcc951042494b0123a2254c965957ea2f937616627e7e02feaa611896a904137e422068cd6fe6decb155e134f907d6dadeabee5f7dfbd5d9d9eddddd97dceb6f021f77fbaf743ece6ee731681bde65eaaaca5b022d4f39d6713c0307a0a29ececece4e4e4cb5a2d80ba60c00e43524c9b76db96a92c9c9134925215551501444361187eaad4cc56d56e7840dfc5ccb995da5b92b6c49330a2a161ec08df41ad5dae5e71f5d5c397a55bd81b635ac7c6f8d2c015e812c43188688136c83e832b2095b16a893b0a57c0f01d3a78767f0a8383e57de8f71f39817d328418a0450bf627044c91672c707d0dc34801a028df4ab83e8ff1b3e03be256090bf6ffa20bf27c4288edab492dafe63fa7bb9b85e2d813ba3d3f76f5ef0fbaf1ef8c6d53802b2338665148d7c838200285380acac110ba6aaa4db744451ebbbd7f2cbc3d334f69a27e3e03cafe7d7f73fe71575575e27c81478ebfe59334e4accbc5e237c92d4129e078aab77ec9cca8e3119ca856876545136c802037661ec5e616b6d0155ae25e8222a44e9cfe18d61e895529aa66550081737f3caf6ae73d937e044ca5b35c9edf67dc95bf96274ec8bacafac7dda542b5548aaeab9127a1527c5ca69573e9454576da96101d58198886e89e7211544542b60058140501c00202e15e0e3815d4abfae9222a4921ba0001a142b6159485230c0358a71b8ba7efc75717655357977dbaacb376da3236c09203fe1ed144159c93f43d45a05ab4f31a024e49f09adbf17529d17aeed12edce0396c7cf59f894368f438c0f88c9b78fcbd7ce27bf8b585442333465165e248e51b9529126669c26c8157e248077bb1f9f5d9ff8f74f3af1ff8f7c727171f2f74f3eb8727d0489252102d9ba2b6f934d4205a6305526c94c7d6c2a2be4bbdedfdfdb5b4a0c9376308d43d87b6adaaead8b7bfafbfde6ee3f43933493769c935801248ea9637e85c85284a9991113569c20955ac985a88ac2c0a600dfcf9311044276d2910348086c05656661786a80dc25a0937222456619c5406b9358bfb3522a5e2796dad6aee6c13ce68eb45b8b43b404306d91feda1b06d676ca3fe537368469d45ed127c0ccc250a3550b6c4656082d43137ccb22ad20de492280146ed79e0726655c29915587fa32236944550c8a1411989423063bc5e5c3b7675fce9e269b43ed0ba802b776d973edecde9b0c2aa02a20a89337c89b03d6924c2873b030765760c81a34b31d3a423d368e7f094ee7c4e5c74e9a9f8be38e8ec660df46f16e3404ae14fbbf728e7b9b2c27ef87cc53830004086cece6bfc2a82cedbf72f4e9c9f5d5e5f3f71f5cf0fb2034046d93b36800478716bf8f2615b46080ca5b6cb18c020c348d58018c9d812f647361da14a6bdeadabd6d5d690ebefddfaee73b939e61d535544c1681afdee6d777f6dad2c9721953041937892a176b096a379dda9d32dcb7c8588a85fd15557ac9c3a30082e65655d81721ecaa2eda310b955a098d42c9374569177db88c8e7cc20a84ecaf82aca2a6885439038125384e8c387413b86c97205981b80350f82b561845b405d4926d540d23c82492e0419256332f8e88266edb07119c8336134ecc9185120b4604355575fce9f3dfce97b72b41605567eaca223bc1ac91b5ac20455a423e7ec9bc98ba2850aab22ecc936cb7fac1c36ca0e1d605f9c4ae874e73e74a168dfdcf7c27138f8370de30ad2e390add13c91446108c00ae154fea37e055c099aca301d493852b37020884eccba3b3f3dbe7206da8a01954367e20c1b800280caa28bfe1b556816b307accccbedee298a31010423595fed2912be2d834889fae2f9ce63b69cc09934267ebea1d01ad92c08882081256f8486c8284698826550cc1a25d7e9eaa019ec2c2a20424379d98a8272b42593b8a492561ec402ec99d39e8e800d81636c83da849ac675f03b365e16a813767ec0384ec96f689157ed8543553e1937a8371f600201022892ea7c9f19502503a088867e2a5b2757ccdfad6999d2a51a9fd0a2ebee9b33dd09225a82356e0cc1959cabe65727175797d7df4d7bdc03ee411da73444575511285a93e7a446e3f9a86afa89c26a96a492d1ce17b79c0c704dad32c9be8e984c7c268cf8a0106e91a1b4ab3a2594942c29b12f125e148223737d3d17ebfe3a924e89539f44753eebc1c4213b10543523c20dbdad757a7671288fef6e60da102a272c82985b616f083fc5f1a02a80ac240499567bebd2d177bbddea9aae717179ea2f577d837e144041ca80a5316a9e4f4f4da1cf6fbef6eeedddd9d9d967d57b86adde6d5a49a8420c837de2a37ec5555302a56eb43a0868801444c22181ac93f816ac9588804db376a2f492507ec55e900c2846015003edbfc5436fccc3fb73468a222209ced5d6cb3261042154034eca711106e4920372425746ca7beda50098c176d3d93b03b44e003b144d9de125a8b8184b857c01590141180944588da054dc274981baaa2a2ca013424be68817c948c756b24fd67717df4e5c7ef9f397ef4d7d5da7d5de6cd9176a8cb1276e2b3471557790df5dcc521dc67bbdd67bbdddeaf0128a2a5b6e046849f66c168cd34eaee78f013d72d43170e99f1e2fff0d1277ccf3c3a989f8f3e16f3eec8d36d41fc7ac2a617363cc15a306c05ec9ccef6e222aeabee471bc3dea37ec5ac97b3c86da506665427e101107fde6b5420f5af6e180d210157dd252b6cc24cc51be9e9f973bc519536d9d9fa75344eaaeabeeddddd61002d937a003588084d5bd2a5b60103bbc1294a4ccc555ec295c9b0f11901d41e0dda5385ea96609c17a989352febaac19105515e8ac6c81f80a452ebb8b87ac585b4ffe969cd967883584886c09145cc547f9469c839c91595955423280b53c4ca8a9852d4126c8a25e226352250b07e044655896f2fa2829d5e852ba87ff6fbced388f0bf830003b8484291084948c8f6b93d3fb07d4ddc279fce3b71f4e9c3144cdde63777773bd1677276ba5da6160da3236d292a80b8a80ceded2544293bcc736cc95000c9135671a78a879f0155836d3e69cd6f74763f1696b746a2e263b183ce4fa5478b085461b580b8f80246bf7cc4a1f96a407eb204540d813eca5b848a0d4defcf4e4f2f4fcababa393f3bba7afcab5d2fbcfaeb20769455457a0363d437ce2024a93e3ebfebfeabec5d5d2479ebea49078168beba7a6d119130a20254e35c08c593aab27f6e5dd94e4d4b5f5d5d551bb97a4cd5b5b7fdee55de9da1421651eeade2544666a206b0a2a52744bbdd0c9384a92d4d7c817a11d814248eab60042a13555d83751a2461ec1395fe6f43fb80798091036c0b28a46a492b02012001b628c9da3a9b2d95504402761ac5615bc924e496eacbc2fd5511b460c86959316fd8aa800955499acb1b8b714610081fe151501d12915dca910296ec6c758ea2f8eae669fce3ffcbb3955bb8ee9d3f7136c0fee5f7ffddbafeeddbf73440f589fae2f2ec041c9bad813cc2cc22a58392dca23d4358413176ed7579a038ad27ac2f7c4aca8802a1d2d74ff0bf61e72d949f17d05c7cf9482a516387c06c1e3e098a3691bd1615492771028120425e2aba500384b771569a95543485b5f5b3f7eba39ab27fcf9fb83bb83db8bebaf7af3ff9255ea8573da52882222a88601d0e1a00b0141aeaadc944188c7569c0a1a5c9c9a133bec82d4318a83b803d477f6e51c4307c1c31cabfd11153575586f366d4976803faf2fc1556a0bb035fbbbb57feb46ca972363792c4cc95557dbe53015841934e25c29291d735954a1681afeb7519ead5eae4fc8ca39c2c4a0206a4b4c874fedb88ce63b921071136454b125646655ddd4bb2657438080cda77ed0927ec99b84a6a052eaaa2ac05cd79205cd1d80a84a85a74054c006957d42b280388812476de8d7e40e290a46ec17cc0886baa79baca48ecf9d54bd5bb86e4e4ab3954d57d7bbebddee63fbef5ff27c1bf96cdddfdfd5a83294efcf3ff1c2ad6ca5bdf0391346086b34f54c04127a9268d6fd7ec9781602a2111f0ac28a37a14b613cc969b851b00d758e0c93b6954001b4b647977a6f2e27923c6410c0e1a7159a8b8944440507e686505561b6de1abebf02a87bf9ce6a4c70b47e1d8edcf14b1da23ba53925a6c55d5b5b07a96096a9ae4f4ecfbcf3bf1e7ef5e750eb211e924caa9adb7f5cffc2884f1e701b01a56d059ef8fc2286004125e05917ecb30ec036ffbdf7cfbff6e79ff0642f2aea044b6fe440c018b6b1b6decc76aa4ded6b57580212c67b7b7bfef5fb251929c9e5ae0d899b0613486d55e59094c65ed38136c89abea014d813c2967b9e999beabdab52661b269c49f643a5b8edbde7a134a9059404dbbaba6d5d45b025904136666e46c91308993a3223484a0688476bc0b23a2102415e70b0ee724b2901e44a045a44159041d81420452ec924ec573ddffaffbdffeff3dff8ffd7a82c288ecb1f5355d43de24d81bed5555b46c4cc93ce6312487ffefd66e9094054427cfaf7bfbc167b1308d655b95833405253ee851b01937a4c126a09394e3c8a0a885c395e85588887eaa59289e1a15589f482e2638b2386d32d2ee126f4f19c81e3e0a1d3e7ebf1dc806f3164bf9b75d0d02120dc9de03b3b93fe3c5257f6a43dea9ae469d94ccbde773e83a3f6ececf2be54703f132521dfd626d939c8e03fbf15ae06971060908449332155ec14c0013208ae83d6edcbdf3cfee7723d4b86b96a96a9ea5b6db5513e0d9536bb6348050cf7409ddafefeedebd77f6e5ddebf7f82c3716bde9bc05442237a7e76868c9374eca1fee0b46813669bc52004a097c17cebfeda5b60d5c81d877444597bebea8c8191af817a01297b511a5faab86ae669b2fed300d67bbd17ce58395346d86904edc938534524e2acbd4a5a12945893561e23b72bee5e3bc2597f925a492b46d22b9c2e289c93dff2ff3fff03e410c917669eb96c6dd08ba1d932f6baaaaaba6baaaacc1bfdd677bbdc0bbd84acdf67b16a9c91bf6ee7d7f7bbe3f3db8bebe72ebe5011907a497f5a3629f876fd813aac34863cca6175a238fc1a952e0a11fdfb7a74d2cbf1a23c88e30582c32b21d32590f3a7a9f878d0bf7c4de6f054e4f11f8285cc820a60179b85d5c9d96deb22bea6fea0a3c6cb30146163ed5bd6bc9535262b67e2cf18f0aa5b6e0455506165552452424550168013ce6b5125ce77e04f471d56be24817a96add0df261b2bed1be6cb7fd57d97b03e81222f07f7bfaebffee6fdfbd1bfd5dec31e1cdcfaec8d24d9800a775b7bec85f46da1365555459958ca9557b8eabec29b73f6ed4fd7fed75ba56d2c9bcd475c6c8df8bbebfd6a4968167e6965835fb9d45555b8ea9bcbcbeaaaa2480fde7bfdcddddda7768057fac60dc89b8aefb491244e8c229352c3a3d54442febd9e87fd36733cfa959554502276023ebbad8576c7546c0857046230837eaa6a9ab2f00a934a17ce77df625a0d7fbb5fa7d16a1ca5b6c836e834f3edfdf6e2ecf909b91cbf2a2c8c93789ba75269fcc301defd23c381f17f5d12cbf4e997c29a99f495af0ef7d6e2daff8c1215ef366e9dbe974e78b74781f36ba6e7217f555522b528ef55155ac251040040840a609eafcf2ab6a95fdcdd3dbebafaebafae9d76f2e2f2f275ba5dcd7c6a04463b6c2551023301d83f23070ddddc4cc6136e908804c41bfd1fe6b3de677734f3e6731afefce4e4de99923896e24d417a8c9952084424d7bf6fd65ed1f41f6eddb9968190cc2bb61581115d8ec99515c62d48845a8d7c019134e0d090287369183b245555ba3d3b16a0bddce24c8ca2bc5e27c81a831424807b7b7bfeeddbbc12b53604436c84497cddeeeeddb3b487d75f4e2f2fc34819809358816cdd0d390736dfe9b5a22e5d939a1455901d29345ac56cf5382cc02a3e83a5b6d9376dad924a4a924294aa573d51da32a1b5a4d90c8504c567dadacb13627e43e8dfe67bbdc6febfdee63b9bfe78d51fc5e8368abe6f4f4fcd937b9dc62480da777cf424a897a9268925ec954080d2a1a2d50d0bfb87f8f84b21a16780d4b34e53e16b9a74c1bd153683e32e91ef44ca9c748ac1adf8fbee3691bdef53696be4825484a2a591aada917bdfb975fdf39b83b76f4eaf2e2e2e9d3b76b85d21ca1261000ec9988c95fa3341df9022bf059feac908553549968f6f79fba96bbedddfd5c1a79c9c0121123377bc543d5b958999da527e137e4924c7bf6edcf6f73fba54357755b02ad53da4ec0006c8b97720b6a0f255aeb50b435839124b46d0a5360c857ec1961f28b503767a7eecaf27c25dd5b86d4f3e0925a924873fee6eddbb7760cc9d9e9e83e8444dd2752c9edcbafeafe7b4087a7672f2e5c767272ba7ff6fdebb77f62526941d57eb22211e9b4126f19bd69827c9798116519208b498aa0204a82c93b48cc118c63d937575d4da86ac7ae455d85b567c85da61937e16cdda73b9bbbbbbde67bd7fd71ba1de67bb2e5faaaaae4657aec9b49877bbdd8134d2d697742b614dc8b8b2335a4955e0df82374cf349085193ab7cf10000c97e36a30727f1b2a6e0d782fffb1052f7c2f55f3d0c9d10c7e387237f40aba8a0120590c1052388e082e41436e54bdd5f5f57bd6bccc7e757937b583110256e8ec93000514a38ec911004880855b48b43121823a0b6e435fbd07fbe17dbebd77fa7d73f637a8d6dd43d45aa835c4b47edabaa236418980ca300dc1b23fdf773d8bdbf68e4d6dd255ea2b55fd7f2205ea542223860d893c4bdec97bed92a930da820a958b2fe2500ecc6cbde6572555d4c817a8978e72fed7d55b5a08327ec12420e0ed9bad75548a52d45c92d67dbe7bfafbf671ddf0efcefcecbd66a96ac93211d66d772a8995558551a8b1700044455435846c8115dc2a98533aa000ba28379002b246141bed7d59b6a5f573948d81b3f0276d85b45a86824c17c1bf5fddd797677c17c16816a9627ec9faae2da8cb85c265b83996a9a41077c8137e9940166e89351dbe512bd444a47ea49eccc32ebda8a1df1a418e09ae0379aa1ee32af6f154caa2f885c87c62dc97cc5974b0cd36f2ef1b958e896b8f8a0837ee3d25938fd28a89c2a2f6d95b924c563dcbcf1c7ef3df9cf4ab393dde83e2f46552a3f1da54447ec98416439669c6ee74b76516d2b063820b272dc90254e035a178eada26bd86a527c050847bed555550841276788d6553b48167b5232d6ddc9c2bb567de9ca69b854b17c88846dadad4537c3700220182291292adb03651bc1cc680d613263226e424454ec93763a03824ccfef6f6e9e3d7aebea0d0966a6ad581be7c83ceafebbea320cdde6752f96237e77f6ed222e3f71f2c655110c9d9d5c9d9d9d7ffdbfdddedf43e0605c0195be6611f569892b0fc290928a8fcd2568673bdd259f4ce2a10be0d0181b284ecb336c85b6cb7f54ed4ccc13d073feedf83d0bdde67ce7eacab2420311d9e9e96bd6b0046dad8136801ade693312ca25279d5448bc5d6ca93bc142144f7c32b9e170ceeca85870904c1f59bf82354f3e959f02f98741fe0f94beb995c2abf8b206bc2296c812a93c14444080aca27690d1996d95e8969d6b757579f4faeaf2eae236ac79fcf9df9ff4f7a06d2001011a2102dc0a90ccc21dcd9fa8527000105504a817042e43c66dbebd7fb9b97f2922080de97e322236d857e590da1faa0680049d93a6a6f1681f5fbafe77bdd020c271b47eb2e4cc95d2a1550523846c833ec937eb6bea27edb37510a10c2e01522b6c81386c857609c010946111d6ae6d857126889393f5bbaaecc2126aaad6d5555fb9d120c834fecadea9a3689483755752c2136aac9d6c7dba3955555535c4f2fb8f27ae7c17c1155272625c6aa6809895809ca1fbbd5f135644355dd4619d4f32ada40d8f679de2f4be5d945dd2c555b5f5b37e9ca336d47d6b55d6427c0168dde63777b7bbedca7a96896a0d7f349034722a6bd5c9c9c96d553d4bd55555989141da573d43138e9c9daecfcf280d4889895af1685f67bbddeeeddbfb9bf5fa7c9249c89bc2c5a7ad4b4aaeb49ae18257a94b7c4d06ecc668706377fd23448845cd0a5e6e97863b4192f4f79822f3ccdcf84c947c3d67184ecef1b9797f446033b494f5b9d57c8a7848cafce9e35e03d6ee6dfceaf9cbd7bfed77dcf72f3aff208c49f20dd3c0722245ad5c767d0f34e01acebc14114537e2a018c16aebfd6e96cc926e015523a12759776b24b67a0908058525a4684c95200d17c1db1bec85b6d69d2608ca13286c0822122a12023a849500d8eceb665502452352c3d7ffa91980d02132b8a62eca80ea2fa17a893b94931b800957ee4e4fcf9fb88f5ff6f738c938f4d4d0543bf4bbed817cccfafbf756c897af2e37bedf265b2f57df9d75f5af6ae5fdcbfdddda37ecbb12aaaa3aac81abdbdb523ec817df834e49c877ea96d73dc9d3b7606a23ec11a51c78732d81c8924a412c416a178016c16f5b5595836c9bfbbb0b72ec55d6bd222a3fe3b001179e1da3831c2b8866dc937ac9352e498366989390f703018aaa86ef037e41418f0e3c978a4a6df4c15d740bee7243abf94b52f32933c3ac2b6bf9c490e2fb76c815615560cdbc1a10a42e9b37e089db1b73bdd556d58016dbddcbc7a755a975e3952ad70cbc6ff0acc2d1a9f465050207ec86ded75610525e8c93729440046c03210886c8c0068037eca2ae11a22b22926806da5feb2feb236d20091769954b899fa4e96201b2280011a53503708254aa88a446080d0828032a925813564042b6c9bac9daa43fa332a02291b5f26171f4e9888cdfbbb98935c89baac97b4ec9376cf816afeff0fd9826bbc3d3bbebc669b26483dbe76a4eaaabb5d94fbebbbffdffef7fbbdc671d4bd4ded8daafeb703206dafad39776d996e4fc75767e17eaf551a977070458cc92789268e35c017a126c83d43c434f3c03808a82a176be6b9aaaaaeab5f6b2b6d666e26154443a54e9e0a5794b12194bfe6f366e41935ac931721ed39166a4b056ec9e882cdd7c1daaeedecb78dec1ff448342c78575570ebde8399cf15f81f1329bf0a7fd74661d38208e1a7c6ace95522301488c837ea1f516a4f6f5fbb03e4f2f3f568c9f261ddf4e7af332be55400940115e1cd17f74af88a828049806672e98a3247ec83767236849d49c9202395424acccaebaa8533cc66519c99f02b9634eca1f6da7d215d764a863386e1e5cec237143058d842c200a5141698080a4f95be14b45958da0a0085fe0ca10b6d20521a4aa8a6cabae3bfcfb0ec2fee5dbeb9bbbf3b3959d567cccac1555fefefe509ab39310beeaf70f38092bdaaa6f4fcb2fd02043f6fdd66c81507c89e6fdcbf3b5d9c892b7b5bdac75d57dd9d9045ec6bb8c4420a997eb64904276605d9afdd83c62689836ae78d5c8136c926c888868ca336cbfaa4054a37511a71116c4524fd04155e056b9b7f2dfc50db8ca01937ad3bbe5ea960a2bfbdcef1e9f9c0df0293fe3402ce1c4a30f16053c3afdd74d2bd78496f7418aa78a1893efbace7c57e67843d122242d20c479db1b01683b1355536f3a7beeff32ced4b7ceab2b43db53ced7ec9b34c0c023814a84a68494959a456655b31823aad0f09f1c64775d707df633060cff2c081073064bd5d5eaae479d5d534e0a12525a25a4982a82e013dd9ec0b7fa5bcf16d937eed880aaa9024094c80644cdbbfcedb6fa5febfcb7bf8fe453d9edf78f0f70f74fe2488aa60aa6efc215f30cbd9faad7e761a70dda99a86f9395d43dca65d5dbaa7db96bd63b47622680e569e0a73ecb592c595096a6bdab02a2b89d97a3733c01a0a0c6a841dcad4eab523762f4fd6f36a0ce3341fc9666e0eafb622804c08aa93b97ef1d0f22aaa6caa4723f325189dc45c8003655058b57bf4ad63d661799b7cf61100cc7e3fee261be5daef0f5e79012edd7be70561520279b71278a801fe4c90753ff14dbe71fce9ffee7bf1f5c2652a044468456c8393c34aaa862b4ec2955cc40908cc452947d4ee257dc6ad6ae4d4bd6bda7cc5a899aad47eb7088124c2bc2baaa2a0500088a9e8b88801938957a662aa9427f38908de1ace0966f91bbde89e660ce9e8b17ae8f95b367b4eedb808a80cbb9992fa4a99cdcaa0ea54a0c63e82faae9ccb164ff21e287cf79e33a520a7fc2336a06a78bf3f9dc667bf6fde38bd74727ffef46a370465014267409c5dda7bf6567ff45120a1020688c0a602d6e6ad5f2755fae261bcbc6a96c76df3c76214f625660ae9c95ccca2d5eca41036c242b10232506a8240c0441abe43331af367e75840e0412023b99117f4855a046e772503606d18cf71868397892050544176ec116805480102ba5693eb77f100eb35b6bdd4dd6c9e47a7bf6dda3c3c3c226557ecbe5faf9d3b76bcbcbcdbb37702484d62881d88ba2aa7de0e6103eb157b72f0e1e76f1f74f9d7afbd54ddedbb5743b3a320e0b67270328a92292a18aa84a6b9dcabd6a9cd6b5fa7dd5a6ab637bd4ac605e462183ec6f6e3c61350580a06d7be84c4d7e5e4444a0c40acbbc77255dc2ae045ec937ee297fd77cf37170d7f550d4bafd9cfacab4d16dee0316dbaa71fbaf00c678fa4443c1a7a7d39ede68e87f51649fb6f902220628884866c3237a4525b5b865410ac2f6ddab57c727ffecdf39bb7fee3588008441c0670d01e8db8d90d9758e7dea280b1a3f8c27357dba5c66dbca75bc67357ec91c80151080c0551c0400910ca7b72d0143d06488c229df2ea413a111201545e852b9b799ca7b405c0441099019801dbbd04dbab6145d93e4dbb1a830a9abab7b7562b123a19bf2720711344549c6d6c23e46f6616256ddca79bc5f4ba948b35c4aa9d462b9944eb85da79babcfaf7ffbf7ff0f01834aca1a8209b0444741e25c2fd8f9cf4e8eedd3c2bc5fa793f38df855c627aa35b0001201a7393794100000200944414459a966b4229adeaea35d57a6bd5fa65b9dc6455909baa62012e47e3b2265c50f0fac310089a4f4945f8bc524553916392f384dbf3869535a4d57d57d578a6925e49427f4b93735cc8ebd5f40a081a14687dcee66e6ad308d5c494f71afefff8db218f07d8e01bbbf9eea1297c7872cdc152f55eba15f8645def2c840a29abc0261bcb8b8b8f1cb9f6e1f9cdd3c3ab37874778265a260c10af2b1683c8642e6f5d344cbaae5f2f2a95c2abdcabbabea79b8a75beea963779683a449a7c6b132001291864e0aae42e075c7357931160666fc79757d96611db8f52faaf303506226fe0307067f38023b8a0082ab39c4404418c5926895330405424042e881b02f6cf09563d4d5739aac0c439db9f1fddb7392acdca71955a94dd5c5c5c5c27178747ba822c5ca6dfde3ef2fdef1df0b26e9eed43985b29b82a2e01c2b80048016feee9cf77ff6efd4dbe5e76fbffce1f3efa62bf3b9f47657db60310354519cd5e4d5ecd986203949adc933217939aa046a013e4a266f84d5150331391e636e74bbbae64d2b86a1fca7feb4037400f48871f8e007e598e0d9ec11853db8610c0107be5d3452a8ea9610230d1e84fb1eb7bd6626c555aece2c5fe641acb5754ce698c6661099c1148b31aa1068804c448ccc93b4e6bd813ebefaf3400db879f2a22a868dda63b73d9db5f6db55695e1e1dda39b7fa5e490361440a06aa9d09bf2171088054dccc240a1cabf0ba5da6f6e3f8c471ba5405d3f71fd542c5d27962ad57962399888895fae14031f3c8c04529580460a886cc893767e887881db178c1a002608e88287ba4899510451898889518ccde714c30a78c722f812407f157a49019dc054d8bc0998091d0d2b8250624200d01941cadeab2625d3873e5f5cedcebaf3c71be6bd3f5c27d5f66a3da6dbe527c03f944b45f7cfbaf7cf9df73f3777e2553b98b27fad4d5801083084e8b73f8bf3f2fbbf6a9ea585109141ccaa95257d57d2a8a6e4ecb747a54715691ac236ca24152780812212b285ec6d5e4026aee81324fdb28ddd9166101989598233bf876cf4d6f9e3463d5e66dd52135c01a0024101339c94154a7935293c31db69002300dc000200aae3d23a1816ece331916b84f099491e6f66e7f1319e3bfc27ee3d4570fe54fff46e8ebf6c5960cdb2319ebfb5e11b7bfcac272399c46a3d9ed93abd7ff4eedefc7fc44f0f0f0fdb7fedbbd7bf6f663d480507c224158785eeeecb44b75b4100b33ea127ee36ec9101437148b73d9cedcba94113b305727e2bc902afc212e8f0c5f77a4f4233112056735cb987f5ddf710581bff24d3349cb29d8e8c5106a15cf0b33bac0e9a339c7ff981a82a200251050c01b4e85a029a181120312101bb210472399415591bc2e8e4e8fec1f94b22f4f9f3bca2eedb33dc39226e6aef6ebaf2eb9f2fb00dc1a88840d43d002254a5ea7d0c4955022b4e47a7a7ad57d22085f15716c0040ad535b7c8167d4366639a665e47a399c4a8a62edb2ce7099229dc00051fac08acd1d9cc83ac6c7035d6fe1eb1ab66d77f7bdc927fdfa3d12237682c29ea8021f667696778b0bb0f78baabacb2b5ce87b4eb6b5ce5bf21bbf7a888320255708a41c5962582679bb84cd6bde3ffee9591935d53765d62d387bedd7ffdff9e47e3f6a96e0e0e0089083ceab1901c00bfc48e1a586ed30073a81611304ec2d6b9ada55537a4d43d4eca13231607a24fe0ded57813abba1f1099891144c045d400b75b5ee6b21d32340dfd0322f25fc8c3a5701c0c433023a32a1bf2a224e42942b96b95d40cc5e58d9a221a1354797ba88bfdeadabe0b713725745ac9cdfb7fcf5c77f2e9e3ddf9f4147a3b9dce8e0f65c81fbdfee9cbcbc5df1eb8fcf1ebdfe20630855c8ca29a82367d572a924552847f5c79f9fbc3d7eb7b7b73f9f435b5b4286529884e6bbdc663b95d2277926c839a6600041327804458a99a24ac9b7aaef0a7f9ba761b7900841309a3f294672bec57cb0f8f28ee5801d3607bfee7922d8d7ddddd8fea18227b9e42e7389ba4ffdfe3178977f3671b837379feb28feb3ec8dead82fcf97994533dcd57d577e7e71101927e9595cc663d393939bd7c7c944d09fecdd313503199c462d30c2b7ba29d8c09439c644800180143b6e6be5f23f665dda79fce1f7da93d2e261408136c1983475325648808932702157be82be01332e796d160222a084808c4ecf9cebb933e38bdf5f916428729121a9aba0f30385c4d57952327c2ba21361e621750246f33b0445b0e056c24252194888280942f1d1d1eb9f6eb9dd663fcf5ebcea99ddbb77fe1db6f0e2e2e2ab6a59cddaf2617afdd3dbdfafb59bd62415a3eed283c63c4e3dfde74f1d70fb10193ebb77223e543b6f427ec9bb6a96277d011d4ba9c46239a623bc93b726b93b7a7822778d5200810d8eacc7b6cc165ffa7124881b07b328200170f9e3a89a885792de060d1d91557311833047d0ff58969d5b16feee7af831a8b6f73fea40efa169d74b82322e89be64ec3eebe4a6fe1b7ddfb5f55c2c2037c2eec1f9cef1edaa6a3b57357139a2c4a2300354e81c5708ea0d1448d607621bb86b127a673b95eae2f2fcfc6576f291ce9d3b760937ee2f462390128b14a6cf1c07bd6fbd60d097e214ec91af959322a1f030803310067dec30977227c4e1fe5a6a488a88a628a8ebe6d040082b812511ac8a9400882611ba98178220f625bd5a42d5718936c8408229baaa25599e46299253d4d7c7878f3c77ed196bdfde74f1e9d9d95acda88c261b84d4bb73d95db9dc70fbefdcfcfdc7b2050315468c92780c8aaf9c74f1cf6f3af871797efafb67faa22435fa890bbe4aa85ca529b112b82a271efe61f606695c2b88889285ff3c9960202812e7778485b49c4c4c00caf6a52271151c4c011910a7263c0b10f654e5e9bd74644eeb2548752e14afda07cb6ab6f4577f029309d64e0f5be31cdcb6cdeaeb7677ea1e705c8d90e07d673e1d53adb4339073b9d4112c4ba996aead6b3248df3a3cf1df8fdfbd727fe8eee1fe9826c8084813031b89226f93683e31960640800a0669ada92b4dbc5ca71712d639bd64af86148813288a21a995c854f93486e1c67ecc13626400d012011847d669e87c9f16804845491198446ba509e7ef2fa208012919970c14f63c654464614455865253d434d6b7b24cb570784544c663abc56bd6b8124912aaa629c5695a5ec4cc1b4cea9d000ced1cef3870f0ab6ae7cf5d79ba5dae1f7bf465b85c4623b2763f57bdd72f1d7c7ffde7bfdd7ffd72e227792411001ffef71f7bf5c7efb740d707feeddeb570bcbcb8dca695695a987b7d86c6279b544bb633c0a7bdc2e0948ef5fe1627bd77251868db03605553040314463bc668a208d72c386f97b7d94bd47d57d94ac7599326fd7273bb6561cea081a7ec9a91012cd087afb2da21bb5bfde62c0c5be31bff051677b1f169fab21919880d8819a8220317e65725557ff4eedf8e78feef79f76f76747474f6dbdf678226602deba97f2f996885930440535fb4ab9996a637bd69880624a23f2b4c9dcbbdcad357a9138880ee4554a69d0c684f60eb20db4c1e0116ec9b3fe0826073602926e1704844484106451928e06db2571119288aa1a1812140236e5f6a3348a6a3b460b85c2a9c949176b73f9d462b73d9ca7db67f5d580e0e974a8588812855555d94e09ab3c3a3a7fa87fc425db96f1f3e7c3b9dcac9e40934bb95fa7dbefcf3df4f70feef09aa8c86dda69fdc73f5f74fb9f57513edb7de46e3d986e429399913a133a88446caa809c0c3554c7cde9276be776ae5f64cc44bbd142aef0cbd9d8dcbc8a2af0b18c9bf3b83b87f0ecbfd2dd0fb6ba73753028d10d287505a3c8811f6a1e7bf54c0bdb4e9307dea9f62ea1daafa818edfd2cd9012c1e7c4e1753f51abaf05e6e6571cec3dbabea35bd130ef0fdd77f76ffaefb873ed874f0e1d3a73ed873e8eecd9039ac5678ef1fc4c7de14224a13319d27609c454c0408e68f640b8c800d5a40cd732d7c418debaf91ba7b1e9850f0f265d136cedf5e6688e3bb77aa9033a90decfc5b37ba304fab2ea4a5933ba14e4279b9dcc6e3f3e70f0f65aee3ef8f3ef6f9d7771bc5c3874f0fd977ed9bb7272ba5dae9e3d7ad43d007827a54e42565135a44a23b9dcc44ae6bbbdbfbf180e0f8eec3b7af4e2fcf2e1e3ca8341b95fab96a92bc2f5bbf7f36c0dea695e47af9d7afbdfbaf7fffefda7beda38df9f1d1d1e5c9dbc6aed49134118c9bc412c0c1a3b67cb759f5aac6457a878a4d53643d5d62fbf7220231d03f07e088d93679011d151294544429ac87f6bb918aafa4e04e5b2726bb48c19a47304253e2cbb915713e0487b945fb3aad9b2394e9c472ef743ccf4964b2b33379549d6bda62d3c38df77fde77e1d3c70f7cf3df9eb9f6eb5fecbbf3c3978f09261044e96e7ec8e48e9a9e0408aae75538c92133da7179c60022b95e585ec3d8137bd4d5fa37a4ef0a3d0bfb68010c037ec84012480e1499d7e4424c81ad6b133564244362aeab67171797e7e7e7bfecd98136e42126073ba8fccd980d55d0ee89744051100b01367d57ef377b9237955b95fc69bcb7f0f1db5fecb3f39f3ef393e3eb3f5e75f784927ddc6e4f4f4bea7d7be6d1db67ffe715e4cc0248265bcd0233024113a1201360e0880199f0e0f06dd4d92bcb8797a71b85a12d2be66ab7bff74f397fffce7ef379492611fbcfdf7eff7cfbbf77f8e1d3abf7feee4ac8d4dbe6aed0102335aeadeab66e3d975565d5b942b0aa5e49c9c9984c63cd844d0d00c5137ea76d0ce0dd3e1142e884ae7f704ae256559e57aa668043b9d4f0f6d1991ca73b93b3fb86a96fb9f6fbd5da6da66937e186ec6eda1fc9338f79c1a1faecdc7f2ce6b94c675a4d003532442f1f6e82616f23cd74d4e74aa3a58004dd1a43cd85a7078e555b46f4894f3156864d36c1a9ff9376dc571541747bf0e72fefbff6c3c707feee98aa6939ae8fed9812101e6780e75285bfabdb90c0cc4bbcd95eec44052b4e4d6b577d6b3a01267d3ef60dba7fd7a6b00a32c656c91c8b37719c642f1a999a6fd902381b37e8fa628726f9864c0c040044944bbc25779e6d1fddf1ef8f7a819b881abc25e4aae253b9dc6ebafaebaabe6ffbaf7dff5fdabd74f6ef0ede3fbc5051394929998842534a0549544594208be6ad74fafb9d5a4f9d7ef54bd6bf8f9e3b3a3eb3ffdffbdf3fbff6ebe2d43c541f1efae75ff9efeffef74f74f74fadb67fecf4f5061155234f0080fbd25f7e641c1c85a3678345f31fadddf57cceb638d673ac4bfed9d83f8819bbadfd0898d207bd6be73f3713477d5a6a9e257dfd0988cb630bb92d0bdd11ee8eb1fb69575bbb35e85a8ce66fbdd01630f0e4ec7571427d45491642aa6801cdb523639226c83daaa7de5b7de5f7de1cf4f7afef4bcaaa2ac3d3d3da6233e2b21033406715bc65b5306a6834622a3ec0453ba60111dcd294e813213579b5355f46c4790e4fa44542406bd1129a86622203f6f20dee3f7e5048708ab3b6745551c0b71245faedf750860901cc4c4d011805d059855da35ba6ab7f0e5ff10f47602d5d5f60223711763f9f1e1e1e2e2ecfb8fcf7f18b877ff8ef8e76f76ffab15dd4796d5f62480c1bc9511260ee25d86a5d4ba663b93eb377eed3a74399f78d4a4f3cf9cf4e72f7cffaeefbdf6140261df8f38f5ff2ff1ef1e8f6f1cdbbb745f2713daac6b96442386603b4da728323a2284f1867e9fe4ed09fe447ce006fe3e22253346400a182d363211455d58ac8e90b9e804d164af6d6b927ecc1b0f8369dc663d57d9ae6dd66e9829062ee7a27a09cdc8449d21c1dc877b0cd759bf2f6e55c6c162f8e9ed1798f5b868d1bd761685bca363bdc71179569727272faddbbffecb3fec9cdeb377feeddb83d3b79b85eedededb9f6eb908cd3228077caeebd7686cfebdbb7e2aa8e8839018b2e86d5dd8aaaa83d0c5788cfc1f6a93cba33c7be86eb07340f64531207804697c293a7cdca96f3d5cb6cfee758ee9094dc8936600fe07621350cb7f0e1eddb772086a00942fda3a3a76fde3e9c4620667c7c7c88cf4f9e3df3c73fde77767efeeb7fedbff3df39dca65fdd7bf4fbafafbc6a1cbd7be02693323504a26a3580162b7b777e1c383c39bfff3cf9ff5c1f3a7d1ccae5d2f39f8f3a7cf5d7977e6fdab377e8201a40adca7d93720dc666ea5e05517be9b3a1a578ea6310a5fc9d000421faef10af8f5573fd30889a7eb3b99b086cd4e33ae9606791b7eeacc0172ef674f42aed9d7ad99c76734626fac6a1cdc4a04dba33b8b61914facfcdb6dd0bb935f8d2e9ad79c5bd6b937e012c47a5dc7e3fb57be6f3c78f0f5bb77f763b95956c6bda55579bc5eb7feff8f6f9cddef7aae3f56130977944cbe1de78e6291d3714ccc04261cca811115cae6a95c261b85c243bab6321c8018453f5161c6f2c660fc02175d25ecdaaa23311023921e634ee3721f488115f82976dc75499abd11411b56332446a4952b89140503e81f8feed3c2a849ad856cdf3a38df38dbfee922202336ee2d9c9c9ca73de7dfde3dfb9fb9ff8f1c7cf9e73fde3bf9dfce767bf8f8dcc65beb050a4663ba62937755954b93922cbff7cf72747c77f8f1e320085c9f9f71f5c7ef1cfaf79fafbef86f7ebfd5bdc4aa2357d6a6a1f4a74f9b75a4908ca4f2786c46840620656812470d9673806d70fd130a000207f5179a94fdb5b186978ae70d38a0939908264617215f46adfef5925aba4984bef82de1f2cdfb1cd7e05cbad1ce0d78ec02c53375e7f9d71e59ea3eddd777b369ad133140f7a0db6fd2a826f7f7ffeeddb37272727474776f7f7f725659bb4ba5da6dbc5130301ed939bb08e8c67c16ed2826a473e0507575af0f28107e3f934a409253be5c9f9f9fbc79f275bc50a1a888a8113ce639fccc09af6b901a74816ff51bbec3eccb040344655135fcd16779f150e2c4393506880dc86415fd6f29b0864426a40ca6868464c26998a8a6a330080541a93150213d9fc7ec95ba23e5e9f224803b9587fedb7f7392f3f3f3bf5ffae3872fdc3df9ffcf7e7ff1c385da7efcf9f3f6aed831a7a9cd53ba5fd9b377edaf1eb701a00387ef4ebdf0f38fdc2f2f4f1d3a7455555156cb57870b85c2e4f4f430347d9c85ee7ec9b8aa4e850005803adb157c237146c3d486ce33897bcd9800105537fd275d17c7568d71c536e1f0338bda8b9e2de9aa914794c1aae7dc4c03ecc80019887c58bfa8c38fd1a146f77cabaa76f1baae75da9ee3a95e66831ee6212f878348eb1ef41e0e0e0e0e0e0e8f8f8f8e8e8e0e0e0aaaa244cdc6653b9e436c8b8b8b445c74f8e1db5f6db5088480a46ea432340cb692d1e48b78e565a48edae3300866db979f2f5e3b76fcecfcec257d58ed329c341f036f8c455786684f62f4be3e5564bd3996ec99c4d5f831241e524c637b388d0d0138f6ae34e0ab4f27ed8137ac188dc09244e81becb9ca6b85dadbe7b495b04c9fc6fad3c3aa79ba6bd66f6fe0011f9f7bfc62b7bff7eff3ef5eda3ebb57d5ff2e71f7795debb7ef2361c3bdb380124caa451ecd575dc7fe6f9cd382a4d6bb7efcf5c3d72f4e9e3d7a72727c7bf6f1a99eefefcbeabe3b3b79cc8be5dac90288882a0a68c41b88519a5fa246cd5551a3440772512086794d10049525f2ce33004d1aac5040804ee6c1600840613647c9997dc0d57d97e954f4ee23543dd2aa18ea45f97d4a80bbb379ce04e4de60a15f6e69c737c917dc331e939e3a729bfbd76c81bc2bc2a8ae8f8f8feddbb777feed5ff9763b9d0329a428c23f272de6aea7ddcf9ef9ef9e9cdfbf6009523358feddcf18af79e4498130818aa43b1dab9bc3b3d3dfee9f9fbc3ddca7d8606caccc8dbebe6c163f6d7ec001cb7e03836a6be6c370c692c65baf75e520c1b74823229841c4553b244056084220a268c1244a85a15b2aaf4fbbfe41c00ca8aa2394f0f1e3c66db9bc3bb8db9de7c81beab610c3bbc54caac0559b7545ff6ff2ef2abeab3b3f7977feed5194f9f76f9e2635f42680555f6fbbfe28963bc5cbc7976f1cfae7d76767ef8e1d3adfdf9755589fc76f5e71f54a4de1c1cda72f4e1711287f56999d916344e65965949a39c66606aa0ed3ad0e73bbe9f5da02d37411cdd412b34e7610bdd1b41008ba8aa71510205069ead6abcd984ac9d7c61925bc3098ad1cbe754b36bd0102bdd7c6ea545cfeeeba680468bbb3ddb27dea7db18faa50d54154f61a8d95cd16f7e70e452bb57be65c8c9ad64372119dc66313483a383c96539a261f5f75f559569f6cb1f844abe2be46635f6bd9fc6b7c8393d1ef35d4899154c2771cca979f2ecf5eb83b79fcfcf4f56dbc55a6b3f037cc9534820928892a14155966694a3408813ab697341110025a4124031daa823947d57d444aaaaa277945a5f15a695e44396eca54155bd6b0a6cc8260b3dd604346e8ccc12ba595654c1188511d08663fdf3fbc5daae669bc5e1c1e1289027c01aa01bc9cc6f0882302553ba52da6d5f1e1d19a80137e49832ddd393146a49abb37ffeff3df3ef3cb1f0eedbbfecf0045042d3d7cf5f7afbdfd6a65fef1f138850c0d6dd5a6b34026e8fde3dfe24156ec2a04d5ec1c5ff529360a2bc8d43d46bc41835a4954330a21ba371b34c114e8c88a06295dc844401926950907acd281a98f496683ce932008897ee12a8207c2be6b95f663888b7b73b62574c0927b088181df26fa06844a2e74f24f93d3b3fb603cc1488480829dbbc61a7222891199120a1d53ff0f8539021d8d94ec3c318dba8278ddddb1ada1b7e7a48bb77fee1d1d19345fc73d577f8020030a6619e2d9d9e96c81f6fdebdf8e1d32e01625401114266712fee4db2345deae8e7203041ba71b8f3c7efbb79f4e9ca7dbc4d5b40446041d5d9b1545170f54d0697dd909911d13cf3cfeacaa97cf89ef5804032caaaac141934a5983df85180145d08ca7bd760f6ea14f980202a091233b8201782294e5c279bf7070a22c4c1a8aac9c4341ee260c4d5b5653d9eef1a121e83eab0c666f2ece4f0e0e0e6d4e8f271bcf1cfcffbd8507d797ef5f71fbffeeb97c0a6bfb73baac9828d279b4249819df0bec93b91b78d32005655015f7f9afb849467dbb9b9490c7d087bffbf26905d192e488aed5724c145fa1241a491d0642afb78572127651758d5088888d3cf81bf1e03119a73e8ea04810ff50c23cd4ad9a281129fdc3410d1d293a3eff6606b5fd219b1dcd0f3f4673c65cfdfd0b7b73b2a80662aaae11a8c44ea9816032389fc667171713f9fcf1d3a747beecdede47268360a7d17d07eebaa6f08b41539e2da757afcf5c39faf1f661792d5b12482b88666ea1cdc2693b2aa233302018ae02cc3fe93460520431d164eebd9d7fea58a3682d533997c14b604812dddc41514980882802200a8a0fe43e35cc130f41bc2a96637a7a7a7272721811cc6a3d9ec663b9e47a00093b63d5b70747be1c3c74e8f805136e866a71bc5cde3a36fb2ecf5ef5ef5a5a6735fafbef3c71fce9e39cd5a8199915140086dbe533961541d57937ece66c4fcf89e7d7816801d5c2f92b6ae440c410109005170d818bea057cb1110219f7c4f3ec81d641d7e10b295ec0606307f882b9a86c858767d4b93569c448120bb2a3abc2730bdf7dfc4fb98c8e751e4f8af3395fab4f7ebf097673b89e1fd777758b1201070111709bd8dd657c28311551b0fd12525e9e47afedb7fe1337bd6b5e47aee009baf5bbf67fe8ce61b4dda71bc5d9dbc5d2694087be6dde673b63392244918b0ae18f1eacc40eefa17388e24b71be8a1eec31faaa68bde317f5626803e85f544caf3b88a7f1108920565063554f49f32133534c8caad7f3989248948cb95d9d9d54a4926825dc8bc2623b9593996ecd92805807c77f4e74feff8d609f7baa223cc7e3f1fc0daac2e26979f1cfaef12da791116e3f97a49abc9394603ac9b3543f65ba888fae74009cd3f2fd8cac2bc81a05384927ee17fcec28325503f6df22aa106f6557332f47172442d1357f038d8d8c122225fb8dd05562b11033b94e1bf56e76fe83ef474d3ce4effc7f6b0cea1c3dfa851ebf23377cacee95477576f1bbf7a3e61bdfaad1f0abd227a607b5888469ec7c5c385a4a0462064309bcbc3f5c2615455eb3fecb308856554f61d3add8ac2217a7493e6adc4548303004bd6b0003fdfdb8849bb4d43b100181c70bbd411f1609bb54f1ba970c9b7b53fa0408af1c832081013270a002228c4048a029377e17158889252462048c062aa83800a400dfa86a48ae8dc620113a0ce8a5a9e4a4f752baaaa2a8244489126c8747bf6fdfbf7f39422a2b74870264435085eae2bc0c7e5fae3ffce395f27582928bc41dc2480944ae6a9012884a4d5a13f4a440c0d305b28b0aeacaa23486bd63521032422024310045040de9d0c322438004d5e5208cee4fece16078088c6a8e21100a048cd78f0afdbd897b04cec9701631e537bfcb233b7e6e6c930689afb13f8a955d1fef636067feac40eaae38988cea62c1af5317bde3beb138aaaa16b1bc83c718949399c4aa054464a4921106ec97376b9dbdbd3355426f9cc232bd67ab66bbb4937a652727b8c595699b96a82a041555d4946ceb3222b35aafa5a022810aa9d0a34d1bb1aa381b264d89e6db30df4ad8df958ca2937602c1bbe4848e2166e0c013222603ae42b11300763105121313881bc6a96ececece0f06fa82a07e1a49399479372198565399fc00154420f077753bd4db99d4763f9c4360af8f7dfbcf8e3af0eef1fdeaaaa264ecd5a6356812682111d4427ece646a81a400175fcb7f77df558637aeadcd56534200264602010155548e5625e75607ce6b6fa8012e812b20b1e8f2e8b91441c8d7a842026ae3af8dd528e7255f1500b387c3cb1a3dddde8ce6d73e381e5e519fd6757ed91674fbb7350e3e05db15bad3a3697c7fd7c53d666eac0be1289e797ce5e84954cbd76cce7a94c8c9bb6ead9a14b35dd09f3943ef9a808a873080bf8666366c2249c8e0d6fba7219785d88e5b9b6fee7bbe88817300b332dc015155168da711c0140cc8c4153b926c06c14afc3ab081b0e852582036108c9d43ba8a16feac06642ac93fb871f26d5f6cfd8d2bc9cc66b73b9de5c85a334710c39d1c68be6adc7a3b5fa655488dca65f7ffffdfdddadf3813902398dabea73b9d479313377d57064481241592c0d31022e85258804c6042a0bed43b96a6defa8f69d04cc3f531d090928a000010503260a0bb94e0d701a113506462a011930507e0b3121b129f7d25423262a044145165badc9f9f5e663d00097c1c220328fefd38140f6c28e7561fa757e6c6e0b05ffd7d67beb13ccf1261045d440d92126db88764800d63a956c74daaaa47151c663b133b8cc85e4f2f2f2555763b9dc7e3f8541e756176ddc7f8ebc237046800a2d43d8e096f1d57d978809730fc27aed4e07564b3df3a686d7f5d5d07bd8e544f1cf6f9c0000108102f88679c6aaaa91ca466044526145e019002ba92017f17996bd0000020094441445881a829241dbcfac4376114453b088c1b0fc8b219cb85c2ab6a5bd7359c136caaaa2bac27dba1b948b5dc5ba12318a5119f7ff9e72ffbfff6ffbeb5f6db1559122353d5dbe5fabd6357d5754150b122367beea89c4d7350750d92044fbd2c06f6473b8b74d75653ee94fc8a0868680c88c006a80430116cc4414c0127f8d8ee3942d7217ea6947702bb9f4d3b66168282999dbfe4ec45de502673ea56ff9d97feba99c73abdd7c8c14cd95bb753dad6beda8d9d6e98d995a732e75a96a65d1d17aa22aee39776a1baaab2066e3f97d57b08cead11ff90b7bffe511417fc5b84d670a6866be5ea4ab4e6e7d945550667b0b2374ffb9fa57107cde6747539b813ac81d3f13d3048dbcf07e6d33543753f82688525c202448013b8588526885729553d5b2ae4192026a015caf0e32503e2a03cefb13f3b2ad6a9bc3b3f5f269c13a391b7f6f6fa885a751b00a54112ba002050eeadad04a80cfb8fb9ff054116f7f6a1992b4754158774fe8fec9027c5ca654445455a3dc8bbeab2bc2982453d9582a4799810502c092b6613f3468928a14213024a0502743260080126f47c1f9ca13937bacf6f2128bd0930580ccccc1088886838bc09030354f570d57d9f6b5e327379a09fb2ceb5dd8fe5c8e35fd5b82f86ce9e327667b0c035f8b7264f6f756345345dc980ccf7308e01bdaea7c05fd17fa41cd803ea87fcfd27dc2f69eaee5879884647427383bbb988a82a0e8d7221b73decf1fdb9ef250484d53c5591804357be5e2e2e2ec9d4bda7ded763f2d6775c44752b6904d784cc34f6881a7af4e8f4ff65f74e65c0ae3f1c51c09925722d430c455551a14ec97dba5fabe6a9e2753189ee11480684952301323990f6f7e2271d5000a288a7c24dedefcb74b610ced1e10f62a124bd5b1dfc0139c9379569de66dffbcffcff3defc72f6fb9fa7554cc263d8bfa06549398c54801022bc9bea63dc4ba26c370d50e986b380a30414f0be3de9fddeed810213444f3b75560d1edeecbebfef2c0e9bded173a34dcaaae7fccdd6f6d8e150feafb78909f1d4ccc6b33b38b270ec7f055ce59545c535eeeb27b7ed92ca104d67b8ceb6bb2f575c99fdae445545b794860b9c4836c8399c47f6f6f898623994c81108088d047c5e273a9b7806a460029145ae5f2fcf4f5e9d9eb88806d5e2b80695c588423443206fd729e9834dfd8e888085533e65708bd6f66ecd9124801240ed6abd1709eab81bca41294ec95b94954d63d63b8bc5da73d920059333e08ce61b490124f0c870453354133c061989cfab3d4d5f271b89fc7eee162deab9fc7eef5adbdf4e71d2fdc8ff3cf5dff59ad6edaf9cf8043f3d72f8f1c38701b82ae6ab012e4623366ed74433bbc3fb04fe278b4bcd01d745960c4e63e133d4263c565c9dad0460e9afe860a084e7d408de5e7eab2249001d8c3a6006807466466663f48d2312223ade88ebbeab149eba735ddf329d46c5254b38bebb94e19fed990fdced0f815f766e07aad51a4cee509477a96cef4b48d115d7f7fe31cef440fc36aead6301baaea7dbe32836755b7805cb2bee500d22600bcb95f2fcf4f5e9f9dbce63b65404534903cd07dc14d768df6dabb8014222c759c83af7d498444820bb988c5b58955019b9ea366e2aa21235b93da71d67955c4bee25b9210504a041a24a2cca30021118ed547c6df0535104913a7b2475be57881f0e0f0ba9d4348800d4822273c5695e6f61c6aa2963ba5eb876f4ed97bfdae63be2b2e222389442ebcea30454a8a801987eb7bfd5b9a9a73109c36c264325f8f62a2a97deb38bf0155cb1e4eb5fe86a13a13f326ba7a91bf003678b4dbd13c486efe1767d4f120f0f4ef8de20f4bb0ff2dee5c7f2b1677dee87d59b6ed78febd35f6e26db9b6dfa1a8fcaf9e1e5fa6bd1f907de18906a844419349569a22d6bda355cd419c8224005b2b80aaaf2e9d3f74f6db9860eb17ed83314783fb02f6ae3f3d7ef2ebbfe6dbc50f0242bc9abe30e0c8453b2ab89c077512aeaff34530010535032ab2949008a4c08869377200937eca99824c858126bd6bd5e42a6d67ddabe62ba278acc841327c293650846602ae34754dc2d7b99d76ed6e68e7066e4a973d638ba5e6ad626556c8521bfdfa8c5b8795293fb7ebafaebafce3ef0fef9cd5deab84a4665de4de89c473d47942f4ba9495595553d5fa6588664438f12870ad8387b36376d42a124062630279919bd5f0a77d32220bd638d41679f47bd670933521c96bb450376357219d84e0094ae0db233b4d947d6651003274b6e893fe66efd12aa2033d0c784f43b5db6252bdd5e8bb8b68e937eaa0fd3bcdd5fd9ec5f2ada223de591000e5b4481ddd0e29e7f92b8926b09277d1429df9fe59c46c6fce9d3371ecaf5997789253407d38222084edca75b8b83f5c5e9b4a6b82329286ecee8710030dc004cdb27477a50830858cfb175bce2db208d60c26f83d3dcd4e653b58016a3d9682b06a256dba5ca697179b0103138abd63986888e3b52748c88247944450185435f6107332240be30580059915484ab4535dbc5e2bc2b4534111e085695e46231fd4bac2a0438faebcf2e5c3b7a78bfb77c78bf3f961f8fbefaa8a2691263d43ae052602778e4feae9e36c8df15d12df5dafade445bd11f636460608cd3b7e78d367b95d3304837793c0bbf13e4eb278912121b856fbf34f10e09df133f6b34bd6bd43d43d43bb29cdb9d1eab65d2c6d95375712e657eeb3354b1bdc575602c752b70ddc5ab3df838d180bfdcc573f1d521584929ff8d19b785d9a08fff466644bfee9d7b69236911743b2f04082c1f93131d6b967db2b4915430175544bc9c01413b76a96ee5631a4000a9901df4438eb277709191a9563155542ed678c5a72a40a92116c8c888b85c2e2ec75f2fce4356110c0c54fd3c52635436215fcb06f72ffa7b721d0c4d00d055804140891911454a95f6c371dcf1dbe322a88000244600bbcbcbc3f3f3d73ed8747afcf977dc6e9f2ec111f0f0f03480da8615411a898803013378c937b9a334f4a58ebd1ae2f5430204633cc9554cc7ee04df166d3327c8010851a761b1c0e778decea67dc56a6db075b73407f2a7ce5609623431dbf5e8925a6a96aeab6756fd8b2c679836b33da0de1b7b6db98b6793c7754c9975313bf6eddeea5671f89afdbbadb4db297688866cefd51260f2f155b5f9817c81bc8545551668c8654158a425b81c2c5c9d96ddcab0ca8bc20924f9b4cd79c5246a006022dd66369a364a23601337583081725f60dd03891381169d4868630ccfcc33edc6c4d6e08bde4d02a5466c0136461134a0b85fae77f9f71f9efee3f717a76727ff5b7deef3c0bf3342e2c14fb84b9e298289a86f7e425fde77d43cb6a66675c517eba9cb2f37afe300dc6e371577285a01890035a6f39f8f3c6ae5f994bf0f5bb769ab57ae623d9ea7557c27a3bdb38902b17192906bc0a2e10fde69018139f3c84bfcd93f7b607b1bee68aa6419c01c320b7891fd8bba1b14e1652c612a53450c75fef0aa88410949843ba9ac825e3f530e13a9b33ba310adee7f7d6bfb75e092eadcbdeaf7c32cbab17173a23eef19ed9ada77ff63ad14e91f3a74fabfc8bd7170331024acd98a20aa5ac9ad60c49996e3b9a8aebdf6ff00991c602398a031a4ee880102faf4f585aee2e4f4f2f4f5a47989827ec9bbc295dc0c19586a86662950cc268280d393e4cd5038406e8000a19566aae9f58ccc48ed3b8018d01ff0f5f3efbefbdff8ff7ef5ff5ff3dfae38573dddf70fafda3ebbc1ac4aa94009b862988247293695dc66b75455488cc4862a2939f1dd22a2ea0e6ee1e5d120b56dc2656151022820314c220317954156113688229264efbdfbefba9491ba8155294a42e44ac272441b31d26239dfdfdf9dc66066b85f265bc5a88da7dbc0130780ae2274a08cca660842a69296ec26081244f14605c0d3828011010d1bca8c6ef4421816f15f8c4c44c22a08d7818d8ea0622eebf0bdf8555bf369153b420483fb83ff6f9e7b7a76f2d0c00d443370ee9f72ef1c3cf6c1680f0edbd3725301aab455880cd109db32fc9e6f40082aa9dd484880685415a75cf2ae22b7764c46666030f14a9f57dc149be508b3399dc1f896612d0596133b7044bf5c3b82249541512e4ac21c41c463b95d39f6ebe3d71f2fecdfb70860ac08080aa2a9ad6a596137e3f3b79dea75b83b3f5e2798a6cea9fa49157b2101cd9a7737eec722d66e9120e743bf8e4027eecdd4aa008aa136c2ac2011bd6bef3c7df8f73f1e7cf1e74fbdf6e9d3b6ade24117ff0f08ba2490519cdbd9a8004c80d715b34dc33e047cdd0aa30c1c3c6195096b5bdd0bd2519bde435e9ec621364faf8f9c3efac427917210bfe65233b21a2057957dbc3314c856154c9208a88c273bedc27548c648a8d361151c04b739f94eed488df8608afb6b871107362e544d7971d7fd940c8def333c0beb38b60340905fd53b1df24bb0168ba1abdafd7b9ee57086c594865add99ffee62ae3e728d1e6bbb3d1672e06d64147db2dce98881cb15238ed24d78995695e47a3d9fc6650124ac9b9a5c27bdc6e9e397cf4eb9faf8e6f125cac03321997bb8bd6ada759b96617e7176f2e97b57dd53d5426520f95e18a1ae09cdc404682e1c70a6c8cef4baaa002a24f296cc224cd35ae98826113480600f2ece4fbefafbef7af7ef5df6e73f1ebc3d536103ddf9fe553ddf3ab5549566f3878385f38224f4cc4466c0ce8dc2159b29ac48bdc130f900b997e61a1226a6bb69b9eacac95140a5406405142df2fbcf2663b9415414a262a204cc1830b1247935357b27676b9dc601263d5bb85fac9ddd413081996ffd60d0177e80ca6d469e4d27c819207f6d67f8fe27ec724f77cd1f42eeddfe8aae2c8ed97f8bfc03cdeb9de16feaa8a6e6cd9ed6f9c9376fac16893b2f5b297fad2d1c759fad775bb37dca797370ec488e86e8d511afeaccda7315304f0c2dd9e7332128a7f4b1b7177af86dc7a4eea48aabea37a41116212aa4e98aa9953b9af9e3efafeed93abffaff0cfa789ab6353d675739a96273b96b9d4dbe5f8ddc8e1da76ea5c691448a88aa44dfc50542f6b89327f17320aa9a0374f767ba9c42655e9e9e9e75f8fbefce3dfcf3dfdd7ef5cf1ebaeabc3d38a2455695d4b89455d466818bfb5e8f4cbdc088c120b7d08dd363474551087767e042a00a2888134071cb67d572d5aeae6aaaaccd5700a002bcbc5c2e2fc7f7f7f12b7265f0eb07330469ca0485fa6d7e7e79d5b93ba0222711763b99fd68f60c01afba1a34638926fad547f781082240f2097b04818d9beb3312289e7f9015a711fcd0b28f8a734f5bbbbde58d5b50c7f97cdf62d81e6a692775844bbd7aad525aee256f6bdd2ee5152a3e0406b711c17ae63221faeab6664f4f771196bd69013220c46a3d949d7ca718e3d7af46f6f6feef387448c0c49a96be5da5bbabba73d4dbe4d67ead38de4b05cc90ef9c948200a950d4afb480246110224eeb283337b3983c5131004822886a7ef4e9ff2f7dfb9f5dfaf38faf9c7bba5f64c072b7b7c124729127a8035226226e0507c0939a874c202a2882a22f5103cce248d14c68dedcd65b6884818bf4c3571a54a669b48018bd6b5dc9920d7bfdc3101d05277a9d5c4348ce64a477bf87d9644a1240fc32006abafe011d049d26fdde51550cba1eedc8ebbb41af653a991a79750ef56d604fc31b5fe4c6b1535b646df33036a694fba95dc4d0539473e0c2eb79f7a7360cbfa81e03db22d45dce5df92800ea2b8dd03c4f8a78191e030c9a7b8bff1a7ffdb402ccc24bd6beb337d57d5691baaa2f9a29ba7db46250378fa4bd6b5555d9dbc79fd45f78bc3d797077e81450554bb6377d4b573d53538b643b75f17f46d9e910338e1dc1584e3602534fe665f7284f3d611008c9dac9489e47ad43ddf6f3df4ff1ff1ff9f3ef4f393bbc5523b83dd37d5143d676481883bbdf82411369cccdb6208c77119d19ded031291e5791dfb5366539b217157b50c97968006956a3b0521818ac9d3f76ea78671180d83cd65504200c81325c0ca1d41d327240c04499970e8dde44792e61f50cc1674dfcc30676bfd9c418ca70a9d6da9b0a60802224755650adf799ba96e3e4162be5403e3e26f5eb3e05d64029e8ff27da2eeb982fd7526f477b878b2bf1b1c8376d154f312402047538d5dc934cfe342f4075b15154155559324b6c1394ec91599b2e6ad418d629a85f2795580797e7ef78f2e3f7b107e7870829945435acd4d9bbe68d3ccdc41951c88bf051ad2d43ca7731a935d9451c282815fddc4300346251106c56db9f4e39fce71fffcffbf1f7cf94dd5e839a6e3d99151da6ec226cc4183653d2ac9051507c2cd539846d7de8e15f1e47adebf3ae178a7db314e390033be27e299990df4cfe9e01933a957d4bc78700a6be5e273ba570642047ffde67b51449eb7efe914ba9d498c522c64400340c56fc8dbef7062f8459c665c021b52c08c15d1dc46484c8e8544fd3809b7045384f387320334ddad1d06790e53e3eb3a5a58005a75a085a423af28ea5df0bb2a99ebc058137461cc8e6e5ddea6615eb2666687533d87b109338f8d0d64c5d084f1b8d02ed0e4cd6a3b40f1c5d462394929ae05b79b019891993b8cabbe66559226d435b9d1aa5ef6fb9f6625657fd3996ab637bd5bdc64421109463c043a7590c4c8984dc30fad80830a56344f105898b0fb55d04d5176cc88805c4aa429f3bfdd7eff7ffdcf2e3af4e351589cedc500391c663b1c22c462553927d57d1ad478785936ae2ba22a871c464893b0780ea291f74b2ba2d6ddca32f368b77e3e0de173f6b223bfce7df05babea37ec292dbc7efcc4401d269c2006aa94581808d3c420398aa62d152278d7d68db15668c898da74a0842f81ecab373be0125a4bb301eba3cd2fde3d41c8d12f97d652d0994a444944eae90ed7f987448fcee8a26cd05fa10e0fae6184662ec90fb2336bb20772a1ffe9b68209b64ddb16b97b1d7a8ef2c4655ad1c1b9e5d11068054ce27ae69e6b1cefb5fca26341241ac8ecf5ddc6637be06e4449bb4f2e9f3735d8c16f6f6f045c42bfd69276d012425d4933314042f1090a96c81d30d626c8ac8c086661d0755f6af70e0924c054154956c8136b93d7af9d76f75f75f7df1e72fbf2aaa2688bea735d47fbbcae99b757792b81782aa9ec623bdb2aa96a4244109388470c4e3115740615064e0680955c9af3df52111189410c1ad0c1898247950d08c0515d57d6d43d8f81525a4f2e5cb8dca65399c44425fcbd22203253243460202523ac94130301a665500fc1681cf984e47dcd9b4a39528c265d466399ecabea32a05688d5dd05c02205555a99d46239bc5e225ecd6dd53de66f6f665555182221406644c6b9a111d0b73dbe89fa6a004412b7b49d3b1e998126a42294bd6bd9fc7e7bf8fe050ecd98357ecc02488e10023e205faca4550118997c6c9c5b113ce4cae0d8fc4e515bd8d691db3a148bd6bddae9896b94a93a8a1cb0c0cd9284287bfdc586c1368138d74553ba852344c8806048c8008e7b4deae6697179f2e5cb84d63662d5b57ec9d3fc198890c244243046fbc30302046912c6151113b6633445c01201b7fc556471eb22c6aeaff1ef9ef9fb9fee9d4663babbcc463b383a5eab6a8150900900998410826c8e323cdd38f60df408228d55ef0c0dcb7d10f18b58a69fa5fde8b25050d880c08c54d257ddaa4a6aeec7627cc40410c0c36068ccb03045137c13b7750ea100e6c08abd9cfc157e23d08e136fe81a085466528c9371541bc5fa65be575555e04ab5c265d57ddc7a3b949314396a9d411b8eab6db0f75400f4d54a0404d57d10161c58fb9b92761dc978d2f7b8c18843ac70e6681bde8d721f597f6bb1eeecb254addc0fcab67c54fe8156a11b94bbd68bd1d4c4fd78e03e4e0b82a81fe77ed1c17c507ec9368039aa2ec8d56d1eff3200c2f2f2437266cc2d111dc623144e6ab611180d53a78486d7afde00ac4424030f6bc2368c263ba22337bd6defa4c6a9e637179f1f72fbdf0f3ef4ecf27db778740ada254c2aa67557941514a7d4d1160e81a8224119991807c192ee8a0e11c86b74215fd1fc13e25749a1a9a22a211970a18f25cca6e3fe210de27a5fa7d9a5ed4bd679330a9a88f27777d68aa50de3b47019cfea4cb3476ce59e4d30339d00009454045baaa27c2388105f943c126954ec97f6f6fe279b89613bbc5e2ac2bcdfdfdff1ff8f7cfadb6fad3972f4ece4f4fcf4f56a4920147d5754c84e978ee179c60ae63564c2ba96bd6b9a3113504c2bc2542866b5aea784402a8c50607e026757d5f8efdcdd56a222e2f981b1e87da8a8b273a05bbaafe1c1d2697d60013e46427ec40cbda1a78d59cb726ee183713c5f9d61b346fde596a75c80ac10093b85b95cfe7a444e37d077745c8c95a3f469450cd435bb016919af4ce157fe709089224ceb187c44644136ce78b588c3b9d4c80fba7cfbbfdc78f1cf6e3cf8330753b9d6ab481a434abc5734549a4e49a6a16920233702e813232c8c39befd212fd30178de1860727bce42144fab32540a1559266df5e5d84180118c827696ad4aadb397cbd4da0ef400d83ccb148c2752ac1cea6ba5788d19d686a47d57efe05a4236204122ec663b3f3f3fbb7fe4e5cb83dc9379555f3bf9dfce1e3c708807be6ddafcf3ffcf9c3efa35a47feeed99c466dbd57868e9410181a892a1b70e35f0ed37b978ac82ae5fd2fd7b03ff69cd671ab356af63a1b107735baea9ea7faef3fae42567413c739a7fd33f4348dd94bd83340fa1d7b1796886661c5de58345ead3ba1b8e379a2b28bf584c0944bde277d9b64d427ec9d4b39c5951aa91886dd4dd6392bfdbb74d448928a188aa98bf0ecde5b41289950a03d9ec75b85a84a9fefed46239faf9c73fbcf5efaffeef3dfdf5e27d77f5b7d62416d79b642ec68d5ec555593886c16d68a6c1d20101150756a9919109d63e2f03cfff59d7b53bd17d17e577fa5b6666f5ec9301800500402d015a0acaa4c172175ac2bdf2ea4e529cb6ce459e7b8ffb9d56bc719b449c4354804200152214098bd9beebdccca5ddd978e53b6fcedb1095edf0c2410c30c9333be2ddf5f7717808e60f855258d33a7919108560441190c10b8a212b507f005fcd380033743fb324f08acc9a089ac3a1b398c0c4713979d0cc2bcdcc8d16cac654485adf4c5eab27e24027e29727a893a952480925a4d7c813e7dfce9fffaff3ffbfddbd77401895cce51b0b1bcb7f63b12bcc16007e8cb9565aa2808afaaa6a9626c8b85c2adeb761550a4cc6327bdad8d01937ecab9f19982049c890519736270ee15cca85b0fe9112c1ab74e879fa37e60c644555955784b5331b41e85036e7c995044930a9d65a7c81bb8ad35ac45915e811bb8db137a899ba943555597f4ec16fd7fb85c226fc9d6d79c8646c6721308aa885cf73bad6214002bdd9edbf9ec7657d1e3ff2ebcfbbf1ff8f76f1ffbeabde667272baea31c0ebaa71d5404f5533361e17c335e27dc9fad78c2cddd18601108cd0ebb13c9ac288360c2d6a816b4a5270e5c8a7073b5e2405665e4a1540d8f544e4c483206995699e4ea86a9b73902246231309a6a186dc6c8303b585a162194d43d4e0ff1198acedb7f5551c9b5c2617be6dda757a76fce9d3bbffbffbff78ffdefb6a4d315036904a49a96a131fca37ec112b8425584438bbca345665124a5fa7db85c22bee298041d67889aafcac2c2573e462391f8bba81e681413b75c0c890ebbb4e9cdec2d6c99716c5d7c7392993bddcc1c404ceadf23148c8c3c83683986050f617b5a2e70880caa945af42d67df273be813e193244c8c98c784929e64c119327e60f32b10019c13a2ec9546ba8208b0e00842ac5daeae46e7412487af5f3bfbff78f78f9cfce7a7975bc9f9c94d3d9d5c275a428eae89265575e24dbe5f2d974b464705274eae0553d43d43d837e01c11a7b04a9ce1c30b363e9222e80e045dc9f68007e274ad184e0b5dc0fd262e420a0482aa94525687720ba4114a1511cf4e14d873cfea8ffb8da479de48c9c877a6f098886dde6836237abfaa9af4e4e4c109a067d5ceabee4f4f4ffaeffdffd82257d59a2033321cba7df2cc0eb26c83d94de8286fd17211cc933ea0097031d6c5ca756a6a122aaa2f222be5daa8843d418ccc13f0e6e0c8846fa7fadbebded241acd729bfe1007b75994e7323e8b3e1af443f9021ad99868f145c067a0ee85d5c462312f5dc6674ccc5972de630e1d40b0de6ab03374a0925237b3d813aa2589c4c025f1daf0b9739a99c3083a492218454e10495398420747a717eff2f1d74f1f3c78b96b7fd43d52ee7ddbba621596dd6fed7541cb13ec9498958681c3afaaeabe945396c272e7c515d61345d24935155439a8e8ed788220bddae803a95ff6d24145468498c293b085c7c233531dc40711491c4d3182bf2195b6d7c02da21770aac983fc95ae9f15355dbc5d640c2c6687a743ff6e5c3f7cf4e9cf1ff0f78cc2c21d9ba234754e936fa0cd455551c53d4366b23e437c1ea208086713e6ab8ba5fa6e3f9f77eb37b999f5fbe7dbe5fa105baac7f11b46cd46c4604301571bf6bfeaf41151e05a61b7500c8f76cd58e71bcd365e1ceebf29cb5a6981f6982513bd461b414ced35ef8124f5ea0ec02f926bdea54463baed182d4a6140a0e3b1923b4d1c31b27f1d19701d4154551db97d35ed649b25198a9d1564109ab41100cb3c9f1fce27171ff4ffcf39fb9fbffbb7ef2e5bf6a642728edbf0a8e409c7045216e8cc13463053075bf492922d9c7beaf4e8e800924553a7fc0040ac3c9bb497b22598b4fc02d1087feda275b1ad6047a30fe9dc2b644859911d32881b3ca2f14521ba55992136fe1d9a96ac04d109d8cb98cc8a0cae13269867d79172311d5dd436e0ac747474132726d5da63f4f9e71f3af1df8e6edcb9d4d5f261b8fbefb9fbd3d3d3d9633971faf4fb975fa59cdf9fffcff1945aeaa6505566fe9012d2697918072d407ec1763772e66a3d894264eb85d26db96999f6ddab3f72f76f7afeebb73eaf5fb6440f2e5cbe74f9d76f8e1d7afcebc7a7e7a76ccced3d5d2f2348020c8e1394c636b152230b5e14b1d77d2e68058016070300440cac75680c2e092e63cc4e0d937ca91eb82bacdc42586b8f6aae8e28a1f7860e074928b3b9129d18b3edc4b5b46cda9fa86bd6bc0c82c188a678d7a820ae2875100615669494c280bdd8956726a8ec181bac1248893c58cbd7f5e09c18b492d3c72f9ef2e79f0e2e271eaea0345159440808cc54f417c6ff6c61e8763355f1ae6623b9c47e3d9ec98c9990369ccc1b5d36e9c9786e0e2b59bbcef0c377f0309d296015cacda34038ed83be7c109bc1cd7b29a60b6ae7239945553bc5ea544a96a9ebfb44473b9dc663b9020c9c9c9cdfbf7ff1d3a74136cba3f3542afb8fbcfbc3e3e3ea28fe36faaad45e993a9b42b535a597222ca9aa89836c808ee5f9f9193728aff9ef0f6c6c838000011fb94441445ff3fdbb77fe6fdef676fbf008f6f7bf6cc7639ac1d9fba79fd4fdbd9054038cb5cbf17ee0de1301e0ddb4da03d3834417f686cfbb0119f091d670873fe4982e7c170269202349238505f0111bfae4925f9269654dc03389938881b84c12aa23209a466e53e0d42a37e2dab77e01cbf813af045535188e75f4fbef79f8e3a78f8f94f15a99ec2283bbb73a14c0346935629a08bc1f618280412489c462634f63ef869e336fe957124479fd46665bfe017ca571d7b5c81e81df9d16f3d0fc739eb590dd9d57378d4523cc15bbea3b02c95e279fefbffeb9948726544c7ffdfbb7e7e7a8edddafecda5ea75f74f74ff5831be03a8f29f6d84ae01a9aaaa34567ca025a5f273985f6cd8b173fdd777a743ffef7ffbf3f9f455147b16a383f9f4f38feddb70f9cd87cf9d3aebbe6c88145e381539b49e5503c278e7c4a2f28f3c81d982c4fd2dd3f8cc890ed8446b7b5426d83f267f21f837830c83d4cd6a5b3c01c3fb3e01c13099df733377dd6a072f9141058c0c3ed7d5ff033b13c7b459936a4d32603565920988c5d5d5df4f3cf0f3ff2f96fc2dc476c84d5ce5d5007002c4bf30473b9bdde24438a2d06eb2e84ea3a1fab14d03651e695fa6e4de70c5318fd2f0a7cde502e1c386c30fe28d55d8bd495db7f2aabe5fa79bc5e1d1d1ddfbf7ff6ddab5f9f7efb9fdbfdb7faaad72e42c99e7d57d5fd97feeedebd7bad6bb4cced9973497209dc66957d59578d609e7559fe26f6728e26179e245ffdf78f7c7df6cb3727272c062ea1e0e389ab6bfeaf96d5d7be4ead9c1dc9880858c15d555d7c8e8c937e25a4e005da2b71d1f980561af89e01366c9d8ecce1bd0eeebd79cbd4c37b7aafc2ff57ecde8064ca6fdcc69249287951bfaa1a351ee931a0b53f4136433faed5ccf278e213f056256b4771444b228d66398881a2708e544ebafaeba70f9c3cf9d74ffa75bc6be9e4182c6ade811d75148944ccbcb0c28a75331f8cd0d09abc49eca3bbe3a5c08ad9d6b318b03c9453446b021190ace9e80478ebd6f081fdb2eaa479d9b86b12c6a57ff4c54c1cb6367f0c0fec9b67dbebcbcb422f77feeddb377ee87f4d7fdebbfe6e4bcbaa2799c6257d7c3b930043d4dba5da3cc6a22a29afaaacd0bc5d5c56c6b3f3c7ab8d3337bd6b717e7e7feb7fdffecdef678a7264012400fe3493f0d2ee6abe63757176ac1595801d32040708e044d21ef02520425256664539c12f6119d14e21f6440770f294d21788f63a11f871be1e25e177db780650dedcdfa82e4bc6b6360da93ee46f220052fe815c0456bbdcb4e9e598faa1e6c9497fe2ac8d4c3f5fba3bf8e3ef4e7cff8ff4fcf5d95792520c557d7c4ce2877eb2548d97be8307b3847c88f655553d9e47a3d9ad22632f3ced84d1f0ca956f4c7178349f1fcaf7f059cd3d11c1efb2bdbe2c1db1dc7cdc5c135a4fedb7fed70f1cd3fe9217a6bd6763b9120a327aa0ccaed3ddcb973baaa2418813dc663d1ffdfd4d3540a3b3b3b8136d57d5214cc0d4b3f3f3dbb77e6b34aac1da7db1783829c4536f1d10093841bb5d27976faf475b856c811c69832913e9144de83b2aa991f6dd23c183c88ba3c558f63b6ac1fb28d724fcb926a3bca1b13703cb24ed342225e08c2cf67b58be5d3a4eccbee25e0d05757d93853dcc6e4dc0309483955c42230aaaaa942adf4bd7fdf26dba79f2f5e3870f0f3970f0f5d9e21d32058415af4c4e3808feab86996b54d496a4670110421985623467afa51c36718617a56ba8351809090dfe6212f0a00bdb985e0d22421179d27b68308d23fc376871884662a511dece35917038509187db3ce7085e279888fddfee77ff0ef0ef3e9c9c98d3ae5fa7d3b9dc686382f73f73e6cd0374d918c00cec937120374427a02717179cca3d3996ecaa03a63b86c6bbb973f6a9c0caea381d3dde0457597b0e153b2aa5da65baba5092874251252498d99f01707b5d146ef1093b7ea6d570b4582368b791a641699100208d281c26a8a0bbeef556cf13e48ae1b3856955b78fb3bd5eb3629b111ee94c0440afebf636be00375c72abedc663d577de46e49f51a80a1a1644e035a4619bb93e2b544439aa67df601c5792eba75fae3ef5c38f0f3cf9f9d9fa7a7c394a00ec72984e28baaee327a4d533992eb9aa523ae6a1bca7cf46544aeab6fed95b0c54f7244469ac9327a80011023da7cb986cfbd2a868e2de0bcfd1d9cdb37c9490453d1de782d7e1f03b6f77e6cd8f1cf0e7073e6c5f6bded97c0e3c5d5d59d13c4441283bdaa44464488abea26c412871154407e2807211de6db101d96d35b582ec50fcc6a6589d4b901c5bd6b1aea2ad2c57e000abdc66adc4c24677db85e3f76f57bd6b6efe1925204c8d5fd37cdb246b8fb98a2817a0aeabe813b24e2b333d6bc4a132906c81e30732d1876cb7dd0f1e5893e5bbb7ca2eca43e1f228dbe5f4539d69e0550224fec97fed415d136ce0909cb2121193f4135ae3a91b806fd7428c3184f1825c8e34022afaeb9faf72ffcf3dfce1f39557d653d0901bde91440d5d0a4985119c78a604a96b60d969d3d0481bb4399c4404194baa37432e06a7ea6b04cec66bad7d0d65b3e1c703662945ec3115a1e7851110276a232fedb1d7cce116fc91cebabea3bbfb1083fe182b113d650f856da31a3276cc2d9fc7eaaad6bd2a3a42cde6ab6bd5fdfff0fdbf6d5b364086aea0045897dba5803b549899dac020832866259d66801c1146738539cb5c81f4f4f4beab666ea1cb42eabea58bbad70b3b6935aabe09429724763b045e963577dd6c6eccf2e5c73bc5e2bddc66a3d9a32aaaaa65de6cb7f59bc4a3bdbc165000470e20d47e3344465894896bdeabb8da00021a9e1a82f498b83da1093ac3a5400f3c5f5b75ec59d1e56cc15ac59c016d14976bd9ab17e4b504b277c45606161b7c7c7cb9dcac2a89de2864749421b88a2b8003b7694ace6c76bb884690136660d233ffae73fbef4e1c3cf5f71f5e9f5e221bab620260649240c205050d34222d089f1072dcced00424f1240b390a03756c5dda91c8eed0f3dc3d82b7962b1d71c064687369bcff60459bf9e1906c95e2d59b5330525a7899fecd9b373f6edc9e47aaa0136aaa2fccc6515cbdef1ddbcdb65064085c2f2bf8d14ecbc678825cf22011535e75e4387cd1129282866857945e4f5fbeeabea99c4c9375821d9cc8d001d6948df58671df04176736a0858016a3d922a3bf11ce998c864c09594309b5fed67db3a7957873bde5f81b761fb5a6c0f367d4b25548b2f1471bbed67e5294292188b08d817ba3fc502b1e4a02920a8803a656c2335d408e80d393e7af4f9efce7ef1d7af8e1faad80ed3a3064aebd651302096e02952cdae9bd59ce07444130d299d9378a40a24445503ec097317ff81fd9cc41928b3c88efb8b52368cec002060ff89385a9f6b9ce349ae67b3851d200cd85e6e66f167dead5bbf7ffef5fbe7ddcf05c1663ce8813e692409dcdced68afb81dacecececafebfa346696b076cb8c49e9667f4687d59c0826657ec97f86e861f2e5ebc55bb175500584a211422881bbf90156463816b25bca66556325e1226d46db740b5ee136411363c2c84fe9a937d7745090f84cb43ecb1cdb51c36afee1e8d9df67a379f8a682463d492934f7171714e1b96a941bb57c8d50c087da49f26dc20333a3bc2427925a4ea7111cc94062be5fafa69b8f8f1cf2f1e76f8e5f9d2727433f1a60188ccd6f9ae6c982012d0efda032f95181796eccc22a670d2005e4ffc1d6d419d10ab7fce7b646d0683ec435c13157fde90e0b7540f852783ef3dd232b786c7111bd6bd5e279d43dcdfbf7ff38f0e30b72ce09c4bc4b9dc6c22c8e778f8c01a770b8bcb23a19c8ee617672f0115fd7ac937c485ecf66550553e7c10abb8b8bac1b0044c22d6ce999d97fecb710579c59f146f49da6abd13ffccab6b8f03a9fc7ed6bda114baca5a1796a78c74bdb837b7b958a71c047feff15b07081f50d6473b2f2c404db7fe287fedb0743bbdd4a2d6cebb415d40f49358a410004228a67d57d7fde4be6aa039dc2f2ebe76fcf9c7efbafce1d393bb85a20426c6bbe4d97f1aeaa6204f6161a6aad53f3d003fcd5b74d38d2ae823bb5011dac682cd8222a496bb3eb01b6444cd027371117e86c6758aab3310ed3744c26c272808ed9ce03d2560a73cea971d88562609889569fc663bb973f6eddbb77d2996ac77254e0db35a2c829b45118ed435ba5fa7de9c53597f60f830915f411190d7b1b73d5985bc494b811c7661857d6b757575d6fd5d805bfa995bfe213b65d405403b94575504395672e8922115d1a23b7003a3b1d02b09494248068eaea69665f78ec586a1a18925af6a1dafb531306f029b73a355e8c3a868fc818d3be13f4e4e4aeab644780e24d59ba24fd7f767676925f88aea2f69d6c5eb38aa2f925fd7fdbdb293f3bbcf4f1d76f1efcf3a7ef2e5b20653d9aa37151292800a3f1a499ffb50bbe197b016905689c462399c4c64e632cef87779fe875aaeedc2740ed0a1182adfad324a69a4ee7b4a8fe87841989de07dbabab2119bb77feedbb77f011daee2fed74e4c38634ad6bd7b658d70f6911005126bdd8e0fbd6265bdfade084b5ca38b911385e45b8888c3bf6eb9b8b8b2343dc06eb29187308e429d44aa831fb7f5a7c8271e469f422e46239be7dfafdebd7bfaf5fbeedb730526cadeb17244de14a07837dc3c1e5410a96c46e4faa6525f6b55f14d9b838b8e5a892ad8f7d311225c8dfa7dbe5da659ed9f69931899ce4c0f54331d178ab96757e79b8f5e7af0f9c7efb93db850b2168a9c55159c7890322b20b287f43cac2eca382004251bf5d0ee36a32efe2158aaa6a83aac85c255a65e16ed166c0ab5e82595b81b7300c50b8197feec9d3708a02cb13894e074d4b3a50f298aa80e287f5583e3e3eabea71b854801411aeaae2838610e92b359325955ba6d17deead5bf4bd6bec93f5534ed31934c49be2255bc36437e411d220b342543627233ff6eb8fcfcfaecd75d45dc4317f2225531227266e636fc0a8edd677b3e0bb778bf7ce47b1f5ad681c322ec7e265394a49cc7a6e0fcad27cdc1e8be6c39cb34d3cde917aaf1f7523b410f227e19358dc663048345c90000e34d5535f279be6bd63dc6629a7b49772704a2beea3b4b295e279f4e9c39f5c38fce5e9e929fa9c75284bc636f948240a0e31babcb626b52d590105d1822c9b43d1581e46f7a8160f3c925279a804a7c6982583879b755d103e078522ae9845501dba1ab25c48306c5d37e7463a03a0b39ae09a11343d6edbf57bb96a966e3f9bd964be5fad1213703fe75fe27e0377d5763c2cd109bb85c88222ab263a6362971004e42991e59fe81185e5f134514c24e8854d6ad5f2f5ebceabec6a59dd27398934d511bb5e66d5a06dc712ba22aa88237ceb703f2b13a794c81bc4e225a46aee4448b67dead1f1f1f279bcbabab8eabe807fcdda7f2d486f3850a8f64f87d47e042d1d5aa222e704385011910414d64fa9d4d322b5d79ec93a0ec45ab444ba5ea79bad0a25c81357b99f4b15d4dd6ada38740e9c47a767671f9e3af5d3c7cf9f9d5e26dd9af662d62d469a17ea27e8001d120a0b5d9b88a08069b196e4be4446d640290b284c4888e340bbf1663b9dc7a746c12896d59557a66a266c166cc50eb21af8136555512823b8036d550b4287471015cc34753fdc31ce8c26e46b544404296dd40bb49cc5303f60173ca51012b0de1540b4e461055daaa6c465c8a4797e7102a3f9fc194be07240a0ac8884e179be5429804636e829c11a2602400888ad5db8a2d7cebeaae3b75faafeab9c4a1b9179a075555b9dc60510c19842b01662ac8b99742aa8ce1d5ae3a7fef9d79f4bd5dabe620b4461132bcb37ecd665dda75fecb3fe8112e44831720457e0190d116c4df1f1d5b9c4b98d72fe05836ceafcf233fcb0348041587643b0d486bd6bd67dd57d52ba7296b6d0195c3c967c82b151c5e143d82a2245d39bd570314d20c956e3cbe6d2eaacca8cea6381497de19815390e261b8b8b8b8b63f963d436b91880a2f125aef2ececf7df5c79f8f1f3e7efcf55210f57dd5f20ec109755912bbbffd7f1b6dd6c7cc43d43399c4285b5663bd9d57d447ec1097255c26526404a036233463131b23560786c255d59c9ed1bf4cc8a78814dec48a0280e505b07b83b82f78ecddc9a2bde6d1aa3aeafd83f81c7d601cea65b2b7193e82709d6f9a972826bcad15005db8b8b0357a61084dc3de07811e63d455ccbf51411125212cd27f5e5dba75d7fdbba2046395bc60900006530416e7f05bc8a5c27951c620066d1e44484ae1d9e03616bfa22f957bc124801c59789936e6d644cbb0afa33f64f123194f778532fff102e046052a84c08b8000000009454e444ea240628'
			 )));

}

sub load_evcode_module
{
    # see if we're using something other than TWIN/STARS
    parse_args_by_eval();

    # defaults for the module and runname
    $evcode_module||='STARS';
    $runname||='window_to_the_stars';
    $modinfo{time_conversion_factor}=1.0;#/3.1557807e7;

    if(defined($evcode_module) && ($evcode_module=~/(.*)_WTTS/))
    {
	# use evcode_module to get the evcode : this should be the default
	# in future versions
	$evcode = $1; print "SET EVCODE $1\n";
	$modinfo{plt1file_stub}=plt1file_stub();
    }
    elsif(defined($evcode) && ($evcode =~ /bonnfires/oi || $ENV{PWD}=~/bonnfires/o))
    {
	# load BONNFIRES module
	$evcode = 'bonnfires';
	$evcode_module='BONNFIRES_WTTS';
	$modinfo{plt1file_stub}='star';
    }    
    else
    {
	# load TWIN or BINSTAR module
	$modinfo{plt1file_stub}=$runname.'.plt';
	opendir(PWD,'.')||confess("cannot open pwd?");
	while($_=readdir(PWD))
	{
	    chomp;
	    if((/^binstar.*.e$/o)||
	       ((defined($evcode)&&($evcode eq 'binstar'))))
	    {
		close PWD;
		@plt1data=('');
		$evcode_module='binstar';
	    }
	}
	close PWD;
    }
    load $evcode_module, ':all';
    return;
}

sub dtconvert
{
    # format timestep for
    my $dt=$_[0];
    # dt is in years by default : convert to s
    $dt *= 3.1557807e7;
    my $unit='s';
    if($dt>3600.0)
    {
	$dt/=3600.0;
	$unit='h';
    }
    if($dt>24.0)
    {
	$dt/=24.0;
	$unit='d';
    }
    if($dt>365.25)
    {
	$dt/=365.25;
	$unit='y';
    }
    if($dt>1e6)
    {
	$dt*=1e-6;
	$unit='Myr';
    }
    return (sprintf '%.3g %s',$dt,$unit);
}

sub refresh_internals_model_list
{
    # manually refresh the internals model list: often useful when restarting a model
    my $latest=shift @internals_buttons;
    map 
    {
	if(defined($_))
	{
	    $_->destroy;
	    undef($_);
	}
    }@internals_buttons;
    @internals_buttons=($latest);

    # remove cached model files
    opendir(INTERNALSDIR,$internalsdir)||confess;
    while(my $f=readdir(INTERNALSDIR))
    {
	chomp $f;
	print "Remove $f? "if($vb); 
	if($f=~/model\.\d+\.\d+/)
	{
	    print "YES\n"if($vb);
	    unlink $internalsdir.'/'.$f if($unlink_tempfiles);
	}
	elsif($vb)
	{
	    print "NO\n";
	}
    }
    close INTERNALSDIR;
    clear_global_caches();

    print "Update with plt1= @plt1data\n"if($vb);

    update_Internals_tab();
}

sub get_web_status
{
    # get data for both stars
    my $s;

    my @cols=('Model number','Age','Timestep','nphase',
	      'Luminosity','Radius','Teff','Mass',
	      'Helium core mass','CO core mass',
	      'Luminosity of hydrogen burning',
	      'Luminosity of helium burning',
	      'Luminosity of carbon burning',
	      'Luminosity due to gravity'
	);

    my @phases=('','PMS','centH','RGB','CHeB','AGB','C','Ne');

    my @l;
    
    foreach my $sn (1,2)
    {
	@{$l[$sn-1]} = split(/\s+/o,tailoffile($runname.'.plt'.$sn));
    }
    @prevwebl = @l if(!defined($prevwebl[0][0]));
   
    foreach my $col (@cols)
    {
	my $col2=$col;
	$col2=~s/Model number/Model/o;
	$col2=~s/Luminosity/L/o;
	$col2=~s/ of hydrogen burning/H/o;
	$col2=~s/ of helium burning/He/o;
	$col2=~s/ of carbon burning/C/o;
	$col2=~s/ due to gravity/g/o;
	$col2=~s/(\w\w)\w* core mass/Mc$1/;
	$s.=sprintf "%10s ",$col2;
	
	foreach my $sn (1,2)
	{
	    my $x=$l[$sn-1][$plt1cols{$col}];
	    my $px=$prevwebl[$sn-1][$plt1cols{$col}];

	    if($col eq 'nphase')
	    {
		$s.=sprintf"%14s   ",$phases[$x]; 
	    }
	    else
	    {
		my $direction;
		if($x > $px)
		{
		    $direction='&uarr;';
		}
		elsif($x < $px)
		{
		    $direction='&darr;';
		}
		else
		{
		    $direction='=';
		}

		$x=0.0 if($x == 1e-30);
		$s.=sprintf "%14.6g %1s ",$x,$direction;
	    }
	}
	$s.="\n";
    }

    @prevwebl=@l;
    return $s;
}

sub update_webserver_data
{
    print "Update http\n"if($vb);
    my $dir=$ENV{HOME}.'/public_html/wtts-web/';
    mkdir $dir if(!-d $dir);
    open(HTTP,">$dir/index.html")||return;
    print HTTP "<HTML><HEAD><META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\"><META HTTP-EQUIV=\"PRAGMA\" CONTENT=\"NO-CACHE\"><META HTTP-EQUIV=\"EXPIRES\"
CONTENT=\"0\"><TITLE>Window To The Stars</TITLE></HEAD><BODY>";
    my $tstat=(stat 'wtts_binstar.plt1')[9];

    if($tstat!=$prevtstat)
    {
	# update status
	$web_status='<PRE>'.get_web_status().'</PRE>';
	$web_status=~s/\n/<BR>/go;
	$prevtstat=$tstat;
    }

    my $unit;
    my $age=(time()-$tstat)/(24*3600.0);
    if($age<1.0)
    {
	# convert to hours
	$age*=24.0;
	if($age<60.0)
	{
	    # convert to minutes
	    $age*=60.0;
	    if($age<60.0)
	    {
		$age*=60.0;
		$age='<1'if($age<1.0);
		$unit='secs';
	    }
	    else
	    {
		$unit='mins';
	    }
	}
	else
	{
	    $unit='hours';
	}
    }
    else
    {
	$unit='days';
    }
    $age="Last write $age $unit ago\n";
    
    print HTTP scalar(localtime()),"<BR>",$web_status,$age,"</BODY></HTML>";
    close HTTP;
}



sub res_slider
{
    # set the scale width!
    my $adj = Gtk2::Adjustment->new($misc_opts_values[23],1,100,1,1,0);
    my $slider= Gtk2::HScale->new($adj);
    $slider->set_digits(0);
    $slider->set_size_request(100,100);
    $slider->signal_connect('value_changed'=>sub{
	$misc_opts_values[23]=$_[0]->get_value; 
			    });
    return $slider;
}

sub guess_every
{
    # guess resolution for gnuplot ('every')
    my $every; # returned
    my $n=$#plt1data; # linecount of plt file

    return $n>0 ? MAX(1,int($n/(0.01*$misc_opts_values[23]*$n))) : 1;
}

sub withstring
{
    # gnuplot's with
    my $lt=$_[0];
    my $lt1=$lt-1;
    $lt1 %= $gnuplot_options{'number of linetypes'};
    return ' with '. (defined($gnuplot_options{'with '.$lt1}) ? $gnuplot_options{'with '.$lt1} : ' lines ')
	.' '.ltstring($lt);
}

sub ltstring
{
    # make a linetype string for gnuplot
    my $lt=$_[0]; # line number
    my $lt1=$lt-1;
    $lt1 %= $gnuplot_options{'number of linetypes'};
    
    my $s=' lt '.
	(defined($gnuplot_options{'line type '.$lt1}) ? $gnuplot_options{'line type '.$lt1} : $lt).' '.
	(defined($gnuplot_options{'line colour '.$lt1}) ? ' lc rgbcolor "'.$gnuplot_options{'line colour '.$lt1}.'"' : ' ');

    $s .= ' lw '.$gnuplot_options{'line width '.$lt1};

    if($gnuplot_options{'with '.$lt1}=~/point/o)
    {
	$s .= ' ps '.$gnuplot_options{'point size '.$lt1}if(defined($gnuplot_options{'point size '.$lt1}));
	$s .= ' pt '.$gnuplot_options{'point type '.$lt1}if(defined($gnuplot_options{'point type '.$lt1}));
    }    
    print "LTS $lt : $s\n" if($vb);
    return $s;
}

sub perhaps_fork
{
    # call a subroutine after calling fork(), if we are allowed to fork,
    # and then exit immediately. Good for doing things in the background and
    # not returning.
    # 
    # Of course you should not do this if you want the function to return
    # something useful!
    my $func=$_[0];
    print "Perhaps fork : os=$os : func=$func\n" if($vb);
    if(($os eq 'unix')&&(fork()==0))
    {
	&{$func};
	exit;
    }
    else
    {
	# no fork : serial run
	&{$func};
    }
}

sub process_region
{
    # make a file plottable by gnuplot with depicts a region of the 
    # kippenhahn diagram

    my $h=$_[0]; # input hash (pointer)
    my $outfile=$_[1]; # output file (suitable for plotting)
    my $offset=$_[2]; # x,y offset for the grid (as fractions of a grid square)
    my $dx=$_[3];
    my $dy=$_[4];
    my @x=sort {$a<=>$b} keys %$h;
    
    my $idy = $dy!=0.0 ? 1.0/$dy : undef;
    my $x=$x[0]-$dx*10;
    my $nx=$#x;
    my $nx1=$#x+1;
    my %probed;
    my $format='%15.10g %15.10g';

    my $offx=$offset*$dx;
    my $offy=$offset*$dy;

    print "process_region : dx=$dx dy=$dy (idy=$idy) : $#x data items : offsets $offx, $offy (from offset fraction $offset)\n" if($vb);

    open(REGION,'>'.$outfile)||confess("Cannot open $outfile : $!");
    for(my $i=0;$i<$nx1;$i++)
    {
	# finite resolution (saves a lot of time!)
	next if(($i>0) && ($i<$nx) && ($x[$i]<$x+$dx));

	# Analyse data at x=$x[$i]
	$x=$x[$i];

	foreach my $this (@{$$h{$x[$i]}})
	{
	    my ($y1,$y2) = split(' ',$this);

	    # vertical line : always accurate because y1,y2 are the base
	    # and top of the region
	    no warnings;
	    printf REGION "%g %g\n%g %g\n\n\n",
	    $x+$offx,$y1+$offy,
	    $x+$offx,$y2+$offy;

	    # now limit y1 to within dy of the grid
	    $y1=$dy*int($y1*$idy) if(defined($idy));
	    
	    # and loop to the right side of the region
	    for(my $y=$y1; $y<=$y2; $y+=$dy)
	    {
		if(!$probed{sprintf $format,$x,$y})
		{
		    my @initial=($x+$offset,$y+$offset);
		    my @final;
		    for(my $j=$i+1;$j<$nx;$j++)
		    {
			# test point $x[$j], $y
			
			# test if the extrapolated point is convective
			my $convtest=0;
			foreach my $testpoint (@{$$h{$x[$j]}})
			{ 
			    my ($testy1,$testy2) = split(' ',$testpoint);
			    if(($y>=$testy1) && ($y<=$testy2))
			    {
				$convtest=1;
				$probed{sprintf $format,$x[$j],$y}=1;
				last;
			    }
			}

			if($convtest)
			{
			    # it is convective
			    # save as 'final' and continue the search
			    @final=($x[$j]+$offset,$y+$offset);
			}
			else
			{
			    # it is not convective
			    $j=$nx1; # break the loop
			}
		    }

		    # if we have (initial and final) data, plot it
		    if($#final>-1)
		    {
			printf REGION "%g %g\n%g %g\n\n\n",@initial,@final;
		    }
		}	
	    }
	}
    }
    close REGION;
}

sub onoff_kipp_region
{
    # take a vertical line (i.e. in y,z) of the kippenhahn data
    # and if it is > $thresh, set to 1, otherwise 0
    my $array=$_[0];
    my $index=$_[1];
    my $thresh=$_[2];
    no warnings;
    map
    {
	${$_}[$index] = 1*(${$_}[$index] > $thresh);
    }@$array;
    use warnings;
}

sub normalise_kipp_line
{
    # take a vertical line (i.e. in y,z) of the kippenhahn data
    # and normalise it to the maximum value
    my $array=$_[0];
    my $index=$_[1];
    my $max=-1e200;
    map
    {
	$max=MAX(${$_}[$index],$max);
    }@$array;

    if(abs($max)>1e-199)
    {
	map
	{
	    ${$_}[$index] /= $max if(defined ${$_}[$index]);
	}@$array;
    }
}


sub enhanced_string
{
    # format a string for the enhanced terminal types of modern gnuplots
    my $s=$_[0];
    if($gnuplot_options{'terminal type'}=~/postscript/io)
    {
	# ps enhanced label
	$s=~s/[eE]\+?0*(.*)/\{\/Symbol \\264\}10^{$1}/;
    }
    else
    {
	# PNG enhanced label
	$s=~s/[eE]\+?(.*)/x10^{$1}/;
    }
    return $s;
}

sub strip_lc
{
    # take a string from withstring() and strip the 'lc rgbcolor'
    my $with=$_[0];
    $with=~s/lc rgbcolor \S+//o;
    return $with;
}

sub prepend_gdfontpath
{
    # prepend the GDFONTPATH to a command string
    my $c=$_[0];
    $c='env GDFONTPATH='.$gdfontpath.' '.$c if(defined($gdfontpath));
    return $c;
}

############################
# Extra Gtk2 functionality #
############################

sub Gtk2::Label::new_from_markup
{ 
    # Gtk2::Label does not provide a new_from_markup option, so do it
    # ourselves
    my $self = $_[0];
    my $string = $_[1];
    $self=Gtk2::Label->new;
    $self->set_markup($string);
    return ($self);
}

sub Gtk2::ComboBox::get_active_text_local 
{
    # provide this function just in case it doesn't exist (GTK2.4 doesn't have it)
    my ($self) = @_;
    my $model = $self->get_model;
    $model->get(
      $model->get_iter(
        Gtk2::TreePath->new_from_string($self->get_active)
      )
    );
}

sub Gtk2::Entry::new_with_text
{ 
    # Gtk2::Entry->new combined with text setting
    my $self = $_[0];
    my $string = $_[1];
    $self=Gtk2::Entry->new;
    $self->set_text($string) if(defined($string));
    return ($self);
}

### buttons, labels, entries etc with tooltips

sub Gtk2::Button::new_with_label_and_tooltip
{
    # allow a button to be made with a label and a tooltip
    my $self = $_[0];
    my $label = $_[1];
    my $tooltip=$_[2];
    $self=Gtk2::Button->new_with_label($label);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}


sub Gtk2::Button::new_with_tooltip
{
    # allow a button to be made with a tooltip
    my $self = $_[0];
    my $label = $_[1];
    my $tooltip=$_[2];

    $self = $label=~/^_/o ? Gtk2::Button->new($label) :  Gtk2::Button->new_with_label($label);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}

sub Gtk2::ToggleButton::new_with_label_and_tooltip
{
    # allow a button to be made with a label and a tooltip
    my $self = $_[0];
    my $label = $_[1];
    my $tooltip=$_[2];
    $self=Gtk2::ToggleButton->new_with_label($label);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}

sub Gtk2::RadioButton::new_with_label_and_tooltip
{
    # allow a button to be made with a label and a tooltip
    my $self = $_[0];
    my $group=$_[1];
    my $label = $_[2];
    my $tooltip=$_[3];
    $self=Gtk2::RadioButton->new_with_label($group,$label);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}

sub Gtk2::Entry::new_with_text_and_tooltip
{
    # allow a button to be made with a label and a tooltip
    my $self = $_[0];
    my $label = $_[1];
    my $tooltip=$_[2];
    $self=Gtk2::Entry->new_with_text($label);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}

sub Gtk2::HScale::new_with_tooltip
{
    # allow a button to be made with a label and a tooltip
    my $self = $_[0];
    my $adjustment = $_[1];
    my $tooltip=$_[2];
    $self=Gtk2::HScale->new($adjustment);
    set_tooltip($self,$tooltip) if(defined($tooltip));
    return ($self);
}

############################################################

sub HRD_options
{
    # HRD options window
    if(!defined($HRD_options_window))
    {
	$HRD_options_window=Gtk2::Window->new();
	$HRD_options_window->set_title('WTTS HRD Options');
	$HRD_options_window->signal_connect (delete_event =>sub{
	    $HRD_options_window->hide; return TRUE; # do not propagate
					     });

	$HRD_options_window->signal_connect(key_press_event => \&main::hide_on_escape);
	$HRD_options_window->set_size_request(400,400);
	
	my $sw=Gtk2::ScrolledWindow->new(undef,undef);
	$sw->set_shadow_type('etched-out');
	$sw->set_policy('automatic','automatic');
	$sw->set_border_width(5);
	$HRD_options_window->add($sw);

	my $v=Gtk2::VBox->new();
 	my @packopts=(FALSE,FALSE,0);

	$v->pack_start(opth({label=>'Brightness',
			     min=>0, max=>200, 'step increment'=>10, 'page increment'=>10, 'page size'=>0,
			     value=>$gnuplot_options{'HRD brightness'},
			     var=>\$gnuplot_options{'HRD brightness'},
			     default=>$gnuplot_options_default{'HRD brightness'}}),
		       FALSE,FALSE,10);
	$v->pack_start(opth({label=>'Contrast',
			     min=>0, max=>200, 'step increment'=>10, 'page increment'=>10, 'page size'=>0,
			     value=>$gnuplot_options{'HRD contrast'},
			     var=>\$gnuplot_options{'HRD contrast'},
			     default=>$gnuplot_options_default{'HRD contrast'}}),
		       FALSE,FALSE,10);
	$v->pack_start(opth({label=>'Hue (red/green)',
			     min=>0, max=>200, 'step increment'=>10, 'page increment'=>10, 'page size'=>0,
			     value=>$gnuplot_options{'HRD hue'},
			     var=>\$gnuplot_options{'HRD hue'},
			     default=>$gnuplot_options_default{'HRD hue'}}),
		       FALSE,FALSE,10);

	my $reset= Gtk2::Button->new('Reset HRD plotting options');
	$reset->signal_connect(clicked=>sub
			       {
				   foreach my $opt (grep {/^HRD/} keys %gnuplot_options)
				   {
				       $gnuplot_options{$opt}=$gnuplot_options_default{$opt};
				   }
				   # destroy and rebuild HRD tab
				   redraw_tab('HRD');

				   # destroy options
				   $HRD_options_window->destroy;
				   $HRD_options_window=undef;
			       });
	$v->pack_start($reset,TRUE,FALSE,10);


	$sw->add_with_viewport($v);
	$sw->show_all;
    }
    
    # always show and present
    $HRD_options_window->show_all();
    $HRD_options_window->present();
}

sub labellist
{
    # return a list of labels suitable for gnuplot
    my $labelcol=$_[0]; # label column
    my $labeldist=$_[1]; # distance between labels
    
    # data array and used x,y columns
    my $labeldata=$_[2]; # (pointer)

    # column numbers
    my $xcol=$_[3];
    my $ycol=$_[4];

    # plot or data range (array)
    my $range=$_[5];

    my $logx=($xcol<0);
    my $logy=($ycol<0);
    $xcol=-$xcol if($xcol<0);
    $ycol=-$ycol if($ycol<0);


    my @labels; # label list : returned
    
    # select x,y,label data
    my @xylabeldata;
 
    map
    {
	# choose the columns of data
	my @x=(split(/\s+/o,$_))[$xcol,$ycol,$labelcol];

	# perhaps log
	$x[0]=log10(logmin>$x[0] ? logmin : $x[0]) if($logx);
	$x[1]=log10(logmin>$x[1] ? logmin : $x[1]) if($logy);

	if(defined($x[0]) && defined($x[1])&& defined($x[2]) && 
	   ($x[0]>=$$range[0]) && 
	   ($x[0]<=$$range[1]) &&
	   ($x[1]>=$$range[2]) && 
	   ($x[1]<=$$range[3]))
	{
	    push(@xylabeldata,[@x]);
	}
    }@$labeldata;

    my $labelaspect=1.0; # previous label x/y (assume starts 1.0)

    # default to given $labeldist
    my $mindistx=$labeldist;
    my $mindisty=$labeldist;
    
    # except for 'Auto' mode which is cleverer
    my $auto=0;
    if($labeldist eq 'Auto')
    {
	$auto=1;
	my $nauto=18; # ~ number of points for auto at font size 20
	$nauto *= 20.0/$gnuplot_options{'font size'}; # modulate for font size
	my $dx=abs($$range[1]-$$range[0]);
	my $dy=abs($$range[3]-$$range[2]);
	$mindistx = $gnuplot_options{'auto labelling x factor'}*$labelaspect*$dx/$nauto;
	$mindisty = $gnuplot_options{'auto labelling y factor'}*$dy/$nauto;
	print "Automatic labeldist x=$mindistx, y=$mindisty\n" if($vb);
    }

    # hypotenuse distance : if > this away, cannot possibly be touching 
    my $mindisthyp2 = $gnuplot_options{'auto labelling hypotenuse factor'}*($mindistx*$mindistx + $mindisty*$mindisty);
 
    my $t0=[gettimeofday];
    
    # original code : 15.4s
    # with fail : 10.2s
    # reverse $j loop : 4.0s
    # nested ifs : 3.37s
    # test on dx,dy : 2.06s
    # remove MINs 0.655s
    # remove other temp vars: 0.106s
    # remove dx,dy : 0.108s (not worth it)
    # >=0 to >-1 : 0.100s

    # NB abs and multiply take ~ the same time,
    # but the abs search seems to be ~25% faster
    # than using multiplication.
    # 
    # The lookups $labels[$j] could be replaced with
    # a variable holding $labels[$j], but the overhead
    # makes the loop slower
    
    my $n=@xylabeldata;
    for(my $i=0;$i<$n;$i++)
    {
	my $thislabel=$xylabeldata[$i]; # [x,y,label]

	# check distance to all other labels < minima
	my $fail=0;
	my $j=$#labels;
	while($j>-1)
	{
	    my $dx = ($$thislabel[0]-$labels[$j][0]);
	    # check x
	    if(abs($dx) < $mindistx)
	    {
		my $dy = ($$thislabel[1]-$labels[$j][1]);
		# check y
		if((abs($dy) < $mindisty) &&
		   # check distance^2
		   ($dx*$dx+$dy*$dy < $mindisthyp2))
		{
		    # failed all criteria : bail out
		    $fail=1;
		    last;
		}
	    }
	    $j--;
	}

	if(!$fail)
	{
	    # we're allowed a label here
	    my $s=$$thislabel[2];

	    # reformat number
	    $s = $s==0.0 ? 0 : ($s eq int($s) ? $s : sprintf('%3.3g',$s));

	    if($auto)
	    {
		$mindistx /= $labelaspect;
		$labelaspect = 0.36 * length($s);
		$mindistx *= $labelaspect;
	    }

	    push(@labels,[@$thislabel[0,1],enhanced_string($s)]);
	}
    }

    if($timings)
    {
	printf "Label sort took %g seconds\n",
	tv_interval($t0,[gettimeofday]);
    }
#    exit;
    return (@labels);
}

sub datarange
{
    # given plt1 data, determine its range.
    #
    # NB optimised to NOT use the MIN,MAX macros, which are slow

    my $data=$_[0]; # data array (pointer)
    my $graphrange=$_[1]; # range given in the range boxes (to check if data is in THAT range)
    my $xcol=$_[2]; # xcol, ycol of the data we are checking
    my $ycol=$_[3];
    my @datarange; # returned
    
    my $logx=($xcol<0);
    my $logy=($ycol<0);
    $xcol=-$xcol if($xcol<0);
    $ycol=-$ycol if($ycol<0);
    
    # which ranges should we use? test with is_numeric
    my @nuserange;
    foreach (0,1,2,3)
    {
	$nuserange[$_] = !is_numeric($$graphrange[$_]);
    }

    # silly values (but must not fail floating point comparisons!)
    @datarange=(1e200,-1e200,1e200,-1e200);

    my $maxcol = $xcol+$ycol+2; # limit split
    my $n=$#{$data}+1;
    my $t0=[gettimeofday];

    for(my $i=0;$i<$n;$i++)
    {
	my @d=split(/\s+/o,$$data[$i],$maxcol);
	
	# check if the point is in range of the plot
	my $x = ($logx ? log10(logmin>$d[$xcol] ? logmin : $d[$xcol]) : $d[$xcol]);
	if((($nuserange[0]) || ($x>=$$graphrange[0])) &&
	   (($nuserange[1]) || ($x<=$$graphrange[1])))
	{
	    my $y = ($logy ? log10(logmin>$d[$ycol] ? logmin : $d[$ycol]) : $d[$ycol]);
	    if((($nuserange[2]) || ($y>=$$graphrange[2])) &&
	       (($nuserange[3]) || ($y<=$$graphrange[3])))
	    {
		# if so, use it to constrain the data range
		$datarange[0] = $datarange[0]<$x ? $datarange[0] : $x;
		$datarange[1] = $datarange[1]>$x ? $datarange[1] : $x;
		$datarange[2] = $datarange[2]<$y ? $datarange[2] : $y; 
		$datarange[3] = $datarange[3]>$y ? $datarange[3] : $y;
	    }
	}
    }

    if($timings)
    {
	printf "Datarange took %g seconds\n",
	tv_interval($t0,[gettimeofday]);
    }

    print "DATARANGE1 @datarange\n"if($vb);

    # if we're stuck with 1e200 then there's no data in range :(
    map
    {
	$_ = '*' if(($_==1e200) || ($_==-1e200));
    }@datarange;

    print "DATARANGE @datarange\n"if($vb);
    return (@datarange);
}

sub get_pltdata
{
    # get plt1data array for the case when we have multiple stars
    # in the plot
    my @data;
    my $nstar=$_[0];
    my $wantlogg=$_[1];

    my @files = ($nstar==1)||($nstar==2) ? ($nstar) : (1,2);

    my $t0=[gettimeofday];

    foreach my $wttslayer (active_wttslayers())
    {
	my $f0=$wttslayer.'/'.$modinfo{plt1file_stub};
	foreach my $x (@files)
	{
	    my $f=$f0.$x;
	    if(ok_file($f) && open(READPLTDATA,'<',$f)) 
	    {
		<READPLTDATA>;  # ignore first line and comments
		push(@data, grep {!/^\s*\#/o} (<READPLTDATA>));
		close READPLTDATA;
	    }
	}
    }

    # remove whitespace always
    map{s/^\s+//;chomp}@data;

    if($wantlogg)
    {
	my $linr = defined $plt1cols{Radius};
	my $Mcol = $plt1cols{Mass};
	my $Rcol = $linr ? $plt1cols{Radius} : $plt1cols{'log Radius'};
	my $linTeff = defined $plt1cols{Teff};
	my $Teffcol = $linTeff ? $plt1cols{Teff} : $plt1cols{'log Teff'};
 	my $maxcol = 2 + MAX($Mcol,MAX($Teffcol,$Rcol));
       
	map
	{	    	    
	    # get M,R in cgs hence calculate logg
	    my @x=split(/\s+/o,$_,$maxcol);
	    my $g;
	    if($linr)
	    { 
		# R is stored linearly:
		# read and calculate logg as an extra variable		
		my $R=$x[$Rcol];
	        $g=$x[$Mcol]/($R*$R);
		
		# add logg to the original string and push onto the data array
		$_ .= ' '.(log10(logmin>$g ? logmin : $g)+loggfac);
	    }
	    else
	    {
		# R is saved as logR, just need to calc. log(M)
		my @x=split(/\s+/o,$_,$maxcol);
	
		# logg = log(GM/RR) = logG +logM -2logR
		my $logg = (loggfac + log10($x[$Mcol]) - 2.0 * $x[$Rcol]);
		$_ .= ' '.$logg;
		$g = 10.0**$logg;
	    }

	    my $Teff = $linTeff ? $x[$Teffcol] : 10.0**$x[$Teffcol];
	    my $calL = ($Teff**4.0)/($g*Lsun);
	    
	    # add calL  = Teff^4/g
	    $_ .= ' '.(log10(logmin>$calL ? logmin : $calL));
	    #printf "Append : teff=%g g=%g logg=%g call=%g Lsun; log call = %g\n",
	    #$Teff,$g,log10($g),$calL,log10($calL);

	}@data;
    }

    
    if($timings)
    {
	printf "Load plt1data took %g seconds\n",
	tv_interval($t0,[gettimeofday]);
    }

    return \@data;
}


sub gnuplot_size
{
    # return x,y for gnuplot terminal size
    my $f=$gnuplot_options{'Plot Oversize'}*$gnuplot_options{'oversize factor'};
    my $x=get_opt(1);
    my $y=get_opt(2);

    # x,y are the current resolution : note that if this is too small
    # you'll just get a mess, so in this case limit x,y
    $x=MAX(640,$x//640) if($x!=1);
    $y=MAX(400,$y//400) if($y!=1);

    #print "X=$x Y=$y\n";

    $x *= $f;
    $y *= $f;

    printf "oversize factor %g, plot oversize %g, get_opt %g\n",
    $gnuplot_options{'oversize factor'},
    $gnuplot_options{'Plot Oversize'},
    get_opt(2) if($vb);

    if($gnuplot_options{'terminal type'}=~/PNG/i)
    {
	# PNG
    }
    else
    {
	# postscript
	# x,y default to inches, which is a little large!
	$x *= 0.015;
	#$y = $x*0.8; # compensate for bad shape of postscript output (why???)
	$y *= 0.015;
    }

    return($x,$y);
}

sub gnuplot_overrides
{
    # extra options which are (perhaps) needed for a gnuplot plt file
    my @l;
    my $options=$_[0];
    if($gnuplot_options{'plot title'})
    {
	push (@l,"set title \"$gnuplot_options{'plot title'}\"\n");
    }
    push (@l,'set border 31 lw '.$gnuplot_options{'axis width'}.' lt 1 lc rgbcolor "'.$gnuplot_options{'axis colour'}."\"\n"); 
 
    # standardise margins
    my %margins;
    if($gnuplot_options{'terminal type'}=~/postscript/io)
    {
        $gnuplot_options{'oversize factor'}=0.5;
	%margins=(lmargin=>0.15,
		  rmargin=>0.95,
		  bmargin=>0.18,
		  tmargin=>0.9
	    );
    }
    else
    {
        $gnuplot_options{'oversize factor'}=1.0;
	%margins=(lmargin=>0.15,
		  rmargin=>0.95,
		  bmargin=>0.15,
		  tmargin=>0.9);
    }
   
    if(defined($options))
    {
	$margins{rmargin} += $$options{extra_right_margin} if(defined($$options{extra_right_margin}));
	$margins{lmargin} += $$options{extra_left_margin} if(defined($$options{extra_left_margin}));
	$margins{tmargin} += $$options{extra_top_margin} if(defined($$options{extra_top_margin}));
	$margins{bmargin} += $$options{extra_bottom_margin} if(defined($$options{extra_bottom_margin}));
    }

    foreach my $margin (keys %margins)
    {
	$gnuplot_options{$margin}=$margins{$margin};
	push(@l,"set $margin at screen $margins{$margin}\n");
    }

    if($gnuplot_options{'key location'} ne 'Default')
    {
	if($gnuplot_options{'key location'} eq 'None')
	{
	    push(@l,"set key off\n");
	}
	else
	{
	    my $k=$gnuplot_options{'key location'};
	    $k .= ' width 1 height 1' if($gnuplot_options{'key location'}=~/box/);
	    push(@l,"set key $k\n");
	}
    }

    push(@l,"set grid\n") if($gnuplot_options{grid}!~/No/);
  
    return @l;
}

sub HRD_radii
{
    # draw lines of constant radius : new algorithm
    # L= 4pi * sigma * R^2 Teff^4 = s * R^2 * Teff^4 (s=4pi*sigma)
    # i.e. R = sqrt(L/s)/T^2
    # log R = 0.5 log (L/s) - 2 log T = const
    #
    # i.e. :: log (L/s) = 4 log T + 2 * logR
    # 
    # or   :: log T = 0.25 * (log (L/s) - 2 * log R)

    my $range=$_[0]; # NB 0,1 are flipped (because and HRD is backwards!)
    my @gp_commands;  # list of gnuplot commands (returned)

    # make linear ranges
    my @linrange;
    foreach (0..3) { $linrange[$_] = 10.0**$$range[$_] }

    my $rmin = MAX(logmin,sqrt($linrange[2])/($linrange[0]*$linrange[0]));
    my $rmax = MAX(logmin,sqrt($linrange[3])/($linrange[1]*$linrange[1]));

    my $lrmin=log10(MAX(logmin,$rmin));
    my $lrmax=log10(MAX(logmin,$rmax));

    my $const1=(log10(Lsun)-log10(stefan_boltzmann*PI4));
 
    my $dlr=($lrmax-$lrmin)/10.0;
    for(my $logR=$lrmin+$dlr*0.5; $logR<$lrmax; $logR+=$dlr)
    {
	my $lR2=2.0*$logR;

	# find where the line crosses the bottom and top teff axes
	my $t0 = MAX($$range[1],0.25*($$range[2]-$lR2));
	my $t1 = MIN($$range[0],0.25*($$range[3]-$lR2));

	# and where it crosses the left and right L axes
	my $L0 = MAX($$range[2],4.0*$$range[1]+$lR2);
	my $L1 = MIN($$range[3],4.0*$$range[0]+$lR2);

	my $r = 10.0**($logR+0.5*$const1)/Rsun;
	my $s=enhanced_string(sprintf '%.3f',$r);
	my $with=withstring(3);
	$with=~s/with \S+//o;

	# draw the "arrow" (a line really as it has no heads)
	push(@gp_commands, "set arrow from first $t0,$L0 to first $t1,$L1 $with nohead back\n");
	
	# put the radius label either to the right of the y2 axis,
	# or just above the x axis
	if($t0==$$range[1])
	{ 
	    my $x=1.02-0.002*length($s);
	    push(@gp_commands, "set label \"{\/*0.8 $s}\" at graph $x,first $L0\n");
	}
	else
	{
	    my $y=-0.01;
	    push(@gp_commands, "set label \"{\/*0.8 $s}\" at first $t0,graph $y\n");
	}
    }
    return @gp_commands;
}


sub direction
{
    #  determine axis data direction
    return $_[0] <= $_[1] ? 1 : -1;
}


sub determine_gnuplot_version
{
    my $r=`$external_programs{gnuplot} --version`;
    return
	$r=~/gnuplot (\S+) patchlevel (\S+)/ ?  ($1.'.'.$2)
	: $r=~/gnuplot (\S+)/ ? $1 
	: undef;
}

sub plt1using
{
    # make a string suitable for gnuplot's "using" from the plot column
    my @cols; # return cols list
    foreach my $k (@_)
    {
	$k='Time' if($k eq 'Age' && !defined $plt1cols{Age}); # use Time if Age not avail

	push (@cols,

	      # data is directly available
	      defined $plt1cols{$k} ? abs($plt1cols{$k})
	      # log data is wanted, only data is available

	      : $k=~/(log|ln) (.*)/o && defined $plt1cols{$2} ? (defined($plt1cols{$2}) ? '('.(($1 eq 'log') ? 'log10' : $1).'($'.abs($plt1cols{$2}).'))' : undef)
	      # want linear data, not logged
	      : defined $plt1cols{'log '.$k} ? '(10.0**($'.abs($plt1cols{'log '.$k}).'))'
	    # ln of the data is available :
	    # in this case we need to raise 10^the data	      

	      : defined $plt1cols{'ln '.$k} ? '(exp($'.abs($plt1cols{'log '.$k}).'))' 
	      # use undef for failure
	      : undef);

    }	
    return @cols;
}

sub offset_cols
{
    # given an array of columns, offset by +1
    my @x=@_;
    map
    {
	if(is_numeric($_) && ($_ eq int($_)))
	{
	    # integer column : easy
	    $_++;
	}
	else
	{
	    # $1, $2 etc format : need to be more clever
	    s/\$(\d+)/'$'.($1+1)/eg;
	}
    }@x;
    return @x;
}

sub plt1data
{
    my $k=$_[0]; # variable key
    my $array=$_[1]; # array (pointer to)
    
    # use Time if Age is not available
    $k='Time' if(!defined $plt1cols{Age} && $k eq 'Age'); 
    
    # look for the data and return it
    return
	# can extract the data directly
	defined $plt1cols{$k} ? $$array[$plt1cols{$k}]
	# data is logged, we want it raw
	: defined $plt1cols{'log '.$k} ? 10.0**($$array[abs($plt1cols{'log '.$k})]) 
	# data is logged, we want it raw
        : defined $plt1cols{'ln '.$k} ? exp($$array[abs($plt1cols{'ln '.$k})]) 
	# no idea, just return undef
	: undef;
}


sub status_string
{
    # make status string for a star

    my $nstar=$_[0]; # star number
    my $l1=$_[1]; # data array
    my $z=$_[2]; # metallicity
    my $s;
    if($#{$l1}>4)
    {
	no warnings;
	$s=sprintf "\n%s : Star %d : Model %d, t=%s, dt=%s, M=%g, Z=%g, L=%g, Teff=%g R=%g",
	maindir(),
	$nstar,
	plt1data('Model number',$l1),
	dtconvert(plt1data('Age',$l1)*$modinfo{time_conversion_factor}),
	dtconvert(plt1data('Timestep',$l1)*$modinfo{time_conversion_factor}),
	plt1data('Mass',$l1),
	$z*1.0,
	plt1data('Luminosity',$l1),
	plt1data('Teff',$l1),
	plt1data('Radius',$l1);

	if(defined(plt1data('Radius/Roche Radius',$l1)))
	{
	    $s.= sprintf " R/RL=%g",plt1data('Radius/Roche Radius',$l1);
	}	

	use warnings;
    }
    return $s;
}

sub text_combobox
{
    # return a generic text combobox given an array

    # input is a hash (pointer) with:
    #
    # labels : pointer to an array of labels
    # tooltip : the tooltip
    # var : (pointer to) the variable which should toggle 
    # active : which one to set active
    # size : array for sizerequest (x,y)
    my $b=Gtk2::ComboBox->new_text;

    # add labels
    map
    {
	$b->append_text($_);
    }@{$_[0]->{labels}};
    
    # set callback
    if(defined($_[0]->{var}))
    {
	$b->{'associated variable'} = $_[0]->{var};
	$b->signal_connect(changed=>sub{
	    my $w=$_[0];
	    ${$w->{'associated variable'}} = $w->get_active; 
			   });
    }

    # set active
    $b->set_active($_[0]->{active}) if(defined($_[0]->{active}));
    
    # size request
    if(defined($_[0]->{size}))
    {
	$b->set_size_request(@{$_[0]->{size}});
    }

    # add tooltip and return
    my $x= set_tooltip_with_eventbox($b,
				     defined $_[0]->{tooltip} ? $_[0]->{tooltip} : 'tooltip undefined');

    return $x;
}

sub togglebutton
{
    # return a generic togglebutton

    # input is a hash (pointer) with:
    #
    # label : the text label
    # tooltip : the tooltip
    # var : (pointer to) the variable which should toggle 
    # active : whether to start active or not

    my $b=Gtk2::ToggleButton->new_with_label_and_tooltip($_[0]->{label},defined $_[0]->{tooltip} ? $_[0]->{tooltip} : 'tooltip undefined');
    $b->set_active(defined($_[0]->{active}) ? $_[0]->{active}:0);
    if(defined($_[0]->{var}))
    {
	# allocate a pointer to the appropriate variable *in the widget itself*
	$b->{'associated variable'} = $_[0]->{var};
	$b->signal_connect(clicked=>sub{
	    my $w=$_[0];
	    ${$w->{'associated variable'}} = 1 - ${$w->{'associated variable'}}; 
			   });
    }
    else
    {
	print "Warning : toggle button for ",$_[0]->{label}," has no associated variable\n";
    }
    return $b;
}

sub palettes_combobox
{
    # combobox for all the palettes
    my $x=$_[0]->{x} || 200;
    my $y=$_[0]->{y} || 35;
    my $r=text_combobox({labels=>[sort keys(%pm3d_palette)],
			 tooltip=>'Here you can choose a colourscheme for your plot. The colours are indicated from lowest Z value to highest Z value.',
			 active=>$_[0]->{active}//2,
			 size=>[$x,$y],
			 var=>$_[0]->{var}
			}
	);
}

sub convert_palette
{
    my $p=$_[0];
    # convert gnuplot palette from English to foreign :)
    return
	$p eq 'Grey Reverse' ? ' gray negative ' 
	: $p eq 'Grey' ? ' gray '
	: $p=~/^cubehelix/ ? $pm3d_palette{$p} 
    :' rgbformulae '.$pm3d_palette{$p};
}

sub opth
{ 
    # generic slider
    my $scale=Gtk2::HScale->new(Gtk2::Adjustment->new($_[0]->{value} // 0,
						      $_[0]->{min} // 0,
						      $_[0]->{max} // 200,
						      $_[0]->{'step increment'}//10,
						      $_[0]->{'page increment'}//10,
						      $_[0]->{'page size'}//0
				));
    $scale->set_digits(0);
    $scale->set_size_request($_[0]->{xsize}//200,
			     $_[0]->{ysize}//50);
    $scale->{'associated variable'} = $_[0]->{var};
   
    $scale->signal_connect(value_changed=>sub{
	my $w=$_[0];
	delay_updates();
	${$w->{'associated variable'}} = $w->get_value; 
			   });

    my $b=Gtk2::Button->new_with_tooltip('Reset',
					 "Reset $_[0]->{label} to the default ($_[0]->{default})");
    $b->signal_connect(clicked=>sub{ $scale->set_value($_[0]->{default})});

    my $h=Gtk2::HBox->new(FALSE,5);
    $h->pack_start(Gtk2::Label->new($_[0]->{label}),FALSE,FALSE,0);
    $h->pack_start($scale,FALSE,FALSE,0);
    $h->pack_start($b,FALSE,FALSE,0);

    return $h;
}



sub datestamp_prefix
{
    return POSIX::strftime("%d/%m/%Y %H:%M:%S:", localtime);
}


sub apply_key_regexp
{
    # apply the 'key regexp' to the titles in gnuplot
    my $title=$_[0];
    if(defined $gnuplot_options{'key regexp'} && $gnuplot_options{'key regexp'}ne'')
    {
	eval "\$title=~".$gnuplot_options{'key regexp'} 
    }
    return $title;
}


############################################################
# Layers

sub active_wttslayers
{
    my $n=0;
    my @act= grep { $wttslayers_toggles[$n++]==1 } @wttslayers;
    return (@act);
}


sub wttslayer
{
    # change to the working wtts directory

    #print "Change to wtts working directory: $wttslayers[$wttslayernum]\n";

    my $change=0;

    if(defined $_[0] && $_[0] eq 'force')
    {
	# change to new directory : need to erase caches
	# and update some things
	print "Force wttslayer\n";
	toggle_hourglass(1);
	$plt1tail = undef;
	clear_global_caches();
	update_current_status();
	refresh_internals_model_list();
	#update_Internals_tab();
	toggle_hourglass(0);
	$change = 1;
    }
    
    if(-d $wttslayers[$wttslayernum])
    {
	chdir $wttslayers[$wttslayernum]
	    or print STDERR "Failed to change to directory: $wttslayers[$wttslayernum]\n";
	$change=1;
    	
	# try shell?
	#`/bin/cd $wttslayers[$wttslayernum]`;
    }

    if($change)
    {
	load_evcode_defaults();
    }
}

sub maindir
{
    # return the main layer's data directory
    return $wttslayers[$wttslayernum];
}

sub wttslayers_options
{
    # show wtts layers (data directories) interface
    my $redraw;

    if(!defined($wttslayers_window))
    {
	$wttslayers_window=Gtk2::Window->new();
	$wttslayers_window->set_name(''); # empty unless redraw required
	$wttslayers_window->set_title('WTTS Data Layers');
	$wttslayers_window->signal_connect (delete_event =>sub{
	    $wttslayers_window->destroy;
	    $wttslayers_window=undef;
					  });
	$wttslayers_window->signal_connect(key_press_event => \&main::hide_on_escape);
	$wttslayers_window->set_size_request(800,800);
	$redraw=1;
    }
    else
    {
	$redraw = $wttslayers_window->get_name();
	($wttslayers_window->get_children)[0]->destroy if($redraw eq 'redraw');
    }

    if($redraw)
    {
	my $sw=Gtk2::ScrolledWindow->new(undef,undef);
	$sw->set_shadow_type('etched-out');
	$sw->set_policy('automatic','automatic');
	$sw->set_border_width(5);
	$wttslayers_window->add($sw);
	
	my $vouter=Gtk2::VBox->new();
	
	# first : new layer button
	my $newbut=Gtk2::Button->new_with_tooltip('_New',
						  'Add a new layer to the WTTS layer list'
	    );

	$vouter->pack_start($newbut,FALSE,FALSE,0);

	$newbut->signal_connect(clicked=>sub{

	    # add new layer and force redraw
	    push(@wttslayers,'undef');
	    push(@wttslayers_toggles,1);

	    $wttslayers_window->set_name('redraw');
	    wttslayers_options();
				});
	
	# add vbox with contents
	$vouter->pack_start(wttslayers_table(),FALSE,FALSE,0);

	$sw->add_with_viewport($vouter);
    }

    # always show and present
    $wttslayers_window->show_all();
    $wttslayers_window->present();

    $wttslayers_window->set_name(''); 

    # set wttslayer in case it has changed
    wttslayer();
}

sub wttslayers_table
{
  
    # wtts layers (data directories) selector
    my $table=Gtk2::Table->new(8,scalar @wttslayers,FALSE);

    my $n=0;
    foreach my $wttslayer (@wttslayers)
    {
	my $h = Gtk2::HBox->new;
	
	my $entry = Gtk2::Entry->new_with_text_and_tooltip($wttslayer,
							   "Enter a directory name here: this stores the data for this layer.");
	$entry->set_name($n);
	$entry->set_width_chars(60);

	# toggle button
	my $toggle = Gtk2::ToggleButton->new_with_label_and_tooltip('Toggle',
								    'Click to enable/disable this layer.');
	
	print "Set toggle $n : $wttslayers_toggles[$n]\n"if($vb);
	$toggle->set_active($wttslayers_toggles[$n]==0 ? FALSE : TRUE);
	$toggle->set_name($n);
	$toggle->signal_connect(clicked=>sub{
	    # clicked :toggle data layer on/off
	    $wttslayers_toggles[$toggle->get_name()] = 
		1- $wttslayers_toggles[$toggle->get_name()];
	    $wttslayers_toggles[$toggle->get_name()];
				});

	# data information
	my $wc=0; map{$wc+=$_ if (defined $_)} layerdata_plt_wc($wttslayer);
	my $datasize_label = Gtk2::Label->new;
	$datasize_label->set_text($wc);

	Glib::Timeout->add(1000,sub{
	    # update every second while the dialog is displayed
	    my $wc=0; map{$wc+=$_ if (defined $_)}layerdata_plt_wc($wttslayer);
	    $datasize_label->set_text($wc);
	    return defined $wttslayers_window;
			   });

	# todo Lock button, Evolve/Terminate status

	# Up and Down buttons
	my $up=Gtk2::Button->new_with_tooltip('_Up','Move this layer up');

	$up->signal_connect(clicked=>sub{
	    my $n=$toggle->get_name();
	    
	    if($n>0 && scalar @wttslayers > 0)
	    { 
		print "Move layer $n up\n" if($vb);
		
		# swap n-1 and n
		my $x=$wttslayers[$n-1];
		$wttslayers[$n-1]=$wttslayers[$n];
		$wttslayers[$n]=$x;
		
		$x=$wttslayers_toggles[$n-1];
		$wttslayers_toggles[$n-1]=$wttslayers_toggles[$n];
		$wttslayers_toggles[$n]=$x;

		$wttslayers_window->set_name('redraw');
		wttslayers_options();
	    }
			    });

	my $down=Gtk2::Button->new_with_tooltip('_Down','Move this layer down');

	$down->signal_connect(clicked=>sub{
	    my $n=$toggle->get_name();
	    
	    if($n+1 < scalar @wttslayers)
	    { 
		print "Move layer $n down\n" if($vb);
		
		# swap n+1 and n
		my $x=$wttslayers[$n+1];
		$wttslayers[$n+1]=$wttslayers[$n];
		$wttslayers[$n]=$x;
		
		$x=$wttslayers_toggles[$n+1];
		$wttslayers_toggles[$n+1]=$wttslayers_toggles[$n];
		$wttslayers_toggles[$n]=$x;

		$wttslayers_window->set_name('redraw');
		wttslayers_options();
	    }
			    });



	# remove button
	my $remove=Gtk2::Button->new_with_tooltip('_Remove',
						  'Remove this layer from the list');

	$remove->set_name($n); # remember array number for later splice

	$remove->signal_connect(clicked=>sub{
	    # remove from layer list and redraw
	    splice(@wttslayers,$remove->get_name(),1);
	    $wttslayers_window->set_name('redraw');
	    wttslayers_options();
				});

	# main button
	my $main  = Gtk2::Button->new_with_tooltip('Main',
						   'Make this layer the main layer. While some tabs work with all layers (e.g. HRD, rho-T, structure) the others work on one layer (i.e. one data set) at a time (e.g. internals, Kippenhahn).');
	$main->set_name($n);
	$main->signal_connect(clicked=>sub{
	    # set to be the main and redraw
	    $wttslayernum = $main->get_name();
	    $wttslayers_window->set_name('redraw');
	    wttslayers_options();
	    print "Call force\n";
	    wttslayer('force');
			      });
	
	# check if this layer is the main layer
	my $ismain = ($wttslayernum==$n);

	# info label
	my $label = Gtk2::Label->new;
	$label->set_text($ismain ? 'Main' : '    ');
 
	# we have one (or no!) wttslayers : don't allow delete or main
	my $sens=$#wttslayers>0 ? TRUE : FALSE;
	$main->set_sensitive($sens);
	$remove->set_sensitive($sens);

	$main->set_sensitive(FALSE) if($ismain);

	if(!(-d $wttslayers[$n]))
	{
	    $main->set_sensitive(FALSE); 
	    my $colour = Gtk2::Gdk::Color->new ( 255*255,0,0);
	    $entry->modify_text ( 'GTK_STATE_NORMAL', $colour);
	}

	# value changed callback
	$entry->signal_connect(changed=>sub{
	    my $w=$_[0];
	    my $n=$entry->get_name();
	    
	    $wttslayers[$n]=$w->get_text;

	    my $colour;
	    if(-d $wttslayers[$n])
	    {
		
		if($n!=$wttslayernum)
		{
		    # is a directory, could be set to main
		    $main->set_sensitive(TRUE) 
		}
		else
		{
		    # is the main directory and is ok : allow evolution
		    $evolve_button->set_sensitive(TRUE);
		}
		$colour = Gtk2::Gdk::Color->new ( 0,0,0);
	    }
	    else
	    {
		# is not a directory, cannot be main, should be red
		$main->set_sensitive(FALSE);
		$colour = Gtk2::Gdk::Color->new ( 255*255,0,0);
		# if main, disallow evolution
		$evolve_button->set_sensitive(FALSE) if($n==$wttslayernum);
	    }
	    $entry->modify_text ( 'GTK_STATE_NORMAL', $colour);
	    wttslayer();
			       });
	
	# pack
	my $c=0;
	$table->attach($entry,$c++,$c,$n,$n+1,'shrink','shrink',0,0);
	foreach my $w ($main, $toggle, $label, $datasize_label,$up,$down,$remove)
	{
	    $table->attach($w,$c++,$c,$n,$n+1,'shrink','shrink',2,0);
	}

	$n++;
    }

    return $table;
}

sub starswitch
{
    my %opts=('Star 1'=>1,
	      'Star 2'=>2,
	      'Stars 1 and 2'=>3);
    return $opts{$_[0]};
}

sub redraw_tab
{
    # redraw tab given by string in $_[0] 
    my $ntab = $tab_numbers{$_[0]};

    # destroy all the frame's children
    map
    {
	$_->destroy;
    }$frames[$ntab]->get_children;

    # set toggle
    $made_tab[$ntab]=0;

    # redraw tab
    eval 'show_'.$_[0].'_tab();';
}

sub wtts_icon
{
    return '9805e474d0a0a1a0000000d09484442500000008000000a5806000000007b9e528000000103725742400eaecc19e0000006026b4744400ff00ff00ff0adb7a3900000090078495370000b0310000b0311000a9c981000000704794d45470ed10d0b09023e3aa32ab000002009444144587addedb97495675d7fdbf9d3c99ee8557b6ceaeaae18e14da6d886e69009101209521807019783068567076526fb83f2fe93f0b31b3f8875c21e12eca7e74c946db06e9121360c220230666184029090435ba534b4ae65775f45d53ed1fc0b787f7cd25babb5d29a525b4809ae7bafcaaeb7ec9b7eee5bfbfdfd0bf7bf7b7f163c68d05ea65baf9e02802b03c07eae5faffbd77dbbed552e8293beca3b8519f9f29b1f6cb19279bc7cc9b1f1f1fb2b45a2ddda5b6f1541d30363636bd8a65baf960cf1d74c17cf3e1f1f1fd67cd7975a75f7e4463f6edc7cc7ed2b56bc055ba5df339256ebc81b30c70140d54b6d9d150ebb7ae2a25aad5fa7df36be6ddab1e3af1676e9976ea4f0fba85be3942932f973d6ad4a786868ede1541df4f83c8f8efb7cc7836e621248f4294eb930303079d977ed97a75ca1cba75e4b3de4b3edcdf455a259f8b25a2d3e59bf8b3e9172e916d0280289fa65baff27272727275c21cba353ff929b8f2eb83aa5da6fec83e8f611248f76150f39220c98ca1c79ba5daebddb7f2006c6c6c65a7ff554efe7262626369bc5efd7da57be974bef50110e81605014070bc5e2ffec68d0b16dca8538f9d9cd073cd074effedb715da65fab82a8eb5729a5ff289007cc345a49e3a82ab9be5faf53b2171cfc65be7dfaf5fb929429f486da7ee44a0bf2a8005eaf91e747dcf30f02f93da186da7f69a45af3c33fcc337265ca1cb23ebeb45a7a35435fa7df301681e7fe9f4bebad8b0c736cfd1e5aa2d9c1106875f5bef2dbe6f8fdc71c72d745a9c7e4609012c60140fda643a1fee7aa16b21b1cbcba5fb56bc69579429c72592578e9b5be58f55bef32ef29b6e11fb1fd8f7ae5705f39320c0daeb6cfb5f306cfb5f306df50fecbbcf8c4e57fc9690693f60bfac5e2ff9c9f7ef9f34b26d0e5ebc8e53a1d8f781401cde2480bfc50e7c5a53ef4fb4e3defdcfeb50f75e78dbfd47eff74fa475f9183a20effa3ef74b45bd4c8e4dbf09a202a46286caa7719497d1b878f65cbbce9abd4c35138002cb7f5536cc5878f0f1e7bc8c8c8ce9e47a3f4cccccc03a3a3a47a3d95142f548c6ad4b98fcf7ef3f41541c9d76f9d3ade67bff4adde6ff6b5b673f27ffbf3e0b8eb50e5bff09d279cf73607ceda8a0a48010b833770b033fd3cbbcf89fd09fc9d285e7c701e00ba63cfa171cb3fe4607cfa83efea7aa12edb7f14154179dcecece7eb65badf1e6bd6bda4333333b26d0e54448f94290b377ee400647474faa78e1a78eb9d9e47e3aec9b1a3a51fe860f57030c91bde33c69b8f3549b6b501241ed9f7ecfd29723d02fed3256049bab5837e2ffa2b9f2cf4224ae3e940748196dad1c663b9f1bf9ef4f7afdb9a9a9a3f006bf6fdeb21b1c94afaffd9fd9fd1a7dbe17ee9b7e65a65badf9dcececed45415c9308a3acc7f13a5f3c3175371d5df003abee3082d5a328b9012e5a310480187fe14084512ba73ef35e29baf16ae3279d3ba530044695671defdfbff6f643a1f1bd4b9639e3478e0da02cfc32f8e3af8200ba75faeb8e1a78e1ab3a9dc6efb07ec5cfc6a5f2552c6db4e3dc9579fdc449caeeb6db7f7407b7e31d72bd0b3ef169457307ed51f306fe3cff187cfedf7d87f93720f74eda5b1ecfcfcfd096a9e535e279f60b6d66b203f37b2560140f6a3070e0cfe937e83e4bdf8101a13e750b5e29f411796ae81d6f3910ef50310e446114809ca9daff70d855752f0ff8f7d96eced9722b8000ebbddee560cfdda0ebfcf296a927c96cf132a281063c93f70caed4fb1f2a04f71ce20e93dd2878ae0e9dc97f6fb585776efe1a381c31edeb20beb8be6e36bfc53d81f7b371c597b135b5e73f96c0e5a89b9794dab566d937e19a2663e9bffe83bfd27313032f6648c0750eb358c388295dcb1ecef30ecebcfba4560fc0cbb39b02fe56520c1f6d0c9386c4cf2076f5ef5165f6cf574aa2da0677a04a63471076eb9fb0caf3bf3684511ece9c7097fa8010e8e6c853e9039106273ff625aae6e514fe5a6fbac08199abe91e5dbd076f4d86dfb4e020f9fda1085bd3c36f4cc8e426ef9c5df65cb3788043a4a4cb00c83c6fab847e072409b7b4a6b040209844800f250e105a42ed90cb7184521b2c1ecb10c3a486cb3585f20f8448e0902c00395f0f040a340fe136c2e0180fe0b6d0ecb3480183b6883a0136cc27426e1980c3e076b00c16487a797a469668fca0c8338a04363bc80d387b6172415813416d02c3847e096c02da3497941252e0883989e5ad5e0e25ac38dccf916efdbbc1769356a5ff2b210e464ae5da0f1ffdaf5349dc805b080340920c00b0d28c330768ca7d6a96354020de51a492496d81af4c1984cb3052519af203ad29bde22db00f128f01fe3047c554ae8822be20e1da34c95b8dcb04a4583769c3f419a422ac906d4e8aca0c415c27b8d22583c393155b28c4216fdea7089f5850abd438fe6641e2746ca904708f6779c716e983a485a65b8e56922cb4205a0195a8cc2724b29430d4ad9494a4524caa57c7feee72870b933fa8261f3312085fe97271b54544ec0b2940987cb6d837a1627d2a2bc16fee12fcc28503ed265b02856b202582c6ee112a8e7ce16502c93a05e9cd83a0f5069631228046048eb9473758395248014aea50ed1acb341e9cc283da0d587c6727ca407dae206176d0a4a307d32a258ccd0f58433e1529d5f819f2ee7497d85928856881760f234ca4b809217e50acb226f106d9747213ecb7c4660121c941441528d43526ba058a75c1b8f990104001b941be232f03682c1a55887da007dfc7270139c8d404ad1c63b54522d85921f874ba0afe230f85b61b6c210292b17867129b4547d6e82da8836d0471525025a349414e6a04b2d80fe04a42c04362450971449778699c0d182c9dc076ab49a2d0a45aa3173651fd5d7e07ffca6e7b73d544a70f02dce47072d3160a90db44329ea138a4212f4d29441102922bc1396a88e6686b20fa521415017c116b018b7c6a942f2e5724009209949139708f27919179145080b9126e6611583c96905d211c0698a6490f7cf99fb5ef5eb7ed10250205831ebf481961f4684f28037984d212fc8054284910168906a7b086c0245185791186783fe0414814c87721fad1220140e254ab6220989cb74014c4de71e758bbe0b466335cdcb7423dda28d8b16b17af5576039dbd343f70f5026793dbc2703e01e1091094931ab3fb480f2802a8967821115c40d811a4f685f4bfeeb71f78f00aa8288d8526fee59f4b3efc629d9a80ac318e2f4042d044456c9702710428f0b6888ac847290da267cccc3f3e63c76e6bf90a2e811ed12d7fb3357012140a1118281b1645da24930d4d3c09a40a80d4142ea44d2860115320681145805599054c88c5222378c65648d13489a3aa54040d1148d114cd50d3df835e1f5bcd5dc2121f482d3f7eb340f5cee648ae8334a36294d05964d82cb74a8098388803e88024d869280cb02c5ae118318bc35a7b377215b6198a8e1120e333803fa31208040940111a4940e4341e1958549058220461e1f993c67d02d74cf78b9eb5cf7cf4ef72e6930d480240d1080984be11ec3acb424fa09583485f481790515c4414c8270a5b64b054c6d3a900d14449c7045d69883754c64355b29256e94c684c9a32c2c11b5549da22878f7cc9e3d7b379c45585357dc875437dd9e53e75ded148fdcf436b9b80a342c9be214dba45ed4763e174096f885048214ab280da1477ad456fd1229d5b71ddc632582217e9a8588c9f7d78b005240301b2251a1dec3857805124ca0c18ce5180f024b2ee8b777076eb9ed6c4cae90e32ffce738f4ed4f7dcf6c5f7d1eb8414ad5e469afe30f9560a41e5970120fec24160925387f8882181a172a6fc35a583c8fcc02d974a45df20a241ed6e49450402d158188574880212a6c003ca50b1782420568da3a133a7fc3a883c33409488b053544105d3cd4cdce22a71b7863680c61837e9e516943fc0554148652245089c1c5c10ea4105477107fac7c08f33a8104005923158124191ec83cb658c042813835a1ca544812ff5fb5fd3ef4ffbdf5200bde2dbc8fbefbcf419bf080ac13ed76841a1feab879607ec6f7ea45a24206d51849a11253ed94a0f1194d6401951877b88f0affb3eda34a4b0ed2228080cb646a9114c52c73601d15d015960766229bc386170a128d55e60260649a6256cadc94b7932add220368664121e4da12838701fdbe06c7e44a5251406da2c857425a015de49c71a58a8b48bc234516e5f14004a28a6503dc63a47884d21794087902a3924f2742e9f6fdaf34edcfb875359a651124214012f19f72ff49f4e71e37cff6fb6f14c5a21f40880328cb70dbe12d1081c99c1482448112cb70b50aabb48cd97f02c5f7816e5580ad3227380c38030a42ed340243830359a34e61d7fdd6c846d52b72670d81c1562ecff50a54317a124b68cb0748a362de5b803f61ed5e416a082c2eb0b41dbc0a0f48a41585346d9621b3b49d942f5c974b1b8f2a87d74c00edb70da055b21260a2094c871a0f14889a498cb651c4db6c71fb6fd6ead985d40da61837845a833fcd732b7a03ccd2d2099a003d812c59b28827d055b1084a2871217a40ebed0c7413e49201e8041827e0929e75cbb9c1d54182c1ed9388b205ea0e392363981f59805dde2e3fe1ceb7c1e9cdd4016d9863d42dd9b7007bb7b7f38cb034a29428dcd018094db25564e75f7f9ae9b71222be2a37b8e2c29821081c847882c2d35e8222d8e83205014c1dbe4c200202798b433a5991273603ed178ab37c34bbe1467d36f82f8ff46b3fead3b44deb4f568df700f71d5888d4b98252907df70f34cf3bf4cd0cb1f4bdcc6a5f35c96be7533c944884bb4061aa9420224cb418a03eef0bc24f8bc27486d87d024a14a1b06d36b8207529cc70027c62825ee983d41c4f0fea0c5650aa0187520981ce32b7827a926bbd3c83b8c2c0a686292dadd04777d368eb6ef13fbab37d3f58b0fd443fbbf51a0912620980f215a1461df5fa9e390015e21ef0f2214112eab9dbe4800a434232c2ecd22f9cf4d768f1dddb78d578a06fdaed5c8eb77f2f1977d2173c5c281fb9248bb6106fce64c32f81e7bd588a3ffc7e2db46b179e75eb6ceddb3dcdb7fed3c79fe9dec1e303d8d6d2252791fb5fe9b281baf002da2ca21df810493cb638050ecb00514f38f34045216e2fc9f7773ddcc0d50710f6973c96c684ac45c81acea0a415c0ec04212aabd586d224b7119dbe21a8019e539f70ff50f89e1746fabc7977b5eecd337cedd4f60a239b11a5d6c5b711bde61d9ca70dde22c8046b25a8d7b88b2a0604708fcdebe38006d97677f42334d8ae8fa726d36c52b1fd1ce79b7938337b1f9b6dd6c3176de4edf764b9b4eb737c0d71fe08b57f02e1fd98f78772c6bb81176e656a62651353539c5b7de5b00cd27bcd2cd6f77f7388a4288a428be6ad77d294408509219087321794487920c812f830ebf7bedbccfea91c73e00fdc9d615c25b95b31ba819a499573c57a7ddc98225cb12ca031ea08ea149258740e5972eb3ebecce68b69f2de18dbb450395b08f3872748f0c74ff28d2bdfb646f9fff441250a57c87c0ea7d544ee1d6c1e534654e416f5f2400369ddf8ce6a581e307df53406efa9d9f4cd1ca3dfb0c3a7bf81bbfeb3d9bded27b1f0cbdee0eaaba6a9b6fcf4937ebcb784eb53788fade9e390e2bb8afe6163e648a5d8f1cdaf30e74f5bf21fb67dcbd855ba6892a5d6486588a4d0ae52cfeb308cca0c569d2fcee96496d0949838bf668b7fe97c9ddb0d167f32bb8a593124129def4e160d11f4eefdfc1e9f93ac5e2175f547e0ffcfcfd0c955aa06b86224a00fa13f3cfb7e2fb8fa2674b264b0f86168746e27e627e9fda1890ae94d95a5c621e06d179a5a3b8d2c83181c3ed854870f257f98afa5720402d171e06643d143269cf2ff2d51c7aefaf660d7f175d6dd6c307765978eba35cebbf7b2f9b7bfdacf8f2bb79b6f3a78877e56d6e4bf2d3488f1f148fb9d588d1f82cb97b177a48bdeebbb97ffb3e2365d4e40ecb14401122e007d814442118e91cbd014962fcd1bf7eadcc5bb5c26ba5cdef3ab79f2d7ff7cc16fa9196a36e441ce9303d4259a195654f3640b686269fbc7877015fea4ef0fda713533e0b546884d12fc8732b8ab2c681d0903a207ebbc707397eaf7cff703bf5bb99eceddd4d7870826a76847e7e1904114886112eb7d444150acbe75b85001bc385f79f4ac5294086c7fadc0d0d03fbcfe9bc9fce79eb9423457ed3f1a3f95b774ccdff3d60f8f767303df39bb8ffcfeaf1277c97f017def6f2075cf07e819fbb63cd3bf9b777cd7c7b73fa7eabf74fe563eab5b8b4b30a0444e01637cf2e20eb31081b83f29c249cf067ee4e6ef1d32c1a9e304178e0237786e8c27f4214ac56539c303fd56ad346da5227fe05212a02023b690d94eb77fd32c7573f42fef1d16c76dbe72b963e9d49a8e1bd61e12ec98ac456dd64ecc07e9ec73ef7215fa02a021c5ba7ccb4b42253e094a5ba222a2127f77059b146ef236e104c135d4efcd4001241a474801c1924483595982e19474e019b31024197603a357627dcbdd3f5ef6d778577dc81feef74b5857b936eedb3acce874649d5f8f61eeab8e6767ebbe67e6f7bd3ecf2d9dd364e3b3dc7bf72b68bae7df75833eced28f9f544a70b6ad80d21118045b6111241e494c9b00be25666fee4666ff3e892c31b849dc5f88d23ed0e2d18209976cfddec50c2018014024056528124140283c6c2b377de3eb1b3e90e2da49953a40e2258dde772d874fe3cfefdd0d810ae79ffa0eb4348a5bb6053d378c9f639bde198498da691387a1ea0467b587529c20540878ec9a1710f41e52428a004ac0f9e5846fc74086eced9c3a77df61d855fe4606ced64815c877207ee4d09127fa8d9e6c942e28836585769cb5e806aed6cc6af31a9da50e2ab421a6d09c4d6848537752cdf775d878f7031cedbf7b2f5fb2e6367c597723ef6997ecf2d63ecb37f20cd2dc7ff1e1a044051c7ed3c7e502498eb14b8532d7044dc52e2d1ea25e73e83bf61e031ec50caf2aa442d13a5ba1fd8b3e985e06d4163bc9858462494ea4d291c18a0737290b37eabc34b367370fc85fcad1a23e5874ccfe7c56d3c772503d664e69fc0e55992abe147288d8b9153f700fb8f406aa56c6fad40dae218314aec15ea49803530b09fb470d5082dd7cba5b0bd95838fd1e0deeff185bd9393204e9e146af1df4233b7f622a2d425f1ab891a98f54a1baea449607e4cb4aaa45f24096506270535650ed926e201ced971411d00a3ce75417d60206bc9da91b5d13b66352c4dd990bdfeb10e1dfb95bcedb76b2f8d4c77968c499ddf92177252925fdcc909e02058f053485182a044e6192406add3643e08d453542616a64731baba3686750f8d38b39d3d920dec16da54828a2293b2c88d23a132b1137659be46a41db6739c4940d136ac10278ba8a9b9548f622a2906796e1a33dd703687486ebbe384e96ac8cd4ff51159235e7be574af5cffe407fdf89cc5a0e0a46ddb870e5a049ae38fde58d9c1ed7733b078eb74fa9b39ca7bf0b6adf2c2804fed29577a9cab3d4b6ef7ac1a72ff6803e15607cfd6c8c4e57c0e8f5e84920f61cbf716460fe50a414322bdf1691219e42d1f02428e5ecf4a77e1d180687434e2e1a6035be366dd6a48b7fea21b3e1e16e18f15cc6f3b769bde06f8d82d1346a0ac2b0201e0d2d2ac5fbd32dbfef4c09b204041164caa1f5d43ae5326f10e448cec72f02bdb3b8cca6184706d934b61839e92b07455b742d36b6b1e0aa01bba74051690a6324883ca7f8f191540134c10e642289f622c4a8c0216ea434cfd7c39c6cf0db18bbe051423ebe9f0e4dd94d6e6f31a0788e4f8a5178185a714ade255fbeb2abb4b793cb7f62667fd759e63f15a826e1b54be4dc820cbbc9c3d384e9e14a3dc78930f4cf5116c32c0faeb691d537d3303a7110bce22e426643b07e9d5330361a764ae10d6b69d578d3f0d3193a20d49bc90ed383fe15a42a59066666f3b173d403a3e90b67362cafbbb24de693d97c3db78bf6a7f1dba6cca2d034a24a6e5256ab829d546e378cc274a734b2b49995c6214960039f219b1b8e2c3a2fc9db6256a19c39ddc841fc865be4daa526676e02f0ab2853651b1ba135bc25aad134d686281d1b604dac87f022c01f6d32ba3683961fddc113c138778d82247926e0ec83e693c56e7c70d0f985d3fc8d963e4e30bd09d8144773728b37b3c6ff2e9c9f57528e5b6683cb7f62666fc718ec2d67ca9e06d47e45c820e9b9c062f97c4e3fced6d31cebd9f71449c403ab6ea7647de708a63ec44a4012e9d7a91485fccc108fae2045ac506a0f47ad299eddb78a4805ba7d14048186eefbff10e68f4c728db787191f18a0f1bf8dfb2e28b0ec3627d499181e5fc71ebbf03bb6fc318b9ad0ce5b9338250ba56d8579802d6f431bfe02da5a5db41fa3161a7e86ecdc158be2eb8492b41486d3d5fe82b034969bef7584fc1c99956cac111e06586bc285c67ecde70376b7b0edfbda6c0daa5505ba3aca5c7816f7e024a284a48c4b78803446c55a35fd0cec5881763def22a1fe803840606c01e370750543ac891bad3b7505c2bf5fcb7b8cce4f79938bbf3b437eeeee75f5bbcf4e43fe457e10cbf20b6a0abdad9cee70f60e9cd1f7449a6b9955f68f8094563c3be7fc93bc1ed73f41252a2980585a8cbdcedd57f07ee8b064646497c67e36cf1ed0732f4c4f14e2cb4f276df93562040f1ff7ff7c43aa11fbbfbbfbbcb5fac5fce89ea177c39f81ba706017eb023b6954dd1e9c67780854aa2c06a7a8e41a8cc5b8e6ed125368e5ae867b37227d3a2826ebb50bf7e6e0c936460bac1e6671ca5c4ee80ac119c0558c8e1f8f2479985332c9b3c01477103a4022dc0fec1e34984d90eb311c0628965a8e1c5f4a94301b4925d0415a0c05d01ef7fbda3c8c51f63e25ff55cfcee34f25b05333f72c2e91b669899d3fd06af19f376166e70bcefcf4d41f8c909005eaf65aad83f99b30fd0a8476836733a876f1cd97f68ec2de0e1bb7ef5d377620db5a9dad7096a831df21d6514b99f30f00b81799fdbfdff7bcdaf3d7095b76f54cab7bc99cefc332e4bd4d4a4eca3ff24adda52e3afb1ffb271c957623cba651a61e025bee20342160d3843d3613d38452ee0b4ddc9e5b519adc01aab867607583a8e67076a54fc916af0e22b478660dd281ca459707ea987be82b2184f05bca348e53998c8199a23d98c3d54397ba949c9701e92483c5ea84587a3b0b843ad12194f89b7f005bac0179f8f6081f3d89b430e30b5226c31943265895bba012ad40786e05ede9049523e9cd1f74c39b3ef8e5c5de59c081c1d7b0809c2eccd683bdd7e7204259d0c6dd6f7587f699b30f7fc1cd5f9161e0d7f0b97317e27afb52dcb8717ec1b8de91a8e5f80284012d0541374e2698f2cfdfdac4d6db09d0f6e773ed9561bd918274662705a036a0aca5175cb9f276166769fad75e666df967a14f6e9503e845806029884b5d62d58c9cc87cb3f416c0a1b850c0e9e5e506a014bb391f0deadf8d543ccd29167f2e21d47e9801452c090030a2327af5be751191b674b481b91f0f31f813c5ea1949b449be55692fc9935b04182c9c2398cc0d5a80d3d3f45a3b8c8cad12ed3b96c9b87ef788a58308f9f52c57bd888ac8d824c52f2d7a3858c019a26a45bdcc8fadf56647dcbf9ac0ea667d30fff0b43b77c3323dcaf4cabce076354ae89195d75336b6ea62fc25667fddcccc39f93a93b7726c4b0b54bf54291c1d5fb48f0c06c7141dbe1999625e98338d43e760541d169c46821e1ded2ad8740e94d4a40a4096161395e49b132c5d7dc5780392e6ef01a487ca41c24bd395ed22de4707653148204e21a581a84d3db227a03919e26b97ce1d729d53db047a3939a3f4bc5fbc42a6158863505e44162f08dbd4808280a08101a13232eed3cd2a8994905744ca6e825506dc0444da4103f15b8c5690f54e4080cc4fe16c746f0ba57b86eebde4cdd61161e0c410dde0a5a44eb8c29039a2149ea01797d03ab6ef103a35fe3a238b907e0cbbe723fcb33724b2eaf97caa3e10358749a891f577d331b1ea72be6393c3df56667afbc4b71ee1a8c71039f2cb008fd2b49f4354a91eda0ca1f84920468857a17ea28536192abdf1e79be4f377eed2d53e82c972611296fa7472dc8a86986f6943fe2948e1c5e4195687c099c4966bb0c89c1b54e47bd93b8b4d6c4692dd5852e0e1a9769a393d5be92c93c3e1580a0391e5181f295c41a52c056c03a3857c45bec4f2247ad28e56e0491c3680349cef1a49a55d18c08a736c8191c102375096735e0fd2f53ab8468ae10be1749a841795788479904b6a79a97100a79802a601596291a98ba8199cbe8181e3b17e1c9be366dfcaea879e140e35f01fe0c85f8e0a6caed8f126273f788e5be033bbfe666ef0c739ec2d672bed10c4e3ffcb368d6d2d22976e1b844909402bf55b3581787fa01242a4b3a7951851e80c8120c22cb7c9778a050ac85a0d29280014f78d8db4b02f22769a5d34781254ed3a9b0b08b2428586d25396dcfc3d96e22b0b4d2a59764a80c839df1aca4f4d484a62d3301a2ca0943d816ca41376d8e02592584a9a3612f417428536924181728cd40c362c0ab88804499a46e8553b3b459633a3452c9963db6b3677faac3c41c08481d1a16499201dc50e1f6d2e3f08e13162314c5e53c0e8e5ec0d4c5dc0e8e580bc92b736e8ec60a8797641074fa206124016232c4e6ef5562f4bf55abb4b789b30fd069e0fda4b6efe1abdad974f6b2774d052d396e2943defacc1dabf5891e9c9269b2575bec922b2afb0832a007a51ed184b508374b3f49054048255fd5484f8410dde5e41616ebd5099dc139531bdbe14661f581cbde25e4c3da9de2ec93a85e007329207ed393052fe913da81a034a96d054213d8a7d818a5917227c44105d1e55cce295a5dee1e4a442203ec11aaa096bd32d6e425a1466b9d169ca3613d613c5f0918a8023bd32de8561b0142558a6da6c0105f039316f9789ca2aa387e15b1ab086c8fb381819bc09abfa9e7c7901cb0dccceb8e160e19f12ff439129aca16dc69f82e7bc7493cb7ebec3a77ff62db6f3172a1f206d61d7fb4e536ca6486587f928f622cb5c5e2012d36d341504e65025b0cb01851e967ad34a1d4021a802025f615623c6a9296168e99495ce670196f0de27a86f211941492e001e34492d8e0c3ec81cb718690a1949d3c858a8175a8292149a213dba5485769878688cd81a15e15c756083d7be96c1c3cb8ced707706b337853d1a65b17ad96640a262f9b95ac94582c798582c1d99bc80d7f885b1aea0a4252e7e698667a681f93e5c90a824582647aed7c6eb0fffee7a1ede2b52e2e4dce1107a4620af88b80fdf68945288a43582d5d7690d240201494708534f35ac6a74e9f22a566dfc9631058107832f2a78210e590c70a80580253a84b0744de9d0efc0000b01494441445ad362b2803fc80d41807509ad282c89efcc0615095ad5a8e5a829285c67ac1a586295fa0273688484ca5744876049668494642074d514898444322460e90aa55a59514e58422150de6d3943627291f2b6a72a0ad67117227a89996ae5a162a0aa484846d1b481087bd5273d62456825c0351408f47e9383fd369c7985c6678bfe1f3472c999d829904b6822f7a14f355333bf2bcef00d72381cf3bc80148454cd8538be568b2c0180f46dd93cabe70386d5fdc9716d16c5f3d1ca4b2a3581ca0182121f2322dcb741635c8fc80d8328cbfbad7d978e5f2de78b96a02be6689ea5cb4d4f2b4967f234a3f454a2aca30478e90cb1a420aea12901c54201138e0d3982b45bca1bd962dd69769c5b1280974e69fa323c01c4644ab74a6c11ad88a9a23a2910ac353ac0fad92ab78e00f4edbf32696ef942dcad4ad9c8c37f4bb792dce564337f4b3d289f7692a05fed6f3ddcf234d86cfc4a228c47a5c3a73df5080d8c8b4489491fa5322c888ba10159222825c0a53ac8def2fc696904608240182902a02fe478e5fab41ed15a020c9058075097f232fe5e84c2eb0741587cbbe713aa21125d030225e9aa094d584249e9cb0347337c2a5f8b04242219ed263b288a29052f50cc6b9c6f7b8e980079826874a6485d2358e890ac90985548aaa85903d33b789b9bdd8a74ee767d3cf013fbf7a9e57b54f2f28e66506d1faa09f99010c99c99f78fee326d0482d0a43257884da09a522578405e102cae0217dae485d48af0459a492126150e547883a207220c4a609ed5f716788efc82a70a3d9e214650ea0384fe1d2d15aa684f4a24b2f452c09278abf5aed9388695c81b4b23b83f6075658bc2568b69054fa789996127927ac5f80d1584520f4922fccb589e697666ea9ce934bb865f23ab9714fa8c136c05c9cc28f8808210b0fa3020c1b3220bbcb68f23c17cfc46de3486d845a19a41a4b22c824aa5fa25f1ca25b1ca15f1c12a1ba700b54e46d962dd6b1cadef65f217c90e40803936db1a597711991209292ca141bc3dfc1608f4202299195b2527b21ca5491f45ae1249b6401b6ae309130a53d252830d9c870b5d56ab3788996b12d2c19b1b46fc7b6cf7004ca718892188941035026c502a42077793cef9cbe0a8241502fdb219b552e7fb7cfc878380ef0a7cfe3f745ab2d3ed4f72c9c20500920da9d95e0a29849df8d0a45da036363ec0f845827da282f93dbcb985be8cc614e2c25927c41dee021544140825135a2b64b4bdfdb6d37347a3d3492052af6f9d94141e51dc4356a6c6480a738c7ff1bd5cd3d33cc5e613ed36c9f76f95291001b55026b804c96201b6502a22afdbea180aff75821dfa36c724cbe3c500204e6498cb642e35ffc8739d9f6a78960fbbd7ff8d5e1ff89786d3d9842b6c261b4197f3bc6b0787e1920482945b213385f213c5fa1d8274c2af28e6b33ac16c48184a039124a0c81f40a4025a2cbe882d04483192a4a120fa0f6d2998893078d2f3a74672b7f05b82c8b76f2cde15097eb44cb1502fc1903c4f71fd9a5e346f767fe449175e42b692e5b3e2044fb35440728e092103e02ecf7a37d08fe71bc8f78d3ee1278f70d3ef12f74a086cbb5606ca3616ebdc2cc7b97289129820a69448c2a04729065245ae51caf28cd4f058041a0d15c4015441e436da16e7ea9ceb5ce033dd522b76bd8b1438c37422ed22097994813e8c65ec248831eef8a7aa7b97bf9bf810e9339c42e83d29120179904eb946ffa930fbfc3ee7ae0f777fff23f7411608e5650dbcafe797ea7933852dc0b4b49298afb44d28007637ca820925297a0f0f4f22bf6eb57262d6202ec518aba412e21188ab87ad271e11e5c37e6c8bfa620ff55b408765b817c99c4f2c326250a6d828f5a5eb4b3d3ee6778bb5d1e77a78c793870f09b3fc1a671c1a671452ae139d8a125444c1598e6ea8747fec270f870e55f32471be402fdd219f63988111df7ea30f6af96daeff31f377bb22b6e7e55e9ae07f0bc39cfd724cb140a63924df365df0a2f65f8bb5c1ee1470d3af700decc0f0f1897a2b0d249485ae61fcc1695d04eb9422fa398c3756f98460c7fcfb4ed5ab75800f2711269fd599e37f88a904c57209fe390700cd7bd16fbf61fbbc3cce23112d3e08f230360aed9a09fe59881310918fec15a9e2e5b3b9f9fb67800722f5be18c799c038209f1aeb63bdff7d26fff3b7c64dd9c2b99f77a44db7514d69fbbd1f7a437feb240875c33d4bc4609df1148dfa85cdfdab36ed7b5263a04e51bc0cf80d7d8f4797aca4cb6bf5570af5be0eb71e11a40ee6578dffe61ff393a4de788320cb8dcdfdba8d7adf84ef5be0cfb6e900f4d27921e50ee37e03f953074de3cc2e2418abe512e23188879dfcf35364f7d1dbfd44fb661c7d20f997cdf7678df2a587a66a53409ffb844d57a01b650410e3b7d6bf9f7d5110e808fd5f8df31b8bfa9b7ab0797814df3758c7b84445af4097d38f9f7dd010e810fffdb5cd77e960f5ca718ef73a11b579302ccdfbea18f7de110c7f765dc71e1b73e170ef9520df17d885ddfc3187bb20ef3522f5b20eb71e1418df3b3e0cf3750aff84326244ffd3abe9510ff5b6400f2c322118dfbc56f9fbc9cd31b550af31a1130bcf2ab4f265ed068faa120c924024442af96edfb5b0bcb6f15c4904ff1b64455c94dc2dda4c00fa4b9ce96f8fdee1f58f79e41e54af9add38bc7eac0af64d881c5e90b1f745d51cba8dd8dfc5310e8f24701fc5ae661cef595cef5dbc35ff6ae38aaf98d974dfbab14f3e88ce7f9f392b897a2b8a44f9a20df9e5f3860b6232b4f00ef764f2a07bff5c26ffb85e5312ed154eb04edbe28018259c130efb45238e7920cf359635fec154e3f419e16a5b0f35e0fedf2237bffb183bdb5172bf29d2ba16fe3cf7accf1cf6236b6edfc0e8f514c5a92478d04509c1995fc71b440df264bd5aaef631797a788cabbf8ec2d38ccdeffa23770eb189c717505b7914a3b8f00f4c2e3007fffe15b1ab481e5d7d05f1d7311796a8829904501f2311e58191e4e8002afbe7f002bee12a7de728ec2d34c2c1afe2b078ebb4edb30b280db282dc9bb386ecdd100c0c8e5ec0e8fbd9ad0d5442597309497d3a20dbcbf83e2fbc93e932e1b7e8db285d19d1b22beec1d95a7096f2e30c2dcedec2d1ebd84b3f4e9c6c6bb238fa3533648316f3e2ecc7f95c99fe32464c0c8c564d74e23aad83f927d7b25aae624502697b906172fb55ca012135c22dc9dd1c2dcdd54b6ef724b6efe6a7da7ce58636586dada492fe3925edda5be5851c2f9f5c937185b67ba37ee2036cc0b7fe5c3f191cbbce8854651459a53e24aad8b08a63ec7607ceda7407d7630ce8932e2da1f32397d9f25de43f72abe7283cd1df95925e3a82abfa45a2df975a259f591e1e1e3fe6cb1f6c8750ad39795fbe7dfafa5da67d49a45af7317c1f7d5b6ddc7ebe7f31d114834eb13e75afad0d58e58c0f917df466ff7b3923b425a42c03c785925fd45a4df05925f8c977ed97f8f77eb3fd96fa0c9f2d68ad393939beb9dc6e60fedf9f6dad7b71541f6536cc9abc1ecb0c3e80be4801e3e83ed559a45efcb1d86cbbb65bad6933ec833e91bdd5e5471d54b284eb049f0d78e34fc837f1ef0f7838191919535fa7df2da7dbefbd5a259e615a4db06ba07cd1725f5a36ea7c17c70aa5daef5c8d8d8d555e2797dda953b6ae7c736f2eb8f2e5141f419c74e32f11e40dfb1d0e0e0e4c0d0d0db1707070fffc259a47bfb82238492f9de6c36ec541543f5da65f35393939feca65baba6c6c6ca270e0c101b20afb2b2f1ef0f78f917e6bc69d21d0c0c0c0daa55baec81a1a1af7d9429cdbbc8be3f39129429bff8ab1e89b928028675ba5dfcd4e4e4ef2eae5dba786070703a77fbbfddb20afba89c03535351c8d8d8597dead5b7643a1d8f87c17cf8a012e44468e36db66dca9b8280286d35715b6ddea65baff3726262edbbd6bd6ba302e4f3df4f59edef9339bcf2fbc1925a8f8e74f3aa63e6cd87ee0e0e0ed81541de6124c1152f1c1c1cf3bfff10d6fc828cffba2ce2000000009454e444ea240628';
}

sub delay_updates
{
    # delay tab updates 
    my $t=time()+$next_update_delay;
    foreach my $tab (keys %next_update_time)
    {
	$next_update_time{$tab}=$t;
    }
}

sub scrolled
{
    my $sw = Gtk2::ScrolledWindow->new;
    $sw->add_with_viewport($_[0]);
    $sw->set_policy('automatic','automatic');
    $sw->set_shadow_type ('etched-out');
    return $sw;
}

sub error_message_box
{
    # send $_[0] to a popup message box
    my $w = Gtk2::MessageDialog->new ($main_window,
                                      'modal',
                                      'error',
                                      'close',
                                      $_[0]);
    $w->show_all;
    $w->run;
    $w->destroy;
}

sub animator_program
{
    # return a name of an executable that can do animating
    return defined $external_programs_version{avconv} ? $external_programs{avconv} :
	defined $external_programs_version{ffmpeg} ? $external_programs{ffmpeg} : undef;
}


sub rhoT_linedata
{
    my $rhot_lines=
    {
    # burning lines stolen from MESA
'H-burn'=>"4.78969822798741   6.0
4.78189708222183   6.01
4.77286010473834   6.02
4.76329633883382   6.03
4.75451579068473   6.04
4.74339690787175   6.05
4.73756939981474   6.06
4.72753435860143   6.07
4.71296890016581   6.08
4.705   6.09
4.69336203352929   6.1
4.68128614677733   6.11
4.66681132554507   6.12
4.65020811322491   6.13
4.63698424349071   6.14
4.62354040990075   6.15
4.60971273284982   6.16
4.59549025831981   6.17
4.58102876389299   6.18
4.56430397960708   6.19
4.54114159017045   6.2
4.525   6.21
4.50658451266207   6.22
4.49148512725685   6.23
4.47057654000302   6.23999999999999
4.45352503213082   6.24999999999999
4.435   6.25999999999999
4.40779645233867   6.26999999999999
4.38253531963781   6.27999999999999
4.36403245778705   6.28999999999999
4.345   6.29999999999999
4.31226891648525   6.30999999999999
4.28746254215969   6.31999999999999
4.26766002929674   6.32999999999999
4.24152997163668   6.33999999999999
4.20677479955526   6.34999999999999
4.18596034013332   6.35999999999999
4.14371112883579   6.36999999999999
4.12   6.37999999999999
4.08221816346583   6.38999999999999
4.0566007845418   6.39999999999999
4.03   6.40999999999999
3.98913880767233   6.41999999999999
3.94655700648397   6.42999999999999
3.91995388810879   6.43999999999999
3.87296395134497   6.44999999999999
3.84837001442738   6.45999999999999
3.805   6.46999999999999
3.76   6.47999999999999
3.72821809837616   6.48999999999999
3.68956220014965   6.49999999999999
3.65390629838709   6.50999999999999
3.6037195168851   6.51999999999999
3.55306848439797   6.52999999999999
3.51772563094731   6.53999999999999
3.4762576406451   6.54999999999999
3.42452945852392   6.55999999999999
3.37228605419013   6.56999999999999
3.33170832696652   6.57999999999999
3.27399996311427   6.58999999999999
3.22   6.59999999999999
3.17282950092519   6.60999999999999
3.09155859357588   6.61999999999999
3.04   6.62999999999999
2.97576594679594   6.63999999999999
2.91543522405769   6.64999999999999
2.86112739801337   6.65999999999999
2.815   6.66999999999999
2.77   6.67999999999999
2.725   6.68999999999999
2.68   6.69999999999999
2.635   6.70999999999998
2.59   6.71999999999998
2.54759770640666   6.72999999999998
2.5   6.73999999999998
2.455   6.74999999999998
2.41   6.75999999999998
2.36739234890936   6.76999999999998
2.32   6.77999999999998
2.275   6.78999999999998
2.23   6.79999999999998
2.18382894662027   6.80999999999998
2.14   6.81999999999998
2.095   6.82999999999998
2.05   6.83999999999998
1.99384530916125   6.84999999999998
1.94771828484446   6.85999999999998
1.89504023625897   6.86999999999998
1.86681103576424   6.87999999999998
1.80340918449138   6.88999999999998
1.76347180760693   6.89999999999998
1.71094107182964   6.90999999999998
1.67722151057022   6.91999999999998
1.61453209567907   6.92999999999998
1.57997042287373   6.93999999999998
1.52775472979444   6.94999999999998
1.49018929005687   6.95999999999998
1.42860576296537   6.96999999999998
1.39597559668259   6.97999999999998
1.34594516142396   6.98999999999998
1.30671006835977   6.99999999999998
1.26851921068194   7.00999999999998
1.21706907480683   7.01999999999998
1.16574154992169   7.02999999999998
1.12703949766729   7.03999999999998
1.08895769088574   7.04999999999998
1.03786062215145   7.05999999999998
0.986781738572807   7.06999999999998
0.949724123819923   7.07999999999998
0.910035856679631   7.08999999999998
0.858594175081557   7.09999999999998
0.806749730320821   7.10999999999998
0.767761428405905   7.11999999999998
0.727719764605935   7.12999999999998
0.673758211251675   7.13999999999998
0.618596335029497   7.14999999999998
0.561958718699052   7.15999999999998
0.503069253103554   7.16999999999998
0.43   7.17999999999997
0.348902582715797   7.18999999999997
0.256618580446555   7.19999999999997
0.178710346535523   7.20999999999997
0.0699999999999998   7.21999999999997
-0.0574795670049441   7.22999999999997
-0.16426210082331   7.23999999999997
-0.308517776724591   7.24999999999997
-0.454218557364303   7.25999999999997
-0.600991629708657   7.26999999999997
-0.748778854785833   7.27999999999997
-0.898134855976735   7.28999999999997
-1.06916906961202   7.29999999999997
-1.23191258868994   7.30999999999997
-1.38105043912758   7.31999999999997
-1.55   7.32999999999997
-1.71672574905799   7.33999999999997
-1.87029455983262   7.34999999999997
-2.5 7.36",
'He-burn'=>"8.48353535111692   6.24999999999999
8.48353535111692   6.25999999999999
8.48353535111692   6.26999999999999
8.48353535111692   6.27999999999999
8.48353535111692   6.28999999999999
8.48353535111692   6.29999999999999
8.48353535111692   6.30999999999999
8.48353535111692   6.31999999999999
8.48353535111692   6.32999999999999
8.48353535111692   6.33999999999999
8.48353535111692   6.34999999999999
8.48353535111692   6.35999999999999
8.48353535111692   6.36999999999999
8.48353535111692   6.37999999999999
8.48353535111692   6.38999999999999
8.48353535111692   6.39999999999999
8.48353535111692   6.40999999999999
8.48353535111692   6.41999999999999
8.48353535111692   6.42999999999999
8.48353535111692   6.43999999999999
8.48353535111692   6.44999999999999
8.48353535111692   6.45999999999999
8.48353535111692   6.46999999999999
8.48353535111692   6.47999999999999
8.48353535111692   6.48999999999999
8.48353535111692   6.49999999999999
8.4840683847216   6.50999999999999
8.48446164685974   6.51999999999999
8.4849939677108   6.52999999999999
8.48552301934284   6.53999999999999
8.48604873685285   6.54999999999999
8.48628670105569   6.55999999999999
8.48679857155455   6.56999999999999
8.48730712407889   6.57999999999999
8.48781229415317   6.58999999999999
8.48821667632563   6.59999999999999
8.48871974545268   6.60999999999999
8.48912927924578   6.61999999999999
8.48962976658947   6.62999999999999
8.49012636912082   6.63999999999999
8.49061901421932   6.64999999999999
8.49110762846762   6.65999999999999
8.49124817873377   6.66999999999999
8.49172386493996   6.67999999999999
8.49219555008669   6.68999999999999
8.49266316136132   6.69999999999999
8.49312662518446   6.70999999999998
8.49353736558171   6.71999999999998
8.49395045885628   6.72999999999998
8.49440873841504   6.73999999999998
8.49486237341265   6.74999999999998
8.4953112841889   6.75999999999998
8.49575539052088   6.76999999999998
8.49581609920219   6.77999999999998
8.49624841018999   6.78999999999998
8.4966759598424   6.79999999999998
8.49709866862233   6.80999999999998
8.49751645646298   6.81999999999998
8.49791531879639   6.82999999999998
8.49831458024272   6.83999999999998
8.49872276912265   6.84999999999998
8.49912549572538   6.85999999999998
8.49952267641245   6.86999999999998
8.49991422738078   6.87999999999998
8.5   6.88999999999998
8.50030489605956   6.89999999999998
8.50069105116918   6.90999999999998
8.50107122603363   6.91999999999998
8.50144533551798   6.92999999999998
8.50180218284978   6.93999999999998
8.50214874357575   6.94999999999998
8.5025003741549   6.95999999999998
8.50284582954672   6.96999999999998
8.50318502568828   6.97999999999998
8.50351787872846   6.98999999999998
8.50355787317832   6.99999999999998
8.50388715798617   7.00999999999998
8.50420970538488   7.01999999999998
8.50452543345927   7.02999999999998
8.50483426098354   7.03999999999998
8.50511026415463   7.04999999999998
8.50537713836044   7.05999999999998
8.50566327267941   7.06999999999998
8.5059424082548   7.07999999999998
8.50621446602872   7.08999999999998
8.50647936782141   7.09999999999998
8.50653787087581   7.10999999999998
8.50679619327198   7.11999999999998
8.50704699883359   7.12999999999998
8.50729021583173   7.13999999999998
8.50752577396345   7.14999999999998
8.50772412391727   7.15999999999998
8.5079142540029   7.16999999999998
8.5081269638486   7.17999999999997
8.50833193209704   7.18999999999997
8.50852909059561   7.19999999999997
8.50871837087222   7.20999999999997
8.50877453127678   7.21999999999997
8.50895461500137   7.22999999999997
8.50912649269386   7.23999999999997
8.50929004745289   7.24999999999997
8.50944508393516   7.25999999999997
8.50956796449847   7.26999999999997
8.50968240853221   7.27999999999997
8.50981118790446   7.28999999999997
8.50992725358585   7.29999999999997
8.51002576456669   7.30999999999997
8.51009704940815   7.31999999999997
8.51011235812158   7.32999999999997
8.51012103494157   7.33999999999997
8.51010419560265   7.34999999999997
8.50987206626314   7.35999999999997
8.50834602060435   7.36999999999997
8.50433256540678   7.37999999999997
8.49399788366298   7.38999999999997
8.47984341998132   7.39999999999997
8.45854687857691   7.40999999999997
8.44   7.41999999999997
8.41733149289749   7.42999999999997
8.4007793447575   7.43999999999997
8.38   7.44999999999997
8.34536761668742   7.45999999999997
8.32621390915301   7.46999999999997
8.29991590438485   7.47999999999997
8.26834595155623   7.48999999999997
8.23616014589044   7.49999999999997
8.20755496626606   7.50999999999997
8.18005670121201   7.51999999999997
8.14   7.52999999999997
8.1127503271663   7.53999999999997
8.08   7.54999999999997
8.04307326025772   7.55999999999997
8.01630878294132   7.56999999999997
7.97156646098887   7.57999999999997
7.93785806978384   7.58999999999997
7.89489466949317   7.59999999999997
7.85194475045026   7.60999999999997
7.82122712069876   7.61999999999997
7.76895640093212   7.62999999999997
7.72   7.63999999999997
7.68464888321024   7.64999999999996
7.63519572917556   7.65999999999996
7.6   7.66999999999996
7.55281304605938   7.67999999999996
7.49719789906383   7.68999999999996
7.45965700487135   7.69999999999996
7.40773266821253   7.70999999999996
7.36   7.71999999999996
7.30617259474819   7.72999999999996
7.27727178408098   7.73999999999996
7.22501946633058   7.74999999999996
7.18   7.75999999999996
7.12   7.76999999999996
7.06   7.77999999999996
6.99151633705787   7.78999999999996
6.89656663131775   7.79999999999996
6.82529622526423   7.80999999999996
6.74149633153038   7.81999999999996
6.64244177731542   7.82999999999996
6.56355362588618   7.83999999999996
6.44413247469669   7.84999999999996
6.31939189410146   7.85999999999996
6.18984531357308   7.86999999999996
6.04   7.87999999999996
5.86   7.88999999999996
5.66551265663323   7.89999999999996
5.45206582335052   7.90999999999996
5.25381258073439   7.91999999999996
5.05100240299576   7.92999999999996
4.84487732346763   7.93999999999996
4.64913817561938   7.94999999999996
4.46140223078154   7.95999999999996
4.26381274249597   7.96999999999996
4.06   7.97999999999996
3.8506888254319   7.98999999999996
3.66303311220746   7.99999999999996
3.47573408703272   8.00999999999996
3.28   8.01999999999996
3.09402375180164   8.02999999999996
2.91117399275541   8.03999999999996
2.73373967489182   8.04999999999996
2.55050634257559   8.05999999999996
2.38   8.06999999999996
2.225813181719   8.07999999999996
2.04648919648914   8.08999999999996
1.9   8.09999999999996
1.73888081933097   8.10999999999996
1.58741131258336   8.11999999999995
1.42996879780716   8.12999999999995
1.29551178030079   8.13999999999995
1.13847840335197   8.14999999999995
1.0   8.15999999999995
0.867849477517827   8.16999999999995
0.740449490356371   8.17999999999995
0.600029822305441   8.18999999999995
0.488490528128802   8.19999999999995
0.363942406833864   8.20999999999995
0.240410490397586   8.21999999999995
0.117956789288436   8.22999999999995",
'C-burn'=>"9.97290925320785   7.10999999999998
9.9729092539461   7.11999999999998
9.97290925564055   7.12999999999998
9.97290925680104   7.13999999999998
9.97290925859597   7.14999999999998
9.97290926261969   7.15999999999998
9.97290926535272   7.16999999999998
9.9729092694514   7.17999999999997
9.97290927843983   7.18999999999997
9.97290928449683   7.19999999999997
9.97290929332103   7.20999999999997
9.97290931227977   7.21999999999997
9.97290932495879   7.22999999999997
9.97290934293353   7.23999999999997
9.97290938081768   7.24999999999997
9.97290940596957   7.25999999999997
9.97290944072009   7.26999999999997
9.97290951264879   7.27999999999997
9.97290956006999   7.28999999999997
9.97290962400352   7.29999999999997
9.97290975408256   7.30999999999997
9.97290983926202   7.31999999999997
9.97290995143312   7.32999999999997
9.97291017591104   7.33999999999997
9.97291032193683   7.34999999999997
9.97291050987888   7.35999999999997
9.97291087992436   7.36999999999997
9.97291111907324   7.37999999999997
9.97291141991591   7.38999999999997
9.97291200258234   7.39999999999997
9.97291237665399   7.40999999999997
9.97291283626218   7.41999999999997
9.97291371105037   7.42999999999997
9.972914268771   7.43999999999997
9.97291493671044   7.44999999999997
9.97291618329464   7.45999999999997
9.97291697193328   7.46999999999997
9.97291788855445   7.47999999999997
9.97291955820126   7.48999999999997
9.9729206045827   7.49999999999997
9.97292177394159   7.50999999999997
9.97292383188988   7.51999999999997
9.97292510455554   7.52999999999997
9.97292644223802   7.53999999999997
9.97292865858856   7.54999999999997
9.97292999645783   7.55999999999997
9.97293123246061   7.56999999999997
9.97293298008541   7.57999999999997
9.97293396039859   7.58999999999997
9.97293445936109   7.59999999999997
9.97293433837768   7.60999999999997
9.97293403109361   7.61999999999997
9.97293245539897   7.62999999999997
9.9729275815157   7.63999999999997
9.97292408405898   7.64999999999996
9.97291774849151   7.65999999999996
9.97290236471449   7.66999999999996
9.97289189895975   7.67999999999996
9.97287550453962   7.68999999999996
9.97283818227632   7.69999999999996
9.97281330035288   7.70999999999996
9.97277640192237   7.71999999999996
9.97269420613085   7.72999999999996
9.97264016877785   7.73999999999996
9.97256207948943   7.74999999999996
9.97238800856189   7.75999999999996
9.97227548943909   7.76999999999996
9.97211526589837   7.77999999999996
9.9717504251302   7.78999999999996
9.97152143752782   7.79999999999996
9.971200001099   7.80999999999996
9.9704369725421   7.81999999999996
9.96997531328177   7.82999999999996
9.96929045351292   7.83999999999996
9.96789044943011   7.84999999999996
9.9668451885872   7.85999999999996
9.9652623113904   7.86999999999996
9.9631120048401   7.87999999999996
9.96072643243727   7.88999999999996
9.95702291101599   7.89999999999996
9.95482240237368   7.90999999999996
9.94965239831382   7.91999999999996
9.94375   7.92999999999996
9.94100445199016   7.93999999999996
9.93129939017921   7.94999999999996
9.92397230703305   7.95999999999996
9.91370695661629   7.96999999999996
9.90615012485299   7.97999999999996
9.8921593205617   7.98999999999996
9.89   7.99999999999996
9.87562042128149   8.00999999999996
9.86622577172156   8.01999999999996
9.85306330533724   8.02999999999996
9.84051579726022   8.03999999999996
9.82528481504765   8.04999999999996
9.81212428670965   8.05999999999996
9.80080196043269   8.06999999999996
9.7825   8.07999999999996
9.77685768269755   8.08999999999996
9.75635939809176   8.09999999999996
9.73520851267845   8.10999999999996
9.72875   8.11999999999995
9.70682202959325   8.12999999999995
9.68515826126455   8.13999999999995
9.675   8.14999999999995
9.65183192605799   8.15999999999995
9.62351743540175   8.16999999999995
9.62125   8.17999999999995
9.59102227808109   8.18999999999995
9.5675   8.19999999999995
9.54243284161508   8.20999999999995
9.52393876859719   8.21999999999995
9.49835747255031   8.22999999999995
9.47640725162128   8.23999999999995
9.44973556242629   8.24999999999995
9.41996659329181   8.25999999999995
9.40625   8.26999999999995
9.36714544411606   8.27999999999995
9.33615388629502   8.28999999999995
9.31128041784872   8.29999999999995
9.27408158174708   8.30999999999995
9.245   8.31999999999995
9.20700299162382   8.32999999999995
9.16733779125818   8.33999999999995
9.12355723369374   8.34999999999995
9.08375   8.35999999999995
9.0421366609697   8.36999999999995
8.99167725385627   8.37999999999995
8.94233367807218   8.38999999999995
8.89146917696657   8.39999999999995
8.83117030260218   8.40999999999995
8.7768191975815   8.41999999999995
8.70636121667638   8.42999999999995
8.63549550390576   8.43999999999995
8.56212194614047   8.44999999999995
8.47376192643011   8.45999999999995
8.385   8.46999999999995
8.2927341933415   8.47999999999995
8.18139354164982   8.48999999999995
8.07957381527241   8.49999999999995
7.9729938100029   8.50999999999995
7.84808712967826   8.51999999999995
7.72004674764215   8.52999999999995
7.56000072742856   8.53999999999995
7.40183590974884   8.54999999999995
7.21872582294028   8.55999999999995
7.04125   8.56999999999995
6.8309058021288   8.57999999999995
6.61125   8.58999999999994
6.36121787701521   8.59999999999994
6.10754713646917   8.60999999999994
5.83979110524921   8.61999999999994
5.51783504576889   8.62999999999994
5.20604171957431   8.63999999999994
4.90972792324578   8.64999999999994
4.60486840537347   8.65999999999994
4.28742535302613   8.66999999999994
3.96006513717954   8.67999999999994
3.63731653059253   8.68999999999994
3.30206288540545   8.69999999999994
2.97461736065346   8.70999999999994
2.63375   8.71999999999994
2.30626840745814   8.72999999999994
1.97143704021187   8.73999999999994
1.64826993739113   8.74999999999994
1.32208730562731   8.75999999999994",
'O-burn'=>"10.5839 7.5
10.5839 8.21
10.57 8.235
10.56 8.2539
10.5546 8.26454
10.542 8.288
10.5409 8.28991
10.53 8.309
10.5198 8.32598
10.51 8.341
10.5127 8.33727
10.5 8.35688
10.493 8.3667
10.4777 8.38777
10.469 8.3991
10.46 8.41
10.44 8.43312
10.4409 8.43209
10.4409 8.432
10.4361 8.43761
10.4358 8.438
10.44 8.43312
10.418 8.456
10.4004 8.47196
10.391 8.4811
10.3923 8.48
10.38 8.48977
10.3707 8.498
10.3508 8.51292
10.337 8.5237
10.321 8.534
10.32 8.53477
10.3006 8.54794
10.264 8.57
10.2649 8.56951
10.2726 8.56526
10.2748 8.564
10.26 8.57209
10.2634 8.57034
10.264 8.57
10.2 8.60339
10.1519 8.62519
10.1417 8.62983
10.1413 8.63
10.1417 8.62983
10.1519 8.62519
10.08 8.65388
10.0448 8.666
10.02 8.67461
10.0029 8.67971
9.9 8.70981
9.78 8.7392
9.72 8.75183
9.66 8.76373
9.6 8.77481
9.54 8.78495
9.48 8.79447
9.42 8.80357
9.36 8.81172
9.3 8.81986
9.24 8.8272
9.18 8.83404
9.12 8.84102
9 8.85311
8.76 8.87444
8.52 8.89244
8.34 8.90423
8.16 8.91463
7.98 8.92424
7.8 8.93294
7.62 8.94083
7.5 8.94597
6.9 8.96806
6.6 8.97764
2.5 9.1",
"\{\/Symbol g\}<4/3"=>"
2 9.51776
2.05459 9.51779
2.05653 9.51779
2.11111 9.51782
2.16499 9.51788
2.16837 9.51788
2.22222 9.51793
2.27549 9.51795
2.28008 9.51795
2.33333 9.51797
2.38676 9.51793
2.391 9.51793
2.44444 9.51789
2.49887 9.51781
2.5011 9.51781
2.55556 9.51773
2.6109 9.51765
2.61133 9.51765
2.66667 9.51757
2.72112 9.51755
2.72333 9.51755
2.77778 9.51752
2.83238 9.51756
2.83427 9.51757
2.88889 9.51761
2.94442 9.51767
2.94447 9.51767
3 9.51774
3.05483 9.51776
3.05629 9.51776
3.11111 9.51779
3.1653 9.51784
3.16805 9.51784
3.22222 9.51789
3.27589 9.5179
3.27967 9.5179
3.33333 9.51791
3.38729 9.51787
3.39047 9.51786
3.44444 9.51782
3.49956 9.51773
3.50043 9.51773
3.55556 9.51764
3.61002 9.51755
3.61223 9.51754
3.66667 9.51745
3.71998 9.51741
3.72449 9.51741
3.77778 9.51737
3.8309 9.51739
3.83575 9.51739
3.88889 9.51741
3.94249 9.51745
3.94638 9.51745
4 9.51748
4.0538 9.51747
4.05732 9.51747
4.11111 9.51746
4.16482 9.51746
4.16852 9.51746
4.22222 9.51746
4.2755 9.51741
4.2801 9.5174
4.33333 9.51735
4.3851 9.51723
4.39283 9.51721
4.44444 9.51709
4.49352 9.51691
4.50691 9.51686
4.55556 9.51668
4.60105 9.51649
4.62196 9.51639
4.66667 9.5162
4.70823 9.51602
4.73733 9.51589
4.77778 9.51572
4.81539 9.51556
4.85272 9.51539
4.88889 9.51523
4.92214 9.51504
4.96923 9.51475
5 9.51457
5.02724 9.51433
5.08887 9.51374
5.11111 9.51354
5.12979 9.51332
5.21274 9.51223
5.22222 9.51212
5.22962 9.51198
5.27441 9.51111
5.32559 9.5102
5.33333 9.51004
5.34533 9.50969
5.41611 9.50776
5.44444 9.50691
5.49977 9.50452
5.50055 9.50448
5.55556 9.50195
5.57733 9.50055
5.61175 9.49798
5.64544 9.49547
5.66667 9.49351
5.702 9.48902
5.72868 9.48485
5.74797 9.48133
5.77778 9.47284
5.77998 9.47198
5.78034 9.47172
5.78068 9.47137
5.79957 9.46116
5.80164 9.45859
5.80421 9.45546
5.8134 9.44966
5.81625 9.44545
5.8194 9.44054
5.82317 9.43769
5.82605 9.43232
5.82677 9.42653
5.8279 9.42512
5.82852 9.41919
5.82355 9.41378
5.8232 9.41143
5.81902 9.40606
5.81126 9.4021
5.80505 9.39615
5.79975 9.39293
5.79604 9.39077
5.78547 9.38071
5.7841 9.3798
5.78289 9.37919
5.77778 9.37573
5.76401 9.36829
5.76182 9.36667
5.756 9.36409
5.7401 9.35799
5.73108 9.35354
5.71452 9.34788
5.70912 9.34542
5.69619 9.3404
5.68782 9.3379
5.66667 9.32969
5.66136 9.3279
5.6601 9.32727
5.65703 9.32613
5.63224 9.31821
5.6228 9.31414
5.60483 9.30832
5.59919 9.30617
5.58451 9.30101
5.57609 9.29858
5.55556 9.29203
5.54546 9.28907
5.54211 9.28788
5.53517 9.28547
5.51611 9.27941
5.50391 9.27475
5.48822 9.26957
5.47753 9.26553
5.46645 9.26162
5.46013 9.25976
5.44444 9.25488
5.42881 9.25033
5.42311 9.24848
5.40466 9.24378
5.39452 9.24125
5.37223 9.23535
5.35898 9.23232
5.33333 9.22632
5.32132 9.22364
5.31571 9.22222
5.29773 9.21801
5.28414 9.21491
5.26046 9.20909
5.24823 9.20602
5.22222 9.19961
5.21219 9.19715
5.20749 9.19596
5.19243 9.19244
5.17505 9.1884
5.15152 9.18283
5.13799 9.17965
5.11111 9.17331
5.10098 9.17089
5.09631 9.1697
5.0822 9.16628
5.06461 9.16206
5.04231 9.15657
5.02803 9.15325
5 9.14697
4.9898 9.14464
4.98452 9.14343
4.9635 9.13912
4.94971 9.13625
4.91951 9.1303
4.90786 9.12806
4.88889 9.12472
4.86353 9.12017
4.84632 9.11717
4.82039 9.11214
4.77778 9.1043
4.77696 9.10414
4.77644 9.10404
4.77453 9.10366
4.73644 9.09579
4.71027 9.09091
4.69331 9.08776
4.66667 9.08321
4.64834 9.07994
4.63556 9.07778
4.60401 9.07205
4.57285 9.06669
4.56109 9.06465
4.55897 9.06424
4.55556 9.06364
4.5171 9.05606
4.49251 9.05152
4.47445 9.04797
4.44444 9.04269
4.43006 9.04008
4.41974 9.03838
4.38426 9.03237
4.34718 9.02689
4.33628 9.02525
4.33506 9.02505
4.33333 9.0248
4.28617 9.01769
4.24367 9.01212
4.23527 9.01058
4.22222 9.00838
4.1903 9.00276
4.16646 8.99899
4.14342 8.99517
4.11111 8.99038
4.09448 8.98782
4.08001 8.98586
4.04469 8.98058
4 8.97459
3.99247 8.97362
3.9846 8.97273
3.93961 8.96673
3.88889 8.96064
3.88495 8.96006
3.88149 8.9596
3.86215 8.95644
3.83825 8.95245
3.79741 8.94646
3.78967 8.94506
3.77778 8.94322
3.74147 8.93762
3.70814 8.93333
3.69011 8.93056
3.66667 8.9276
3.63663 8.92375
3.60167 8.9202
3.57992 8.91732
3.55556 8.91459
3.52316 8.9109
3.48303 8.90707
3.46678 8.90443
3.44444 8.90106
3.41856 8.897
3.39571 8.89394
3.36653 8.89002
3.33333 8.8861
3.31014 8.88355
3.27772 8.88081
3.24807 8.87775
3.22222 8.87557
3.18708 8.87183
3.14155 8.86768
3.12671 8.86583
3.11111 8.86413
3.07117 8.85927
3.02687 8.85455
3.01536 8.85273
3 8.85064
2.96257 8.84584
2.92113 8.84141
2.90738 8.83923
2.88889 8.83684
2.85451 8.83235
2.81293 8.82828
2.79654 8.82607
2.77778 8.82413
2.7366 8.82002
2.66689 8.81518
2.66667 8.81516
2.66662 8.81516
2.66651 8.81515
2.60654 8.80913
2.55556 8.80446
2.54437 8.80334
2.52834 8.80202
2.48923 8.79673
2.44444 8.79148
2.43351 8.79018
2.41709 8.78889
2.36877 8.7847
2.33333 8.78238
2.30022 8.77967
2.23983 8.77576
2.22987 8.77485
2.22222 8.77428
2.17387 8.77004
2.16569 8.76931
2.11111 8.76521
2.09787 8.76419
2.07125 8.76263
2.03181 8.75887
2 8.7565
1.96564 8.75356
1.91117 8.74949
1.90131 8.74803
1.88889 8.74648
1.84602 8.74143
1.7874 8.73636
1.7836 8.73568
1.77778 8.73496
1.72917 8.72898
1.69507 8.72659
1.66667 8.72435
1.66093 8.72391
1.64513 8.72323
1.58907 8.71927
1.55556 8.71775
1.50901 8.7156
1.48552 8.71495
1.44444 8.71345
1.42483 8.71242
1.35857 8.7101
1.34822 8.70834
1.33333 8.70644
1.29923 8.701
1.2553 8.69697
1.23557 8.69539
1.22222 8.69457
1.17322 8.69118
1.16514 8.69058
1.11111 8.68745
1.0916 8.68614
1.04734 8.68384
1.01799 8.68171
1 8.68067
0.944583 8.67726
0.944151 8.67724
0.888889 8.67449
0.865487 8.67347
0.78183 8.67071
0.779047 8.67056
0.777778 8.6705
0.774692 8.67034
0.70333 8.66637
0.666667 8.66482
0.623706 8.66265
0.586182 8.6612
0.555556 8.65985
0.542175 8.65916
0.504568 8.65758
0.469373 8.65463
0.444444 8.65304
0.399513 8.64975
0.361235 8.64774
0.333333 8.6461
0.324143 8.64553
0.29966 8.64444
0.253293 8.64077
0.222222 8.63927
0.173967 8.63702
0.155314 8.63654
0.111111 8.63494
0.0879992 8.63404
0.00494455 8.6319
0 8.63173",
'e-cap'=>" -0.184854938516E+01  0.857999992371E+01
 -0.178042869886E+01  0.858344681803E+01
 -0.171455300827E+01  0.858689371235E+01
 -0.164886550348E+01  0.859034060667E+01
 -0.158715881327E+01  0.859378750100E+01
 -0.152692651071E+01  0.859723439532E+01
 -0.146925565942E+01  0.860068128964E+01
 -0.141943093141E+01  0.860412818396E+01
 -0.137602778411E+01  0.860757507829E+01
 -0.133573618322E+01  0.861102197261E+01
 -0.129615935925E+01  0.861446886693E+01
 -0.125554207192E+01  0.861791576126E+01
 -0.121275912134E+01  0.862136265558E+01
 -0.116694707971E+01  0.862480954990E+01
 -0.111791902640E+01  0.862825644422E+01
 -0.106547208795E+01  0.863170333855E+01
 -0.101048656049E+01  0.863515023287E+01
 -0.953402301319E+00  0.863859712719E+01
 -0.892611122131E+00  0.864204402151E+01
 -0.833226949952E+00  0.864549091584E+01
 -0.772793467185E+00  0.864893781016E+01
 -0.713174464327E+00  0.865238470448E+01
 -0.654249796519E+00  0.865583159881E+01
 -0.596705000762E+00  0.865927849313E+01
 -0.539848766925E+00  0.866272538745E+01
 -0.484593507112E+00  0.866617228177E+01
 -0.430288508760E+00  0.866961917610E+01
 -0.376683477335E+00  0.867306607042E+01
 -0.324672082863E+00  0.867651296474E+01
 -0.272332228197E+00  0.867995985906E+01
 -0.222046812545E+00  0.868340675339E+01
 -0.171001016395E+00  0.868685364771E+01
 -0.122070195092E+00  0.869030054203E+01
 -0.732603799755E-01  0.869374743636E+01
 -0.249428735052E-01  0.869719433068E+01
  0.218482931147E-01  0.870064122500E+01
  0.663543805218E-01  0.870408811932E+01
  0.108919158652E+00  0.870753501365E+01
  0.150459051921E+00  0.871098190797E+01
  0.191608665826E+00  0.871442880229E+01
  0.232914882142E+00  0.871787569661E+01
  0.274608987939E+00  0.872132259094E+01
  0.316979606434E+00  0.872476948526E+01
  0.360085481825E+00  0.872821637958E+01
  0.403179214746E+00  0.873166327390E+01
  0.447169005761E+00  0.873511016823E+01
  0.493662420737E+00  0.873855706255E+01
  0.537672067676E+00  0.874200395687E+01
  0.583055924325E+00  0.874545085120E+01
  0.627568734580E+00  0.874889774552E+01
  0.671695193339E+00  0.875234463984E+01
  0.715690712128E+00  0.875579153416E+01
  0.758900372122E+00  0.875923842849E+01
  0.801320663563E+00  0.876268532281E+01
  0.843956906939E+00  0.876613221713E+01
  0.884758325507E+00  0.876957911145E+01
  0.926516551888E+00  0.877302600578E+01
  0.967067262397E+00  0.877647290010E+01
  0.100705347351E+01  0.877991979442E+01
  0.104792135404E+01  0.878336668875E+01
  0.108666589272E+01  0.878681358307E+01
  0.112679282853E+01  0.879026047739E+01
  0.116588854039E+01  0.879370737171E+01
  0.120435111345E+01  0.879715426604E+01
  0.124295901115E+01  0.880060116036E+01
  0.128049522697E+01  0.880404805468E+01
  0.131736151056E+01  0.880749494900E+01
  0.135379631409E+01  0.881094184333E+01
  0.138995745512E+01  0.881438873765E+01
  0.142606454338E+01  0.881783563197E+01
  0.146215639835E+01  0.882128252630E+01
  0.149823970561E+01  0.882472942062E+01
  0.153456171203E+01  0.882817631494E+01
  0.157078652939E+01  0.883162320926E+01
  0.160650171121E+01  0.883507010359E+01
  0.164260771981E+01  0.883851699791E+01
  0.168007561437E+01  0.884196389223E+01
  0.171605365065E+01  0.884541078655E+01
  0.175133217422E+01  0.884885768088E+01
  0.178714214712E+01  0.885230457520E+01
  0.182228677750E+01  0.885575146952E+01
  0.185691459929E+01  0.885919836384E+01
  0.189102558730E+01  0.886264525817E+01
  0.192534814559E+01  0.886609215249E+01
  0.195901634995E+01  0.886953904681E+01
  0.199167939403E+01  0.887298594114E+01
  0.202526487492E+01  0.887643283546E+01
  0.205820106366E+01  0.887987972978E+01
  0.208996917455E+01  0.888332662410E+01
  0.212289811779E+01  0.888677351843E+01
  0.215535702704E+01  0.889022041275E+01
  0.218686591078E+01  0.889366730707E+01
  0.221903803654E+01  0.889711420139E+01
  0.225074327968E+01  0.890056109572E+01
  0.228237969910E+01  0.890400799004E+01
  0.231377601985E+01  0.890745488436E+01
  0.234438217154E+01  0.891090177869E+01
  0.237480568568E+01  0.891434867301E+01
  0.240534781032E+01  0.891779556733E+01
  0.243584358440E+01  0.892124246165E+01
  0.246618946616E+01  0.892468935598E+01
  0.249640128872E+01  0.892813625030E+01
  0.252672012510E+01  0.893158314462E+01
  0.255688748833E+01  0.893503003894E+01
  0.258659039661E+01  0.893847693327E+01
  0.261634963107E+01  0.894192382759E+01
  0.264615498093E+01  0.894537072191E+01
  0.267579501497E+01  0.894881761624E+01
  0.270531680991E+01  0.895226451056E+01
  0.273450295150E+01  0.895571140488E+01
  0.276287180419E+01  0.895915829920E+01
  0.279067227939E+01  0.896260519353E+01
  0.281909588846E+01  0.896605208785E+01
  0.284743938618E+01  0.896949898217E+01
  0.287472547804E+01  0.897294587649E+01
  0.290212047341E+01  0.897639277082E+01
  0.292986716337E+01  0.897983966514E+01
  0.295704644946E+01  0.898328655946E+01
  0.298331982076E+01  0.898673345379E+01
  0.301010310990E+01  0.899018034811E+01
  0.303712893628E+01  0.899362724243E+01
  0.306393807308E+01  0.899707413675E+01
  0.309049150495E+01  0.900052103108E+01
  0.311687646882E+01  0.900396792540E+01
  0.314308324501E+01  0.900741481972E+01
  0.316913625147E+01  0.901086171404E+01
  0.319505943771E+01  0.901430860837E+01
  0.322087966381E+01  0.901775550269E+01
  0.324657754364E+01  0.902120239701E+01
  0.327207500567E+01  0.902464929133E+01
  0.329749603633E+01  0.902809618566E+01
  0.332294807718E+01  0.903154307998E+01
  0.334828889000E+01  0.903498997430E+01
  0.337323081796E+01  0.903843686863E+01
  0.339812924890E+01  0.904188376295E+01
  0.342317404809E+01  0.904533065727E+01
  0.344803313018E+01  0.904877755159E+01
  0.347221467693E+01  0.905222444592E+01
  0.349619291956E+01  0.905567134024E+01
  0.352043044882E+01  0.905911823456E+01
  0.354468321643E+01  0.906256512888E+01
  0.356846601573E+01  0.906601202321E+01
  0.359172527592E+01  0.906945891753E+01
  0.361499349406E+01  0.907290581185E+01
  0.363834643297E+01  0.907635270618E+01
  0.366167113571E+01  0.907979960050E+01
  0.368481414120E+01  0.908324649482E+01
  0.370784544977E+01  0.908669338914E+01
  0.373063353969E+01  0.909014028347E+01
  0.375309498215E+01  0.909358717779E+01
  0.377538505293E+01  0.909703407211E+01
  0.379796616868E+01  0.910048096643E+01
  0.382050506935E+01  0.910392786076E+01
  0.384273143152E+01  0.910737475508E+01
  0.386475986745E+01  0.911082164940E+01
  0.388680219039E+01  0.911426854373E+01
  0.390876626150E+01  0.911771543805E+01
  0.393064103214E+01  0.912116233237E+01
  0.395238758637E+01  0.912460922669E+01
  0.397393161668E+01  0.912805612102E+01
  0.399542569430E+01  0.913150301534E+01
  0.401695215698E+01  0.913494990966E+01
  0.403842009394E+01  0.913839680398E+01
  0.405963858211E+01  0.914184369831E+01
  0.408047633439E+01  0.914529059263E+01
  0.410145516132E+01  0.914873748695E+01
  0.412253878951E+01  0.915218438127E+01
  0.414347763731E+01  0.915563127560E+01
  0.416393302707E+01  0.915907816992E+01
  0.418396767280E+01  0.916252506424E+01
  0.420435396016E+01  0.916597195857E+01
  0.422487405266E+01  0.916941885289E+01
  0.424516607840E+01  0.917286574721E+01
  0.426487922247E+01  0.917631264153E+01
  0.428416911245E+01  0.917975953586E+01
  0.430375011873E+01  0.918320643018E+01
  0.432350831815E+01  0.918665332450E+01
  0.434325952043E+01  0.919010021882E+01
  0.436284683914E+01  0.919354711315E+01
  0.438224763014E+01  0.919699400747E+01
  0.440157625742E+01  0.920044090179E+01
  0.442082608448E+01  0.920388779612E+01
  0.443998341052E+01  0.920733469044E+01
  0.445905423855E+01  0.921078158476E+01
  0.447803812081E+01  0.921422847908E+01
  0.449695075634E+01  0.921767537341E+01
  0.451579853239E+01  0.922112226773E+01
  0.453456711620E+01  0.922456916205E+01
  0.455321648702E+01  0.922801605637E+01
  0.457167955954E+01  0.923146295070E+01
  0.459008308948E+01  0.923490984502E+01
  0.460853206160E+01  0.923835673934E+01
  0.462697957204E+01  0.924180363367E+01
  0.464531404423E+01  0.924525052799E+01
  0.466336424945E+01  0.924869742231E+01
  0.468111627280E+01  0.925214431663E+01
  0.469903657656E+01  0.925559121096E+01
  0.471707097770E+01  0.925903810528E+01
  0.473505185763E+01  0.926248499960E+01
  0.475276277645E+01  0.926593189392E+01
  0.476998976021E+01  0.926937878825E+01
  0.478716271125E+01  0.927282568257E+01
  0.480461353434E+01  0.927627257689E+01
  0.482214037892E+01  0.927971947121E+01
  0.483952292738E+01  0.928316636554E+01
  0.485659619406E+01  0.928661325986E+01
  0.487334212425E+01  0.929006015418E+01
  0.489019887626E+01  0.929350704851E+01
  0.490720459748E+01  0.929695394283E+01
  0.492420656088E+01  0.930040083715E+01
  0.494106332964E+01  0.930384773147E+01
  0.495776357238E+01  0.930729462580E+01
  0.497441007665E+01  0.931074152012E+01
  0.499101603678E+01  0.931418841444E+01
  0.500756811543E+01  0.931763530876E+01
  0.502406507505E+01  0.932108220309E+01
  0.504049206250E+01  0.932452909741E+01
  0.505681646768E+01  0.932797599173E+01
  0.507299249157E+01  0.933142288606E+01
  0.508913201995E+01  0.933486978038E+01
  0.510530210516E+01  0.933831667470E+01
  0.512148140065E+01  0.934176356902E+01
  0.513760840513E+01  0.934521046335E+01
  0.515358186588E+01  0.934865735767E+01
  0.516926950877E+01  0.935210425199E+01
  0.518481242732E+01  0.935555114631E+01
  0.520043159814E+01  0.935899804064E+01
  0.521610785252E+01  0.936244493496E+01
  0.523179971196E+01  0.936589182928E+01
  0.524744760195E+01  0.936933872361E+01
  0.526298123846E+01  0.937278561793E+01
  0.527838524525E+01  0.937623251225E+01
  0.529379351115E+01  0.937967940657E+01
  0.530915430080E+01  0.938312630090E+01
  0.532440132278E+01  0.938657319522E+01
  0.533949436793E+01  0.939002008954E+01
  0.535444280202E+01  0.939346698386E+01
  0.536932839697E+01  0.939691387819E+01
  0.538433277758E+01  0.940036077251E+01
  0.539940120276E+01  0.940380766683E+01
  0.541438062993E+01  0.940725456115E+01
  0.542921341688E+01  0.941070145548E+01
  0.544391153411E+01  0.941414834980E+01
  0.545853785655E+01  0.941759524412E+01
  0.547318526371E+01  0.942104213845E+01
  0.548783185213E+01  0.942448903277E+01
  0.550245044578E+01  0.942793592709E+01
  0.551703570210E+01  0.943138282141E+01
  0.553156964284E+01  0.943482971574E+01
  0.554602059981E+01  0.943827661006E+01
  0.556034351057E+01  0.944172350438E+01
  0.557450026010E+01  0.944517039870E+01
  0.558863845844E+01  0.944861729303E+01
  0.560280857669E+01  0.945206418735E+01
  0.561700570287E+01  0.945551108167E+01
  0.563121041858E+01  0.945895797600E+01
  0.564538973437E+01  0.946240487032E+01
  0.565949948071E+01  0.946585176464E+01
  0.567349696043E+01  0.946929865896E+01
  0.568742187178E+01  0.947274555329E+01
  0.570129556383E+01  0.947619244761E+01
  0.571511984430E+01  0.947963934193E+01
  0.572889694548E+01  0.948308623625E+01
  0.574262951776E+01  0.948653313058E+01
  0.575632058532E+01  0.948998002490E+01
  0.576997345764E+01  0.949342691922E+01
  0.578359144765E+01  0.949687381355E+01
  0.579717823647E+01  0.950032070787E+01
  0.581072831330E+01  0.950376760219E+01
  0.582423707921E+01  0.950721449651E+01
  0.583770581351E+01  0.951066139084E+01
  0.585113563289E+01  0.951410828516E+01
  0.586452749835E+01  0.951755517948E+01
  0.587788496998E+01  0.952100207380E+01
  0.589122017584E+01  0.952444896813E+01
  0.590453648669E+01  0.952789586245E+01
  0.591782866540E+01  0.953134275677E+01
  0.593108348816E+01  0.953478965109E+01
  0.594427735788E+01  0.953823654542E+01
  0.595737675011E+01  0.954168343974E+01
  0.597034107580E+01  0.954513033406E+01
  0.598328167445E+01  0.954857722839E+01
  0.599629487880E+01  0.955202412271E+01
  0.600936419299E+01  0.955547101703E+01
  0.602245246163E+01  0.955891791135E+01
  0.603550239713E+01  0.956236480568E+01
  0.604844365130E+01  0.956581170000E+01
  0.606120587384E+01  0.956925859432E+01
  0.607375870753E+01  0.957270548864E+01
  0.608634949623E+01  0.957615238297E+01
  0.609903196767E+01  0.957959927729E+01
  0.611177431554E+01  0.958304617161E+01
  0.612453719975E+01  0.958649306594E+01
  0.613727916926E+01  0.958993996026E+01
  0.614996648645E+01  0.959338685458E+01
  0.616258444513E+01  0.959683374890E+01
  0.617515005395E+01  0.960028064323E+01
  0.618768644789E+01  0.960372753755E+01
  0.620019291617E+01  0.960717443187E+01
  0.621267050450E+01  0.961062132619E+01
  0.622512012439E+01  0.961406822052E+01
  0.623754254566E+01  0.961751511484E+01
  0.624993841488E+01  0.962096200916E+01
  0.626230825849E+01  0.962440890349E+01
  0.627465439350E+01  0.962785579781E+01
  0.628699198144E+01  0.963130269213E+01
  0.629932503726E+01  0.963474958645E+01
  0.631164853301E+01  0.963819648078E+01
  0.632394986578E+01  0.964164337510E+01
  0.633620849181E+01  0.964509026942E+01
  0.634839659590E+01  0.964853716374E+01
  0.636048098636E+01  0.965198405807E+01
  0.637243491086E+01  0.965543095239E+01
  0.638438588738E+01  0.965887784671E+01
  0.639638876381E+01  0.966232474103E+01
  0.640843872834E+01  0.966577163536E+01
  0.642052157950E+01  0.966921852968E+01
  0.643261519737E+01  0.967266542400E+01
  0.644469228030E+01  0.967611231833E+01
  0.645672474893E+01  0.967955921265E+01
  0.646868969459E+01  0.968300610697E+01
  0.648062351634E+01  0.968645300129E+01
  0.649255516891E+01  0.968989989562E+01
  0.650445074847E+01  0.969334678994E+01
  0.651628495425E+01  0.969679368426E+01
  0.652805026874E+01  0.970024057858E+01
  0.653977769637E+01  0.970368747291E+01
  0.655152600827E+01  0.970713436723E+01
  0.656336169516E+01  0.971058126155E+01
  0.657532768445E+01  0.971402815588E+01
  0.658726469903E+01  0.971747505020E+01
  0.659911284304E+01  0.972092194452E+01
  0.661086229647E+01  0.972436883884E+01
  0.662252120208E+01  0.972781573317E+01
  0.663410742310E+01  0.973126262749E+01
  0.664564173591E+01  0.973470952181E+01
  0.665714227745E+01  0.973815641613E+01
  0.666862001452E+01  0.974160331046E+01
  0.668009314339E+01  0.974505020478E+01
  0.669158689699E+01  0.974849709910E+01
  0.670310757332E+01  0.975194399343E+01
  0.671465510982E+01  0.975539088775E+01
  0.672622255623E+01  0.975883778207E+01
  0.673779621229E+01  0.976228467639E+01
  0.674935668800E+01  0.976573157072E+01
  0.676088125402E+01  0.976917846504E+01
  0.677234861828E+01  0.977262535936E+01
  0.678378505924E+01  0.977607225368E+01
  0.679520387815E+01  0.977951914801E+01
  0.680660576738E+01  0.978296604233E+01
  0.681799160106E+01  0.978641293665E+01
  0.682936244565E+01  0.978985983097E+01
  0.684071956318E+01  0.979330672530E+01
  0.685206440326E+01  0.979675361962E+01
  0.686339857106E+01  0.980020051394E+01
  0.687472007280E+01  0.980364740827E+01
  0.688602692811E+01  0.980709430259E+01
  0.689731982819E+01  0.981054119691E+01
  0.690859960529E+01  0.981398809123E+01
  0.691986623649E+01  0.981743498556E+01
  0.693112042126E+01  0.982088187988E+01
  0.694236247786E+01  0.982432877420E+01
  0.695359264528E+01  0.982777566852E+01
  0.696481109042E+01  0.983122256285E+01
  0.697602182118E+01  0.983466945717E+01
  0.698724106956E+01  0.983811635149E+01
  0.699846940193E+01  0.984156324582E+01
  0.700970069671E+01  0.984501014014E+01
  0.702092297266E+01  0.984845703446E+01
  0.703211871657E+01  0.985190392878E+01
  0.704326587317E+01  0.985535082311E+01
  0.705433982966E+01  0.985879771743E+01
  0.706531628202E+01  0.986224461175E+01
  0.707621666243E+01  0.986569150607E+01
  0.708717304441E+01  0.986913840040E+01
  0.709819301460E+01  0.987258529472E+01
  0.710926463612E+01  0.987603218904E+01
  0.712037055553E+01  0.987947908337E+01
  0.713148809586E+01  0.988292597769E+01
  0.714259382741E+01  0.988637287201E+01
  0.715366816395E+01  0.988981976633E+01
  0.716470101755E+01  0.989326666066E+01
  0.717570214568E+01  0.989671355498E+01
  0.718669472027E+01  0.990016044930E+01
  0.719767901931E+01  0.990360734362E+01
  0.720865338118E+01  0.990705423795E+01
  0.721961834028E+01  0.991050113227E+01
  0.723057435253E+01  0.991394802659E+01
  0.724152179989E+01  0.991739492091E+01
  0.725246099530E+01  0.992084181524E+01
  0.726339218796E+01  0.992428870956E+01
  0.727431711517E+01  0.992773560388E+01
  0.728524710346E+01  0.993118249821E+01
  0.729618567063E+01  0.993462939253E+01
  0.730713075255E+01  0.993807628685E+01
  0.731807542625E+01  0.994152318117E+01
  0.732900753304E+01  0.994497007550E+01
  0.733990989132E+01  0.994841696982E+01
  0.735076121428E+01  0.995186386414E+01
  0.736153780993E+01  0.995531075846E+01
  0.737222356164E+01  0.995875765279E+01
  0.738292184912E+01  0.996220454711E+01
  0.739367506356E+01  0.996565144143E+01
  0.740447978121E+01  0.996909833576E+01
  0.741532616991E+01  0.997254523008E+01
  0.742619866037E+01  0.997599212440E+01
  0.743707779472E+01  0.997943901872E+01
  0.744794319353E+01  0.998288591305E+01
  0.745877760682E+01  0.998633280737E+01
  0.746957199340E+01  0.998977970169E+01
  0.748034514160E+01  0.999322659601E+01
  0.749111329453E+01  0.999667349034E+01
  0.750187762249E+01  0.100001203847E+02
  0.751263678303E+01  0.100035672790E+02
  0.752338922141E+01  0.100070141733E+02
  0.753413537661E+01  0.100104610676E+02
  0.754487562589E+01  0.100139079620E+02
  0.755561028736E+01  0.100173548563E+02
  0.756633962232E+01  0.100208017506E+02
  0.757706648781E+01  0.100242486449E+02
  0.758779961070E+01  0.100276955392E+02
  0.759854056370E+01  0.100311424336E+02
  0.760928878547E+01  0.100345893279E+02
  0.762003837525E+01  0.100380362222E+02
  0.763077863778E+01  0.100414831165E+02
  0.764149396020E+01  0.100449300109E+02
  0.765216482389E+01  0.100483769052E+02
  0.766276863355E+01  0.100518237995E+02
  0.767329401350E+01  0.100552706938E+02
  0.768383813067E+01  0.100587175881E+02
  0.769443027941E+01  0.100621644825E+02
  0.770506936697E+01  0.100656113768E+02
  0.771574828132E+01  0.100690582711E+02
  0.772645437300E+01  0.100725051654E+02
  0.773717077780E+01  0.100759520598E+02
  0.774787870843E+01  0.100793989541E+02
  0.775856074672E+01  0.100828458484E+02
  0.776920507522E+01  0.100862927427E+02
  0.777982733402E+01  0.100897396370E+02
  0.779044580778E+01  0.100931865314E+02
  0.780106143852E+01  0.100966334257E+02
  0.781167529502E+01  0.101000803200E+02
  0.782228615916E+01  0.101035272143E+02
  0.783289248961E+01  0.101069741087E+02
  0.784349461705E+01  0.101104210030E+02
  0.785409280845E+01  0.101138678973E+02
  0.786468727484E+01  0.101173147916E+02
  0.787527944611E+01  0.101207616860E+02
  0.788587662258E+01  0.101242085803E+02
  0.789648199271E+01  0.101276554746E+02
  0.790709577574E+01  0.101311023689E+02
  0.791771424116E+01  0.101345492632E+02
  0.792832950248E+01  0.101379961576E+02
  0.793892873722E+01  0.101414430519E+02
  0.794949472136E+01  0.101448899462E+02
  0.796000659906E+01  0.101483368405E+02
  0.797044255539E+01  0.101517837349E+02
  0.798086837290E+01  0.101552306292E+02
  0.799133692692E+01  0.101586775235E+02
  0.800185021173E+01  0.101621244178E+02
  0.801240460803E+01  0.101655713121E+02
  0.802299097717E+01  0.101690182065E+02
  0.803359541088E+01  0.101724651008E+02
  0.804420082388E+01  0.101759119951E+02
  0.805478947687E+01  0.101793588894E+02
  0.806534641799E+01  0.101828057838E+02
  0.807587150243E+01  0.101862526781E+02
  0.808639125898E+01  0.101896995724E+02
  0.809690874328E+01  0.101931464667E+02
  0.810742481065E+01  0.101965933611E+02
  0.811794044079E+01  0.102000402554E+02
  0.812845449985E+01  0.102034871497E+02
  0.813896545794E+01  0.102069340440E+02
  0.814947360496E+01  0.102103809383E+02
  0.815997917439E+01  0.102138278327E+02
  0.817048240581E+01  0.102172747270E+02
  0.818098761310E+01  0.102207216213E+02
  0.819149998831E+01  0.102241685156E+02
  0.820202135911E+01  0.102276154100E+02
  0.821255056372E+01  0.102310623043E+02
  0.822308260265E+01  0.102345091986E+02
  0.823360806006E+01  0.102379560929E+02
  0.824411294236E+01  0.102414029872E+02
  0.825457904831E+01  0.102448498816E+02
  0.826498495161E+01  0.102482967759E+02
  0.827533166472E+01  0.102517436702E+02
  0.828570568214E+01  0.102551905645E+02
  0.829612162058E+01  0.102586374589E+02
  0.830657980488E+01  0.102620843532E+02
  0.831707516578E+01  0.102655312475E+02
  0.832759767931E+01  0.102689781418E+02
  0.833813228131E+01  0.102724250362E+02
  0.834866247182E+01  0.102758719305E+02
  0.835917130003E+01  0.102793188248E+02
  0.836964568203E+01  0.102827657191E+02
  0.838010095391E+01  0.102862126134E+02
  0.839055437805E+01  0.102896595078E+02
  0.840100649916E+01  0.102931064021E+02
  0.841145808113E+01  0.102965532964E+02
  0.842191002576E+01  0.103000001907E+02"
    };
    return $rhot_lines;
}


sub make_rhoT_lines
{
    # make rho-T lines
    my $dir=$_[0];
    my $linedata = rhoT_linedata(); 
    my $labels;
    my $gp;
    my $i=3;
    my %elem = (H=>1,
		He=>4,
		C=>12,
		O=>16);
    foreach my $line (sort 
		      {
			  if($a=~/([A-Z][a-z]*)-burn/)
			  {
			      if($b=~/([A-Z][a-z]*)-burn/)
			      {
				  return ($elem{($a=~/([A-Z][a-z]*)-burn/)[0]} <=> 
					  $elem{($b=~/([A-Z][a-z]*)-burn/)[0]});
			      }
			      else
			      {
				  return -1;
			      }
			  }
			  elsif($b=~/([A-Z][a-z]*)-burn/)
			  {
			      return 1;
			  }
			  else
			  {
			      return $a cmp $b;
			  }			      
		      }
		      keys %$linedata)
    {
	open(my $f,'>',"$dir/line$i.dat")||confess("cannot open $dir/line$i.dat");
	print {$f} $$linedata{$line}; 
	close $f;
	no warnings;
	my $w = withstring($i);
	$gp .= ", \"$dir/line$i.dat\" $w title \"$line\"  ";
	use warnings;
	$i++;
    }

    return {lines=>$gp,
	    labels=>$labels};
}

sub solarsymbol
{
    if($gnuplot_options{'terminal type'}=~/postscript/io)
    {
	# use computer modern symbol
	return '{/CMSY10 \\014}';
    }
    else
    {
	# try the UTF8 symbol
	return '⊙';
    }
}

sub check_consts
{
    foreach my $x (qw/PI PI4 value_of_log10 ivalue_of_log10 logmin G logG Msun logMsun Rsun logRsun loggfac Lsun stefan_boltzmann/)
    {
	my $y=eval $x;
	print "Check $x = $y\n"if($vb);
	if($y=~/,/o)
	{
	    print STDERR "Constant $x='$y' has the wrong decimal point format! There should be no commas.\n\nTry changing your locale to the default, 'C'.\nFor example in a bash shell run:\n\n      export LC_ALL=C\n\nand then run window to the stars.\n";
	    exit;
	}
    }
}
