#!/usr/bin/perl
use strict;
use warnings;
use lib qw(/usr/local/lib/mpmasql/modules);
use IO::Handle;
use Term::ReadLine;
use File::Util::Tempdir qw(get_user_tempdir);
use File::Temp;
use File::Path;
use MpmasInputfile;
use MpmasMILP;
use CtUtilities qw(is_in_list);
use Term::ReadKey;
use Number::Format;
##Version:
my $thisversion = "4.18";
my $lastchange = "17-06-2026";

=head1 mqlmatrix 

Perl script to examine, change and solve MPMAS matrix files in ASCII format (.mtx, .mpx or .mip, .dat)
(c)	Christian Troost, Univ. Hohenheim, 2009-2026



=cut

my $longwelcome = <<WC;

	Interactive MP problem explorer for MPMAS 
	Version: $thisversion, last change: $lastchange
	(c) Troost, C., Univ. Hohenheim, 2009-2025	
	
WC

my $shortwelcome = <<WC2;
	mqlmatrix v$thisversion		
WC2

my @tempfiles;
my $pager = "less";
my $spreadsheet = "oocalc";
my $tempfilepath = "/tmp/mpmasql/";
my $outdir = "/tmp/mpmasql/";
my $milpcheck = "/usr/local/bin/mpmasMipSolver";


if ($^O eq "MSWin32") {
	$spreadsheet = "start excel ";
	$pager = "notepad";
	$tempfilepath = get_user_tempdir();
	$outdir  =  get_user_tempdir();
	$milpcheck = "mpmasMipSolver.exe";

	
}
else {
	$pager = "most" if ( `which most`);
	$spreadsheet ="libreoffice --calc" if ( `which libreoffice`);
	$spreadsheet ="gnumeric" if ( `which gnumeric`);
#	system("mkdir", "-p", $tempfilepath);
#	system("mkdir", "-p", $outdir);
    &mkpath($tempfilepath);
    &mkpath($outdir);

}



my $matrixfile;
my $actdesname;
my $condesname;
my $mxmode;
my $mpxformat = 0;
my $mpmas_subformat;
my $tableau_compact = 0;
my $desdir;
my $milpcheck_options = "";




my $shell = 1;
my $printformat = "txt";

my $import_solution = 0;
my @children;
my $counter = 0;
my $ini = "mqlmatrix.conf";
my $limit_for_immediate = 50000;
my $ra;
our %MqlmatrixGlobalOptions = (
		colwidth => 30,
		digits_solve => 4,
		dstab => "."
	);

my $lastSolvedFn;
my $poolSize = 0;

if (-e $ini) {
	open (my $INI,"<", $ini) or die "Error when trying to read configuration:$ini\n $!\n";
	while (<$INI>) {
		my $line = $_;
		if ($line =~ /^\s*outdir\s*=\s*(.*)$/){
			$outdir = $1;
			$outdir =~ s/\s*$//;
		}
		elsif ($line =~ /^\s*(milpcheck|solver)\s*=\s*(.*)$/){
			$milpcheck = $2;
			$milpcheck =~ s/\s*$//;
		}
		elsif ($line =~ /^\s*infofiles\s*=\s*(.*)$/){
			$desdir = $1;
			$desdir =~ s/\s*$//;
		}
		elsif ($line =~ /^\s*(milpcheck|solver)_options\s*=\s*(.*)$/){
			$milpcheck_options = $2;
			$milpcheck_options =~ s/\s*$//;
		}
		elsif ($line =~ /^\s*colwidth\s*=\s*(\d+)$/){
			$MqlmatrixGlobalOptions{colwidth} = $1;
		}
		elsif ($line =~ /^\s*decimal_separator_for_tab\s*=\s*(\w)\s*$/){
			$MqlmatrixGlobalOptions{dstab} = $1;
		}
		elsif ($line =~ /^\s*digits_solve\s*=\s*(\d+)$/){
			$MqlmatrixGlobalOptions{digits_solve} = $1;
		}
		elsif ($line =~ /^\s*limit_immediate_write\s*=\s*(\d+)$/){
			$limit_for_immediate = $1;
		}
	}
	close ($INI);
}



#Parse commandline arguments
while(@ARGV) {
	my $element = shift @ARGV;
	 if (not $element =~ /^-/) {
			# solutionfile name
			$matrixfile = $element;
			}
	elsif ($element =~ /^-A/) {
			# activity description file 
			$actdesname = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-O/) {
			# output directory
			$outdir = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-I/) {
			#directory of constraint and activity description files
			$desdir = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-C/) {
			# constraint description file 
			$condesname = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}			  
	elsif ($element =~ /^-M/) {
			#MilpCheck
			$milpcheck = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-L/) {
			#MilpCheck
			$limit_for_immediate = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-F/) {
			$mxmode = "Failed";
			}			  
	elsif ($element =~ /^-S/) {
			$mxmode = "Single";
	}	
	elsif ($element =~ /^-T/) {
			$mxmode = "Test";
	}	
	elsif ($element =~ /^-D/) {
			$mxmode = "Debug";
	}
	elsif ($element =~ /^-V/) {
			$mxmode = "Mpmas";
			$mpmas_subformat = shift @ARGV if ($ARGV[0] =~ /^(\d+)/);
			print "Error: Please specify a valid subformat for option -V.\n(Run mqlmatrix --help for more information.)\n" and exit(1) unless (defined $mpmas_subformat and $mpmas_subformat >= 0 and $mpmas_subformat <= 2);
	}
	elsif ($element =~ /^--compact/ or $element =~ /^-c/) {
			$tableau_compact = 1;
	}
	elsif ($element =~ /^--risk_aversion/ ) {
			$ra = shift @ARGV unless ($ARGV[0] =~ /^-/);
	}
	elsif ( $element =~ /^--decseptab/ ) {
			$MqlmatrixGlobalOptions{dstab} = shift @ARGV unless ($ARGV[0] =~ /^-/);
	}
	
#	elsif ($element =~ /^-p/) {
#			$shell = 0;
#			$printformat = "mtx";
#	}
	elsif ($element =~ /^-t/) {
			$shell = 0;
			$printformat = "txt";
	}
	elsif ($element =~ /^-R/) {
			$import_solution = 1;
	}
      elsif ($element =~ /^-XP/) {
			$pager = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
      elsif ($element =~ /^-XS/) {
			$spreadsheet = shift @ARGV unless ($ARGV[0] =~ /^-/);
			}
	elsif ($element =~ /^-h/ or $element =~ /^-\?/ or $element =~ /^--help/) {
		print $shortwelcome;
		print <<HELP;
		
Usage:
	mqlmatrix <filename> [options]

Options:
	File format:
	-F		expect matrix in failed format (.mip/.mpx)
	-S		expect matrix in single agent format (.mtx)
	-D		expect matrix in MPMAS debug format (.mip/.mpx)
	-T		expect matrix in mpmasMipSolver debug format (.tst)

	Override default settings:
	-A <filename>	specify file containing activity description
	-C <filename>   specify file containing constraint description
	-O <directory>	override output directory
	-I <directory>	specify directory containing description files
	(if not specified, the directories are read from mqlmatrix.conf in the working directory
	and names of description files are guessed from the matrix file name)

	-M <filename>	filename of mpmasMipSolver executable
			(Linux default: /usr/local/bin/mpmasMipSolver)
	-XP <command>	set program for paging (defaults: most | less)
	-XS <command>	set spreadsheet program for table views (defaults: gnumeric | oocalc)
	
	-L	<value>		set limit for number of activities or rows from which on temp files on disk are used
					instead of RAM for certain file preparation operations. Adapt if you run into memory issues.
					(Default: 50000)
	
	Others:	
	-t			print matrix in tableau format(.txt) and exit
	-R			import solution vector
	-h, -?, --help		print this help
	-v			print version information

HELP

#   	-p			print matrix in single agent format (.mtx) and exit

#	-V <subformat>	expect matrix in mpmas input file format (.dat) 
#			<subformat>
#			0: basic format 
#			1: with second production stage ("harvest milp")
#			2: with advanced consumption model
#			11: TSPC, with second production stage (not implemented)
#			12: TSPC, with advanced consumption model (not implemented)
#	(specifying one of these formats is obligatory)
#
#	--compact, -c	expect tableau in compact format (only in combination with -V and -S)
#
#	--risk_aversion <value> set risk aversion coefficient for evaluation of Q matrix to <value>

		exit (0);
	}
	elsif ($element =~ /^-v/ ) {
		print $shortwelcome;
		print $longwelcome;
		exit(0);
	}
}

print STDERR "Error: No matrix specified\n" and exit(1) unless (defined $matrixfile);
print STDERR "Error: You did not indicate the matrix file format\n(-F: Failed MILP,\n -S: Single Matrix, -D: Debugging MILP, -V <subformat>: Mpmas Input File)\n" and exit(1) unless (defined $mxmode);


&mkpath($outdir);

my $scen;
my $ag = "X";
my $type;
my $milpnumber;
my $solutionfile;
if ($mxmode eq "Failed") {
	print STDERR "Warning: expecting .mip or .mpx file when using format option -F\n" unless ($matrixfile =~/\.(mip|mpx)$/); 
	#$matrixfile =~ /Failed_(\w+?)_(\d+)_(...)Loaded(\d*)\w*\.(mip|mpx)/;
	$matrixfile =~ /(\w+?_)\d+_(\d+)\w+\d+MIP(\d+)_(FailedPreLoaded|FailedOslLoaded|LowTarget).(mip|mpx)/;

	$scen = $1;
	$ag = $2;
	$type = $4;
	$milpnumber = $5;
	$mpxformat = 1 if ($5 eq "mpx");
}
elsif ($mxmode eq "Debug") {
	print STDERR "Warning: expecting .mip or .mpx file when using format option -D\n" unless ($matrixfile =~/\.(mip|mpx)$/); 
	$matrixfile =~ /(\w+?_)(\d+)_(\d+)\w+\d+MIP(\d*)\.(mip|mpx)/;
	$scen = $1;
	my $period = $2;
	$ag = $3;
	$mpxformat = 1 if ($5 eq "mpx");
	$type = "Dbg";
	$milpnumber = $4;
	$mxmode = "Failed";
}
elsif ($mxmode eq "Test") {
	print STDERR "Warning: expecting .tst file when using format option -T\n" unless ($matrixfile =~/\.(tst)$/); 
	if ($matrixfile =~ /\/?(\w+)MILP_(\w+)\.tst$/) {
		$scen = $1;
		$ag = $2;
	}
	elsif ($matrixfile =~ /\/?(\w+)MILP\.tst$/)  {
		$scen = $1;
		$ag = "";		
	}
	$mpxformat = 1 ;#if ($5 eq "mpx");
	$type = "Dbg";
	$mxmode = "Failed";
}
elsif ($mxmode eq "Single") {
	print STDERR "Warning: expecting .mtx file when using format option -S\n" unless ($matrixfile =~/\.mtx$/); 
	if ($matrixfile =~ /\/?(\w+)MILP_(\w+)\.mtx$/) {
		$scen = $1;
		$ag = $2;
	}
	elsif ($matrixfile =~ /\/?(\w+)MILP\.mtx$/)  {
		$scen = $1;
		$ag = "";		
	}
	$type = "RE";
	$milpnumber = "";
}
elsif ($mxmode eq "Mpmas") {
	print STDERR "Warning: expecting .dat file when using format option -V\n" unless ($matrixfile =~/(\.cpt|\.dat)$/); 
	$matrixfile =~ /\/?(\w+)MILP(\w+)?(\.cpt|\.dat)$/;
	$scen = $1;
	$ag = $2 || "blank";
	$type = "RE";
	$milpnumber = "";
}
$solutionfile = $matrixfile;
$solutionfile =~ s/(.mtx|.mpx|.mip|.tst)\s*$/.pri/;


if (not defined $desdir) {
	if ( not $matrixfile =~ /\// ) {
		$desdir = "./";
	}
	elsif ( $matrixfile =~ /^\.\/input\/dat/ ) {
		$desdir = "./input/dat/";
	}
	elsif ( $matrixfile =~ /^\.\/out\/test/ ) {
		$desdir = "./input/dat/";
	}
	elsif ( $matrixfile =~ /^input\/dat/ ) {
		$desdir = "./input/dat/";
	}
	elsif ( $matrixfile =~ /^out\/test/ ) {
		$desdir = "./input/dat/";
	}	
	else {
		$desdir = "./input/dat/";
	}
}


print $shortwelcome."\n";
print "Pager:\t$pager\n"; 
print "Spreadsheet:\t$spreadsheet\n";

print "\nMatrix file:\t\t". $matrixfile."\n";  
print "Identified Scenario $scen, agent $ag\n";

if (not defined $actdesname) {
	$actdesname = $desdir.$scen."ActivityInfo.txt";	
}
if (not defined $condesname) {
	$condesname = $desdir.$scen."ConstraintInfo.txt" ;	
}




print "Constraint Description file:\t\t". $condesname."\n";  
print "Activity Description file:\t\t". $actdesname."\n";  

#Test whether files exist, otherwise suggestive error message:
unless (-e  $matrixfile  ) {
	print STDERR "\nERROR: The file $matrixfile does not exist.\n";
	exit(1);
}
unless (-e  $actdesname  ) {
	print STDERR "\n  ERROR: Did not find the activity description file:\n   '$actdesname'\n";
	print STDERR "\n   If the filename seems correct, check whether file exists.\n";
	print STDERR "\n   If the filename is wrong, check whether you used the correct\n   file format option:\n";
	print STDERR "\t-D debugging matrix,\n\t-F failed matrix,\n\t-S mpmasMipSolver input.\n";
	print STDERR "\n   In case the error persists, use the -A or -I option to specify\n   the correct file.\n\n";
	exit(1);
}
unless (-e  $condesname  ) {
	print STDERR "\n  ERROR: Did not find the constraint description file:\n   '$condesname'\n";
	print STDERR "\n   If the filename seems correct, check whether file exists.\n";
	print STDERR "\n   If the filename is wrong, check whether you used the correct\n    file format option:\n";
	print STDERR "\t-D debugging matrix,\n\t-F failed matrix,\n\t-S mpmasMipSolver input.\n";
	print STDERR "\n   In case the error persists, use the -C or -I option to specify\n   the correct file.\n\n";
	exit(1);
}

##Recreate files
MpmasInputfile->set_global_references($scen, $outdir, $outdir, undef, undef, undef, undef, undef, undef,undef,undef,undef, undef, undef );
my $matrix = MpmasMILP->recreate_from_file($matrixfile, $mxmode, $actdesname, $condesname, $mpxformat, $mpmas_subformat, $tableau_compact, 1);
$matrix->set_config_entry("LIMIT_FOR_IMMEDIATE", $limit_for_immediate) ;

my $status = -1;
if ($import_solution) {
    print "Solution imported from $solutionfile\n";
    $matrix->import_solution_vector($solutionfile, $ra) ;
    $status = "Solution imported from $solutionfile, ".`date +%T` ;
}


unless ($shell) {
	if ($printformat eq "txt") {
		
		
		my $out;
		$matrixfile =~ s/(.mtx|.mpx|.mip)\s*$/.txt/;
		open ($out, ">", $matrixfile ) or (print "Error: Could not write tableau output file $matrixfile:\n$!\n" and exit(1));
		print_tableau_part($out, [( 0.. ($matrix->milpinfo("NumCol") -1)) ],[(0..($matrix->milpinfo("NumRow") -1))], undef,$MqlmatrixGlobalOptions{dstab});
		close($out);
		print "Decision problem in tableau format written to $matrixfile\n";
	}
	elsif ($printformat eq "mtx") {
		print_matrix($matrix, $ag, $type);
	}
	exit 0;
}
else {
	my $term = Term::ReadLine->new('Mpmas QL Matrix');
	print "Interactive mode (using ".$term->ReadLine.")\n";
	while ($shell) {
		my $prompt = "mqlmatrix>";
		my $commandstring = $term->readline($prompt); #ReadLine(0) ;
		#$term->addhistory($commandstring);
		$commandstring =~ s/^\s*//;
		$commandstring =~ s/\s*$//;
		if ($commandstring eq "exit" or $commandstring eq "quit") {
			$shell = 0;
		}
		elsif ($commandstring =~ /^help pooling(\s.*)?/) {
			my ($out, $pid, $full, $nf, $typefilter) = check_options($1, undef, \@tempfiles);
			push @children, $pid if $pid;
			list_help_pooling($out);
			close $out;
		}
		elsif ($commandstring =~ /^help(\s.*)?/) {
			my ($out, $pid, $full, $nf, $typefilter) = check_options($1, undef, \@tempfiles);
			push @children, $pid if $pid;
			list_help($out);
			close $out;
		}
		elsif ($commandstring =~ /^\s*set\s+(colwidth)(?:\s+|\s*=\s*)(\d+)/	) {
			$MqlmatrixGlobalOptions{$1} = $2;
		}
		elsif ($commandstring =~ /^\s*set\s+(digits_solve)(?:\s+|\s*=\s*)(\d+)/	) {
			$MqlmatrixGlobalOptions{$1} = $2;
		}
		elsif ($commandstring =~ /^\s*set\s+(decimal_separator_for_tab)(?:\s+|\s*=\s*)(.)/	) {
			$MqlmatrixGlobalOptions{dstab} = $2;
		}
		elsif ($commandstring eq "info") {
			print "Number of rows:\t".$matrix->milpinfo("NumRow")."\n";
			print "Number of columns:\t".$matrix->milpinfo("NumCol")."\n";
			print "Integers:\t".$matrix->milpinfo("IntegAct")."\n";
			print "Quadratic variables:\t".$matrix->milpinfo("QuadraticAct")."\n";
			print "Objective value:\t".$matrix->milpinfo("TargetValue")."\n" if (defined $matrix->milpinfo("TargetValue") );
			print "Objective value (linear part):\t".$matrix->milpinfo("ObjectiveValueLin")."\n" if (defined $matrix->milpinfo("ObjectiveValueLin") );
			print "Objective value (quadratic part):\t".$matrix->milpinfo("ObjectiveValueQuad")."\n" if (defined $matrix->milpinfo("ObjectiveValueQuad") );
			print "Variance in risk model (r = ". $matrix->milpinfo("RiskAversionCoef")  ."):\t".$matrix->milpinfo("VarianceRiskModel")."\n" if (defined $matrix->milpinfo("VarianceRiskModel") );
			print "Risk aversion coefficient now set to:\t $ra\n" if (defined $ra);
			if ($status eq "-1") {
				  print "Status:\tno attempt to solve yet\n" ;
			}
			else {
				  print "Status:\t$status\n" 
			}
		}
		elsif ($commandstring =~ /^printtxt\s*(\w*)/) {
			$type = $1 if (defined $1);
			print_matrix($matrix,$ag, $type, 1, $MqlmatrixGlobalOptions{digits_solve});
			print "\n";
		}
		elsif ($commandstring =~ /^printmtx\s*(\w*)/) {
			$type = $1 if (defined $1);
			print_matrix($matrix,$ag, $type, 0,$MqlmatrixGlobalOptions{digits_solve});
			print "\n";
		}
		elsif ($commandstring eq "reload") {
			$matrix = undef;
			$matrix = MpmasMILP->recreate_from_file($matrixfile, $mxmode, $actdesname, $condesname, $mpxformat, $mpmas_subformat, $tableau_compact, 1);
			
			if ($import_solution ) {
				my $retcode = 0;
				eval {
					local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
					$retcode = $matrix->import_solution_vector($solutionfile, $ra);
				};
				print "Solution could not be imported:\n$@\n" if ($@);
				if ($retcode) {
					$status = "Solution imported from $solutionfile, ".`date +%T` ;
				}
				
			}
		}
		elsif ($commandstring =~ /^set_solver_options\s*(.*)/) {
			if ($1 eq "NULL") {
				$milpcheck_options = "";
			}
			else {
			    $milpcheck_options = $1;
			    chomp($milpcheck_options);
			}
		}
		elsif ($commandstring =~ /^set_risk_aversion\s*([\d.]*)/) {
			if ($1 eq "NULL") {
				$ra = undef;
			}
			else {
			    $ra = $1;
			}
		}
		elsif ($commandstring =~ /^append_solver_options\s*(.*)/) {
		        $milpcheck_options .= " ".$1;
			chomp($milpcheck_options);
		}
		elsif ($commandstring =~ /^solver_options/) {
		      if (defined $milpcheck_options) {
			    print $milpcheck_options."\n";
		      }
		      else {
			    print "No solver options defined\n";
		      }
		}
		elsif ($commandstring =~ /^set_solver_exec\s*(.*)/) {
		        $milpcheck = $1;
				chomp($milpcheck);
		}
		elsif ($commandstring =~ /^solver_exec/) {
				print $milpcheck."\n";
		}
		elsif ($commandstring =~ /^solve\s*(\d)?/) {
			print STDERR "Warning: You have loaded a matrix indicating -V, which stands for the MPMAS input file format (.dat).\nAttempting to solve this format usually leads to unexpected results, because of missing agent-specific RHS, missing yields, investment coefficients and cost, missing dynamic coefficients for perennials, et cetera.\n" if ($mxmode eq "Mpmas");
			my $modestring = $1;

			my $type = "_temp";
			$type .= ++$counter;
			
			if (defined $modestring) { 
			      $modestring = " -J ".$modestring;
			      print "Using$modestring\n";
			};
			my $fn = print_matrix($matrix,$ag, $type, 0, $MqlmatrixGlobalOptions{digits_solve});
			$fn =~ s/.mtx$//;
			
			#delete potentially existing old solutions to avoid confusion
			unlink($fn.".pri"); 
			
			foreach (   glob( "${fn}_solPool_*.pri" ) ) {
				unlink ($_);	
			}			
			
			
			$lastSolvedFn = $fn;
			
			my $string = $milpcheck." ".$fn;
			$string .= $modestring if (defined $modestring);
			$string .= " ".$milpcheck_options if (defined $milpcheck_options);
			$string .= " -pris";
			
			print "$string\n";
			my $sretcode = system($string);
			
			if ($sretcode == 0) {
				$fn .= ".pri";
				my $retcode = 0;
				eval {
					local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
					$retcode = $matrix->import_solution_vector($fn, $ra);
				};
				if ($@) {
					print "Solution could not be imported:\n $@\n Probably solution failed, check solver screen output.";
					$status = "Last solution failed, ".`date +%T`. $@;
				}
				else {
					print "Solution has been imported.\n";
					$status = "Last solution successful, ".`date +%T` ;
				} 
			}
			else {
					print "Solution failed, check solver screen output above.";
					$status = "Last solution failed, ".`date +%T`. $@;
			}
		}

		elsif (0) { #($commandstring =~ /^solve\s*(\d)?\s*(-test)?/) { #old style, obsolete
			print STDERR "Warning: You have loaded a matrix indicating -V, which stands for the MPMAS input file format (.dat).\nAttempting to solve this format usually leads to unexpected results, because of missing agent-specific RHS, missing yields, investment coefficients and cost, missing dynamic coefficients for perennials, et cetera.\n" if ($mxmode eq "Mpmas");
			my $modestring = $1;
			my $teststring = $2;
			my $type = "_temp";
			$type .= ++$counter;
			
			if (defined $modestring) { 
			      $modestring = " -mode ".$modestring;
			      print "Using$modestring\n";
			};
			my $fn = print_matrix($matrix,$ag, $type, 0);
			$fn =~ s/.mtx$//;
			my $string = $milpcheck." ".$fn;
			$string .= " ".$teststring if (defined $teststring);
			$string .= $modestring if (defined $modestring);
			$string .= " ".$milpcheck_options if (defined $milpcheck_options);
			
			print "$string\n";
			system($string);
			$fn .= ".pri";
			eval {
				local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
				$matrix->import_solution_vector($fn, $ra);
			};
			if ($@) {
				print "$@";
				$status = "Last solution failed, ".`date +%T`. $@;
			}
			else {
				print "Solution has been imported.\n"
				
			}
			$status = "Last solution successful, ".`date +%T` ;
		}
		
		elsif ($commandstring =~ /^impsol\s+(.*)/) {
			
			$solutionfile = $1 if (defined $1);
			print "Importing solution from $solutionfile\n";
			my $retcode = 0;
			eval {
				local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
				$retcode = $matrix->import_solution_vector($solutionfile, $ra);
			};
			print "$@" if ($@);
			if ($retcode) {
				$status = "Solution imported from $solutionfile, ".`date +%T` ;
			}
		}

		elsif ($commandstring =~ /^impsol_cplexxml\s+(.*)/) {
			$solutionfile = $1 if (defined $1);
			print "Importing solution from $solutionfile\n";
			eval {
				local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
				$matrix->import_cplex_sol_xml($solutionfile, $ra);
			};
			print "$@" if ($@);
			$status = "Solution imported from $solutionfile, ".`date +%T` ;
		}
		 
		 
		elsif ($commandstring =~ /^imppool(.+)?/) {
			
			my $solutionstub;
			if (defined $1) {
				$solutionstub= $1 
			}
			elsif (defined $lastSolvedFn) {
				$solutionstub = $lastSolvedFn."_solPool_";
			}
			if (defined  $solutionstub ) {
			
				$matrix->clear_solution_pool();
				$poolSize = 0;
				my $i = -1;
				for my $solutionf (glob ( "${solutionstub}*.pri"  ) ) {
				
					if ( $solutionf =~ /_(\d+).pri$/ ) {
						$i = $1; ;
					}
					else {
						$i = -1;
					}
					print "Importing solution $i from $solutionf\n";
					my $retcode = 0;
					eval {
						local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
						$retcode = $matrix->import_solution_vector($solutionf, $ra, $i);
					};
					print "$@" if ($@);
					if ($retcode) {
						$status = "Pool solution imported from $solutionf, ".`date +%T` ;
						$poolSize = $poolSize > $i + 1 ? $poolSize : $i +1;
					}
				}
				print "Sorry, no solution pool files with the given prefix $solutionstub found ...\n" unless ($poolSize > 0);
				
			}
			
			else {
				
				print "Error: No filename prefix for solution pool files defined and also no successful solve attempt yet\n";
				
			}
		} 
		 
		 
		elsif ($commandstring =~ /^col\s+(\d+)/) {
			my $k = $matrix->activity_at_col($1);
			print "Error: Column $1 does not exist\n" and next unless (defined $k);
			print_info($k);	
		}
		elsif ($commandstring =~ /^Q\s+(\d+)/) {
			my $k = $matrix->activity_at_col($1);
			print "Error: Column $1 does not exist\n" and next unless (defined $k);
			my $width = 30;
			if ($k->attr("quadratic")) {
			  print "Q matrix for activity ". $k->id. "\n";
			  for my $i ($k->subelement_fields("quadratic")) {
			      print sprintf("%-".$width.".".($width -3) ."s\t%f\n", $i, $k->subelement_field("quadratic", $i));
			  }
			}
			else {
			  print "Column $1 is not quadratic\n" 
			}
			
		}
		elsif ($commandstring =~ /^multiply\s+Q\s+([-\d.E+e]+)/) {
			my $m = $1;
			foreach my $j ($matrix->sorted_quadratic_activities() ) {
			    my $k = $matrix->activity($j);
			    foreach my $i ($k->subelement_fields("quadratic")) {
				  $k->modify_subelement_field("quadratic", $i, $k->subelement_field("quadratic", $i) * $m);
			    }
			  }
		}
		elsif ($commandstring =~ /^divide\s+Q\s+([-\d.E+e]+)/) {
			my $m = $1;

			foreach my $j ($matrix->sorted_quadratic_activities() ) {
			    my $k = $matrix->activity($j);
			    foreach my $i ($k->subelement_fields("quadratic")) {
				  $k->modify_subelement_field("quadratic", $i, $k->subelement_field("quadratic", $i) / $m);
			    }
			  }
		}
		elsif ($commandstring =~ /^coef\s+col\s+(\d+)(\s+.+)?/) {
			
			my $k = $matrix->activity_at_col($1);
			print "Error: Column $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Tableau Coefficients of Col $1 (Activity ".$k->id."):\n";
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $c = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t%s\n", $i, $c->id, $k->coeff($c->id), $c->attr("name")) if (defined $k->coeff($c->id)) ;
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^coef con\s+([:\w]+)(\s+.+)?/) {
			my $k = $matrix->constraint($1);
			print "Error: Constraint $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Tableau Coefficients of Constraint $1 (Row ". $k->lp_row."):\n";
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $c = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t%s\n", $i, $c->id, $c->coeff($k->id), $c->attr("name")) if (defined $c->coeff($k->id));
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^coef act\s+([:\w]+)(\s+.+)?/) {
			my $k = $matrix->activity($1);
			print "Error: Activity $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Tableau Coefficients of Activity $1 (Col ".$k->lp_column."):\n";
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $c = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t%s\n", $i, $c->id, $k->coeff($c->id), $c->attr("name")) if (defined $k->coeff($c->id)) ;
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^coef\s+row\s+(\d+)(\s+.+)?/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Tableau Coefficients of Row $1 (Constraint ". $k->id."):\n";
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $c = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t%s\n", $i, $c->id, $c->coeff($k->id), $c->attr("name")) if (defined $c->coeff($k->id));
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^terms\s+row\s+(\d+)(\s+.+)?/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Resolved terms (Solution * Coefficient) in row $1 (Constraint ". $k->id."):\n";
			for my $i (0..$matrix->milpinfo("NumCol")-1) {			
				my $c = $matrix->activity_at_col($i);
				my $sol = $c->attr("solution") ?  $c->attr("solution") : 0;
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t$nf\t$nf\t%s\n", $i, $c->id, 
				
    				$sol * $c->coeff($k->id), $c->coeff($k->id),  $sol, 
							
				$c->attr("name")) if (defined $c->coeff($k->id) && ($sol != 0 || $full));
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^terms con\s+([:\w]+)(\s+.+)?/) {
			my $k = $matrix->constraint($1);
			print "Error: Constraint $1 does not exist\n" and next unless (defined $k);
			my ($out, $pid, $full, $nf, $typefilter, $integ, $width, $periodfilter) = check_options($2, undef, \@tempfiles);
			push @children, $pid if $pid;
			print $out "Tableau Coefficients of Constraint $1 (Row ". $k->lp_row."):\n";
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $c = $matrix->activity_at_col($i);
								my $sol = $c->attr("solution") ?  $c->attr("solution") : 0;
				print $out sprintf("%d\t%-".$width.".".($width-3)."s\t$nf\t$nf\t$nf\t%s\n", $i, $c->id,
    				 $sol * $c->coeff($k->id), $c->coeff($k->id),  $sol,
							
				$c->attr("name")) if (defined $c->coeff($k->id) && ($sol != 0 || $full));
			} 
			close ($out);
		}
		elsif ($commandstring =~ /^row\s+(\d+)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			print_info($k);	
		}
		elsif ($commandstring =~ /^con\s+([:\w]+)/) {
			my $k = $matrix->constraint($1);
			print "Error: Constraint $1 does not exist\n" and next unless (defined $k);
			print_info($k);	
		}
		elsif ($commandstring =~ /^act\s+([:\w]+)/) {
			my $k = $matrix->activity($1);
			print "Error: Activity $1 does not exist\n" and next unless (defined $k);
			print_info($k);	
		}
		elsif ($commandstring =~ /^modify\s+row\s+(\d+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w]+)\s*\)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			my $new = $4;
			if (defined $3 and $3 eq "+") {
				$new += $k->attr("$2");
			}
			elsif (defined $3 and $3 eq "*") {
				$new *= $k->attr("$2");				
			}
			$k->set_attr("$2", $new);
			print "\n";
			print_info($k);
		}
		elsif ($commandstring =~ /^modify\s+rows\s+(\d+)\s*\.\.\s*(\d+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w]+)\s*\)/) {
			for my $i ($1..$2) {
			    my $k = $matrix->constraint_at_row($i);
			    print "Error: Row $i does not exist\n" and next unless (defined $k);
				my $new = $5;
				if (defined $4 and $4 eq "+") {
					$new += $k->attr("$3");
				}
				elsif (defined $4 and $4 eq "*") {
					$new *= $k->attr("$3");				
				}

			    $k->set_attr("$3", $new);
			    #print "\n";
			    #print_info($k);
			}
		}
		elsif ($commandstring =~ /^modify\s+col\s+(\d+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)/) {
			my $k = $matrix->activity_at_col($1);
			print "Error: Column $1 does not exist\n" and next unless (defined $k);
			my $new = $4;
			if (defined $3 and $3 eq "+") {
				$new += $k->attr("$2");
			}
			elsif (defined $3 and $3 eq "*") {
				$new *= $k->attr("$2");				
			}
			$k->set_attr("$2", $new);
			print "\n";
			print_info($k);
			
		}
		elsif ($commandstring =~ /^modify\s+cols\s+(\d+)\s*\.\.\s*(\d+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)/) {
		    for my $i ($1..$2) {
			my $k = $matrix->activity_at_col($i);
			print "Error: Col $i does not exist\n" and next unless (defined $k);
			my $new = $5;
			if (defined $4 and $4 eq "+") {
				$new += $k->attr("$3");
			}
			elsif (defined $4 and $4 eq "*") {
				$new *= $k->attr("$3");				
			}

			$k->set_attr("$3", $new);
			#print "\n";
			#print_info($k);
		    }
		}
#		elsif ($commandstring =~ /^modify\s+priorities\s*(\w*\s*)/) {
			
#			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter) = check_options($1,undef, \@tempfiles);

#			my 

#		    for my $k (@{$matrix->get_activities_of_type ($typefilter)}) {

#				if ($k->attr("mperiod") == $periodfilter) {
				
#					my $new = $value;
#					if (defined $op and $op eq "+") {
#						$new += $k->attr("$attrib");
#					}
#					elsif (defined $op and $op eq "*") {
#						$new *= $k->attr("$attrib");				
#					}
#
#					$k->set_attr("$attrib", $new);
#				}
#
#		    }
#		}
		elsif ($commandstring =~ /^modify\s+actstype\s+(\w+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)(\s.*)?/) {
			my $type = $1;
			my $attrib = $2;
			my $op = $3;
			my $value = $4;
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter) = check_options($5,undef, \@tempfiles);

		    for my $k (@{$matrix->get_activities_of_type ($type)}) {

				if (not defined $periodfilter or $k->attr("mperiod") == $periodfilter) {
				
					my $new = $value;
					if (defined $op and $op eq "+") {
						$new += $k->attr("$attrib");
					}
					elsif (defined $op and $op eq "*") {
						$new *= $k->attr("$attrib");				
					}

					$k->set_attr("$attrib", $new);
				}

		    }
		}
		elsif ($commandstring =~ /^modify\s+constype\s+(\w+)\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)(\s.*)?   /   ) {
			my $type = $1;
			my $attrib = $2;
			my $op = $3;
			my $value = $4;
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter) = check_options($5,undef, \@tempfiles);
			
		    for my $k (@ {$matrix->get_constraints_of_type($1) }) {
				
				if (not defined $periodfilter or $k->attr("mperiod") == $periodfilter) {
					my $new = $value;
					if (defined $op and $op eq "+") {
						$new += $k->attr("$attrib");
					}
					elsif (defined $op and $op eq "*") {
						$new *= $k->attr("$attrib");				
					}

					$k->set_attr("$attrib", $new);
				}
		    }
		}	
		
		elsif ($commandstring =~ /^modify\s+actspattern\s+\/([^\/]+)\/\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)(\s.*)?/) {
			my $pattern = $1;
			my $attrib = $2;
			my $op = $3;
			my $value = $4;
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter) = check_options($5,undef, \@tempfiles);
			#print "$pattern", "\n";
		    for my $k (@{$matrix->get_activities_matching_pattern ($pattern)}) {

				if (not defined $periodfilter or $k->attr("mperiod") == $periodfilter) {
				
					my $new = $value;
					if (defined $op and $op eq "+") {
						$new += $k->attr("$attrib");
					}
					elsif (defined $op and $op eq "*") {
						$new *= $k->attr("$attrib");				
					}

					$k->set_attr("$attrib", $new);
				}

		    }
		}
		elsif ($commandstring =~ /^modify\s+conspattern\s+\/([^\/]+)\/\s*\(\s*(\w+)\s*(\+|\*)?=\s*([-.\w+]+)\s*\)(\s.*)?   /   ) {
			my $pattern = $1;
			my $attrib = $2;
			my $op = $3;
			my $value = $4;
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter) = check_options($5,undef, \@tempfiles);
			
		    for my $k (@ {$matrix->get_constraints__matching_pattern($pattern) }) {
				
				if (not defined $periodfilter or $k->attr("mperiod") == $periodfilter) {
					my $new = $value;
					if (defined $op and $op eq "+") {
						$new += $k->attr("$attrib");
					}
					elsif (defined $op and $op eq "*") {
						$new *= $k->attr("$attrib");				
					}

					$k->set_attr("$attrib", $new);
				}
		    }
		}	
		
		
		
		
		
		
		elsif ($commandstring =~ /^modify\s+coef\s+(\d+)\s*(\d+)\s+([-.\d]+)/) {
			my $k = $matrix->activity_at_col($1);
			my $l = $matrix->sorted_constraints($2);
			print "Error: Column $1 does not exist\n" and next unless (defined $k);
			print "Error: Row $2 does not exist\n" and next unless (defined $l);
			$k->set_coeff("$l", $3);
			print "Tableau Coefficients of Col $1 (Activity ".$k->id."):\n";
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $c = $matrix->constraint_at_row($i);
				print sprintf("%d\t%30s\t%.4f\t%s\n", $i, $c->id, $k->coeff($c->id), $c->attr("name")) if (defined $k->coeff($c->id)) ;
			}
		}
		elsif ($commandstring =~ /^rhs\s+(\d+)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			print sprintf("%d\t%20s\t%.4f\t%20s\n", $1, $k->id, $k->attr("value"), $k->attr("name"));
		}
		elsif ($commandstring =~ /^lhs\s+(\d+)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			print "LHS of Row($1) based on Solution\n";
			
			print $matrix->calculate_lhs_of_constraint($k)."\n";
		}
		elsif ($commandstring =~ /^slack\s+(\d+)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			print "Slack of Row($1) based on Solution\n";
			
			print $matrix->calculate_slack_of_constraint($k)."\n";
		}
		elsif ($commandstring =~ /^lhslb\s+(\d+)/) {
			my $k = $matrix->constraint_at_row($1);
			print "Error: Row $1 does not exist\n" and next unless (defined $k);
			print "Minimum LHS of Row($1) based on Lower Bounds of Columns\n";
			print $matrix->calculate_lhs_of_constraint($k, "lbound")."\n";
		}
		elsif ($commandstring =~ /^list\s+act(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			#print "$pattern", "\n";
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%20s\t%s\n", $i, $k->id, $k->attr("type"), $k->attr("name") ) 
					if ((not defined $typefilter or $typefilter eq $k->attr("type")) and ($integs == 0 or $k->attr("integ") == 1 ) 
					and (not defined $periodfilter or $periodfilter == $k->attr("mperiod"))
					and (not defined $pattern or $k->id() =~ /$pattern/)) ;
			}
			close($out);
			
		}
		elsif ($commandstring =~ /^list\s+con(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%20s\t%s\n", $i, $k->id, $k->attr("type"), $k->attr("name") ) 
				if ( (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/)) ;
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+==(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%20s\t%s\n", $i, $k->id, $k->attr("type"), $k->attr("name") ) if ($k->attr("eqtype") == 3) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/);
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+>=(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%20s\t%s\n", $i, $k->id, $k->attr("type"), $k->attr("name") ) if ($k->attr("eqtype") == 2) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) ;
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+rhs(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				
				my $k = $matrix->constraint_at_row($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $k->attr("value"), $k->attr("name") ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ( $full or sprintf($nf,$k->attr("value")) != 0)  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/))  ;
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+lhslb(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			
			
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				my $lhs = $matrix->calculate_lhs_of_constraint($k, "lbound");
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $lhs, $k->attr("name") ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($full or sprintf($nf, $lhs) != 0) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod"))and (not defined $pattern or $k->id() =~ /$pattern/) );
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+lhs0(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				if (not defined $typefilter or $typefilter eq $k->attr("type") ) {
				      my $lhs = $matrix->calculate_lhs_of_constraint($k);
				      print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $lhs, $k->attr("name") ) if (($full or sprintf($nf, $lhs) == 0)  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $pattern or $k->id() =~ /$pattern/) );
				}
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+lhs(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				if (not defined $typefilter or $typefilter eq $k->attr("type") ) {
				      my $lhs = $matrix->calculate_lhs_of_constraint($k);
				      print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $lhs, $k->attr("name") ) if (($full or sprintf($nf, $lhs) != 0)  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $pattern or $k->id() =~ /$pattern/) );
				}
			}
			close($out);
		}

		elsif ($commandstring =~ /^list\s+slack0(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				if (not defined $typefilter or $typefilter eq $k->attr("type") ) {
				      my $slack = $matrix->calculate_slack_of_constraint($k);
				      print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $slack, $k->attr("name") ) if (($full or sprintf($nf, $slack) == 0)  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $pattern or $k->id() =~ /$pattern/) );
				}
			}
			close($out);
		}
		elsif ($commandstring =~ /^list\s+slack(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter,$integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				if (not defined $typefilter or $typefilter eq $k->attr("type") ) {
				      my $slack = $matrix->calculate_slack_of_constraint($k);
				      print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $slack, $k->attr("name") ) if (($full or sprintf($nf, $slack) != 0)  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $typefilter or $typefilter eq $k->attr("type") ) and (not defined $pattern or $k->id() =~ /$pattern/) );
				}
			}
			close($out);
		}

		
		
		elsif ($commandstring =~ /^list\s+sol(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $k->attr("solution") ?  $k->attr("solution") : 0, $k->attr("name") ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and ( $full or ( defined $k->attr("solution") and sprintf($nf, $k->attr("solution")) != 0 and sprintf($nf, $k->attr("solution")) != -1 ))  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) );
			}
			close($out);
			
		}
		
		elsif ($commandstring =~ /^list\s+poolsol\s+(\d+)(\s.*)?/){
			
			my $si = $1;
			if ($si >= $poolSize) {
				print "Error: Solution pool is smaller than ", $si -1 , "\n";
			}
			else {
				my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($2,undef, \@tempfiles);
				push @children, $pid if $pid;
				
				print $out sprintf("-\t%-".$width.".".($width -3) ."s", "Objective value" ) ;
				print $out sprintf("\t$nf", $matrix->get_milpinfo_solution_pool($si));
				print $out "\t-\n";
				
				for my $i (0..$matrix->milpinfo("NumCol")-1) {
					my $k = $matrix->activity_at_col($i);
					print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $k->get_pooled_solution($si) ?  $k->get_pooled_solution($si) : 0, $k->attr("name") ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and ( $full or ( defined $k->attr("solution") and sprintf($nf, $k->attr("solution")) != 0 and sprintf($nf, $k->attr("solution")) != -1 ))  and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) );
				}
				close($out);
			}
		}
		elsif ($commandstring =~ /^list\s+poolsols(\s.*)?/){
			if ($poolSize < 1) {
				print "Solution pool is empty\n";
			}
			else {
				my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
				push @children, $pid if $pid;
				
				
				print $out sprintf("LPCol\t%-".$width.".".($width -3) ."s", "ActCode" );
				for my $si ( 0 .. $poolSize -1 ) {
					print $out "\tSol_".$si;
				}
				print $out "\t-\n";
				print $out sprintf("-\t%-".$width.".".($width -3) ."s", "Objective value" ) ;
				for my $si ( 0 .. $poolSize -1 ) {
					print $out sprintf("\t$nf", $matrix->get_milpinfo_solution_pool($si));
				}
				print $out "\t-\n";
				
				
				
				
				for my $i (0..$matrix->milpinfo("NumCol")-1) {
					my $k = $matrix->activity_at_col($i);
					
					
					if (
						(not defined $typefilter or $typefilter eq $k->attr("type") ) 
						and ($integs == 0 or $k->attr("integ") == 1 ) 
						and (not defined $periodfilter or $periodfilter == $k->attr("mperiod"))
						and (not defined $pattern or $k->id() =~ /$pattern/)
					) {
						
						my $doPrint = 0;
						
						if ($full) {
							$doPrint = 1;
						}
						else {
							for my $si (0 .. $poolSize -1 ) {
								if ($k->get_pooled_solution($si) > 0.000001) {
									$doPrint = 1;
									last;
								}
							}	
						}
					
						if ($doPrint) {
							print $out sprintf("%d\t%-".$width.".".($width -3) ."s", $i, $k->id);
							for my $si (0 .. $poolSize -1 ) {
								print $out sprintf("\t$nf", $k->get_pooled_solution($si) )  ;
							}
							print $out sprintf("\t%s\n", $k->attr("name") );
						}
					}
				}
				close($out);
			}
		}
		
		elsif ($commandstring =~ /^diffints\s+poolsols\s*(\d+,\s*\d+[,\d\s]*)?(\s.*)?$/){
			
			if ($poolSize < 1) {
				print "Solution pool is empty\n";
			}
			else {
				my $options = $2;
				my @solutionlist = (0 .. $poolSize -1);
				if (defined $1) {
					@solutionlist = split /\s*,\s*/, $1;
				}	
					
				if ( @solutionlist < 2) {
					print "List at least two solutions to compare or omit list for all solutions\n";
				}
				else {
					my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($options,undef, \@tempfiles);
					push @children, $pid if $pid;
					
					print $out sprintf("LPCol\t%-".$width.".".($width -3) ."s", "ActCode" );
					for my $si ( @solutionlist ) {
						print $out "\tSol_".$si;
					}
					print $out "\t-\n";
					print $out sprintf("-\t%-".$width.".".($width -3) ."s", "Objective value" ) ;
					for my $si (@solutionlist ) {
						print $out sprintf("\t$nf", $matrix->get_milpinfo_solution_pool($si));
					}
					print $out "\t-\n";
					
					for my $i (0..$matrix->milpinfo("NumCol")-1) {
						my $k = $matrix->activity_at_col($i);
						
						
						if (
							($k->attr("integ") == 1) and
							(not defined $typefilter or $typefilter eq $k->attr("type") )  
							 and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) 
							 and (not defined $pattern or $k->id() =~ /$pattern/)
						) {
							my $first = $k->get_pooled_solution($solutionlist[0]);
							my $hasdiff = 0;
							
							for my $si ( @solutionlist[1 .. $#solutionlist ] ) {
								if ($k->get_pooled_solution($si)  != $first) {
									$hasdiff = 1;
									last;
								}
							}
							if ($hasdiff) {
								print $out sprintf("%d\t%-".$width.".".($width -3) ."s", $i, $k->id ) ;
								for my $si (@solutionlist ) {
									print $out sprintf("\t$nf", $k->get_pooled_solution($si));
								}
								print $out sprintf("\t%s\n", $k->attr("name"));
								
							}

							
						}
					}
					close($out);
				}
			}
		}
		
		
		elsif ($commandstring =~ /^list\s+orow(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t$nf\t%s\n", $i, $k->id, $k->attr("orow") ?  $k->attr("orow") : 0, $k->attr("name") ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and ( $full or ( defined $k->attr("orow") and sprintf($nf, $k->attr("orow")) != 0 and sprintf($nf, $k->attr("orow")) != -1 )) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) );
			}
			close($out);
			
		}
		elsif ($commandstring =~ /^list\s+int(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			
			print $out "Integer Activities\n";
			print $out "Col\tCode\tPriority\tType\tName\n";
			my $cc = 0;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				(++$cc and print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%d\t%s\t%s\n", $i, $k->id, $k->attr("priority"), $k->attr("type") ,$k->attr("name") ) ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and (defined $k->attr("integ") and $k->attr("integ") == 1) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) );
				
			}
			print $out "$cc Marked Integers\t(".$matrix->milpinfo("IntegAct")." according to Matrix Info)\n";
			close ($out);
		}
		elsif ($commandstring =~ /^list\s+quadratic(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			
			print $out "Quadratic activities\n";
			print $out "Col\tCode\tName\n";
			my $cc = 0;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				(++$cc and print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%s\n", $i, $k->id,  $k->attr("name") ) ) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and (defined $k->attr("quadratic") and $k->attr("quadratic") == 1) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/));
				
			}
			close ($out);
		}
		elsif ($commandstring =~ /^list\s+lb(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			
			print $out "Activities with Lower Bounds != 0\n";
			print $out "Col\tCode\tLBound\tName\n";
			my $cc = 0;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%e\t%s\n", $i, $k->id, $k->attr("lbound"), $k->attr("name") )  if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and (defined $k->attr("lbound") and $k->attr("lbound") !=0 ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/));
			}
			close $out;
		}
		elsif ($commandstring =~ /^list\s+ub(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			
			print $out "Activities with Upper Bounds < 1.0E+31\n";
			print $out "Col\tCode\tUBound\tName\n";
			my $cc = 0;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				print $out sprintf("%d\t%-".$width.".".($width -3) ."s\t%e\t%s\n", $i, $k->id, $k->attr("ubound"), $k->attr("name") )  if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and ($integs == 0 or $k->attr("integ") == 1 ) and (defined $k->attr("ubound") and $k->attr("ubound") < 1.0E+31 ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod"))and (not defined $pattern or $k->id() =~ /$pattern/) );
			}
			close $out;
		}
		elsif ($commandstring =~ /^set\s+ubinf(\s+\d+)?(\s+.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($2,undef, \@tempfiles);
			my $value;
			$value = $1 if (defined $1);
			
			push @children, $pid if $pid;
			
			print $out "Resetting activities with infinite upper bounds to upper bound = ", defined ($value) ? $value : 10000000000,"\n";
			my $cc = 0;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				$k->set_attr("ubound", defined ($value) ? $value : 10000000000) if ((not defined $typefilter or $typefilter eq $k->attr("type") ) and (defined $k->attr("ubound") and $k->attr("ubound") > 9.9E+30 ) and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) and (not defined $pattern or $k->id() =~ /$pattern/) );
			}
			close $out;
		}
				
		elsif ($commandstring =~ /^accounting(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			my %accounting;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				 $accounting{$k->attr("account")} += ( $k->attr("orow") ?  $k->attr("orow") : 0 ) * ( $k->attr("solution") ?  $k->attr("solution") : 0 ) if  ( defined $k->attr("solution") and sprintf($nf, $k->attr("solution")) != 0 and sprintf($nf, $k->attr("solution")) != -1 );
			}
			for my $j (sort {$a cmp $b} keys %accounting) {
								 print $out sprintf("%-".$width.".".($width -3) ."s\t$nf\n", $j, $accounting{$j});
			}
			close($out);
			
		}
		elsif ($commandstring =~ /^type_accounting(\s.*)?/){
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $pattern) = check_options($1,undef, \@tempfiles);
			push @children, $pid if $pid;
			my %accounting;
			for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				 $accounting{$k->attr("type")} += ( $k->attr("orow") ?  $k->attr("orow") : 0 ) * ( $k->attr("solution") ?  $k->attr("solution") : 0 ) if  ( (not defined $typefilter or $typefilter eq $k->attr("type") )  and  defined $k->attr("solution") and sprintf($nf, $k->attr("solution")) != 0 and sprintf($nf, $k->attr("solution")) != -1 and (not defined $periodfilter or $periodfilter == $k->attr("mperiod")) );
			}
			for my $j (sort {$a cmp $b} keys %accounting) {
								 print $out sprintf("%-".$width.".".($width -3) ."s\t$nf\n", $j, $accounting{$j});
			}
			close($out);
			
		}	
		elsif ($commandstring =~ /^tab\s+(\d+)\s*\.\.\s*(\d+)\s*:\s*(\d+)\.\.(\d+)(\s*-.+)?/) {
			my ($astart, $aend, $cstart, $cend) = ($1,$2,$3,$4);
			if ($astart < 0 or $aend > $matrix->milpinfo("NumCol") -1 or $cstart < 0 or $cend > $matrix->milpinfo("NumRow") -1) {
			    print "Invalid dimensions\n";
			}
			else {
			      my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter) = check_options($5, 3, \@tempfiles);
			      push @children, $pid if $pid;
			      print_tableau_part($out, [($astart..$aend)],[($cstart..$cend)], $nf, $MqlmatrixGlobalOptions{dstab});
			      close $out;
			}
		}
		elsif ($commandstring =~ /^tab\s+([\w\s,]+)\s+:\s+([\w\s,]+)(\s*-.+)?/) {
			my @atypes = split /\s*,\s*/, $1;
			my @ctypes = split /\s*,\s*/, $2;
			my $options = $3;
			$ctypes[$#ctypes] =~ s/\s*$//;
			$atypes[$#atypes] =~ s/\s*$//;
			#print join("|", @atypes)."\n";
			#print join("|", @ctypes)."\n";
			#print $options."\n";
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter) = check_options($options, 3, \@tempfiles);
			push @children, $pid if $pid;
			my (@alist ,@clist);
			 for my $i (0..$matrix->milpinfo("NumCol")-1) {
				my $k = $matrix->activity_at_col($i);
				if (is_in_list($k->attr("type"), \@atypes))  {
				    push @alist, $i;
				}
			 }
		         for my $i (0..$matrix->milpinfo("NumRow")-1) {
				my $k = $matrix->constraint_at_row($i);
				if (is_in_list($k->attr("type"), \@ctypes))  {
				    push @clist, $i;
				}
			 }
			 print_tableau_part($out, \@alist,\@clist, $nf, $MqlmatrixGlobalOptions{dstab});
             close $out;                         
		}
		elsif ($commandstring =~ /^tab\s*$/) {
			my ($out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter) = check_options($5, 3, \@tempfiles);
			  push @children, $pid if $pid;
			  print_tableau_part($out, [( 0.. ($matrix->milpinfo("NumCol") -1)) ],[(0..($matrix->milpinfo("NumRow") -1))], $nf,$MqlmatrixGlobalOptions{dstab});
			  close $out;
		}
		else {
			print "unrecognized command\n"
		}
	}
}

waitpidall(\@children, \@tempfiles);
unlink($tempfilepath);
exit(0);

sub print_tableau_part {
      my ($out, $alist, $clist, $nf, $ds) =@_;
      
      my $numberFormatter = Number::Format->new(DECIMAL_POINT => $ds);
      
      my @temptab;
	  push @{$temptab[2]}, "Description:";
	  push @{$temptab[1]}, "Id:";
	  push @{$temptab[0]}, "Type:";
	  push @{$temptab[3]}, "Unit:";
	  push @{$temptab[4]}, "Integer:";
	  push @{$temptab[5]}, "L. bound:";
	  push @{$temptab[6]}, "U. bound:";
	  push @{$temptab[7]}, "Column index:";
	  push @{$temptab[8]}, "Obj.Coef.:";
      for my $i (@$alist) {
	      my $k = $matrix->activity_at_col($i);
	      push @{$temptab[2]}, $k->name;
	      push @{$temptab[1]}, $k->id;
	      push @{$temptab[0]}, $k->attr("type");
	      push @{$temptab[3]}, ($k->attr("unit") // " ");
	      push @{$temptab[4]}, $k->attr("integ");
	      push @{$temptab[5]}, $k->attr("lbound");
	      push @{$temptab[6]}, $k->attr("ubound");
	      push @{$temptab[7]}, $k->lp_column;
	      push @{$temptab[8]}, $numberFormatter->format_number($k->attr("orow"));
      }
      for my $i (@temptab) {
	      print $out "\t\t\t\t\t\t\t".join("\t",@$i)."\n";
      }
      print $out "Row\tConstraint group(type)\tConstraint code\tConstraint description\tUnit\tLHS\tSign\tRHS\t\n";
            
      for my $j (@$clist) {
	      my $l = $matrix->constraint_at_row($j);
	      print $out join("\t",$j,  $l->attr("type"), $l->id, $l->name, $l->attr("unit") // " ", "",$l->showsign(),  $numberFormatter->format_number($l->attr("value")));
	      for my $i (@$alist) {
		    my $k = $matrix->activity_at_col($i);
		    print $out "\t";
		  #  print $out sprintf("$nf",$k->coeff($l->id)) if (defined $k->coeff($l->id));
		  print $out $numberFormatter->format_number($k->coeff($l->id)     ) if (defined $k->coeff($l->id));
		  
	      }
	      print $out "\n";
      }
}

sub check_options {
      my $options = shift;
      my $force_pipe = shift;
      my $tempfilesref = shift;
      my $nf = "% 14.4f";
      $nf = "%e" if (defined $options and $options =~ /-e/);
      $nf = "% 14.$1f" if (defined $options and $options =~ /-d\s*(\d+)/);
      my $full = (defined $options and $options =~ /-f/);
      my $pipe;
      my $file;
      
      if (defined $force_pipe) {
	    $pipe = $force_pipe
      }
      elsif (defined $options and $options =~ /-l/) {
		$pipe = 1;
      }
      elsif (defined $options and $options =~ /-o\s*([^-]+)/) {
		  $pipe = 2;
		  $file = $1;
      };
      
      my ($out, $pid) = less_fork( $pipe,$file,$tempfilesref);
      my $typefilter;
      $typefilter = $1 if (defined $options and $options =~ /-t\s*(\w+)/);
      my $integs = 0;
      $integs = 1 if (defined $options and $options =~ /-i/);
      my $width = $MqlmatrixGlobalOptions{colwidth};
      $width = $1 if (defined $options and $options =~ /-w\s*(\d+)/);
      
      my $periodfilter; 
      $periodfilter = $1 if (defined $options and $options =~ /-p\s*(\d+)/);
      
      my $patternfilter; 
      $patternfilter = $1 if (defined $options and $options =~ /-r\s*\/([^\/]+)/);
      
      
      return $out, $pid, $full, $nf, $typefilter, $integs, $width, $periodfilter, $patternfilter;
}

  sub list_help {
	  my $out = shift;
	  print $out <<HELP;
	  Commands:
	  
	  GENERAL:
	  exit			quits the interactive session
	  help			prints this help (Option -l)
	  quit			quits the interactive session

	  I/O
	  printmtx {<suffix>}	saves the matrix as .mtx into a file with the indicated <suffix>
	  printtxt {<suffix>}	saves the commented matrix into a file with the indicated <suffix>
	  reload		reloads the matrix from the original file
	  solve {<solverId>}	tries to solve the matrix and to import the solution vector if successful 
	  impsol {<filename>}	import solution vector from .pri file
	  impsol_cplexxml <filename> import solution from CPLEX .sol file (XML format)
	  
	  COLUMN/ROW INFORMATION
	  info				prints general information about the Matrix
	  act <id>			prints information about activity with mpmasql identifier <id>
	  con <id>			prints information about constraint with mpmasql identifier <id>
	  col <C>			prints information about matrix column no. <C>
	  row <R>			prints information about matrix row no. <R>
	  coef act <id>     prints all coefficients <> 0 of activity with mpmasql identifier <id> 
	  coef col <C>      prints all coefficients <> 0 of matrix column no. <C> (Option -l)
	  coef con <id>     prints all coefficients <> 0 of constraint with mpmasql identifier <id> 
	  coef row <R>      prints all coefficients <> 0 of matrix row no. <R> (Option -l)
	  terms con <id>    prints all final terms (coefficient * solution)  <> 0 of constraint with mpmasql identifier <id> 
	  terms row <R>     prints all final terms (coefficient * solution)  <> 0 of matrix row no. <R> 
	  rhs <R>			prints the rhs for row <R>
	  lhs <R>			calculates the LHS for row <R> based on the current solution
	  lhslb <R>			calculates the lhs for row <R> based on the lbounds of columns
	  slack <R>			calculates the slack for row <R> based on the current solution
	  Q <C>				show Q matrix entries for column C
	  multiply Q <value>		multiply Q matrix by <value>
	  divide Q <value>		divide Q matrix by <value>

	  LISTING
	  list act          list all activities (Options: -l -o -t -i)
	  list con          list all constraints (Options: -l -o -t)
	  list ==           list all equality constraints (Options: -l -o -t)
	  list lhs          prints all LHS values <> 0 (Options: -l -f -e -d -o -t)
  	  list lhs0         prints all LHS values == 0 (Options: -l -f -e -d -o -t)
	  list rhs          prints all RHS values <> 0 (Options: -l -f -e -d -o -t)
  	  list slacks       prints all slack values <> 0 (Options: -l -f -e -d -o -t)
    	  list slacks0      prints all slack values == 0 (Options: -l -f -e -d -o -t)
	  list sol          lists the solution for all activities where it is <> 0 (Options: -l -f -e -d -o -t -i)
	  list orow         lists the objective function coefficient for all activities where it is <> 0 (Options: -l -f -e -d -o -t -i)
	  list int          lists all integer activities (Options: -l -o -t)
	  list lb           lists all activities with lower bounds > 0 (Options: -l -o -t -i)
	  list ub 			lists all activities with upper bounds < 1.0E+31  (Options: -l -o -t -i)
	  list lhslb        lists the LHS based on lbounds where LHS <> 0 (Options: -l -f -e -d -o -t)
	  accounting        lists obtained objective value splitted by activity accounts (Options: -l -e -d -o )
	  type_accounting   lists obtained objective value splitted by activity types (Options: -l -e -d -o -t )

	  Options for list/coef/terms: 
		  -l 			redirects the output to a separate window (Linux: less or most, Windows: notepad)
		  -o <filename>		redirects the output to <filename>
		  -e			scientific number format
		  -d <n>		number of digits (default <n> = 3) 	
		  -f			prints all elements, not only those <> 0 (resp. == 0 for lhs0, slack0)
		  -t <type>		list only constraints/activities of mpmasql type <type>
		  -i			list only integer activities
		  -w <number>	specify width for identifier column in output
		  -p <number>	list only activities/constraints of planning period <p>
		  -r /<regexp>/ list only activities/constraints with identifiers matching regular expression pattern <regexp>
														  
	  TABLE VIEW
	  tab <C1>..<C2> : <R1>..<R2>	view tableau between columns C1 and C2 and rows R1 and R2 in spreadsheet
	  tab <typelistA> : <typelistC>	view tableau for activities/constraint of types given in spreadsheet, 
					where <typelist> is comma-separated list of mpmasql types
					
	  MODIFICATIONS		
	  modify coef <C> <R> <new value>  		sets coefficient in column <C> and row <R> to <new value>
	  modify row <R> (<attr> = <new value>)		sets attribute <attr> of row <R> to <new value>
	  modify col <C> (<attr> = <new value>)		sets attribute <attr> of column <C> to <new value>
	  modify rows <R1..R2> (<attr> = <new value>)	sets attribute <attr> of rows <R1> to <R2> to <new value>
      modify cols <C1..C2> (<attr> = <new value>)	sets attribute <attr> of column <C1> to <C2> to <new value>
	  modify constype <T> (<attr> = <new value>)	sets attribute <attr> of all constraints of type T to <new value>
      modify actstype <T> (<attr> = <new value>)	sets attribute <attr> of all activities of type T to <new value>
	  modify conspattern /<reg exp>/ (<attr> = <new value>)	sets attribute <attr> of all constraints matching regular expression <reg exp> to <new value>
      modify actspattern /<reg_exp>/ (<attr> = <new value>)	sets attribute <attr> of all activities matching regular expression <reg exp> to <new value>
	  
	  (Note: 	In modify col(s)/row(s)/actstype/constype 
				the '=' may also be replaced by *= or += to multiply with/add to the existing value
				
				modify actstype/constype/actspattern/conspattern accept the -p option to restrict to a certain planning period 
	   )
	  
	  set ubinf			Resetting activities with infinite upper bounds to upper bound = 10000000000
						(helps in debugging unbounded LPs)
						
	  SOLVER OPTIONS
	  solver_options		show options passed to mpmasMipSolver
	  append_solver_options		append to options passed to mpmasMipSolver
	  set_solver_options		change options passed to mpmasMipSolver
	  solver_exec			show which solver executable has currently been selected
	  set_solver_exec		choose solver executable
 
	  help pooling		explains advanced commands to explore non-optimal feasible integer solutions


	  PREFERENCES
	  set colwidth <value>		specify width for identifier column in output globally
	  set digits_solve <value>	decimals precision when writing out matrix for solving

	  OTHER OPTIONS
	  set_risk_aversion <value>	set risk aversion coefficient for evaluation of quadratic part of objective values
					(Note: has no effect on current Q matrix)
HELP
  };
sub list_help_pooling {
	  my $out = shift;
	  print $out <<HELPPOOL;
	  
	  SOLUTION POOLING:
	  
	  Solution pooling allows you to retain or explore feasible 
	  integer solutions besides the optimal solution found.
	  This can be helpful for sensitivity analysis, but also 
	  if solving takes very long: By exploring the solution pool
	  you may discover which variables differ between solutions 
	  and which variables CPLEX needs to branch and compare.
	  (Note: Usually, this only refers to alternative solutions 
	  for integer values, solutions that difer in continuous variables 
	  will not be considered. It also usually does not comprise 
	  all feasible solutions, as the number of solutions that
	  CPLEX looks for is limited [see hint at the end of this help].)
	  
	  Currently this feature has only been implemented for CPLEX, 
	  so you need to choose the mpmasMipSolverCplex executable
	  (either in mqlmatrix.conf, with -M or using set_solver_exec).
	  
	  
	  HOW TO:
	  
	  There are two modes of actions: 
	  (i) only retain the suboptimal solutions that are anyway found 
		 during the optimization process
	  (ii) actively search for alternative feasible solutions 
	     (usually less optimal solutions) after the optimization process.
	  
	  1. To activate these processes you have to pass a special 
	     option to mpmasMipSolverCplex:
	  
		For (i), you need to add the option "-pool", e.g. 
		
			append_solver_options -pool
	  
		For (ii), you need to add the option "-pool_extra", e.g. 
		
			append_solver_options -pool_extra
			
	  2. Then you just solve the model as usual:
	  
			solve
			
		(Note: As usual, the optimal solution is iported automatically 
		 and can be accessed with list sol etc.)	
			
	  3. After solving	the solution pool can be imported with
	  
			imppool
			
	  4. There are four commands that help you explore the solution pool:
	  
			list poolsols 	
					lists all solutions side by side 
					(by default only showing activities that 
					are nonzero in any of the pooled solutions)

			list poolsol <n>  
					lists solution number <n> from the solution pool
	  
			diffints poolsols 
					lists the differences in integer variables 
					between the pooled solutions

			diffints poolsols <x,y,...> 
					lists the differences in integer variables 
					between the listed pooled solutions number x, y, ...
		
	  All of these commands take the usual flags -l, -o, -d, -e, -f, -t, -p, -i, -w that "list sol" also accepts,
	  so you can e.g. pipe the results to a file or pager.


	  SAVING AND RELOADING ALTERNATIVE SOLUTIONS

	  The solution pool is saved in in ..._solPool_<n>.pri files, 
	  which -- like the temporary solution of mqlmatrix --
	  are saved in the /tmp/mpmasql folder. This folder is by default 
	  emptied by the OS at each reboot/login or even more often. 
	  If you want to retain the full solutions for later use, 
	  you should copy those files to a different folder.
	  
	  You can later reload them by specifying the first part of 
	  their path/name up and including the "_solPool_ " part of 
	  their name after the imppool command. E.g.
	  
			imppool ~/Desktop/example1/basic5_example__MILP_15_temp1_solPool_


	  CONTROL OF THE SOLUTION POOLING PROCESS

	  The exploration process is controlled by a number of CPLEX parameters that you can specify in the CPLEX options file,
	  which control for example the maximum number of solutions that CPLEX retains during the extra search, whether it only
	  checks for solutions within a certain gap to the optimal solution etc. 
	  
	  For more information, search the IBM CPLEX documentation on: 
		solution pool intensity,
		solution pool replacement strategy,
		maximum number of solutions generated for solution pool by populate,
		maximum number of solutions kept in solution pool,
		absolute gap for solution pool,
		relative gap for solution pool. 	  
	  
HELPPOOL

  };

sub print_matrix {
	  my $matrix = shift;
	  my $ag = shift;
	  my $type = shift;
	  my $mode = shift;
	  my $digs = shift;
	  my $triplets = shift || 1;
	  $matrix->blank_file;
	  $matrix->set_config_entry("_INPUT_DELIMITERS", 1);
	  $matrix->set_config_entry("MATRIX_TRIPLETS", $triplets);
	  $matrix->set_milpinfo_entry("Digs", $digs );
	  $matrix->prepare_special(1, $ag.$type, {});
	  my $fn = $matrix->write($mode, $ag.$type,{},1);
	  return $fn;
  }
  sub print_info {
	  my $element = shift;
	  print $element->id(),"\n";
	  my @temp = @{$element->list_of_attribute_fields};
	  foreach my $i (@temp) {
		  print sprintf("%20s\t%s\n", $i, $element->attr($i)) if ($element->attr_defined($i));
	  }

  }
  sub less_fork {
	  my $dofork = shift;
	  my $file = shift;
	  my $tempfilesref = shift;
	  if (not $dofork) {
		  open (my $out, ">&STDOUT" );
		  return $out;
	  }
	  elsif ($dofork == 1) {
	      pipe (PIPE_FROM_MAIN, PIPE_TO_LESS);
	      PIPE_TO_LESS->autoflush(1);
	      my $pid = fork();
	      if ($pid) {
		      close (PIPE_FROM_MAIN);
		      my $fn = "mpmasql".sprintf ("%03d", scalar @$tempfilesref).".temp";
		      push @$tempfilesref, $fn;
		      return (*PIPE_TO_LESS{IO}, $pid );
	      }
	      else {
		      close (PIPE_TO_LESS);
		      my $n = $#{$tempfilesref} + 1 ;
		      
		      my $fn = "mpmasql".sprintf ("%03d", scalar @$tempfilesref).".temp";
		      push @$tempfilesref, $fn;
		      
		      open (my $file ,">", "$tempfilepath/$fn") or die "Could not open temp file:\n $!\n";
		      while ( <PIPE_FROM_MAIN> ){
			      
			      print $file $_;
		      };
		      
		      close ($file);
		      if ($^O eq "linux") {
				exec('gnome-terminal -e "'.$pager." $tempfilepath/".$fn.'"'. '  > '.$tempfilepath.'/pager_err.log 2>&1');
			  }
			  elsif ($^O eq "MSWin32") {
#			    print($pager." $tempfilepath/".$fn);
				exec($pager." $tempfilepath/".$fn);
			  }
			  
		      exit (0);
	      }
	 }
	elsif ($dofork == 2) {
		  open (my $out, ">", $file ) or (print "Error $! when trying to open $file\n" and return ">&STDOUT");
		  print "Output is written to file $file\n";
		  return $out;
	}
	elsif ($dofork == 3) {
	      pipe (PIPE_FROM_MAIN, PIPE_TO_LESS);
	      PIPE_TO_LESS->autoflush(1);
	      my $pid = fork();
	      if ($pid) {
		      close (PIPE_FROM_MAIN);
		      my $fn = "mpmasql".sprintf ("%03d", scalar @$tempfilesref).".temp";
		      push @$tempfilesref, $fn;
		      return (*PIPE_TO_LESS{IO}, $pid );
	      }
	      else {
		      close (PIPE_TO_LESS);
		      my $fn = "mpmasql".sprintf ("%03d", scalar @$tempfilesref).".temp";
		      push @$tempfilesref, $fn;
		      open (my $file ,">", "$tempfilepath/$fn") or die "Could not open temp file:\n $!\n";
		      while ( <PIPE_FROM_MAIN> ){
			      
			      print $file $_;
		      };
		      
		      close ($file);
		      exec($spreadsheet." $tempfilepath/".$fn. '  > '.$tempfilepath.'/spreadsheet_err.log 2>&1');
		      
		      exit (0);
	      }
	 }
}
sub waitpidall {
	my $arrayref = shift;
	my $filearrayref = shift;
	for my $f (@$filearrayref) {
	    unlink("$tempfilepath/$f");
	}
	waitpid($_, 0) foreach @{$arrayref};
}
