package Sim::OPT;
# Copyright (C) 2008-2015 by Gian Luca Brunetti and Politecnico di Milano.
# This is Sim::OPT, a program for detailed metadesign of buildings managing parametric explorations through the ESP-r building performance simulation platform and performing optimization by block coordinate descent.
# This is free software.  You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.

use v5.14;
# use v5.20;
use Exporter;
use parent 'Exporter'; # imports and subclasses Exporter

use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
use Math::Trig;
use Math::Round;
use List::Util qw[ min max reduce shuffle];
use List::MoreUtils qw(uniq);
use List::AllUtils qw(sum);
use Statistics::Basic qw(:all);
use Storable qw(dclone);
use IO::Tee;
use File::Copy qw( move copy );
use Set::Intersection;
use List::Compare;
use Data::Dumper;
use POSIX;
#$Data::Dumper::Indent = 0;
#$Data::Dumper::Useqq  = 1;
#$Data::Dumper::Terse  = 1;
use Data::Dump qw(dump);
use feature 'say';   
#use feature qw(postderef);
#no warnings qw(experimental::postderef);
#use Sub::Signatures;
#no warnings qw(Sub::Signatures); 
#no strict 'refs';
no strict; 
no warnings;
use warnings::unused;
use Sim::OPT::Morph;
use Sim::OPT::Sim;
use Sim::OPT::Report;
use Sim::OPT::Descend;
use Sim::OPT::Takechance;
use Sim::OPT::Parcoord3d;

our @ISA = qw( Exporter ); # our @adamkISA = qw(Exporter);
#%EXPORT_TAGS = ( DEFAULT => [qw( &opt &prepare )]); # our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
#@EXPORT   = qw(); # our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw( 
opt takechance
odd even _mean_ flattenvariables count_variables fromopt_tosweep fromsweep_toopt convcaseseed 
convchanceseed makeflatvarnsnum calcoverlaps calcmediumiters getitersnum definerootcases
callcase callblocks deffiles makefilename extractcase setlaunch exe start
_clean_ getblocks getblockelts getrootname definerootcases populatewinners 
getitem getline getlines getcase getstepsvar tell wash flattenbox enrichbox filterbox givesize 
$configfile $mypath $exeonfiles $generatechance $file $preventsim $fileconfig $outfile $tofile $report 
$simnetwork @themereports %simtitles %reporttitles %retrievedata 
@keepcolumns @weights @weightsaim @varthemes_report @varthemes_variations @varthemes_steps 
@rankdata @rankcolumn @reporttempsdata @reportcomfortdata %reportdata 
@report_loadsortemps @files_to_filter @filter_reports @base_columns @maketabledata @filter_columns 
@files_to_filter @filter_reports @base_columns @maketabledata @filter_columns %vals 
@sweeps @mediumiters @varinumbers @caseseed @chanceseed @chancedata $dimchance $tee @pars_tocheck retrieve
$target %dowhat readsweeps
);

$VERSION = '0.53.00'; # our $VERSION = '';
$ABSTRACT = 'Sim::OPT is an optimization and morphing program oriented to problem decomposition that can be used with simulation programs receiving text files as input and emitting text files as output. It can mix sequential and parallel block coordinate search methods.';

#################################################################################
# Sim::OPT 
#################################################################################

# FUNCTIONS' SPACE
###########################################################
###########################################################

sub odd 
{
    my $number = shift;
    return !even ($number);
}

sub even 
{
    my $number = abs shift;
    return 1 if $number == 0;
    return odd ($number - 1);
}

sub _mean_ { return @_ ? sum(@_) / @_ : 0 }


sub countarray
{
	my $c = 1;
	foreach (@_)
	{
		foreach (@$_)
		{
			$c++;
		}
	}
	return ($c); # TO BE CALLED WITH: countarray(@array)
}

sub countnetarray
{
	my @bag;
	foreach (@_)
	{
		foreach (@$_)
		{
			push (@bag, $_);
		}
	}
	@bag = uniq(@bag);
	return scalar(@bag); # TO BE CALLED WITH: countnetarray(@array)
}

sub sorttable # TO SORT A TABLE ON THE BASIS OF A COLUMN
{
	my $num = $_[0];
	my @table = @{ $_[1] };
	my @rows;
	foreach my $line (@table) 
	{
	    chomp $line;
	    $sth->execute(line);

	    my @row = $sth->fetchrow_array;
	    unshift (@row, $line);
	    push @rows, \@row;
	}

	@rows = sort { $a->[$num] cmp $b->[$num] } @rows;

	foreach my $row (@rows) {
	    foreach (@$row) {
		print "$_";
	    }
	    print "\n";
	} #TO BE CALLED WITH: sorttable( $number_of column, \@table);
	return (@table);
}

sub _clean_
{ # IT CLEANS A BASKET FROM CASES LIKE "-", "1-", "-1", "".
	my $swap = shift;
	my @arraytoclean = @$swap;
	my @storeinfo;
	foreach (@arraytoclean) 
	{ 
		$_ =~ s/ //;
		unless ( !( defined $_) or ($_ =~ /^-/) or ($_ =~ /-$/) or ($_ =~ /^-$/) or ($_ eq "") or ($_ eq "-") )
		{
			push(@storeinfo, $_)
		}
	}
	return  @storeinfo; # HOW TO CALL THIS FUNCTION: clean(\@arraytoclean). IT IS DESTRUCTIVE.
}

sub present
{
	foreach (@_)
	{
		say "### $_ : " . dump($_);
		say TOFILE "### $_ : " . dump($_);
	}
}
	

sub flattenvariables # IT LISTS THE NUMBER OF VARIABLES PLAY IN A LIST OF BLOCK SEARCHES. ONE COUNT FOR EACH LIST ELEMENT.
{		
	my @array = @_;
	foreach my $case (@array)
	{
		@casederef = @$case;
		my @basket;
		foreach my $block (@casederef)
		{
			@blockelts = @$block;
			push (@basket, @blockelts);
		}
		my @basket = sort { $a <=> $b} uniq(@basket);
		push ( @flatvarns, \@basket ); ###
		# IT HAS TO BE CALLED WITH: flatten_variables(@treeseed);
	} # say "\@NUMVARNS!: " . dump(@numvarns);
}

sub count_variables # IT COUNTS THE FLATTENED VARIABLES
{
	my @flatvarns = @_;
	foreach my $group (@flatvarns)
	{
		my @array = @$group;
		push ( @flatvarnsnum, scalar(@array) );
		# IT HAS TO BE CALLED WITH: count_variables(@flatvarns);
	}
}

# flatten_variables ( [ [1, 2, 3] , [2, 3, 4] , [3, 4, 5] ], [ [1, 2], [2, 3] ] );
#count_variables ([1, 2, 3, 4, 5], [1, 2, 3]);
#say "COUNTFLATTENEDVARNS: @countflattenedvarns";

sub fromopt_tosweep # IT CONVERTS A TREE BLOCK SEARCH FORMAT IN THE ORIGINAL OPT'S BLOCKS SEARCH FORMAT.
{
	my %thishash = %{ $_[0] }; #say "dump(%thishash): " . dump(%thishash);
	my @casegroup = @{ $thishash{casegroup} } ; #say "dump(\@casegroup): " . dump(@casegroup);
	my @chancegroup = @{ $thishash{chancegroup} }; #say "dump(\@chancegroup): " . dump(@chancegroup);
	my @sweeps;
	my $countcase = 0;
	foreach my $case (@casegroup)
	{
		my @blockrefs = @$case; #say "dump(\@blocks): " . dump(@blocks);
		my @chancerefs = @{ $chancegroup[$countcase] }; #say "dump(\@chancerefs): " . dump(@chancerefs);
		my @sweepblocks;
		my $countblock = 0;
		foreach my $elt (@blockrefs)
		{
			my @blockelts = @$elt; #say "dump(\@blockelts): " . dump(@blockelts);
			my $attachpoint = $blockelts[0]; #say "attachpoint: $attachpoint";
			my $blocklength = $blockelts[1]; #say "blocklength: $blocklength";
			my @chances = @{ $chancerefs[$countblock] }; # say "dump(\@chancerefs): " . dump(@chancerefs);
			my @sweepblock = @chances[ $attachpoint .. ($attachpoint + $blocklength - 1) ];	#say "dump(\@sweepblock): " . dump(@sweepblock);
			push (@sweepblocks, [@sweepblock]);
			$countblock++;
		}
		push (@sweeps, [ @sweepblocks ] );
		$countcase++;
	}
	# IT HAS TO BE CALLED THIS WAY: fromopt_tosweep( { casegroup => \@caseseed, chancegroup => \@chanceseed } );
	return (@sweeps);
}

sub fromopt_tosweep_simple # IT CONVERTS A TREE BLOCK SEARCH FORMAT IN THE ORIGINAL OPT'S BLOCKS SEARCH FORMAT.
{
	my %thishash = @_; #say "dump(%thishash): " . dump(%thishash);
	my $casegroupref = $thishash{casegroup};
	my @blocks = @$casegroupref; #say "dump(\@casegroup): " . dump(@casegroup);
	my $chancegroupref = $thishash{chancegroup};
	my @chances = @$chancegroupref; #say "dump(\@chancegroup): " . dump(@chancegroup);
	my $countblock = 0;
	foreach my $elt (@blocks)
	{
		my @blockelts = @$elt; #say "dump(\@blockelts): " . dump(@blockelts);
		my $attachpoint = $blockelts[0]; #say "attachpoint: $attachpoint";
		my $blocklength = $blockelts[1]; #say "blocklength: $blocklength";
		my $chancesref = $chances[$countblock]; # say "dump(\$chancesref): " . dump($chancesref);
		my @chances = @$chancesref; #say "dump(\@chances): " . dump(@chances);
		my @sweepblock = @chances[ $attachpoint .. ($attachpoint + $blocklength - 1) ];	#say "dump(\@sweepblock): " . dump(@sweepblock);
		push (@sweepblocks, [@sweepblock]);
		$countblock++;
	}
	# IT HAS TO BE CALLED THIS WAY: fromopt_tosweep( casegroup => [@caserefs_alias], chancegroup => [@chancerefs_alias] ); # IT IS NOT RELATIVE TO CASE: JUST ONE CASE, THE CURRENT.
	return (@sweepblocks);
}

sub checkduplicates
{
	my %hash = %{$_[0]};
	my @slice = @{$hash{slice}};
	my @sweepblocks = @{$hash{sweepblocks}};
	my $signal = 0;
	foreach my $blockref (@sweepblocks)
	{
		@block = @$blockref;
		if ( @slice ~~ @block )
		{
			$signal++;
		}
	}
	if ($signal == 0) { return "no"; }
	else { return "yes" };
}

sub fromsweep_toopt # IT CONVERTS THE ORIGINAL OPT'S BLOCKS SEARCH FORMAT IN A TREE BLOCK SEARCH FORMAT.
{
	my ( @bucket, @secondbucket );
	my $countcase = 0;
	foreach (@_) # CASES
	{
		my ( @blocks, @chances );
		my $countblock = 0;
		foreach(@$_) # BLOCKS
		{
			#say "dump(\@\$_): " . dump(@$_);
			my $swap = $flatvarns[$countcase];
			my @varns = @$swap; #say "dump(\@varns): " . dump(@varns);
			my @block = @$_;
			my $blocksize = scalar(@block);
			my $lc = List::Compare->new(\@varns, \@block);
			my @intersection = $lc->get_intersection; #say "dump(\@intersection): " . dump(@intersection);
			my @nonbelonging;
			foreach (@varns)
			{
				my @parlist;
				unless ($_ ~~ @intersection)
				{
					push (@nonbelonging, $_);
				}
			}
			#say "dump(\@nonbelonging): " . dump(@nonbelonging);
			push (@blocks, [@intersection, @nonbelonging] ); # say "dump(\@blocks): " . dump(@blocks);
			push (@chances, [0, $blocksize] );
			$countblock++;
		}
		push (@bucket, [ @blocks ] );
		push (@secondbucket, [@chances]);
		$countcase++;
	}
	@chanceseed = @bucket;
	@caseseed = @secondbucket;
	return (\@caseseed, \@chanceseed);
	# IT HAS TO BE CALLED THIS WAY: fromsweep_toopt(@sweep);
}

sub convcaseseed # IT ADEQUATES THE POINT OF ATTACHMENT OF EACH BLOCK TO THE FACT THAT THE LISTS CONSTITUING THEM ARE THREE, JOINED.
{
	my $ref = shift;
	my %hash = %{$ref};
	my @chanceseed = @{ $hash{ chanceseed } }; #say $tee "IN\@chanceseed:" . dump(@chanceseed);
	my @caseseed = @{ $hash{ caseseed } }; #say $tee "IN\@caseseed:" . dump(@caseseed);
	my $countcase = 0;
	foreach $case (@caseseed)
	{
		my $chance = $chanceseed[$countcase];
		my $countblock = 0;
		my @blockrefs = @$case;
		my @chancerefs = @$chance;
		foreach (@blockrefs)
		{
			my $chancelt = scalar ( @{ $chancerefs[$countblock] } ); #say $tee "IN\$chancerefs\[\$countblock]" . dump($chancerefs[$countblock]); say $tee "IN\$chancelt" . dump($chancelt);
			my $numofelts = ( $chancelt / 3 ); #say $tee "IN\$numofelts" . dump($numofelts);
			${$_}[0] = ${$_}[0] + $numofelts;
			$countblock++;
		}
		$countcase++;
	} # TO BE CALLED WITH: convcaseseed(\@caseseed, \@chanceseed). @caseseed IS globsAL. TO BE CALLED WITH: convcaseseed(@caseseed);
	return(@caseseed);
}


sub convchanceseed # IT COUNTS HOW MANY PARAMETERS THERE ARE IN A SEARCH STRUCTURE,
{
	foreach (@_)
	{
		foreach (@$_)
		{
			push (@$_, @$_, @$_); #say "\@\$_ @$_";
		}
	} # IT HAS TO BE CALLED WITH convchanceseed(@chanceseed). IT ACTS ON @chanceseed, WHICH IS globsAL.
	return(@_);
}

sub tellnum
{
	@arr = @_;
	my $response = (scalar(@_)/2);
	return($response); # TO BE CALLED WITH tellnum(%varnums);
}

sub calcoverlaps
{
	my $countcase = 0;
	foreach my $case(@sweeps)
	{
		my @caseelts = @{$case};
		my $contblock = 0;
		my @overlaps;
		foreach my $block (@caseelts)
		{
			my @pasttblock = @{$block[ $contblock - 1 ]};
			my @presentblock = @{$block[ $contblock ]};
			my $lc = List::Compare->new(\@pasttblock, \@presentblock);
			my @intersection = $lc->get_intersection; #say "dump(\@intersection): " . dump(@intersection);
			push (@caseoverlaps, [ @intersection ] );
			$countblock++;
		}
		push (@casesoverlaps, [@overlaps]); # globsAL!
		$countcase++;
	}
}

sub calcmediumiters
{
	my @varinumbers = @_;
	my $countcase = 0;
	my @mediumiters;
	foreach ( @varinumbers )
	{
		my $countblock = 0;
		foreach ( keys %$_ )
		{
			#say "inner dump (\$_): " . dump ($_);
			#say "dumpalias (\$varinumbers[\$countcase]{\$_}): " . dump ($varinumbers[$countcase]{$_});
			unless ( defined $mediumiters[$countcase]{$_} )
			{
				$mediumiters[$countcase]{$_} = ( ceil( $varinumbers[$countcase]{$_} / 2 ) );
			}
		}
		$countcase++;
	} # TO BE CALLED WITH: calcmediumiters(@varinumbers)
	return ( @mediumiters );
}
	
sub getitersnum
{ # IT GETS THE NUMBER OF ITERATION. UNUSED. CUT
	my $countcase = shift;
	my $varinumber = shift;
	my @varinumbers = @_;
	my $itersnum = $varinumbers[$countcase]{$varinumber};
	#say "\$itersnum IN = $itersnum";
	return $itersnum;
	# IT HAS TO BE CALLED WITH getitersnum($countcase, $varinumber, @varinumbers);
}

sub makefilename # IT DEFINES A FILE NAME GIVEN A %carrier.
{
	my %carrier = @_;
	my $filename = "$mypath/$file" . "_";
	my $countcase = 0;
	foreach $key (sort {$a <=> $b} (keys %carrier) )
	{
		$filename = $filename . $key . "-" . $carrier{$key} . "_"; #say "filename: $filename";
	} 
	return ($filename); # IT HAS TO BE CALLED WITH: makefilename(%carrier);
}

sub getblocks
{ # IT GETS @blocks. TO BE CALLED WITH getblocks(\@sweeps, $countcase)
	my $swap = shift; 
	my @sweeps = @$swap;
	my $countcase = shift;
	my @blocks = @{ $sweeps[$countcase]};
	return (@blocks);
}

#@blocks = getblocks(\@sweeps, 0);  say "dumpA( \@blocks) " . dump(@blocks);

sub getblockelts
{ # IT GETS @blockelts. TO BE CALLED WITH getblockelts(\@sweeps, $countcase, $countblock)
	my $swap = shift;
	my @sweeps = @$swap;
	my $countcase = shift;
	my $countblock = shift;
	my @blockelts = sort { $a <=> $b } @{ $sweeps[$countcase][$countblock] };
	return (@blockelts);
}

sub getrootname
{
	my $swap = shift;
	my @rootnames = @$swap;
	my $countcase = shift;
	my $rootname = $rootnames[$countcase];
	return ($rootname);
}

sub extractcase # IT EXTRACTS THE ITEMS TO BE CHANCED FROM A  %carrier, UPDATES THE FILE NAME AND CREATES THE NEW ITEM'S CARRIER
{
	my $file = shift; #say "file: $file";
	my $carrierref = shift; #say "\$carrierref: " . dump($carrierref);
	my @carrierarray = %$carrierref; #say "\@carrierarray: " .  dump(@carrierarray);
	my %carrier = %$carrierref;  #say "\%carrier: " . dump(%carrier);
	my $num = ( scalar(@carrierarray) / 2 ); #say "\$num: $num";
	my $transfile = $file;
	$transfile = "_" . "$transfile";
	my $counter = 0;
	my %provhash;
	while ($counter < $num)
	{	#say "\$counter: $counter";
		$transfile =~ /_(\d+)-(\d+)_/; #say "\$1: $1, \$2: $2"; #say "\$transfileBEFORE: $transfile";
		if ( ($1) and ($2) )
		{
			$provhash{$1} = "$2"; 
		}
		$transfile =~ s/$1-$2//; #say "\$transfileAFTER: $transfile";
		$counter++;
	} #say "provhash: " . dump(%provhash);
	foreach my $key (keys %provhash)
	{
		$carrier{$key} = $provhash{$key}; #say "carrier: " . dump(%carrier);
	}
	my $to = makefilename(%carrier); # say "\$to: $to"; say "carrier: " . dump(%carrier);
	return($to, \%carrier); # IT HAS TO BE CALLED WITH: extractcase("$string", \%carrier), WHERE STRING IS A PIECE OF FILENAME WITH PARAMETERS.
}

sub definerootcases #### DEFINES THE ROOT-CASE'S NAME.
{
	my @sweeps = @{ $_[0] }; #say "dump( \@sweeps) PRE: " . dump(@sweeps); 
	my @miditers = @{ $_[1] }; #say "dump( \@miditers) PRE: " . dump(@miditers); 
	my @rootnames;
	my $countcase = 0;
	foreach my $sweep (@sweeps)
	{
		my $case = $miditers[$countcase];
		my %casetopass;
		my $rootname;
		foreach $key (sort {$a <=> $b} (keys %$case) )
		{
			$casetopass{$key} = $miditers[$countcase]{$key};
		}
		foreach $key (sort {$a <=> $b} (keys %$case) )
		{
			$rootname = $rootname . $key . "-" . $miditers[$countcase]{$key} . "_";
		}
		$rootname = "$file" . "_" . "$rootname"; 
		$casetopass{rootname} = $rootname;
		chomp $rootname;
		push ( @rootnames, $rootname);
		$countcase++;
	}
	return (@rootnames); # IT HAS TO BE CALLED WITH: definerootcase(@mediumiters).
}

sub populatewinners
{
	my @rootnames = @{ $_[0] };
	my $countcase = $_[1];
	my $countblock = $_[2];
	my @winneritems;
	foreach $case ( @rootnames )
	{
		push ( @{ $winneritems[$countcase][$countblock] }, $case );
		$countcase++;
	}
	return( @winneritems );
}


sub getitem
{ # IT GETS THE WINNER OR LOSER LINE. To be called with getitems(\@winner_or_loser_lines, $countcase, $countblock)
	my $swap = shift;
	my @items = @$swap;
	my $countcase = shift;
	my $countblock = shift;
	my $item = $items[$countcase][$countblock];
	my @arr = @$item;
	my $elt = $arr[0];
	return ($elt);
}
	
sub getline
{
	my $item = shift;
	my $file = "$mypath/" . "$item";
	return ($file);
}

sub getlines
{
	my $swap = shift;
	my @items = @$swap;
	my @arr;
	my $countcase = 0;
	foreach (@items)
	{
		foreach ( @{ $_ } )
		{
			push ( @{ $arr[$countcase] } , getline($_) );
		}
		$countcase++;
	}
	return (@arr);
}


sub getcase
{
	my $swap = shift;
	my @items = @$swap;
	my $countcase = shift;
	my $itemref = $items[$countcase];
	my %item = %{ $itemref };
	return ( %item );
}

sub getstepsvar
{ 	# IT EXTRACTS $stepsvar
	my $countvar = shift;
	my $countcase = shift;
	my $swap = shift;
	my @varinumbers = @$swap; 
	my $varnumsref = $varinumbers[ $countcase ]; 
	my %varnums = %{ $varnumsref };
	my $stepsvar = $varnums{$countvar};
	return ($stepsvar)
} #getstepsvar($countvar, $countcase, \@varinumbers);

sub givesize
{	# IT RETURNS THE SEARCH SIZE OF A BLOCK.
	my $sliceref = shift;
	my @slice = @$sliceref;
	my $countcase = shift;
	my $varinumberref = shift;
	my $product = 1;
	foreach my $elt (@slice)
	{
		my $stepsize = Sim::OPT::getstepsvar($elt, $countcase, $varinumberref);
		$product = $product * $stepsize;
	}
	return ($product); # TO BE CALLED WITH: givesize(\@slice, $countcase, \@varinumbers);, WHERE SLICE MAY BE @blockelts in SIM::OPT OR @presentslice OR @pastslice IN Sim::OPT::Takechance
} 

sub wash # UNUSED. CUT.
{
	my @instances = @_;
	my @bag;
	my @rightbag;
	foreach my $instanceref (@instances)
	{
		my %d = %{ $instanceref };
		my $to = $d{to};
		push (@bag, $to);
	}
	my $count = 0;
	foreach my $instanceref (@instances)
	{
		my %d = %{ $instanceref };
		my $to = $d{to};
		if ( not ( $to ~~ @bag ) )
		{
			push ( @rightbag, \%d );
		}
	}
	return (@rightbag); # TO BE CALLED WITH wash(@instances);
}

sub flattenbox
{
	my @basket;
	foreach my $eltsref (@_)
	{
		my @elts = @$eltsref;
		push (@basket, @elts);
	}
	return(@basket);
}


sub integratebox
{
	my @arrelts = @{ $_[0] }; #say "\@arrelts " . dump(@arrelts);
	my %carrier = %{ $_[1] }; #say "\%carrier " . dump(%carrier);
	my $file = $_[2]; #say "\$file " . dump($file);
	my @newbox;
	foreach my $eltref ( @arrelts )
	{
		my @elts = @{ $eltref }; #say "\@elts " . dump(@elts);
		my $target = $elts[0]; #say "\$target " . dump($target);
		my $origin = $elts[3]; #say "\$origin " . dump($origin);
		my @result = extractcase( $target, \%carrier ); #say "\@result: " . dump(@result);
		my $righttarget = $result[0];
		my @result = extractcase( $origin, \%carrier );
		my $rightorigin = $result[0];
		push (@newbox, [ $righttarget, $elts[1], $elts[2], $rightorigin ] );
	}
	return (@newbox); # TO BE CALLED WITH: integratebox(\@flattened, \%mids), $file); # %mids is %carrier. $file is the blank root folder.
}


sub filterbox
{
	@arr = @_;
	my @basket; 
	my @box;
	foreach my $case ( @arr )
	{
		my $elt = $case->[0];
		if ( not ( $elt ~~ @box ) )
		{
			my @bucket;
			foreach $caseagain ( @arr )
			{
				my $el = $caseagain->[0];
				if ( $elt ~~ $el )
				{
					push ( @bucket, $case );
				}
			}
			my $parent = $bucket[0];
			push ( @basket, $parent );
			foreach ( @basket )
			{
				push ( @box, $_->[0] );
			}
		}
	}
	return( @basket );
}

sub callcase # IT PROCESSES THE CASES.
{
	my $swap = shift;
	my %dat = %{$swap};
	my $countcase = $dat{countcase}; #say $tee "IN CALLCASE dump(\$countcase): " . dump($countcase);
	my $countblock = $dat{countblock}; #say $tee"dump(\$countblock): " . dump($countblock);
	my @miditers = @{ $dat{miditers} }; #say $tee "dump(\@miditers): " . dump(@miditers); # IT BECOMES THE CARRIER. INITIALIZED AT FIRST BLOCKS; INHERITED AFTER.
	my @sweeps = @{ $dat{sweeps} }; #say $tee"dumpIN( \@sweeps) " . dump(@sweeps);
	my @sourcesweeps = @{ $dat{sourcesweeps} }; #say $tee"dumpIN( \@sourcesweeps) " . dump(@sourcesweeps);
	my @winneritems = @{ $dat{winneritems} }; #say $tee"dumpIN( \@winneritems) " . dump(@winneritems);
	my %dirfiles = %{ $dat{dirfiles} }; #say $tee"dumpIN( \%dirfiles) " . dump(%dirfiles);
	my @uplift = @{ $dat{uplift} }; #say $tee"dumpIN( \@uplift) " . dump(@uplift);
	my @backvalues = @{ $dat{backvalues} }; #say $tee "dump(\@backvalues): " . dump(@backvalues);
	#eval($getparshere);
	
	my $rootname = getrootname(\@rootnames, $countcase); #say "dump(\$rootname): " . dump($rootname);
	my @blockelts = getblockelts(\@sweeps, $countcase, $countblock); #say "dumpIN( \@blockelts) " . dump(@blockelts);
	my @blocks = getblocks(\@sweeps, $countcase);  #say "dumpIN( \@blocks) " . dump(@blocks);
	my $toitem = getitem(\@winneritems, $countcase, $countblock); #say "dump(\$toitem): " . dump($toitem);
	my $from = getline($toitem); #say "dump(\$from): " . dump($from);
	#my @winnerlines = getlines( \@winneritems ); say "dump(\@winnerlines): " . dump(@winnerlines);
	my %varnums = getcase(\@varinumbers, $countcase); #say "dumpININ---(\%varnums): " . dump(%varnums); 
	my %mids = getcase(\@miditers, $countcase); #say "dumpININ---(\%mids): " . dump(%mids); 
	#eval($getfly);
	
	if ($countblock == 0 ) { my %dirfiles; }
	$dirfiles{simlist} = "$mypath/$file-simlist--$countcase";
	$dirfiles{morphlist} = "$mypath/$file-morphlist--$countcase";
	$dirfiles{retlist} = "$mypath/$file-retlist--$countcase"; 
	$dirfiles{replist} = "$mypath/$file-replist--$countcase"; # # FOR RETRIEVAL
	$dirfiles{descendlist} = "$mypath/$file-descendlist--$countcase"; # UNUSED FOR NOW
	$dirfiles{simblock} = "$mypath/$file-simblock--$countcase-$countblock";
	$dirfiles{morphblock} = "$mypath/$file-morphblock--$countcase-$countblock";
	$dirfiles{retblock} = "$mypath/$file-retblock--$countcase-$countblock"; 
	$dirfiles{repblock} = "$mypath/$file-repblock--$countcase-$countblock"; # # FOR RETRIEVAL
	$dirfiles{descendblock} = "$mypath/$file-descendblock--$countcase-$countblock"; # UNUSED FOR NOW
	
	#if ($countblock == 0 ) 
	#{
	#	( $dirfiles{morphcases}, $dirfiles{morphstruct}, $dirfiles{simcases}, $dirfiles{simstruct}, $dirfiles{retcases}, 
	#	$dirfiles{retstruct}, $dirfiles{repcases}, $dirfiles{repstruct}, $dirfiles{mergecases}, $dirfiles{mergestruct}, 
	#	$dirfiles{descendcases}, $dirfiles{descendstruct} );
	#}
	
	#open( OUTFILE, ">>$outfile" ) or die "Can't open $outfile: $!"; 
	open( TOFILE, ">>$tofile" ) or die "Can't open $tofile: $!"; 
	
	#if ( ($countcase > 0) or ($countblock > 0) )
	#{
		#say $tee "#Called for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	#}

	#my @taken = extractcase("$toitem", \%mids); #say "------->taken: " . dump(@taken);
	#my $to = $taken[0]; #say "to-------->: $to";
	#my %carrier = %{$taken[1]}; #say "\%instancecarrier:--------->" . dump(%instancecarrier);
	#say $tee "#Calling a new block for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	my $casedata = { 
				countcase => $countcase, countblock => $countblock,
				miditers => \@miditers,  winneritems => \@winneritems, 
				dirfiles => \%dirfiles, uplift => \@uplift,
				backvalues => \@backvalues,
				sweeps => \@sweeps, sourcesweeps => \@sourcesweeps, 
			};
	#say $tee "IN OPT.pm, \$casedata: " . dump($casedata); 
	callblocks( $casedata );
	if ( $countblock != 0 ) { return($casedata); }
}

sub callblocks # IT CALLS THE SEARCH ON BLOCKS.
{	
	my $swap = shift;
	my %dat = %{$swap};
	my $countcase = $dat{countcase}; #say $tee "dump(\$countcase): " . dump($countcase);
	my $countblock = $dat{countblock}; #say $tee "dump(\$countblock): " . dump($countblock);
	my @sweeps = @{ $dat{sweeps} }; #say $tee"IN CALLBLOCKS dumpIN( \@sweeps) " . dump(@sweeps);
	my @sourcesweeps = @{ $dat{sourcesweeps} }; say $tee"dumpIN( \@sourcesweeps) " . dump(@sourcesweeps);
	my @miditers = @{ $dat{miditers} }; #say $tee "dump(\@miditers): " . dump(@miditers);
	my @winneritems = @{ $dat{winneritems} }; #say $tee "dumpIN( \@winneritems) " . dump(@winneritems);
	my %dirfiles = %{ $dat{dirfiles} }; #say $tee "dumpIN( \%dirfiles) " . dump(%dirfiles);
	my @uplift = @{ $dat{uplift} }; #say $tee "dumpIN( \@uplift) " . dump(@uplift);
	my @backvalues = @{ $dat{backvalues} }; #say $tee "dump(\@backvalues): " . dump(@backvalues);
	#eval($getparshere);
	
	my $rootname = getrootname(\@rootnames, $countcase); #say $tee "dump(\$rootname): " . dump($rootname);
	my @blockelts = getblockelts(\@sweeps, $countcase, $countblock); #say $tee "dumpIN( \@blockelts) " . dump(@blockelts);
	my @blocks = getblocks(\@sweeps, $countcase);  #say $tee "dumpIN( \@blocks) " . dump(@blocks);
	my $toitem = getitem(\@winneritems, $countcase, $countblock); #say $tee "dump(\$toitem): " . dump($toitem);
	my $from = getline($toitem); #say $tee "dump(\$from): " . dump($from);
	my %varnums = getcase(\@varinumbers, $countcase); #say $tee "dumpININ---(\%varnums): " . dump(%varnums); 
	my %mids = getcase(\@miditers, $countcase); #say $tee "dumpININ---(\%mids): " . dump(%mids); 
	#eval($getfly);
	#say $tee "#Called for a new block for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	#say $tee "#Calling to define new files for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	my $blockdata = 
	{ 
		countcase => $countcase, countblock => $countblock,
		miditers => \@miditers,  winneritems => \@winneritems, 
		dirfiles => \%dirfiles, uplift => \@uplift, 
		backvalues => \@backvalues,
		sweeps => \@sweeps, sourcesweeps => \@sourcesweeps, 
	}; 
	#say $tee "\ndumpBLOCK(\$blockdata): " . dump($blockdata) . "\n\n";
	deffiles( $blockdata );
}
	
sub deffiles # IT DEFINED THE FILES TO BE CALLED. 
{
	my $swap = shift;
	my %dat = %{$swap};
	my $countcase = $dat{countcase}; #say $tee  "#dump(\$countcase): " . dump($countcase);
	my $countblock = $dat{countblock}; #say $tee  "#dump(\$countblock): " . dump($countblock);
	my @sweeps = @{ $dat{sweeps} }; #say $tee"IN DEFFILES dumpIN( \@sweeps) " . dump(@sweeps);
	my @sourcesweeps = @{ $dat{sourcesweeps} }; #say $tee"dumpIN( \@sourcesweeps) " . dump(@sourcesweeps);
	my @miditers = @{ $dat{miditers} }; #say $tee  "#dump(\@miditers): " . dump(@miditers);
	my @winneritems = @{ $dat{winneritems} }; #say $tee  "#dumpIN( \@winneritems) " . dump(@winneritems);
	my %dirfiles = %{ $dat{dirfiles} }; #say $tee  "#dumpIN( \%dirfiles) " . dump(%dirfiles);
	my @uplift = @{ $dat{uplift} }; #say $tee  "#dumpIN( \@uplift) " . dump(@uplift);
	my @backvalues = @{ $dat{backvalues} }; #say $tee "dump(\@backvalues): " . dump(@backvalues);
	#eval($getparshere);
	
	my $rootname = getrootname(\@rootnames, $countcase); #say $tee  "#dump(\$rootname): " . dump($rootname);
	my @blockelts = getblockelts(\@sweeps, $countcase, $countblock); #say $tee "#dumpIN( \@blockelts) " . dump(@blockelts);
	my @blocks = getblocks(\@sweeps, $countcase);  #say $tee "#dumpIN( \@blocks) " . dump(@blocks);
	my $toitem = getitem(\@winneritems, $countcase, $countblock); #say $tee  "#dump(\$toitem): " . dump($toitem);
	my $from = getline($toitem); #say $tee  "#dump(\$from): " . dump($from);
	my %varnums = getcase(\@varinumbers, $countcase); #say $tee  "#dumpININ---(\%varnums): " . dump(%varnums); 
	my %mids = getcase(\@miditers, $countcase); #say $tee  "#dumpININ---(\%mids): " . dump(%mids);
	#eval($getfly);
	
	#say $tee "#Called to define new files for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	
	my $rootitem = "$file" . "_"; #say "\$rootitem $rootitem";
	my (@basket, @box);
	push (@basket, [ $rootitem ] );
	foreach my $var ( @blockelts )
	{
		my @bucket;
		my $maxvalue = $varnums{$var}; #say $tee  "#\$countblock $countblock, var: $var, maxvalue: $maxvalue";
		foreach my $elt (@basket)
		{
			my $root = $elt->[0]; #say $tee  "#\$root " . dump($root);
			my $cnstep = 1;
			while ( $cnstep <= $maxvalue)
			{
				my $olditem = $root;
				my $item = "$root" . "$var" . "-" . "$cnstep" . "_" ; #say $tee  "#\$item making: $item, \$root: $root, \$var: $var, \$cnstep: $cnstep, \$root: $root ";
				push (@bucket, [$item, $var, $cnstep, $olditem] ); #say $tee  "#\@bucketIN " . dump(@bucket);
				$cnstep++;
			}
		} 
		@basket = ();
		@basket = @bucket;
		push ( @box, [ @bucket ] );
		#say $tee  "#\@box INOUT" . dump(@box);
	} 
	#say $tee  "#\@box!: " . dump ( @box );
	
	my @flattened = flattenbox(@box); #say $tee  "#\@flattened: " . dump(@flattened) . ", " . scalar(@flattened);
	my @integrated = integratebox(\@flattened, \%mids, $file); #say $tee  "#\@integrated " . dump(@integrated) . ", " . scalar(@integrated);
	my @finalbox = filterbox(@integrated); #say $tee  "#\@finalbox " . dump(@finalbox) . ", " . scalar(@finalbox); 
	
	#say $tee "#Calling to instruct the launch of new searches for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	my $datatowork = 
	{ 
		countcase => $countcase, countblock => $countblock,
		miditers => \@miditers,  winneritems => \@winneritems, 
		dirfiles => \%dirfiles, uplift => \@uplift, 
		basket => \@finalbox,
		backvalues => \@backvalues,
		sweeps => \@sweeps, sourcesweeps => \@sourcesweeps, 
	} ;
	#say $tee "\ndumper-datatowork: " . dump($datatowork) . "\n\n";
	setlaunch( $datatowork );
}	

sub setlaunch # IT SETS THE DATA FOR THE SEARCH ON THE ACTIVE BLOCK.
{
	my $swap = shift;
	my %dat = %{$swap};
	my $countcase = $dat{countcase}; #say "dump(\$countcase): " . dump($countcase);
	my $countblock = $dat{countblock}; #say "dump(\$countblock): " . dump($countblock);
	my @sweeps = @{ $dat{sweeps} }; #say $tee"IN SETLAUNCH dumpIN( \@sweeps) " . dump(@sweeps);
	my @sourcesweeps = @{ $dat{sourcesweeps} }; #say $tee"dumpIN( \@sourcesweeps) " . dump(@sourcesweeps);
	my @miditers = @{ $dat{miditers} }; #say "dump(\@miditers): " . dump(@miditers);
	my @winneritems = @{ $dat{winneritems} }; #say "dumpIN( \@winneritems) " . dump(@winneritems);
	my %dirfiles = %{ $dat{dirfiles} }; #say "dumpIN( \%dirfiles) " . dump(%dirfiles);
	my @uplift = @{ $dat{uplift} }; #say "dumpIN( \@uplift) " . dump(@uplift);
	my @basket = @{ $dat{basket} }; #say "dumpIN( \@basket) " . dump(@basket);
	my @backvalues = @{ $dat{backvalues} }; #say $tee "dump(\@backvalues): " . dump(@backvalues);
	#eval($getparshere);
			
	my $rootname = getrootname(\@rootnames, $countcase); #say "dump(\$rootname): " . dump($rootname);
	my @blockelts = getblockelts(\@sweeps, $countcase, $countblock); #say "dumpIN( \@blockelts) " . dump(@blockelts);
	my @blocks = getblocks(\@sweeps, $countcase);  #say "dumpIN( \@blocks) " . dump(@blocks);
	my $toitem = getitem(\@winneritems, $countcase, $countblock); #say "dump(\$toitem): " . dump($toitem);
	my $from = getline($toitem); #say "dump(\$from): " . dump($from);
	my %varnums = getcase(\@varinumbers, $countcase); #say "dumpININ---(\%varnums): " . dump(%varnums); 
	my %mids = getcase(\@miditers, $countcase); #say "dumpININ---(\%mids): " . dump(%mids); 
	#eval($getfly);
	
	#say $tee "#Called to instruct the launch of new searches for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	
	my ( @instances, %carrier);
	#if ($countblock == 0)
	#{
	#	%carrier = %mids; #say "\%carrier! STARTING:--->" . dump(%carrier);
	#}
	#else 
	#{
	#	my $prov = "_" . "$winnerline";
	#	%carrier = extractcase( $prov , \%carrier ); #say "\%carrier! EXTRACTED:--->" . dump(%carrier);
	#}

	foreach my $elt ( @basket )
	{
		
		my $newpars = $$elt[0]; #say "\$newpars : $newpars"; 
		my $countvar = $$elt[1]; #say "\$countvar : $countvar"; 
		my $countstep = $$elt[2]; #say "\$countstep : $countstep"; 
		my $oldpars = $$elt[3]; #say "\$oldpars : $oldpars"; 
		my @taken = extractcase("$newpars", \%mids); #say "--->taken: " . dump(@taken);
		my $to = $taken[0]; #say "to--->: $to";
		#my %instancecarrier = %{$taken[1]}; #say "\%instancecarrier!:--->" . dump(%instancecarrier); # UNUSED
		my @olds = extractcase("$oldpars", \%mids); #say "--->@olds " . dump(@olds);
		my $origin = $olds[0]; #say "$origin--->: $origin";
		push (@instances, 
		{ 
			countcase => $countcase, countblock => $countblock,
			miditers => \@miditers,  winneritems => \@winneritems, 
			dirfiles => \%dirfiles, uplift => \@uplift, 
			to => $to, countvar => $countvar, countstep => $countstep,
			origin => $origin,
			backvalues => \@backvalues,
			sweeps => \@sweeps, sourcesweeps => \@sourcesweeps, 
		} );
	} 
	#say $tee "#Calling to execute the launch of new searches for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	
	#say $tee "\ninstances: " . dump(@instances). "\n\n"; ### ZZZ
	exe( @instances ); # IT HAS TO BE CALLED WITH: setlaunch(@datatowork). @datatowork ARE CONSTITUTED BY AN ARRAY OF: ( [ \@blocks, \%varnums, \%bases, $name, $countcase, \@blockelts, $countblock ], ... )
}	
		
sub exe
{     
	my @instances = @_;
	
	my $firstinst = $instances[0];
	my %d = %{ $firstinst };
	my $countcase = $d{countcase}; #say "dump(\$countcase): " . dump($countcase);
	my $countblock = $d{countblock}; #say "dump(\$countblock): " . dump($countblock);
	my %dirfiles = %{ $d{ dirfiles } };
	
	say $tee "#Executing new searches for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
	#say $tee "Do what:" . dump(%dowhat);
	
	if ( $dowhat{morph} eq "y" ) 
	{ 
		say $tee "#Calling morphing operations for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
		#say $tee "WITH: \@instances " . dump(@instances) . ", \$countcase $countcase, \$countblock $countblock, \%dirfiles " . dump(%dirfiles) . ".";
		my @result = Sim::OPT::Morph::morph( 
		{ 
			configfile => $configfile, instances => \@instances, countcase => $countcase, countblock => $countblock,
			dirfiles => \%dirfiles
		} );
		$dirfiles{morphcases} = $result[0];
		$dirfiles{morphstruct} = $result[1];
	}

	if ( $dowhat{simulate} eq "y" )
	{ 
		say $tee "#Calling simulations for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
		my @result = Sim::OPT::Sim::sim( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
		$dirfiles{simcases} = $result[0]; #say $tee "\$dirfiles{simcases} : " . dump( $dirfiles{simcases} );
		$dirfiles{simstruct} = $result[1]; 
	}

	if ( $dowhat{retrieve} eq "y" )
	{ 
		say $tee "#Calling retrieval of results for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
		my @result = Sim::OPT::Report::retrieve( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
		$dirfiles{retcases} = $result[0]; #say $tee "\$dirfiles{retcases} : " . dump( $dirfiles{retcases} );
		$dirfiles{retstruct} = $result[1];
		$dirfiles{notecases} = $result[2];
	}

	if ( $dowhat{report} eq "y" )
	{ 
		say $tee "#Calling reporting of results for case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
		my @result = Sim::OPT::Report::report( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
		$dirfiles{repcases} = $result[0];
		$dirfiles{repstruct} = $result[1];
		$dirfiles{mergestruct} = $result[2];
		$dirfiles{mergecases} = $result[3];
		$dirfiles{repfilebackup} = $result[4];
	}

	if ( $dowhat{descend} eq "y" )
	{ 
		say $tee "#Calling descent in case " . ($countcase +1) . ", block " . ($countblock + 1) . ".";
		my @result = Sim::OPT::Descend::descend( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles, repfile => $repfile
		} );
		$dirfiles{descendcases} = $result[0];
		$dirfiles{descendstruct} = $result[1];
	}

	if ( $dowhat{substitutenames} eq "y" )
	{
		 Sim::OPT::Report::filter_reports( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
	}

	if ( $dowhat{filterconverted} eq "y" )
	{
		 Sim::OPT::Report::convert_filtered_reports( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
	}

	if ( $dowhat{make3dtable} eq "y" )
	{
		 Sim::OPT::Report::maketable( 
		{ 
			instances => \@instances, countcase => $countcase, countblock => $countblock, 
			dirfiles => \%dirfiles
		} );
	} #say "getexe: " . dump(@instances);
} # END SUB exe
	
sub start
{
###########################################
say "\nHi. This is Sim::OPT,
a morphing and optimizazion program exploiting problem decomposition.
Please insert the name of a configuration file for OPT (Unix path):\n";
###########################################
	$configfile = <STDIN>;
	chomp $configfile;
	if (-e $configfile ) { ; }
	else { &start; }
}

sub readsweeps
{
	my @struct = @_;
	my @sourcestruct = @{ dclone( \@struct ) };
	foreach ( @struct )
	{
		foreach ( @$_ )
		{
			if ( ( $_->[0] eq "s" ) or ( $_->[0] eq "p" ) )
			{
				shift( @$_ );
			}
		}
	}
	return ( \@struct, \@sourcestruct );
}

###########################################################################################
		
sub opt
{
	&start;
	#eval `cat $configfile`; # The file where the program data are

	require $configfile;
	#if ( not ( $outfile ) ) { $outfile = "$mypath/$file-$fileconfig-feedback.txt"; } 
	if ( not ( $tofile ) ) { 
		unless ( ( "$^O" eq "MSWin32" ) or ( "$^O" eq "MSWin64" ) )
		{
			$tofile = "$mypath/$file-tofile.txt"; 
		}
		else
		{
			$tofile = "$mypath\\$file-tofile.txt"; 
		}
	} 
	
	$tee = new IO::Tee(\*STDOUT, ">>$tofile"); # GLOBAL ZZZ

#	if ($casefile) { eval `cat $casefile` or die( "$!" ); }
#	if ($chancefile) { eval `cat $chancefile` or die( "$!" ); }

	print "\nNow in Sim::OPT. \n";
		
	#open( OUTFILE, ">>$outfile" ) or die "Can't open $outfile: $!"; 
	open( TOFILE, ">>$tofile" ) or die "Can't open $tofile: $!"; 
		
	#unless (-e "$mypath") 
	#{ 
	#	if ($exeonfiles eq "y") 
	#	{
	#		`mkdir $mypath`; 
	#	} 
	#}
	#unless (-e "$mypath") 
	#{ 
	#	print TOFILE "mkdir $mypath\n\n"; 
	#}
	
	if ( not ( $target ) ) { $target = "opt"; }
	
	#####################################################################################
	# INSTRUCTIONS THAT LAUNCH OPT AT EACH SWEEP (SUBSPACE SEARCH) CYCLE
	
	if ( not ( ( @chanceseed ) and ( @caseseed ) and ( @chancedata ) ) )
	{
		if ( ( @sweepseed ) and ( @chancedata ) ) # IF THIS VALUE IS DEFINED. TO BE FIXED. ZZZ
		{
			my $yield = fromsweep_toopt( @sweeps ); #say $tee "Dumper(\$yield): " . Dumper($yield);
			@caseseed = @{ $yield[0] }; #say $tee "Dumper(\@caseseed): " . Dumper(@caseseed);
			@chanceseed = @{ $yield[1] }; #say $tee "Dumper(\@chanceseed): " . Dumper(@chanceseed);
		}
	}
			
	#say $tee "\@chanceseedINI: " . Dumper(@chanceseed); # GLOBAL ZZZ
	#say $tee "\@caseseedINI: " . Dumper(@caseseed); # GLOBAL ZZZ
	@chanceseed = convchanceseed(@chanceseed); #say $tee "convchanceseed Dumper(\@chanceseed): " . Dumper(@chanceseed); # GLOBAL ZZZ	
	@caseseed = convcaseseed( { caseseed => \@caseseed, chanceseed => \@chanceseed } ); #say $tee "convcaseseed Dumper(\@caseseed): " . Dumper(@caseseed) ; # GLOBAL ZZZ
	#say $tee "Dumper(\@chancedata): " . Dumper(@chancedata) ; # GLOBAL ZZZ
	#say $tee "Dumper(\$dimchance): " . Dumper($dimchance) ; # GLOBAL ZZZ
	my (@sweeps_);
	
	if ( ( $target eq "takechance" ) and (@chancedata) and ( $dimchance ) ) 
	{
		my @obt = Sim::OPT::Takechance::takechance( \@caseseed, \@chanceseed, \@chancedata, $dimchance ); say $tee "PASSED: \@sweeps: " . dump(@sweeps);
		@sweeps_ = @{ $obt[0] };
		@caseseed_ = @{ $obt[1] };
		@chanceseed_ = @{ $obt[2] };
		open( MESSAGE, ">./search_structure_that_may_be_adopted.txt" );
		say MESSAGE "\@sweeps_ " . Dumper(@sweeps_);
		say MESSAGE "\THESE VALUES OF \@sweeps IS EQUIVALENT TO THE FOLLOWING VALUES OF \@caseseed AND \@chanceseed: ";
		say MESSAGE "\n\@caseseed " . Dumper(@caseseed_);
		say MESSAGE "\n\@chanceseed_ " . Dumper(@chanceseed_);
		close MESSAGE;
		
		if ( not (@sweeps) ) # CONSERVATIVE CONDITION. IT MAY BE CHANCED. ZZZ
		{
			@sweeps = @sweeps_ ; # say "\@tree: " . Dumper(@tree);
		}
	}		
	elsif ( $target eq "opt" )
	{
		#my  $itersnum = $varinumbers[$countcase]{$varinumber}; say "\$itersnum: $itersnum"; 
		#say "dump(\@varinumbers), " . dump(@varinumbers); #say "dumpBEFORE(\@miditers), " . dump(@miditers);
		my ( $sweepz_ref, $sourcesweeps_ref ) = readsweeps( @sweeps );
		my @sweepz = @$sweepz_ref;
		my @sourcesweeps = @$sourcesweeps_ref; #say $tee "STARTING, \@sourcesweeps : " . dump( @sourcesweeps );
		if ( @sweepz )
		{
			@sweeps = @sweepz;
		}
		
		calcoverlaps( @sweeps ); # PRODUCES @calcoverlaps WHICH IS global. ZZZ
		#say $tee "VARINUMBERS: " . dump ( @varinumbers );
		@mediumiters = calcmediumiters( @varinumbers ); #say $tee "(\@mediumitems): " . dump(@mediumiters); # globsALS. ZZZ
		#$itersnum = getitersnum($countcase, $varinumber, @varinumbers); #say "\$itersnum OUT = $itersnum";
		
		@rootnames = definerootcases(\@sweeps, \@mediumiters); #say $tee "BEGINNING \@rootnames " . dump(@rootnames); 
		
		my $countcase = 0;
		my $countblock = 0;

		my @winneritems = populatewinners( \@rootnames, $countcase, $countblock ); #say $tee "BEGINNING \@winneritems " . dump(@winneritems);
		
		callcase( { countcase => $countcase, rootnames => \@rootnames, countblock => $countblock, 
		miditers => \@mediumiters,  winneritems => \@winneritems, sweeps => \@sweeps, sourcesweeps => \@sourcesweeps } ); #EVERYTHING HAS TO BEGIN IN SOME WAY
	}
	elsif ( ( $target eq "parcoord3d" ) and (@chancedata) and ( $dimchance ) ) 
	{
		Sim::OPT::Parcoord3d::parcoord3d;
	}
	
	close(OUTFILE);
	close(TOFILE);
	exit;
} # END 

#############################################################################

1;

__END__

=head1 NAME

Sim::OPT.

=head1 SYNOPSIS

  use Sim::OPT;
  opt;

=head1 DESCRIPTION

Sim::OPT is an optimization and morphing program oriented to problem decomposition that can be used with simulation programs receiving text files as input and emitting text files as output. Some of Sim::OPT's optimization modules (Sim::OPT, Sim::OPT::Descent) pursue optimization through block search, with blocks (subspaces) that can overlap. Sim::OPT allows a free intermix of a sequential search approach (inexact Gauss-Seidel method) and a parallell one (inexact Jacobi method). Among other things, this possibility can be useful to model human design procedures and compare their efficiency. The Sim::OPT::Takechange module seeks for the least explored search paths when exploring new search spaces sequentially. The Sim::OPT::Morph, Sim::OPT's morphing module, manipulates text files recognizing variables by position. The Sim::OPT::Parcoord3d module converts 2D parallel coordinates plots in Autolisp instructions for obtaining 3D plots as Autocad drawings.

Sim::OPT's morphing and reporting modules contain several additional functions specifically targeting the ESP-r building performance simulation platform. A working knowledge of ESP-r (http://www.esru.strath.ac.uk/Programs/ESP-r.htm) is necessary to use those functionalities.

To install Sim::OPT, the command <cpanm Sim::OPT> has to be issued as a superuser. OPT can then be loaded through the command <use Sim::OPT> in a Perl repl. To ease the launch, the batch file "opt" (which can be found packed in the "optw.tar.gz" file in "examples" folder in this distribution) may be copied in a work directory and the command <opt> may be issued (not as a superuser). That command will call OPT with the settings specified in the configuration file. When launched, Sim::OPT will ask the path to that file, which must contain a suitable description of the operations to be accomplished and point to an existing simulation model.

In "optw.tar.gz" there is an example of an OPT configuration file for an ESP-r model and another one for an EnergyPlus (another building performance simulation program) model for the same building and the same morphing operations. The "$mypath" variable in the configuration file must be set to the work directory where the base model reside.

To run the morphing functions of OPT without making OPT launch the simulation program, the setting <$exeonfiles = "n";> should be specified in the configuration file. That way the commands will only be printed to a file. This can be aimed to inspect the commands that OPT would give the simulation program.

Besides an OPT configuration file, separate configuration files for propagation of constraints may be created. Those can be useful to give the morphing operations greater flexibility. Propagation of constraints can regard the geometry of a model, solar shadings, mass/flow network, controls, and generic text files descripting a simulation model.

The simulation model folders and the result files that will be created in a parametric search will be named as the base model, plus numbers and other characters  naming model instances. For example, the instance produced in the first iteration for a root model named "model" in a search constituted by 3 morphing phases and 5 iteration steps each will be named "model_1-1_2-1_3-1"; and the last one "model_1-5_2-5_3-5".

The structure of block searches is described through the variable "@sweeps" in the configuration file. Each case is listed inside square brackets. And each search subspace (block) in each case is listed inside square brakets. For example: a sequence constituted by two brute force searches, one regarding parameters 1, 2, 3 and the other regarding parameters 1, 4, 5, 7 would be described with: @sweeps = ( [ [ 1, 2, 3 ] ] , [ [ 1, 4, 5, 7 ] ] )   (1). And a sequential block search with the first subspace regarding parameters 1, 2, 3 and the second regarding parameters 3, 4, 5, 6 would be described with: @sweeps = ( [ [ 1, 2, 3 ] , [ 3, 4, 5, 6 ] ] ). The same search could be made parallel by putting in front of the first subspace list the letter "p", which would signal that the search in that block and in the one following it has to be made in parallel and not sequentially: ( [ [ "p", 1, 2, 3 ] , [ 3, 4, 5, 6 ] ] )   (2). The possibility of articulating a mix of parallel and sequential searches is important!, because it allows to design search structures with a depth, embodying precedence, and therefore time, it them. 

The number of iterations to be taken into account for each parameter for each case is specified in the "@varinumbers" variable. To specifiy that the parameters of the last example are to be tried for three values (iterations) each, @varinumbers has to be set to ( { 1 => 3, 2 => 3, 3 => 3, 4 => 3, 5 => 3, 6 => 3 } ).

The instance number that has to considered the basic one, corresponding to the root case, is specified by the variable "@miditers". "@miditers" for the last example may be for instance set to ( { 1 => 2, 2 => 2, 3 => 2, 4 => 2, 5 => 2, 6 => 2 } ).

Sim::OPT has begun to be written to assist building simulation, then it has mutated into something that can used for simulation-based design-aimed search in general.

Where Sim::OPT may be fit for a task? Where a certain exploration is complex and/or when it is to be confronted through decomposition, by dividing a problem in overlapping subproblems; when there aren't slick tools suitable to decomposition-based, simulation-based optimization; when spending a day, or two, or three setting up a model may spare months of work.

Where it may not be suitable for the task? Due to the investments which is necessary for getting acquainted with its raw interface, for quick shots at small explorations.

The program works under linux. Instructions that may allow its use under Windows have been included in the code, but have yet never been tested.

=head2 EXPORT

"opt".

=head1 SEE ALSO

Three annotated examples ("esp.pl" for ESP-r and "ep.pl" for EnergyPlus, and "trap.pl" for a mix of sequential and block search) can be found packed in the "optw.tar.gz" file in "examples" directory in this distribution. They constitute the available documentation. Additionally, reference to the source code may be made.

=head1 AUTHOR

Gian Luca Brunetti, E<lt>gianluca.brunetti@polimi.itE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008-2015 by Gian Luca Brunetti and Politecnico di Milano. This is free software.  You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2 or later.


=cut
