#! /usr/bin/perl -w

## (See usage below for description.)

# Then print the total estimated time for the simulation, too.
# (Requires average time of a single run.)

use strict;

#print "ARGV: @ARGV\n";
&usage if $#ARGV < 0;

my $filename_prefix = shift;

my @params = @{&readParamFile};

# Display data structure
# Calculate total number of runs
my $steps = 1;
foreach my $param (@params) {
  my %param = %$param;
  print "Param: ", %param, "\n";
  $steps *= $param{steps};
}

print "" . ($#params + 1) . " parameters. $steps total number of simulations.\n";

# Count to steps and put into param file

my $filename = $filename_prefix . ".par";
open OFILE, ">$filename" or die "Cannot open $filename for writing!\n";
print OFILE "$steps " . ($#params + 1) . "\n";
for (my $i = 0; $i < $steps; $i++ ) {
  print OFILE paramConf($i);
}

close OFILE if tell(OFILE) != -1;

# Parameter values according to a specific count
sub paramConf {
  my $count = shift;
  my $orig_count = $count;
  
  my $param_row = "";
  foreach my $param (@params) {
    my %param = %$param;
    my $param_step = $count % $param{steps};
    #print "Param $param{name}, val=", $param_step, "\n";
    my $param_val;
    if (exists $param{range_low}) { # Linear increment parameter
      if ( $param{steps} == 1) {
	$param_val = $param{range_low};
      } else {
	$param_val = $param{range_low} + 
	  $param_step * ($param{range_high} - $param{range_low}) / 
	    ( $param{steps} - 1); # inclusive
      }
    } elsif (exists $param{base}) { # Geometric increment parameter
      $param_val = $param{base} * pow($param{mult}, $param_step);
    } elsif (exists $param{start}) { # counter
      $param_val = $param{start} + $orig_count;
    } else {
      die "Invalid parameter: $param\n";
    }
    $param_row .= $param_val . " ";
    $count /= $param{steps};
  }
  $_ = $param_row . "0\n";
}

# Returns the minimum of two values
sub min {
  my $a = shift;
  my $b = shift;

  if ( $a >= $b ) {
    $_ = $b;
  } else {
    $_ = $a;
  }
}

# Returns the power of a number
sub pow {
  my $base = shift;
  my $power = shift;

  $_ = exp($power * log($base));
}

sub readParamFile {
  my @params;

  # Read std input
  while (<>) {
    next if /^\s*#/;		# Skip comment inputs.
    if (/(\w+)\s+([\.\-\w]+)([\*\s]+)([\.\-\w]+)([\^\s]+)(\w+)/) {
      print "Name: '$1', '$2'$3'$4'$5'$6'\n";
      my $name = $1;
      my $base = $2;
      my $top = $4;
      my $steps = $6;
      $_ = $3;
      if (/\*/) {
	# multiplicative increment
	push @params, { name => "$name", base => $base,
			mult => $top, steps => $steps };
      } else {
	# additive increment
	push @params, { name => "$name", range_low => $base,
			range_high => $top, steps => $steps };
      }
    } elsif ((/(\w+)\s+([\.\-\w]+)\s*\+\+\s*([\.\-\w]*)/)) {
      # counter
      print "Name: '$1', '$2'++ '$3'\n";
      push @params, { name => "$1", start => $2,
		      increment => $3, steps => 1 };
    }
  }

  $_ = \@params;
}

sub usage {
  print << "END";
 Usage:
	$0 param_file_prefix < param_desc_file

 Generates one Genesis parameter file, to scan
 a full parameter range. Each parameter is changed
 to a fixed number of steps within its range.

 Parameter ranges come from an input file param_desc_file with a row for each param.
 It can have the following forms:
 1. Additive increments
 	param_name range_low range_high num_steps
 2. Multiplicative increments
 	param_name base_val *mul_factor ^num_steps
 3. Independent counter (indicates trial number)
	param_name start_val ++ [increment]

 See the examples in the brute_scripts/paramRanges*.txt.

 The parameters are written with order of appearance to Genesis files.

 Cengiz Gunay <cgunay\@emory.edu>, 2004/05/15

END

    exit -1;
}