package MpmasMILPAbstract;

use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Carp qw(croak);
use Data::Dumper;
use MpmasInputfile;
use MpmasInputBlock;
use MpmasInputBlockImmediate;
use MpmasInputBlockWithValueReplace;
use IndexedTable;
use SimpleObjectRegistry;
use MpmasMILPActivity;
use MpmasMILPConstraint;
use CtUtilities qw(numbers_robust_to_sorting);
use Scalar::Util;


our @ISA = qw(MpmasInputfile);

use constant  {
  OSL_INFINITY => 10 ** 31,
  OLD		   => 0,
  
};

sub osl_inifinity {
    return OSL_INFINITY;
}
sub filename {
	my $self = shift;
	return "MILP".$self->file_suffix if (defined $self->file_suffix);
	return "MILP";
}

sub extension {
	my $self = shift;
	return $self->{extension} if (exists $self->{extension});
	return ".dat";
}

sub set_extension {
	my $self = shift;
	my $str = shift;
	$self->{extension} = $str;
}

sub extension_cm {
	my $self = shift;
	return "$self->{extension_cm}" if (exists $self->{extension_cm});
	return ".txt";
}

sub set_extension_cm {
	my $self = shift;
	my $str = shift;
	$self->{extension_cm} = $str;
}

sub prepare_shared {
	my ($self) = @_;
	$self->sort_constraints();
	$self->sort_activities();
	$self->calculate_milpinfo();
}
sub prepare_special {
	my ($self) = @_;
	$self->set_extension(".dat");
	$self->set_extension_cm(".txt");
	if ($self->tableau_compact) {
	    $self->set_extension(".cpt");
	}
	
	#my $NumRow = $self->milpinfo("NumRow");
	#my $NumCol = $self->milpinfo("NumCol");
	#my $ZeroAct = $self->milpinfo("ZeroAct");
	#my $ZeroCon = $self->milpinfo("ZeroCon");
	#my $digs = $self->milpinfo("Digs");
	
	$self->consistency_check_activities();
	
	$self->add_matrix_title();
	$self->add_objrow();
	$self->add_tableau();
	$self->add_fixed_independent_rhs();
	$self->add_type_and_range_of_constraints();
	#$self->add_marked_integers();
	$self->add_column_bounds();
	$self->add_integer_sets();
	$self->add_quadratic_info();
	$self->add_epsila();
	$self->add_timevariant_coefficients();
}
sub make_constraint_order_hash {
	my ($self) = @_;
	my $g = 0;
	my %conrank;
	for (@{$self->get_constraint_type_order()}) { $conrank{$_} = ++$g; } ## Define order of constraint types in ini-File
	$self->{conrank} = \%conrank;
}
sub sort_constraints {
	my $self = $_[0];
	my $ConstraintTypes = $_[1];
	##Sort Constraints
	
	unless (defined $ConstraintTypes) {
		$self->make_constraint_order_hash();
		for my $element ($self->constraints){
			my $x =$element->attr("type");
			my $y = $element->id;		
			print STDERR "Error: constraint type $x not listed in CONTYPEORDER (Constraint $y)\n" and exit(1) unless $self->{conrank}->{$x};
			print STDERR "Error Constraint $y has been defined twice!\n" and exit(1) if (exists $ConstraintTypes->{$y});
			$ConstraintTypes->{$y}[0] = $self->{conrank}->{$x};
			if($self->config_entry("MULTIPERIOD")) {
				eval {   
				       use warnings FATAL => qw/uninitialized numeric/;
					$ConstraintTypes->{$y}[1] = $element->attr("mperiod")*1; #*1 used to invoke test for numeric 
				};		      
				print STDERR "Error in mperiod attribute of constraint ".$element->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;

				$ConstraintTypes->{$y}[2] = numbers_robust_to_sorting($y)
			}
		}
	}
	
	my @SortedConstraint;
	if($self->config_entry("MULTIPERIOD")) { #not used anymore, now overloaded in MpmasMILPMultiperiod
		
		@SortedConstraint = sort {$ConstraintTypes->{$a}[1] <=> $ConstraintTypes->{$b}[1] 
						|| $ConstraintTypes->{$a}[0] <=> $ConstraintTypes->{$b}[0] 
							|| $ConstraintTypes->{$a}[2] cmp $ConstraintTypes->{$b}[2]   } keys %{$ConstraintTypes} ; ##	
	}
	else {
		@SortedConstraint = sort {$ConstraintTypes->{$a}[0] <=> $ConstraintTypes->{$b}[0] || numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b)  } keys %{$ConstraintTypes} ; ##	
	}
	my $g = -1;
	for my $i (@SortedConstraint){
		my $x = $self->constraint($i);
		$x->set_lp_row( ++$g);
		print  "Constraint $i placed into row". $x->lp_row()."\n" if $::loudorder;
	
	}
	print "\tConstraints have been sorted\n"if $::loudorder;
	$self->set_sorted_constraints(\@SortedConstraint);
}
sub make_activity_order_hash {
	my ($self) = @_;
	my $g = 0;
	my %actrank;
	for (@{$self->get_activity_type_order()}) { $actrank{$_} = ++$g; } ## Define order of activity types 
	$self->{actrank} = \%actrank;
}
sub sort_activities {
	my $self = $_[0];
	my $ActivityTypes = $_[1];
	##Determine Order of Activities
	my $g = 0;

	unless (defined $ActivityTypes) {
		$self->make_activity_order_hash();
		for my $element ($self->activities){
			#check for existence of type
			my $x = $element->attr("type");
			my $y = $element->id;
			print STDERR "Error Activity $y has been defined twice!\n" and exit(1) if (exists $ActivityTypes->{$y});
			$ActivityTypes->{$y} = $x;
			print STDERR "Error for Activity $y: activity type $x not listed in ACTTYPEORDER\n" and exit(1) unless $self->{actrank}->{$x};
		}
	}
	my @SortedActivity = sort {$self->{actrank}->{ $ActivityTypes->{$a}} <=> $self->{actrank}->{$ActivityTypes->{$b}} || numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b) } keys %{$ActivityTypes} ; ##
	$g = -1;
	for my $i (@SortedActivity){
		my $x = $self->activity($i);
		$x->set_lp_column(++$g);
		print  "Activity $i placed into column $g \n" if $::loudorder;
	
	}
	print "\tActivities have been sorted\n" if $::loudorder;
	$self->set_sorted_activities(\@SortedActivity);
}

sub calculate_milpinfo {
	my ($self) = @_;
	my $NumRow = $self->number_of_constraints;
	my $NumCol = $self->number_of_activities;

	my $IntegAct = 0;
	my $QuadraticAct = 0;
	my ($ZeroCon, $ZeroAct) = (0,0);
	my $exchanges = {};
	my $number_of_exchanges = {};
	for my $milpid (@{$self->milplist}) {
			$number_of_exchanges->{$milpid} = 0;
	}
	my @PeriodStartCols = (0);
	my @PeriodStartRows = (0);
	my $curMPeriod = 0;
	# count number of Zero Constraints and Matrices
	
	for my $t2 ($self->sorted_constraints){
		my $t = $self->constraint($t2);
		$ZeroCon += 1 if (lc($t->attr("type")) eq "maszero" or lc($t->attr("type")) eq "masforget");
		
		#adapt time-variant RHS to chosen start year
		if ($t->has_subelement("exchange")) {
		    for my $sf (sort { numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b) } $t->subelement_fields("exchange")) {
			    $sf =~ /(\w+)_(\d+)$/;
			    print STDERR "Error in constraint ".$t->id.", #exchange>: Exogenous change is only possible for the value field of a constraint, not for field '".$1."'\n"  unless ($1 eq "value");
			    #either change value in LP
			    if ($2 <= $self->config_entry("STARTYEAR")) {
					for my $milpid (@{$self->milplist}) {
						$t->set_attr("value", $t->milpspecific_subelement_field("exchange", $sf, $milpid), $milpid); 
					}
			    }
			    #or include in list of changes of matrix coefficients
			    else {
					my $period = $2 - $self->config_entry("STARTYEAR") + 1;
					for my $milpid (@{$self->milplist}) {
						$number_of_exchanges->{$milpid}++ unless (exists $exchanges->{$period}->{$milpid}->{-1}->{$t->lp_row}) ;
						$exchanges->{$period}->{$milpid}->{-1}->{$t->lp_row} = $t->milpspecific_subelement_field("exchange", $sf, $milpid);
					}				
			    }
		    }
		}
		if ($self->config_entry("MULTIPERIOD") > 0) {
					      	
		
			while ($curMPeriod < $t->attr("mperiod")) {
				$curMPeriod += 1;
				push @PeriodStartRows, $t->lp_row();
			}
		}

			
	}
	my @SortedQuadAct;
	$curMPeriod = 0;
	for my $t2 ($self->sorted_activities){
		my $t = $self->activity($t2);
		$ZeroAct +=  1 if (lc($t->attr("type")) eq "maszero" or lc($t->attr("type")) eq "masfuture"  or lc($t->attr("type")) eq "masxc_inc_trans");
		eval {   
			 use warnings FATAL => qw/uninitialized numeric/;
			 $IntegAct += 1	if ($t->attr_defined("integ") && $t->attr("integ") == 1) 
		};
		print STDERR "Error in integer attribute of activity ".$t->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;
		#print $t->attr("name")."\n" if (!$t->attr_defined("integ") ) ;
	      
		#identify quadratic activities
		if ($t->attr_defined("quadratic") && $t->attr("quadratic") == 1) {
		    $QuadraticAct += 1 ;
		    push @SortedQuadAct, $t2;
		}
		#adapt time-variant coefficients to chosen start year
		if ($t->has_subelement("exchange")) {
		    for my $sf (sort { numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b) } $t->subelement_fields("exchange")) {
				
			    $sf =~ /([\w:_]+)_(\d+)$/;
			    my $what = $1;
			    my $when =$2;
			    if ($what eq "orow") {
				  print STDERR "Error: exogenous change of coefficients of objective row is only possible for activities of type MASzero\nCheck #exchange> orow in activity " .$t->id. "\nUse normal 'orow_<year>' field instead, if this is does not have to be a MASzero activity for other reasons.\n" and exit(1)  unless (lc($t->attr("type")) eq "maszero" );
				  #either change value in LP
				  if ($when <= $self->config_entry("STARTYEAR")) {
				      for my $milpid (@{$self->milplist}) {
							$t->set_orow($t->milpspecific_subelement_field("exchange", $sf, $milpid), $milpid); 
				      }
				  }
				  #or include in list of changes of matrix coefficients
				  else {
				      my $period = $when - $self->config_entry("STARTYEAR") + 1;
				      for my $milpid (@{$self->milplist}) {
							$number_of_exchanges->{$milpid}++ unless (exists $exchanges->{$period}->{$milpid}->{$t->lp_column}->{-1}) ;
							$exchanges->{$period}->{$milpid}->{$t->lp_column}->{-1} = $t->milpspecific_subelement_field("exchange", $sf, $milpid);
				      }
				  }
			    }
			    else {
				  my $c = $self->constraint($what);
				  print STDERR "Error in activity ".$t->id.", #exchange>: Constraint '".$what."' has not been defined anywhere in class file\n"  unless (defined $c);
				  #either change value in LP
				  if ($when <= $self->config_entry("STARTYEAR")) {
				      for my $milpid (@{$self->milplist}) {
						$t->set_coeff($what, $t->milpspecific_subelement_field("exchange", $sf, $milpid), $milpid); 
				      }
				  }
				  #or include in list of changes of matrix coefficients
				  else {
				      my $period = $when - $self->config_entry("STARTYEAR") + 1;
				      for my $milpid (@{$self->milplist}) {
						$number_of_exchanges->{$milpid}++ unless (exists $exchanges->{$period}->{$milpid}->{$t->lp_column}->{$c->lp_row}) ;
						$exchanges->{$period}->{$milpid}->{$t->lp_column}->{$c->lp_row} = $t->milpspecific_subelement_field("exchange", $sf, $milpid);
					  }
					
				  }
			    }
		    }
		}
		if ($self->config_entry("MULTIPERIOD") > 0) { ##obsolete now overloaded in MpmasMILPMultiperiod
		
		      eval {   
		       use warnings FATAL => qw/uninitialized numeric/;		
		
			while ($curMPeriod < $t->attr("mperiod")) {
				$curMPeriod += 1;
				push @PeriodStartCols, $t->lp_column();
			}
		      };
		      print STDERR "Error in mperiod attribute of activity ".$t->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;
		}
	}
	$self->set_sorted_quadratic_activities(\@SortedQuadAct);
	$self->set_exchanges($exchanges);
	$self->set_number_of_exchanges($number_of_exchanges);


	print "\e[K \rMILP".$self->file_suffix() .":\n";
	print "\tNumber of rows:\t\t $NumRow \n";
	print "\tNumber of columns:\t $NumCol \n";
	#print "\tNumber of nonzero constraints:\t ".($NumRow - $ZeroCon)." \n" unless ($self->config_entry("MPMASCHECKSOFF"));
	#print "\tNumber of nonzero activities:\t ".($NumCol -$ZeroAct)." \n" unless ($self->config_entry("MPMASCHECKSOFF"));
	print "\tNumber of integers:\t $IntegAct \n\n";
	my $milpinfo = {(
				NumCol => $NumCol,
				NumRow => $NumRow,
				ZeroAct => $ZeroAct,
				ZeroCon => $ZeroCon,		
				IntegAct => $IntegAct,
				QuadraticAct => $QuadraticAct,
				Digs	=> $self->config_entry("DIGS"),
				PeriodStartCols => \@PeriodStartCols,
				PeriodStartRows => \@PeriodStartRows
	)};
	$self->set_milpinfo($milpinfo);
}
sub consistency_check_activities {
	my ($self) = @_;
	###Consistency check
	my $NumCol = $self->milpinfo("NumCol");
	my $checkact = $self->number_of_activities();
	print STDERR "Number of Activities inconsistent: ". scalar $checkact. " != ".$NumCol."\n" and exit(1) unless (scalar $checkact == $NumCol);
}
sub add_matrix_title {
	my ($self, $singlemode) = @_;
	my $matrixtitle = MpmasInputBlock->new(8,1);
	$matrixtitle->add_comment_row("THE MATRIX FILE");
	$matrixtitle->add_comment_row("");
	$matrixtitle->add_comment_row("1", "PARAMETERS AND COEFFICIENTS"); 
	$matrixtitle->add_comment_row("1.1","Information on the size of the programming matrix","","","","","","","Number of constraints","Number of activities");			
	$matrixtitle->add_row("","","","","","","","",$self->milpinfo("NumRow"), $self->milpinfo("NumCol"),  $self->delim("#END_ROWCOL"));
	$matrixtitle->add_row("");
	unless ($singlemode)  {
		$matrixtitle->add_comment_row("1.2","Other information about the matrix","","","","","","","Number nonzero constraints","Number nonzero activities","Proportion of matrix filled");
		$matrixtitle->add_row("","","","","","","","",$self->milpinfo("NumRow") - $self->milpinfo("ZeroCon"),$self->milpinfo("NumCol") - $self->milpinfo("ZeroAct"), "0.3",   $self->delim("#END_NONZERO"));	
		$matrixtitle->add_row("");
	}
	$matrixtitle->add_comment_row("1.8","Information on the shape of the programming matrix","","","","","","","Number of matrix blocks","Is the matrix transposed?  (yes=1 / no=0)","Index of  first constraint  in block 0","Index of  first activity  in block 0","Index of  first constraint  in block 1","Index of  first activity  in block 1","Index of  first constraint  in block 2","Index of  first activity  in block 2") ;	
	$matrixtitle->add_row("","","","","","","","",1,0,0,0, $singlemode ? "" : $self->delim("#END_INFOBLX")) if (not $self->config_entry("MATRIX_TRIPLETS"));
	$matrixtitle->add_row("","","","","","","","",0,  $self->delim("#END_INFOBLX")) if ($self->config_entry("MATRIX_TRIPLETS"));

	$matrixtitle->add_row("");
	$matrixtitle->add_row("");
	$self->add_block($matrixtitle);
	return $matrixtitle;
}
sub add_objrow {
	my ($self, $singlemode) = @_;
	my $digs = $self->milpinfo("Digs");

	my $numMilps = scalar @{ $self->milplist };

	if ( $self->config_entry("MULTIPERIOD")  and $self->get_skip_comments() ) {
		my $objrow;
		
		if ($self->milpinfo("NumCol") > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps ) {
			$objrow = MpmasInputBlockImmediate->new(9,20, $self->get_skip_comments());
			
			for my $k (1..8) {
				$objrow->add_column("");
			}
			if ($singlemode) {
			    my $dRate = $self->config_entry("DISCOUNT_RATE");
			    for my $h (@{$self->sorted_activity_objects}) {
		    

				    eval {
					    use warnings FATAL => qw/uninitialized numeric/;
					    $objrow->add_column(sprintf("%g", $h->discounted_orow( undef ,$self->argument,$dRate )));
				    };
				    print STDERR "Error in objective value of ".$h->id.": ", MpmasInputfile::shortError($@),"\n" and exit(1) if ($@);
			    }
		    }
		    else {
		        for my $h (@{$self->sorted_activity_objects}) {
		    
				    eval {
					    use warnings FATAL => qw/uninitialized numeric/;
					    $objrow->add_column(sprintf("%g", $h->orow( undef ,$self->argument)));
				    };
				    print STDERR "Error in objective value of ".$h->id.": ", MpmasInputfile::shortError($@),"\n" and exit(1) if ($@);
			    }
		    }
			$objrow->end_row();
			$objrow->add_noncommentonly_row ( $self->delim("#END_OBJFUNC"));
		}
		else {
			$objrow = MpmasInputBlock->new(9,20);
			my @o1; 
			$#o1 = 8;
			@o1 = ("") x @o1;
    	    my $dRate = $self->config_entry("DISCOUNT_RATE");
			for my $h (@{$self->sorted_activity_objects}) {
			

				eval {
					use warnings FATAL => qw/uninitialized numeric/;
					push @o1,  sprintf("%g", $singlemode ? $h->discounted_orow( undef ,$self->argument, $dRate) : $h->orow( undef ,$self->argument));
				};
				print STDERR "Error in objective value of ".$h->id.":\n", MpmasInputfile::shortError($@),"\n" and exit(1) if ($@);
			}
			$objrow->add_noncommentonly_row (@o1,  $self->delim("#END_OBJFUNC"));
			$objrow->add_commentonly_row("");

		}
		$objrow->add_row("");
		$objrow->add_row("");
		$self->add_block($objrow);
			
			
	}
	else {
		#prepare Objective function; note: in DBG-Modes it is written into the Head of the Tableau
		my $objrow = MpmasInputBlock->new(9,20);
		my @o1; #Scientific Number format
		$#o1 = 8;
		@o1 = ("") x @o1;
		my @o2; #Decimal Number format
		my $multiperiod = $self->config_entry("MULTIPERIOD");
	    my $dRate = $self->config_entry("DISCOUNT_RATE");
		for my $h (@{$self->sorted_activity_objects}) {
			my $fd;

			if (lc($h->attr("type", $self->argument)) eq "masfoodbuy") { #Note: food cunsumption is neutral with respect to income, but price needs to go to market file anyway
				push @o1,  sprintf("%e", 0);
				push @o2,  sprintf("%.".$digs."f", 0);
			}
			else {
				push @o1,  sprintf("%e", ($singlemode  && $multiperiod)? $h->discounted_orow( undef ,$self->argument, $dRate) : $h->orow( undef ,$self->argument));
				push @o2,  sprintf("%.".$digs."f", ($singlemode  && $multiperiod)? $h->discounted_orow( undef ,$self->argument, $dRate) : $h->orow( undef ,$self->argument));
			}
		}
		
		$objrow->add_comment_row ("2",uc("Objective Function Coefficients"));
		$objrow->add_noncommentonly_row (@o1,  $self->delim("#END_OBJFUNC"));
		$objrow->add_commentonly_row("");
		$objrow->add_row("");
		$objrow->add_row("");
		$self->add_block($objrow);		

		##write head of Tableau if in DBG mode
		my @empty;
		$#empty = 101;
		@empty = ("") x @empty;	
		my @constinfoheader;
		my $matrixtableau = MpmasInputBlock->new(9,30);
		if (not $self->tableau_compact and not $self->config_entry("MATRIX_TRIPLETS") ) {	
			$matrixtableau->add_comment_row("3",uc("Programming Matrix"));
			@constinfoheader = ("mpmasql id","mpmasql type","Equation type","Range","LHS","Sign","RHS","Name","Unit");
		}
		else {
			$matrixtableau->add_comment_row("3 A", "Info on matrix activities");
			@constinfoheader = @empty[0..6];
		}
		$matrixtableau->add_comment_row("block1",@empty[0..5], "mpmasql id","","", $self->sorted_activity_attr("id") );
		$matrixtableau->add_comment_row(@empty[0..6], "mpmasql type","","", $self->sorted_activity_attr("type") );
		$matrixtableau->add_comment_row(@empty[0..6], "Name","","", $self->sorted_activity_attr("name") );
		$matrixtableau->add_comment_row(@empty[0..6], "Unit","","", $self->sorted_activity_attr("unit") );
		$matrixtableau->add_comment_row(@empty[0..6], "Solution vector","","",  );
		$matrixtableau->add_comment_row(@empty[0..6], "Objective function","","",@o2, $self->delim("#END_OBJFUNC"));
		$matrixtableau->add_comment_row(@empty[0..6], "Integers? (informational, not marked anymore)","","", $self->sorted_activity_vtype( $self->argument) );	
		$matrixtableau->add_comment_row(@empty[0..6], "Fix in third MILP?","","", $self->sorted_activity_attr("consfix", $self->argument) );	
		$matrixtableau->add_comment_row(@empty[0..6], "Fix in Marketing MILP?","","", $self->sorted_activity_attr("marketfix", $self->argument) ) if (defined $self->config_entry("PRODUCER_ORGS") and $self->config_entry("PRODUCER_ORGS") > 0);	
		$matrixtableau->add_comment_row(@empty[0..6], "Lower bound","","", $self->sorted_activity_attr("lbound", $self->argument) );	
		$matrixtableau->add_comment_row(@empty[0..6], "Upper bound","","", $self->sorted_activity_attr("ubound", $self->argument) );		
		$matrixtableau->add_comment_row(@constinfoheader,"Index","","",$self->sorted_activity_attr("LPColumn") );		
		$self->add_block($matrixtableau);
		return $matrixtableau;
	}
}
sub add_tableau {
	my ($self, $singlemode, $curRHS, $rhshash) = @_;
	my $matrixtableau2 = MpmasInputBlock->new(9,34);
	my $NumRow = $self->milpinfo("NumRow");
	my $NumCol = $self->milpinfo("NumCol");
	my $numMilps = scalar @{ $self->milplist };

	my $digs = $self->milpinfo("Digs");
	my @empty;
	$#empty = 101;
	@empty = ("") x @empty;	

	if (not $self->tableau_compact and not $self->config_entry("MATRIX_TRIPLETS") ) {
		    my @tableau;
		    my @emptyrow; 
		    $#emptyrow = $NumCol - 1;
		    @emptyrow = (0) x @emptyrow; 
		    for my $j (0..$NumRow-1) {  
			push @tableau, [(@emptyrow)];
		    }
		    #make rows
		    foreach my $act ($self->sorted_activities){
			    my $x = $self->activity($act);
			    next if $x->empty;
			    my $lc = $x->lp_column;
			    for my $rowelement ($x->coefficients){
				    my $co = $self->constraint($rowelement);
				    print STDERR "Error!!!: Constraint $rowelement of activity $act is not declared anywhere in class file.\n" and exit(1) unless (defined $co); 
				    next if $co->empty;
				    my $c = $co->lp_row;
				    print STDERR "Error!!!: Constraint $rowelement of activity $act is not declared anywhere in class file.\n" and exit(1)  unless defined $c;
				    print STDERR "Error!!!: Coefficient of activity $act  for constraint $rowelement is not defined\n" and exit(1)  unless defined ($x->coeff($rowelement, $self->argument) );  
				    $tableau[$c][$lc] =  $x->coeff($rowelement, $self->argument) ;
				    print "Error: $act\t $rowelement \t $c\t $tableau[$c][$lc]\n" if ($tableau[$c][$lc] =~ /[^\s\d.+Ee-]/);
			    }
		    }

		    for my $k (0..$NumRow -1 ) { #Loop over constraints
			    my $i = $self->sorted_constraints($k);
			    my $h =	$self->constraint($i);
			    print STDERR  "Constraint $i has not been defined\n" and exit(1) unless (defined $h);
			    my @temp;

				#information block left if in DBG mode
			    
				my $rhsvalue;			
				if (defined $h->attr("value",$self->argument)) {
					$rhsvalue = sprintf("%.".$digs."f", $h->attr("value",$self->argument));
				}
				elsif ($singlemode and defined $rhshash && exists $rhshash->{$curRHS}->{$i}) {
					$rhsvalue = sprintf("%.".$digs."f",$rhshash->{$curRHS}->{$i});
				}
				elsif ($singlemode and not defined $rhshash) {
					$rhsvalue = "&get_value(\$cur, \"$i\")";
				}
				else {	
					$rhsvalue = sprintf("%.".$digs."f",0) ;
				}
		
			    push @temp, ($i,  $h->attr("type"), $h->attr("eqtype",$self->argument), $h->attr("range",$self->argument), "",$h->attr("showsign",$self->argument),$rhsvalue,$h->attr("name"),$h->attr("unit"),$h->lp_row);
				#empty columns for input mode to align with commented mode
			    my @temp_sc = @empty[0..9];

			    $matrixtableau2->add_commentonly_row( @temp, map {sprintf("%.".$digs."f",$_)} @{$tableau[$k]} );
			    $matrixtableau2->add_noncommentonly_row(@temp_sc, map{sprintf("%e",$_)}  @{$tableau[$k]});
		    }
			$matrixtableau2->add_row(@empty[0..9],  $self->delim("#END_BLOCK"));
			$matrixtableau2->add_row("");
			$matrixtableau2->add_row("");
			$self->add_block($matrixtableau2);
			return; #$matrixtableau2;
	}

	elsif (! MpmasMILPConstraint->has_coeffWithConstraints()  &&( $self->config_entry("MATRIX_TRIPLETS") || $self->tableau_compact == 1)) {	


		my $matrixcoefficients;
		if ($NumCol > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps or $NumRow > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps) {
			$matrixcoefficients = MpmasInputBlockImmediate->new(9,35, $self->get_skip_comments());
		}
		else {
			$matrixcoefficients = MpmasInputBlock->new(9,35);
		}
		$matrixcoefficients->add_empty_rows(2);
		$matrixcoefficients->add_comment_row("3 B", "Matrix coefficients");
		$matrixcoefficients->add_empty_rows(1);

		$matrixcoefficients->add_comment_row(@empty[0..6], "Constraint", "Activity", "RowId", "ColumnId", "Coefficient");

		my $count_coef = 0;
		foreach my $act ($self->sorted_activities){
			my $x = $self->activity($act);
			#next if $x->empty;
			my $col = $x->lp_column;

			for my $rowelement (sort { $a cmp $b} $x->coefficients){
				my $co = $self->constraint($rowelement);
				print STDERR "Error: Constraint $rowelement of Activity $act is not declared anywhere in class file.\n" and exit(1) unless defined $co; 
				if ($x->empty or $co->empty) {
					#even if empty enter at least a zero for relative extraGap calculation in CoinPackedMatrix
					$matrixcoefficients->add_row(@empty[0..6],$co->id, $act, $co->lp_row, $col, sprintf("%e", 0)  ) ;
				}
				else {
					print STDERR "Error: $act\t $rowelement \t".$co->id."\t".$x->coeff($rowelement, $self->argument)."\n" and exit(1) if ($x->coeff($rowelement, $self->argument) =~ /[^\s\d.+Ee-]/);
				
				#if (  $x->coeff($rowelement, $self->argument) != 0)  { 
				#Note: We explicitly keep zero coefficients that were given as explicit zero in the .mpm file. These usually come from
				# places were mpmas inserters have been used in the MULTIPERIOD version. Keeping them in the list allows us
				# to use them as placeholders for the values that will later be inserted
				# MPMAS uses as sparse Matrix format and otherwise it needs to reallocate and shift
				# the whole matrix whenever a coefficient has to be inserted that was previously zero.
				      $matrixcoefficients->add_row(@empty[0..6],$co->id, $act, $co->lp_row, $col, sprintf("%e", $x->coeff($rowelement, $self->argument))  ) ;
				  }
				      $count_coef++;
				#}
			}
		}
		$matrixtableau2->add_row(@empty[0..7],"Number of Coefficients:",$count_coef ) ;#if ($self->tableau_compact);
		$matrixtableau2->add_row(@empty[0..9],  $self->delim("#END_NUMTRIPLETS"));

		$matrixcoefficients->add_row(@empty[0..9],  $self->delim("#END_BLOCK"));
		$self->add_block($matrixtableau2);
		$self->add_block($matrixcoefficients);
		return;# $matrixcoefficients;
	}	
	elsif ( MpmasMILPConstraint->has_coeffWithConstraints()  &&( $self->config_entry("MATRIX_TRIPLETS") || $self->tableau_compact == 1)) {	


		my $matrixcoefficients;
		if ($NumCol > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps or $NumRow > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps) {
			$matrixcoefficients = MpmasInputBlockImmediate->new(9,35, $self->get_skip_comments());
		}
		else {
			$matrixcoefficients = MpmasInputBlock->new(9,35);
		}
		$matrixcoefficients->add_empty_rows(2);
		$matrixcoefficients->add_comment_row("3 B", "Matrix coefficients");
		$matrixcoefficients->add_empty_rows(1);

		$matrixcoefficients->add_comment_row(@empty[0..6], "Constraint", "Activity", "RowId", "ColumnId", "Coefficient");

		my $count_coef = 0;
		foreach my $co ($self->sorted_constraints() ){#lazy constraints case no captured, since overloaded in MpmasMILPMultiperiod
			my $x = $self->constraint($co);
			#next if $x->empty;
			my $row = $x->lp_row;

			for my $rowelement (sort { $a cmp $b} $x->coefficients){
				my $act = $self->activity($rowelement);
				print STDERR "Error: Activity $rowelement of constraint $co is not declared anywhere in class file.\n" and exit(1) unless defined $act; 
				next if $act->empty;
				print STDERR "Error: $co\t $rowelement \t".$act->id."\t".$x->coeff($rowelement, $self->argument)."\n" and exit(1) if ($x->coeff($rowelement, $self->argument) =~ /[^\s\d.+Ee-]/);
				#if (  $x->coeff($rowelement, $self->argument) != 0)  { 
				#Note: We explicitly keep zero coefficients that were given as explicit zero in the .mpm file. These usually come from
				# places were mpmas inserters have been used in the MULTIPERIOD version. Keeping them in the list allows us
				# to use them as placeholders for the values that will later be inserted
				# MPMAS uses as sparse Matrix format and otherwise it needs to reallocate and shift
				# the whole matrix whenever a coefficient has to be inserted that was previously zero.
				$matrixcoefficients->add_row(@empty[0..6],$co, $act->id, $row, $act->lp_column(), sprintf("%e", $x->coeff($rowelement, $self->argument))  ) ;
				$count_coef++;
				#}
			}
		}
	
	
		
		$matrixtableau2->add_row(@empty[0..7],"Number of Coefficients:",$count_coef ) ;#if ($self->tableau_compact);
		$matrixtableau2->add_row(@empty[0..9],  $self->delim("#END_NUMTRIPLETS"));

		$matrixcoefficients->add_row(@empty[0..9],  $self->delim("#END_BLOCK"));
		$self->add_block($matrixtableau2);
		$self->add_block($matrixcoefficients);
		return;# $matrixcoefficients;
	}
}
sub add_fixed_independent_rhs {
	my ($self) = @_;
	
	#Zero Constraints
	my $indepconhead = MpmasInputBlock->new(3,40);
	$indepconhead->add_row("4","FIXED AND INDEPENDENT CONSTRAINTS");
	
	my $indepcon = MpmasInputBlock->new(3,40);
	$indepcon->add_comment_row("","", "Constraint","Index of constraint",	"Value to be inserted");
	#my @s = ;
	if ($self->config_entry("MULTIPERIOD")) {
		my $count = 0;
		

	    for my $h (@{$self->sorted_constraint_objects}){									
			    if ($h->attr("value",$self->argument)  != 0 ) {
				    $indepcon->add_row("","",$h->id, $h->lp_row(), $h->attr("value",$self->argument) || 0) 	;
				    $count++
			    }
	    }
	
		
		$indepconhead->add_row("","", "Number of constraints", $count);

	}
	else {
		$indepconhead->add_row("","", "Number of constraints", $self->milpinfo("ZeroCon"));
		my @s = $self->sorted_constraints;
		for my $i ($#s-$self->milpinfo("ZeroCon") + 1..$#s){									
				my $h	= $self->constraint( $s[$i] );
	#				if (lc($h->attr("type")) eq "maszero" or lc($h->attr("type")) eq "masforget") 
				$indepcon->add_row("","",$h->id, $i, $h->attr("value",$self->argument) || 0) 	;
		}
	}
	
	$self->add_block($indepconhead);
	
	$indepcon->add_row("","","", $self->delim("#END_FIXRHS"));
	$self->add_block($indepcon);
	
	return $indepcon;
}
sub add_type_and_range_of_constraints {
	my ($self, $singlemode, $curRHS, $rhshash, $newsinglemode) = @_;
	my $digs = $self->milpinfo("Digs");
	my $numMilps = scalar @{ $self->milplist };
	
	my $typeandrange;
	
	if ($self->config_entry("MULTIPERIOD") &&
			 ($self->milpinfo("NumRow") > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps ) ) {

		$typeandrange = MpmasInputBlockImmediate->new(3,90, $self->get_skip_comments());
	}
	else {
		$typeandrange  = MpmasInputBlockWithValueReplace->new(5,90);		;	
	}
	
		
	$typeandrange->add_row("9","TYPE AND RANGE OF CONSTRAINTS");

	if ($singlemode) {
		$typeandrange->add_comment_row("","", "","","","Type","","Lower Bound", "","Upper Bound");
		my $varcon = 0;
		for my $h (@{$self->sorted_constraint_objects}) {
			$varcon = 0;
			my $i = $h->id;
			my $rhsvalue;		
			if (defined $h->attr("value",$self->argument) && $h->attr("value",$self->argument) != 0) {
				$rhsvalue = sprintf("%.".$digs."e", $h->attr("value",$self->argument));
			}	
			elsif ( defined $rhshash && exists $rhshash->{$curRHS}->{$i}) {
				$rhsvalue = sprintf("%.".$digs."e",$rhshash->{$curRHS}->{$i});
			}
			elsif ( (not defined $rhshash) and ($newsinglemode == 0)) {
				$rhsvalue = "&get_value(\"$i\")";
				$varcon = 1;
			}
			else {	
				$rhsvalue = sprintf("%.".$digs."e",0) ;
			}
	    
			my $eqtype = $h->attr("eqtype",$self->argument);
			my $range = $h->range($self->argument);
			if ($eqtype == 3 and defined $h->attr("epsilon") and $h->attr("epsilon") != 0) {
			      $eqtype	=	1;
			      $rhsvalue	=	$rhsvalue + $h->attr("epsilon") / 2 unless $varcon;
			      $rhsvalue	=	"$rhsvalue + 0.5 * ". $h->attr("epsilon") if $varcon;
			      $range	=	$h->attr("epsilon");
			}      
			my ($c_lbound, $c_ubound);
			if ($eqtype == 1) {
				$c_ubound = $rhsvalue;
				$c_lbound = - OSL_INFINITY;
				if ($range != -1 * OSL_INFINITY ) {
				      $c_lbound = $rhsvalue + $range unless $varcon; ##range expected to be negative, therefore addition
				      $c_lbound = "$rhsvalue + $range" if $varcon;
				  }
			}
			elsif ($eqtype == 2) {
				$c_lbound = $rhsvalue;
				$c_ubound = OSL_INFINITY;
				if ($range != OSL_INFINITY ) {
				      $c_ubound = $rhsvalue + $range unless $varcon;
				      $c_ubound = "$rhsvalue + $range" if $varcon;
				}
			}
			elsif ($eqtype == 3) {
				$c_ubound = $rhsvalue;
				$c_lbound = 0;
			}
			$typeandrange->add_noncommentonly_row("","","","","",$eqtype, $c_lbound, $c_ubound);	
			
		}

	}
	else {
		$typeandrange->add_comment_row("","Constraint","Description","Unit","RowId","Equation type","Range");
		
		for my $h (@{$self->sorted_constraint_objects}) {

			$typeandrange->add_noncommentonly_row("","","","","",$h->attr("eqtype",$self->argument),"",$h->range($self->argument));
			
			if($self->config_entry("MATRIX_TRIPLETS") || $self->tableau_compact == 1) {
				$typeandrange->add_commentonly_row("",$h->id, $h->attr("name"),$h->attr("unit"),$h->lp_row,$h->attr("eqtype",$self->argument), $h->range($self->argument));
			}
		}
	}
	$typeandrange->add_row("","","", "","",$self->delim("#END_TCONSTRAINTS"));
	$typeandrange->add_row("");
	$typeandrange->add_row("");
	$self->add_block($typeandrange);
	return $typeandrange;
}







sub obsolete_add_marked_integers {
	my ($self, $singlemode) = @_;
	my $marked = MpmasInputBlock->new(3,100);	
	if ($singlemode) {
		$marked->add_comment_row("10", "MARKED INTEGERS, LOWER BOUNDS, UPPER BOUNDS ON COLUMNS");
		$marked->add_row("");
		for my $i ($self->sorted_activities) {
			my $h = $self->activity($i);
			my $vtype = 0;
			
			if ($h->attr_defined("quadratic", $self->argument) && $h->attr("quadratic", $self->argument) ){
			  if ($h->attr_defined("integ", $self->argument) && $h->attr("integ", $self->argument)) {
						$vtype = 2 ;
			  }
			  else {
				$vtype = 3  ;
			  }
			}
			$marked->add_noncommentonly_row("","","", $vtype   ,
					$h->attr_defined("lbound",$self->argument) ? $h->attr("lbound",$self->argument) : $h->get_default_value("lbound"), 
					$h->attr_defined("ubound",$self->argument) ? $h->attr("ubound",$self->argument) : $h->get_default_value("ubound") );#new
		}
		$marked->add_row("","","","#END_MARKED");
	}
	else {		
		#moved to add_column bounds
	}
	$marked->add_row("");
	$marked->add_row("");
	$self->add_block($marked);
	return $marked;
}
sub add_column_bounds {
	my ($self, $singlemode) = @_;
	
	my $numMilps = scalar @{ $self->milplist };
	my $marked ;
	if ($self->milpinfo("NumCol") > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps ) {
		$marked = MpmasInputBlockImmediate->new(3,100, $self->get_skip_comments());
	}
	else {
		$marked= MpmasInputBlock->new(3,100);	
	}
	
	
	if ($singlemode) {
		$marked->add_comment_row("10", "MARKED INTEGERS, LOWER BOUNDS, UPPER BOUNDS ON COLUMNS");
		$marked->add_row("");
		for my $h (@{$self->sorted_activity_objects}) {

			my $vtype = 0;
			
			if ($h->attr_defined("quadratic", $self->argument) && $h->attr("quadratic", $self->argument)) {
			  if ($h->attr_defined("integ", $self->argument) && $h->attr("integ", $self->argument) ){
						$vtype = 2 ;
			  }
			  else {
				$vtype = 3  ;
			  }
			}
			eval {use warnings FATAL => qw/uninitialized numeric/;
			$marked->add_noncommentonly_row("","","", $vtype   ,
					$h->attr_defined("lbound",$self->argument) ? sprintf("%g", $h->attr("lbound",$self->argument)) : $h->get_default_value("lbound"), 
					$h->attr_defined("ubound",$self->argument) ? sprintf("%g" ,$h->attr("ubound",$self->argument)) : $h->get_default_value("ubound") );#new
			};
			print STDERR "Error in lbound or ubound of activity ".$h->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;
		}
		$marked->add_row("","","","#END_MARKED");
		$marked->add_empty_rows(2);
		$self->add_block($marked);
	}
	else {	 
		my $bounds;	
		my $bounds2;	
		
		if ($self->milpinfo("NumCol") > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps ) {
			$bounds = MpmasInputBlockImmediate->new(3,110, $self->get_skip_comments());
			$bounds2 = MpmasInputBlockImmediate->new(3,111, $self->get_skip_comments());
		}
		else {
			$bounds = MpmasInputBlock->new(3,110);	
			$bounds2 = MpmasInputBlock->new(3,111);
		}
		$bounds->add_row("");
		$bounds2->add_row("");
		$marked->add_row("");

		#$bounds->add_comment_row("11", "LOWER BOUNDS ON COLUMNS") ;	

		for my $h (@{$self->sorted_activity_objects}) {


			my $vtype = 0;
			if ($h->attr_defined("quadratic", $self->argument) && $h->attr("quadratic", $self->argument)) {
			  if ($h->attr_defined("integ", $self->argument) && $h->attr("integ", $self->argument)) {
				$vtype = 2 ;
			  }
			  else {
				$vtype = 3  ;
			  }
			}
			$marked->add_noncommentonly_row("","","",$vtype); #new
			eval {use warnings FATAL => qw/uninitialized numeric/;
				$bounds->add_noncommentonly_row("","","",$h->attr_defined("lbound",$self->argument) ? sprintf("%g",$h->attr("lbound",$self->argument)) : $h->get_default_value("lbound"));
				$bounds2->add_noncommentonly_row("","","",$h->attr_defined("ubound",$self->argument) ? sprintf("%g",$h->attr("ubound",$self->argument)) : $h->get_default_value("ubound"));
			};
			print STDERR "Error in lbound or ubound of activity ".$h->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;
		}
		$marked->add_row("","","",  $self->delim("#END_MARKED"));
		$marked->add_empty_rows(2);
		$bounds->add_row("","","",  $self->delim("#END_LBOUND"));						
		$bounds2->add_row("","","", $self->delim("#END_UBOUND"));
		$bounds2->add_row("");
		$self->add_block($marked);
		$self->add_block($bounds);
		$self->add_block($bounds2);
		
	}
	return $marked
}
sub add_integer_sets {
	my ($self) = @_;

	my $NumRow = $self->milpinfo("NumRow");
	my $NumCol = $self->milpinfo("NumCol");
	my $numMilps = scalar @{ $self->milplist };

	my $isets;
	if ($NumCol > $self->config_entry("LIMIT_FOR_IMMEDIATE") / $numMilps ) {
		$isets = MpmasInputBlockImmediate->new(2,130, $self->get_skip_comments());
	}
	else {
		$isets = MpmasInputBlock->new(2,130);	
	}

	 

	my $curInteg = 0;
	for my $h (@{$self->sorted_activity_objects}) {

		if (defined $h->attr("sos") and $h->attr("sos") ne "-1") {
		    #add activity to SOS
		    my $sos = $self->SOS($h->attr("sos"));
		    print STDERR "Error: Activity ".$h->id." has been included into SOS ".$h->attr("sos").", but ".$h->attr("sos") ." has not been defined anywhere in the .cll file\n" and exit(0) unless (defined $sos);
		    $sos->add_activities($h);
		}
		elsif ($h->attr_defined("integ") && $h->attr("integ") == 1 ) {
			$curInteg++;
			$isets->add_row("","Integer info Nr. $curInteg","");
			$isets->add_row("","Number of variables in set", 1);
			$isets->add_row("","Type of set", 4);
			
			eval {	use warnings FATAL => qw/uninitialized numeric/;
				$isets->add_row("","Priority", $h->attr_defined("priority", $self->argument) ? sprintf( "%d",$h->attr("priority", $self->argument)) : $h->get_default_value("priority")  );
				$isets->add_row("","Column index", $h->lp_column);
				$isets->add_row("","Downside pseudo costs", $h->attr_defined("pseudo_down", $self->argument) ? sprintf( "%g", $h->attr("pseudo_down", $self->argument)) : $h->get_default_value("pseudo_down")  );
				$isets->add_row("","Upside pseudo costs", $h->attr_defined("pseudo_up", $self->argument) ? sprintf( "%g",$h->attr("pseudo_up", $self->argument)) : $h->get_default_value("pseudo_up"));
			};
			print STDERR "Error in priority, pseudo_up or pseudo_down of integer activity ".$h->id.":\n". MpmasInputfile::shortError($@)."\n" and exit(1) if $@;
			$isets->add_row("");
		}	

		
	}
	$self->add_block($isets);	
	my $so_sets = MpmasInputBlock->new(2,131);
	my $empty_sos = 0;
	for my $h ($self->SOSs) {
		if ($h->number_of_activities_in_set <= 0) {
		    $empty_sos++;
		    next;
		}
		print STDERR "Error: Invalid type '".$h->attr("type"). "' for SOS ". $h->id."\n" and exit(1) unless ($h->attr("type") > 0 and $h->attr("type") < 4);
		$so_sets->add_row("","SOS ".$h->name,"");
		$so_sets->add_row("","Number of variables in set", $h->number_of_activities_in_set);
		$so_sets->add_row("","Type of set", $h->attr("type") );
		$so_sets->add_row("","Priority", $h->attr("priority",$self->argument) );
		my @cindices;
		foreach ($h->activities) { push @cindices, $_->lp_column; }
		$so_sets->add_row("","Column indices", @cindices);
		my @cdownpseudo;
		foreach ($h->activities) { push @cdownpseudo, $_->attr("pseudo_down"); }
		$so_sets->add_row("","Downside pseudo costs", @cdownpseudo);
		my @cpositions;
		foreach ($h->activities) { push @cpositions, $_->attr("sos_position"); }
		$so_sets->add_row("","Reference row", @cpositions);
		$so_sets->add_empty_rows(1);	
	}
	$so_sets->add_row("","","",	  $self->delim("#END_INTEGERS"));
	$self->add_block($so_sets);

	my $isetshead = MpmasInputBlock->new(2,129);	
	$isetshead->add_comment_row("13", "INTEGER SETS");
	$isetshead->add_row("");
	$isetshead->add_row("","Total number of integer sets", $curInteg );
	$isetshead->add_row("","Total number of SOS", $self->get_SOS_registry->counter - $empty_sos);	
	$self->add_block($isetshead);	
}
sub add_quadratic_info {
	my ($self, $singlemode) = @_;
	if ( $self->milpinfo("QuadraticAct") > 0) {
	    my $quadratics = MpmasInputBlock->new(2,135);
	    $quadratics->add_comment_row(15, "Quadratic terms");			
	    $quadratics->add_comment_row("", "Number of quadratic variables", $self->milpinfo("QuadraticAct"));
	    $quadratics->add_empty_rows(2);
	    $quadratics->add_row("", "Use quadratic barrier algorithm for continuous quadratic problems", 0) unless ($singlemode);
	    $quadratics->add_comment_row("", "Q matrix for standard form" );
	    $quadratics->add_empty_rows(2);
	    for my $i ($self->sorted_quadratic_activities) {
		my $h = $self->activity($i);	
		my @row = ("","$i");
		for my $j ($self->sorted_quadratic_activities) {
			      my $k = $self->activity($j);	
#			      print "$i; $j; ".$h->subelement_field("quadratic", $j)."\n";
			      print STDERR "Error: Inconsistent element of Q matrix for activities $i and $j:\n $i -> $j = ".$h->subelement_field("quadratic", $j)."\n $j -> $i = ". $k->subelement_field("quadratic", $i). "\n" and exit(1) if (defined $k->subelement_field("quadratic", $i) and defined $h->subelement_field("quadratic", $j) and $k->subelement_field("quadratic", $i) != $h->subelement_field("quadratic", $j) );
			      if (defined $h->subelement_field("quadratic", $j) ) {
				  push @row, $h->subelement_field("quadratic", $j);
			      }
			      elsif (defined $k->subelement_field("quadratic", $i)) {
				  push @row, $k->subelement_field("quadratic", $i);
			      }
			      else {
				  push @row, 0;
			      }
			  
		}
		  $quadratics->add_row(@row);
	    }	
	    $quadratics->add_row("","","", $self->delim("#END_QUADRATIC"));
	    $quadratics->add_empty_rows(2);
	    $self->add_block($quadratics);
		return $quadratics;
	}
}
sub add_epsila {
	my ($self) = @_;
	my $epsila = MpmasInputBlock->new(2,140);
	$epsila->add_comment_row (17, "APPLY LOWER AND UPPER BOUNDS OF EPSILON TO THE FOLLOWING EE CONSTRAINTS:");			
	$epsila->add_comment_row("", "Note: You can provide individual epsilon for each EE constraint");
	
	my $epsilatab = MpmasInputBlock->new(2,150);	
	my $cc = 0;
	for my $k ($self->sorted_constraints) {
		my $kk = $self->constraint($k);
		if ($kk->attr("eqtype") == 3 and  defined $kk->attr("epsilon")) {
			$epsilatab->add_row("","",$kk->lp_row, sprintf("%e", $kk->attr("epsilon",$self->argument)));
			$cc++;
		}

	}
	$epsila->add_row("","Number of EE contraints", $cc);
	$epsilatab->add_row("","","", $self->delim("#END_EPSILON"));
	$epsilatab->add_empty_rows(2);
	$self->add_block($epsila);		
	$self->add_block($epsilatab);		
	return $epsilatab;
}
sub add_shadow_price_calc {
	my ($self) = @_;
			#Shadow price computation
		my $shadow = MpmasInputBlock->new(2,150);
		$shadow->add_comment_row("18","SAVE SHADOW PRICES, ESPECIALLY FOR MIXED INTEGER PROBLEMS:");									
		$shadow->add_comment_row("", "Note: This feature works only for adjacent constraints, for example all soil types. You might also compute all shadow prices for all constraints but this will take time.");
		my $numshadow = 0;
		if (defined $self->config_entry("SHDW_SWITCH") and $self->config_entry("SHDW_SWITCH") == 1 ) {
			$numshadow = defined $self->config_entry("SHDW_NUM") ? $self->config_entry("SHDW_NUM") : 0;
		}
	
		$shadow->add_row("","Number of shadow prices to be computed",$numshadow);
		if ($numshadow > 0) {
			my $lpmode;
			print STDERR "Error: You turned on shadow price calculation (SHDW_SWITCH), but you did not specify SHDW_LPMODE!" and exit (1) unless (defined $self->config_entry("SHDW_LPMODE") );
			if (uc($self->config_entry("SHDW_LPMODE")) =~ /^\s*P/ or $self->config_entry("SHDW_LPMODE") =~ /\s*1\s*/ ) {
				$lpmode = 1;
			}
			elsif (uc($self->config_entry("SHDW_LPMODE")) =~ /^\s*I/ or $self->config_entry("SHDW_LPMODE") =~ /\s*0\s*/ ) {
				$lpmode = 0;
			}
			else {
				print STDERR "Error: Invalid value for SHDW_LPMODE!" and exit (1);
			}
			$shadow->add_row("","LP mode", $lpmode);
		
			print STDERR "Error: You turned on shadow price calculation (SHDW_SWITCH), but you did not specify SHDW_FIRST!" and exit (1) unless (defined $self->config_entry("SHDW_FIRST") );
			my $k  = $self->constraint($self->config_entry("SHDW_FIRST"));
			print STDERR "Error: Constraint ".$self->config_entry("SHDW_FIRST")." used in configuration entry SHDW_FIRST has not been defined!" and exit (1) unless (defined $k );
			my $firstshdwcon = $k->lp_row;
			$shadow->add_row("","First constraint index", $firstshdwcon);				
		}
		$shadow->add_row("","","", $self->delim("#END_SHADOW"));
		$shadow->add_empty_rows(2);
		$self->add_block($shadow);
		return $shadow;
}
sub add_timevariant_coefficients {
	my ($self, $singlemode) = @_;
	my $timevariants = MpmasInputBlock->new(2,160);
	$timevariants->add_comment_row("19","CHANGE COEFFICIENTS IN LP MATRIX DURING SIMULATION:");
	$timevariants->add_comment_row("","Note: This feature allows to change LP coefficients and RHS values  during simulation, e.g. improved technical coefficients starting in period 5");
	$timevariants->add_empty_rows(1);
	$timevariants->add_row("","Number of matrix elements to be changed", $self->get_number_of_exchanges()->{$self->argument} || 0);	
	$timevariants->add_comment_row("","", "period","LP column","LP row","value");

	for my $period (sort {$a <=> $b} keys %{$self->get_exchanges}) {
		for my $col (sort {$a <=> $b} keys %{$self->get_exchanges->{$period}->{$self->argument}}) {
			for my $row (sort {$a <=> $b} keys %{$self->get_exchanges->{$period}->{$self->argument}->{$col}}) {
				$timevariants->add_row("","",$period, $col, $row, $self->get_exchanges->{$period}->{$self->argument}->{$col}->{$row} );
			}
		}
	}
	$timevariants->add_row("","","", $self->delim("#END_CHANGING"));
	$self->add_block($timevariants);
	return $timevariants;
}


sub write_octave {
	my ($self, $grouping ) = @_;
	
	my $out;		
	
	  my %groupcodes;
	my (@grouplist, @integerlist, @lboundlist, @uboundlist, @orowlist, @coeflist);
	foreach my $act ($self->sorted_activities){
		my $x = $self->activity($act);
		my $col = $x->lp_column;
		my $group = -1;
		$group = $x->get_group($grouping) if (defined $x->get_group($grouping));
		$groupcodes{$group} = -2;
		push @grouplist, $group;


		push @integerlist, $x->attr("integ", $self->argument) ? 73 : 67 ;
		push @lboundlist, $x->attr("lbound", $self->argument);
		push @uboundlist, $x->attr("ubound", $self->argument);
		push @orowlist, (lc($x->attr("type", $self->argument)) eq "masfoodbuy") ? 0 :  $x->orow(undef, $self->argument);
      
		for my $rowelement ($x->coefficients){
			die "Error!!!: Constraint $rowelement of Activity $act is not declared anywhere in class file.\n" unless $self->constraint($rowelement); 
			my $co = $self->constraint($rowelement);
			die "Error!!!: Constraint $rowelement of Activity $act is not declared anywhere in class file.\n" unless defined $co; 
			push @coeflist,  [($co->lp_row + 1, $col +1, $x->coeff($rowelement, $self->argument)  )] if (  $x->coeff($rowelement, $self->argument) != 0) ;
			print STDERR "Error: $act\t $rowelement \t".$co->id."\t".$x->coeff($rowelement, $self->argument)."\n" and exit(1) if ($x->coeff($rowelement, $self->argument) =~ /[^\s\d.+Ee-]/);
		}

	}
	my $c = 0;
	for my $g (sort {$a cmp $b} keys %groupcodes) {
	      if ($g eq "-1") {
		  $groupcodes{$g} = -1;
	      }
	      else {
		  $groupcodes{$g} = ++$c;
	      }
	}
	my @gcodelist = map {$groupcodes{$_}} @grouplist;
	my (@eqtypelist, @rhsvalues);
	my %eqconv = (
	    1 => 85,
	    2 => 76,
	    3 => 83
	);
	foreach my $con ($self->sorted_constraints){
		my $x = $self->constraint($con);
		push @eqtypelist, $eqconv{$x->attr("eqtype")};
		push @rhsvalues,0;
	}
	my $fn = $self->outdir().$self->mname()."_OMILP.dat";
	open($out, ">", $fn ) or die "Can't open $fn: $!";
	print "\e[K \r";
	print "Writing $fn...\r";
	print $out $self->milpinfo("NumRow")."\t".$self->milpinfo("NumCol")."\n";

	print $out join("\t", @gcodelist)."\n";
	print $out join("\t", @integerlist)."\n";
	print $out join("\t", @lboundlist)."\n";
	print $out join("\t", @uboundlist)."\n";
	print $out join("\t", @orowlist)."\n";
	print $out "\n";
	print $out join("\t", @eqtypelist)."\n";
	print $out join("\t", @rhsvalues)."\n";
	print $out "\n";
	#coefficients
	for my $c ( @coeflist) {
	    print $out join("\t", @{$c})."\n";
	}
	#print "..done!\n";
	close ($out);
	return \%groupcodes;
}
sub recreate_from_file {
	my ($class, $matrixfile,$mxmode, $actdesname, $condesname, $mpxformat, $mpmas_subformat, $tableau_compact_in, $verbose) = @_;

	my $activities = IndexedTable->create_from_ascii($actdesname, 1, "ActivityInfo", '\t',1);
	my $constraints = IndexedTable->create_from_ascii($condesname, 1, "ConstraintInfo", '\t',1);
	my $NewConstraintRegistry = SimpleObjectRegistry->create();
	my $NewActivityRegistry = SimpleObjectRegistry->create();
	my $NewSOSRegistry = SimpleObjectRegistry->create();
	MpmasMILPActivity->switch_off_defaults;
	MpmasMILPConstraint->switch_off_defaults;
	MpmasMILPSOS->switch_off_defaults;
	my @quadratics;
	for my $act ($activities->query([],0) ) {
		my %fields;
		$fields{LPColumn} = $act;
		$fields{name} = $activities->query([$act],3);
		$fields{integ} = 0; #set default here
		$fields{type} = $activities->query([$act],5);
		if ($activities->exists_columnname("MPeriod") )  {
			$fields{unit} = $activities->query([$act],6);
		}
		else {
			$fields{account} = $activities->query([$act],6);
		}
		$fields{quadratic} = 0;
		$fields{mperiod} = 0;
		if ($activities->exists_columnname("MPeriod") )  {
			$fields{mperiod} =  $activities->query_by_columnname([$act],"MPeriod") ;
		}
		my $mn = $activities->query([$act],2);
		my $new = MpmasMILPActivity->create($mn, \%fields, undef, undef ,0);
		$new->set_lp_column($act);
		$new->set_standard_milp(10);
		$NewActivityRegistry->add_objects($new);
	}
	for my $con ($constraints->query([],0) ) {
		my %fields;
		$fields{LPRow} = $con;
		$fields{name} = $constraints->query([$con],3);
		$fields{type} = $constraints->query([$con],5);
		$fields{mperiod} = 0;
		if ($constraints->exists_columnname("MPeriod") )  {
			$fields{unit} =  $constraints->query_by_columnname([$con],"Unit") ;
			$fields{mperiod} =  $constraints->query_by_columnname([$con],"MPeriod") ;
		}	
		my $mn = $constraints->query([$con],2);
		my $new = MpmasMILPConstraint->create($mn, \%fields,undef, undef ,0);
		$new->set_lp_row($con);
		$new->set_standard_milp(10);
		$NewConstraintRegistry->add_objects($new);
	}
	my $blocks = -1;
	print "Reading matrix from $matrixfile...\n" if ($verbose);
	open (my $fn, "<", $matrixfile) or die "Error when opening $matrixfile: $!\n";
	my $part = "pre";

	my ($NumRow, $NumCol, $ninteg, $nsos, %integhash, $percfilled, $NumZeroRow, $NumFineTune, $NumCoef);
	my $cc = 0;
	my $ci = 0;

	LINES: while (<$fn>  ) {	
		chomp;
		my $line = $_;
		$line =~ s/^\t*//;
		
		if ($part eq "pre") {
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*NROWS/ or $line =~ /^\s*NumRows/);
			if ($line =~ /^\s*\d*/ ) {
				$line =~ s/^\s*//;
				$line =~ s/\s*$//;
				($NumRow, $NumCol) = split /\s+/, $line;
				print STDERR "Number of Activities in ActivityInfo ".$NewActivityRegistry->counter." does not fit Number of Activities in Matrix File ($NumCol)!\n" and exit(1) unless ($NumCol == $NewActivityRegistry->counter);
				print STDERR "Number of Constraints in ConstraintInfo".$NewConstraintRegistry->counter." does not fit Number of Constraints in Matrix File ($NumRow)!\n" and exit(1) unless ($NumRow == $NewConstraintRegistry->counter);
				$part = "shape";
				$part = "mpmas_info_block" if ($mxmode eq "Mpmas");
				
			}
			else {
				print STDERR "File does not fit specified format ($mxmode)\n" and exit(1);
			}
			
		}
		elsif ($part eq "mpmas_info_block") { #only for .dat files
			if (OLD) {
				if ($line =~ /^\s*\d+/ ) {
				    $cc++;
				}
				if ($cc == 6) {
				    $cc = 0;
				    $part = "shape"
				}
			}
			else {
				if ($line =~ /#END_LABSWITCHES/) {
					$cc = 0;
				    $part = "shape"
				}
				else {
					$cc++;
				}
			}


		}
		elsif ($part eq "shape") {
			#can for now only handle one block
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*NumBlx/);
			next LINES if ($line =~ /^\s*#END_ROWCOL/);
			if ($line =~ /^\s*\d/ and $cc == 0) {
				$line =~ s/^\s*//;
				$line =~ s/\s*$//;
				my $trans;
				($blocks, $trans) = split /\s+/, $line;
				print STDERR "Blocked matrices and transposed matrices are not yet supported\nRead blocks: $blocks, transposed: $trans\n"  and exit(1) unless ($blocks == 0  or ($blocks == 1 and $trans == 0));
				$cc++;
				if ($mxmode eq "Single" or $mxmode eq "Mpmas") {
					$part = "orow";
					$cc= 0;
				}
			}
			elsif ($line =~ /^\s*\d*/ and $cc == 1) {
				$cc = 0;
				$part = "orow";
			}
		}
		elsif ($part eq "orow") {
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*Matrix Block/);
			next LINES if ($line =~ /^\s*Objective Row/);
			next LINES if ($line =~ /^\s*#END_INFOBLX/);
			if ($line =~ /^\s*(-?\d)/) {
				my (@orow) = split /\t+/, $line, $NumCol +1;
				#Note: last column in row is #END_OBJFUNC, is discarded
				for my $i (0..($NumCol -1)) {
					my $actcode = 	$activities->query([$i], 2);
					my $act = $NewActivityRegistry->get_object_by_id($actcode);
					$act->set_attr("orow", $orow[$i]);
				}
				
				$part = "tableau";
			}
		}
		elsif ($part eq "tableau" and not $mpxformat and not $tableau_compact_in and not $blocks == 0) {
		     
		     
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumRow) {
				my @row = split /\t+/, $line, $NumCol;
				my $concode = 	$constraints->query([$cc], 2);
				for my $i (0..$#row) {
					my $actcode = 	$activities->query([$i], 2);
					my $act = $NewActivityRegistry->get_object_by_id($actcode);
					$act->set_coeff($concode, $row[$i]) if ($row[$i] != 0);
				}				
				$cc++;	
			}
			if ($cc >= $NumRow) {
				$part = "cons";
				$part = "nonzerocons" if ($mxmode eq "Mpmas");
				$cc = 0;
			}
		      
		}
		elsif ($part eq "tableau" and $mpxformat) {
			
		    if ($line =~ /^\s*EquaRHS/) {
			      $part = "cons";
			      $cc = 0;
			      next LINES;
			}
			elsif ($line =~ /^\s*(-?\d)/) {
			      my ( $row, $col, $coef) = split /\s+/, $line;
			      #print join("/", $row, $col, $coef)."\n";
			      my $actcode = 	$activities->query([$col], 2);
			      print STDERR "Activity No. $col not found!\n" and exit(0) unless (defined $actcode);
			      my $concode = 	$constraints->query([$row], 2);
			      print STDERR "Constraint No. $row not found!\n" and exit(0) unless (defined $concode);
			      my $act = $NewActivityRegistry->get_object_by_id($actcode);
			      $act->set_coeff($concode, $coef);
			}
		}
		elsif ($part eq "tableau" and $blocks == 0) {
			if (not defined $NumCoef and $line =~ /^\s*(\d+)$/) {
			      $NumCoef = $1;
			      next LINES;
			}
  	       next LINES if ($line =~ /#END_NUMTRIPLETS/);

			if ($line =~ /^\s*(-?\d)/) {
			      my ( $row, $col, $coef) = split /\s+/, $line;
			      #print join("/", $row, $col, $coef)."\n";
			      my $actcode = 	$activities->query([$col], 2);
			      print STDERR "Activity No. $col not found!\n" and exit(0) unless (defined $actcode);
			      my $concode = 	$constraints->query([$row], 2);
			      print STDERR "Constraint No. $row not found!\n" and exit(0) unless (defined $concode);
			      my $act = $NewActivityRegistry->get_object_by_id($actcode);
			      $act->set_coeff($concode, $coef);
			}
			if ($line =~ /\s*#END_BLOCK/) {
			      $part = "cons";
     			  $part = "nonzerocons" if ($mxmode eq "Mpmas");
			      $cc = 0;
			}
		}
	    elsif ($part eq "tableau" and $tableau_compact_in) {
			if (not defined $NumCoef and $line =~ /^\s*(\d+)/) {
			      $NumCoef = $1;
			}
			elsif ($line =~ /^\s*(-?\d)/) {
			      my ( $row, $col, $coef) = split /\s+/, $line;
			      #print join("/", $row, $col, $coef)."\n";
			      my $actcode = 	$activities->query([$col], 2);
			      print STDERR "Activity No. $col not found!\n" and exit(0) unless (defined $actcode);
			      my $concode = 	$constraints->query([$row], 2);
			      print STDERR "Constraint No. $row not found!\n" and exit(0) unless (defined $concode);
			      my $act = $NewActivityRegistry->get_object_by_id($actcode);
			      $act->set_coeff($concode, $coef);
			      $cc++;
			}
			if (defined $NumCoef and $cc >= $NumCoef) {
			        $part = "cons";
				$part = "nonzerocons" if ($mxmode eq "Mpmas");
				$cc = 0;
			}
		}
		elsif ($part eq "nonzerocons") {
			      next LINES if ($line =~ /^\s*$/);
			      next LINES if ($line =~ /#END_BLOCK/);
			      if ($line =~ /^\s*(\d+)/ and not defined $NumZeroRow) {
				    $NumZeroRow = $1;
			      }
			      elsif ($line =~ /^\s*\d+/ and $cc < $NumZeroRow) {
				  my @row = split /\t+/, $line;
				  my $concode =  $constraints->query([$row[0]], 2);
				  my $con = $NewConstraintRegistry->get_object_by_id($concode);
				  $con->set_attr("value", $row[1]);
				  $cc++;
			      }
			      if ($NumZeroRow == 0 or $cc >= $NumZeroRow) {
				  $cc = 0;
				  $part = "fine_tune";
				  $part = "disinvest_info" if ($mpmas_subformat == 2);
				  $part = "consfix_info" if ($mpmas_subformat == 1);
			      }
		}
		elsif ($part eq "disinvest_info") {
			      next LINES if ($line =~ /^\s*$/);
			      next LINES if ($line =~ /#END_FIXRHS/);

			      if ($line =~ /^\s*(\d+)/) {
				  $cc++;
			      }
			      if ($cc == 1) {
				  $cc = 0;
				  $part = "consfix_info";
			      }		
		}
		elsif ($part eq "consfix_info") {
			      next LINES if ($line =~ /^\s*$/);
			      next LINES if ($line =~ /#END_FIXRHS/);
			      next LINES if ($line =~ /#END_CONS/);
			      if ($line =~ /^\s*(\d+)/ and $cc < $NumCol) {
				      my $actcode = $activities->query([$cc], 2);
				      my $act = $NewActivityRegistry->get_object_by_id($actcode);
				      $act->set_attr("consfix", $1);
				      $cc++;
			      }
			      if ($cc >= $NumCol) {
				      $part = "fine_tune";
				      $cc = 0;
			      }
		}
		elsif ($part eq "fine_tune") {
			      next LINES if ($line =~ /^\s*$/);
   			      next LINES if ($line =~ /#END_FIXRHS/);
			      next LINES if ($line =~ /#END_CONS/);
			      next LINES if ($line =~ /#END_FIXCONS/);
			      if ($line =~ /^\s*(\d+)/ and not defined $NumFineTune) {
				      $NumFineTune = $1;
			      }
			      elsif ($line =~ /^\s*\d+/ and $cc < $NumFineTune) {
				  $cc++;
			      }
			      if ($NumFineTune == 0 or $cc >= $NumFineTune) {
				  $cc = 0;
				  $part = "cons";
			      }
		}
		elsif ($part eq "cons") {
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*EquaRHS$/);
			next LINES if ($line =~ /#END_/);
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumRow) {
				my @row = split /\t+/, $line;
				my $concode = 	$constraints->query([$cc], 2);
				my $con = $NewConstraintRegistry->get_object_by_id($concode);
				$con->set_attr("eqtype", $row[0]);
				if ($row[0] == 1 ) {
					if ($mxmode eq "Failed" or $mxmode eq "Single"){
					      $con->set_attr("range", ($row[1] == -1 * OSL_INFINITY) ? $row[1] : $row[1] - $row[2]  );
					      $con->set_attr("value", $row[2]) 	
					}
					elsif ($mxmode eq "Mpmas") {
						$con->set_attr("range", $row[1]);
					}
					$con->set_attr("showsign", "'<=");
				      
				}
				elsif ($row[0] == 2) {
					if ($mxmode eq "Failed" or $mxmode eq "Single") {
						$con->set_attr("value", $row[1]);
						$con->set_attr("range", ($row[2] == OSL_INFINITY) ? $row[2] : $row[2] - $row[1]);
					}
					elsif ($mxmode eq "Mpmas") {
						$con->set_attr("range", $row[1]);
					}
					$con->set_attr("showsign", "'>=");
				}
				elsif ($row[0] == 3 ) {
					if ($mxmode eq "Failed" or $mxmode eq "Single") {
						$con->set_attr("range", $row[1]);
						$con->set_attr("value", $row[2])
					}
					elsif ($mxmode eq "Mpmas") {
						$con->set_attr("range", $row[1]);
					}	
					$con->set_attr("showsign", "'==");
				}
				$con->set_attr("value", 0) unless (defined $con->attr("value"));
				$cc++;
			}
			if ($cc >= $NumRow) {
				$part = "act" if ($mxmode eq "Failed" or $mxmode eq "Single");
				$part = "marked" if ($mxmode eq "Mpmas");
				$cc = 0;
			}
		}
		elsif ($part eq "act") { #only for Failed and Single
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*TypeVar$/);
			next LINES if ($line =~ /#END_TCONSTRAINTS/);
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumCol) {
				my @row = split /\t+/, $line;
				my $actcode = 	$activities->query([$cc], 2);
				my $act = $NewActivityRegistry->get_object_by_id($actcode);
				if ($row[0] >= 2 ) {
				    $act->set_attr("quadratic", 1);
				    push  @quadratics , $act->id;
				}
				$act->set_attr("lbound", $row[1]);
				$act->set_attr("ubound", $row[2]);
				$cc++;
			}
			if ($cc >= $NumCol) {
				$part = "ninteg";
				$cc = 0;
			}
		}
		elsif ($part eq "marked") { #only for Mpmas
			next LINES if ($line =~ /^\s*$/);		
			next LINES if ($line =~ /#END_TCONSTRAINTS/);
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumCol) {
			      if ($1 >= 2 ) {
				    my $actcode = 	$activities->query([$cc], 2);
				    my $act = $NewActivityRegistry->get_object_by_id($actcode);
				    $act->set_attr("quadratic", 1);
				    push  @quadratics , $act->id;
			      }
			      $cc++;
			}
			if ($cc >= $NumCol) {
				$part = "lbound";
				$cc = 0;
			}
		}
		elsif ($part eq "lbound") { #only for Mpmas
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /#END_MARKED/);
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumCol) {
				my $actcode = 	$activities->query([$cc], 2);
				my $act = $NewActivityRegistry->get_object_by_id($actcode);
				$line =~ s/\s+$//;
				$act->set_attr("lbound", $line);
				$cc++;
			}
			if ($cc >= $NumCol) {
				$part = "ubound";
				$cc = 0;
			}
		}
		elsif ($part eq "ubound") { #only for Mpmas
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /#END_LBOUND/);
			if ($line =~ /^\s*(-?\d)/ and $cc < $NumCol) {
				my $actcode = 	$activities->query([$cc], 2);
				my $act = $NewActivityRegistry->get_object_by_id($actcode);
				$line =~ s/\s+$//;
				$act->set_attr("ubound", $line);
				$cc++;
			}
			if ($cc >= $NumCol) {
				$part = "ninteg";
				$cc = 0;
			}
		}
		elsif ($part eq "ninteg") {
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /#END_UBOUND/);
			next LINES if ($line =~ /#END_MARKED/);
			next LINES if ($line =~ /^\s*NumInt$/);
			if ($line =~ /\s*(\d+)/) {
				$ninteg = $1;
				$part = "nsos";
			}
		}
		elsif ($part eq "nsos") {
			next LINES if ($line =~ /^\s*$/);
			next LINES if ($line =~ /^\s*NumSOS$/);
			if ($line =~ /\s*(\d+)/) {
				$nsos = $1;
				$part = "end";
				$part = "Q" if (scalar @quadratics > 0);
				$part = "isets" if ($ninteg > 0);
			}
		}
		elsif ($part eq "isets") {
			next LINES if ($line =~ /^\s*$/);
			if ($line =~ /^\s*(-?\d+\.?\d*)/) {
				if ($cc <= 1) {
					#discard number of activities (asummed to be 1) and type of set (4)
					$cc++;
				}
				elsif ($cc == 2) {
					$integhash{priority} = $1;
					$cc++;
				}
				elsif ($cc == 3) {
					$integhash{act} = $1;
					$cc++;
				}
				elsif ($cc == 4) {
					$integhash{pseudo_down} = $1;
					$cc++;
				}
				elsif ($cc == 5) {
					$integhash{pseudo_up} = $1;
					
					my $actcode = 	$activities->query([ $integhash{act} ], 2);
					print STDERR "Error in integer sets: Activity for LP column $integhash{act} does not exist!\n" and exit(1) unless (defined $actcode);
					my $act = $NewActivityRegistry->get_object_by_id($actcode);
					print STDERR "Error in integer sets: Activity $actcode for LP column $integhash{act} does not exist!\n" and exit(1) unless (defined $act);
					$act->set_attr("integ", 1);
					$act->set_attr("priority", $integhash{priority});
					$act->set_attr("pseudo_down", $integhash{pseudo_down});
					$act->set_attr("pseudo_up", $integhash{pseudo_up});
					$cc = 0;
					$ci++;
				}

			}
			if ($ci >= $ninteg) {
			      $ci = 0;
			      $part = "end";
			      $part = "Q" if (scalar @quadratics > 0);
			      $part = "sos" if ($nsos > 0);
			      #$part = "eqrelax" if ($mxmode eq "Mpmas");
			}
		}
		elsif ($part eq "sos") {
			next LINES if ($line =~ /^\s*$/);
			if ($line =~ /^\s*(-?\d+)/) {
				if ($cc == 0) {
					$integhash{num_act} = $1;
					$cc++;
				}
				elsif ($cc == 1) {
					$integhash{type} = $1;
					$cc++;
				}
				elsif ($cc == 2) {
					$integhash{priority} = $1;
					$cc++;
				}
				elsif ($cc == 3) {
					$integhash{act} = [ split /\t/, $line ];
					$cc++;
				}
				elsif ($cc == 4) {
					$integhash{pseudo_down} =  [ split /\t/, $line ];
					$cc++;
				}
				elsif ($cc == 5) {
					$integhash{refrow} = [ split /\t/, $line ];
					my $sos =  MpmasMILPSOS->create("sos_$ci",{},{},{}, 0 );
    
					$sos->set_attr("name","sos_$ci");
					$sos->set_attr("type", $integhash{type});
					$sos->set_attr("priority",$integhash{priority}); 
					
					$NewSOSRegistry->add_objects($sos);
					for my $ai ( 0..$#{$integhash{act}} ) { 
						  my $actcode = $activities->query([ $integhash{act}->[$ai] ], 2);
						  my $act = $NewActivityRegistry->get_object_by_id($actcode);
						  $act->set_attr("integ", 1);
						  $act->set_attr("sos", "sos_$ci");
						  $act->set_attr("sos_position", $integhash{refrow}->[$ai]);
						  $act->set_attr("pseudo_down",$integhash{pseudo_down}->[$ai]);
					}
					
					$cc = 0;
					$ci++;
				}

			}
			if ($ci >= $nsos) {
			      $ci = 0;
			      $part = "end";
			      $part = "Q" if (scalar @quadratics > 0);
			      #$part = "eqrelax" if ($mxmode eq "Mpmas");
			}
		}
		elsif ($part eq "Q") {
		  next LINES if ($line =~ /^\s*$/);
 		  next LINES if ($line =~ /#END_INTEGERS/);

		  if ($cc == scalar(@quadratics)) {
		      $part = "end";
		      $cc=0;
		      next;
		   }
		  elsif ($line =~/Number of quadratic variables:\s+(\d+)/) {
		      print STDERR "Warning: number of quadratic variables indicated, does not correspond to number of activities marked as quadratic!" if (scalar(@quadratics) != $1);
		  }
		  # Line "Q matrix in standard form" simply ignored
		  if ($line =~ /^\s*(-?\d+)/) {
		      $line =~ s/^\s*//;
		      my $actcode = $quadratics[$cc];
		      my $act = $NewActivityRegistry->get_object_by_id($actcode);
		      print STDERR "Error: Unknown qudaratic activity $actcode\n" and exit(1) unless (defined $act);
		      my @row = split /\s+/, $line;
		      for my $oth (@quadratics ) {
			my $v = shift @row;
			$act->add_subelement_field("quadratic", $oth, $v);
			#print $act->id."; ". $oth ."; $v\n";
		      }
		      $cc++;
		  }
		}
		if ($part eq "end") {
		    last;
		}
	}
	close $fn;
	print "...done\n" if ($verbose);
	print "Initialize matrix structure in memory...\n" if ($verbose);
	my @SortedAO = sort {$a->lp_column <=> $b->lp_column} $NewActivityRegistry->object_list;
	my @SortedCO = sort {$a->lp_row <=> $b->lp_row} $NewConstraintRegistry->object_list;
	my @SortedActivity;   
	foreach my $i(@SortedAO) {push @SortedActivity , $i->id  };
	my @SortedConstraint;   
	foreach my $i(@SortedCO) {push @SortedConstraint , $i->id  } ;

	my $self = eval ($class."->new(10,1)");
	$self->set_SOS_registry( $NewSOSRegistry);
	$self->set_constraint_registry( $NewConstraintRegistry);
	$self->set_activity_registry($NewActivityRegistry);
	$self->set_sorted_constraints(\@SortedConstraint);
	$self->set_sorted_activities(\@SortedActivity);
	$self->set_sorted_quadratic_activities(\@quadratics);

	MpmasMILPActivity->switch_on_defaults;
	MpmasMILPConstraint->switch_on_defaults;
	MpmasMILPSOS->switch_on_defaults;

	my $milpinfo = {(
				NumCol => $NumCol,
				NumRow => $NumRow,
				ZeroAct => 0,
				ZeroCon => $NumZeroRow || 0,		
				IntegAct => $ninteg,
				SOS	=> $nsos,
				Digs => 4,
				QuadraticAct => scalar(@quadratics)
			)};
	$self->set_milpinfo($milpinfo);
	
	MpmasInputfile->set_milplist([("10")]);


	print "...done\n" if ($verbose);
	return $self;
}

sub calculate_lhs_of_constraint {
			my ($self, $k, $attribute) = @_;
			$k = $self->constraint($k) if (not ref $k);
			$attribute = "solution" unless (defined $attribute);
			
			if ($attribute eq "solution" and $self->getLhsStatus() == 1) {
				return $k->attr("lhs");				
			}
			else {
				my $lhs = 0;
				for my $i (0..$self->milpinfo("NumCol")-1) {
					my $c = $self->activity_at_col($i);
					$lhs += $c->coeff($k->id) * $c->attr($attribute) if (defined $c->coeff($k->id));
				}
				return $lhs;
			}
}
sub calculate_slack_of_constraint {
			my ($self, $k, $attribute) = @_;
			$k = $self->constraint($k) if (not ref $k);
			$attribute = "solution" unless (defined $attribute);
			
			if ($attribute eq "solution" and $self->getSlacksStatus() == 1) {
				return $k->attr("slack");				
			}
			else {
				my $lhs = 0;
				for my $i (0..$self->milpinfo("NumCol")-1) {
					my $c = $self->activity_at_col($i);
					$lhs += $c->coeff($k->id) * $c->attr($attribute) if (defined $c->coeff($k->id));
				}
				return $k->attr("value") - $lhs;
			}
}
sub calculate_linear_part_of_objval {
			my ($self) = @_;
			my $sum = 0;
			for my $i (0..$self->milpinfo("NumCol")-1) {
				my $c = $self->activity_at_col($i);
				$sum += $c->orow() * $c->attr("solution") ;
			}
			return $sum;
}
sub clear_solution_pool {
	my ($self) = @_;
	$self->delete_milpinfo_solution_pool();
	for my $h ($self->activities() ) {
		$h->clear_pooled_solutions();
	}
}
sub import_solution_vector {
	my ($self,  $solutionfile, $ra, $forpool) = @_;
	open (my $fh, "<", $solutionfile) or (warn "Error when trying to open $solutionfile: $!\n", return 0);
	#print "Importing Solution Vector from $solutionfile\n";
	my $cc = 0;
	my $t = 0;
	$self->{imported_lhs} = 0;
	$self->{imported_slacks} = 0;
	
	while (<$fh>) {
		chomp;
		my $line = $_;

		next if ($line =~ /^\s*$/); #skip empty lines
		if ($line =~ /\s*Target value:\s*?((\s|-)[-\d.E+e]+)/) {
			$t = $1;
			if (defined $forpool) {
				$self->set_milpinfo_solution_pool($forpool,$t);
			}
			else {
				$self->set_milpinfo_entry("TargetValue",$t);
			}
		}
		elsif ($line =~/^\s*?((\s|-)[-\d.E+e]+)/) {
			my $h = $self->activity_at_col($cc);
			if (defined $forpool) {
				$h->set_pooled_solution($forpool, $1);			}
			else {
				$h->set_attr("solution", $1);
			}
			$cc++;
		}
	}
	close $fh;
	if ($self->milpinfo("QuadraticAct") > 0 ) {
		$t = $t * (-1);
		$self->set_milpinfo_entry("TargetValue",$t);
		my $l = $self->calculate_linear_part_of_objval();
		my $q = $t - $l;
		$self->set_milpinfo_entry("ObjectiveValueLin", $l);
		$self->set_milpinfo_entry("ObjectiveValueQuad", $q);
		if (defined $ra) {
	      		$self->set_milpinfo_entry("VarianceRiskModel", $q * (-2) / $ra);
			$self->set_milpinfo_entry("RiskAversionCoef", $ra);
		}
	}
	
	my $lhsfile = $solutionfile;
	$lhsfile =~ s/\.pri$/\.lhs/;
	
	if ( -e $lhsfile) {
		open (my $fh2, "<", $lhsfile) or (warn "Error when trying to open $lhsfile: $!\n");
		#print "Importing LHS Vector from $solutionfile\n";
		my $cc = 0;
		my $t = 0;
		while (<$fh2>) {
			chomp;
			my $line = $_;

			next if ($line =~ /^\s*$/); #skip empty lines
			if ($line =~ /\s*Target value:\s*?((\s|-)[-\d.E+e]+)/) {
				next;
			}
			elsif ($line =~/^\s*?((\s|-)[-\d.E+e]+)/) {
				my $h = $self->constraint_at_row($cc);
				$h->set_attr("lhs", $1);
				$cc++;
			}
		}
		$self->{imported_lhs} = 1;
		close $fh2;
	}
	
	my $slacksfile = $solutionfile;
	$slacksfile =~ s/\.pri$/\.sla/;
	if ( -e $slacksfile) {
		open (my $fh2, "<", $slacksfile) or (warn "Error when trying to open $slacksfile: $!\n");
		#print "Importing Slacks Vector from $solutionfile\n";
		my $cc = 0;
		my $t = 0;
		while (<$fh2>) {
			chomp;
			my $line = $_;

			next if ($line =~ /^\s*$/); #skip empty lines
			if ($line =~ /\s*Target value:\s*?((\s|-)[-\d.E+e]+)/) {
				next;
			}
			elsif ($line =~/^\s*?((\s|-)[-\d.E+e]+)/) {
				my $h = $self->constraint_at_row($cc);
				$h->set_attr("slack", $1);
				$cc++;
			}
		}
		$self->{imported_slacks} = 1;
		close $fh2;
	}
	
	
	return 1;
}
sub import_cplex_sol_xml {
	my ($self,  $solutionfile, $ra) = @_;
	open (my $fh, "<", $solutionfile) or croak "Error when trying to open $solutionfile: $!";
	#print "Importing Solution Vector from $solutionfile\n";
	foreach my $act ($self->sorted_activities){
			my $x = $self->activity($act);
			$x->set_attr("solution", 0);
	}
	my $cc = 0;
	my $t = 0;
	while (<$fh>) {
		chomp;
		my $line = $_;

		next if ($line =~ /^\s*$/); #skip empty lines
				
		if ($line =~  /^\s*objectiveValue="([-\d.E+e]+)"/) {
			$t = $1;
			$self->set_milpinfo_entry("TargetValue",-1*$t);
		}
		elsif ($line =~/^\s*<variable index="(\d+)" value="([-\d.E+e]+)"/) {
			my $h = $self->activity_at_col($1);
			croak "Variable index $1 out of bounds in $solutionfile\n" unless (defined $h);
			$h->set_attr("solution", $2);
		}
	}
	close $fh;
	
	return 1;
}
sub write {
	
	my ($self, $mode,$cur, $valuetab, $singlemode, $newsinglemode) = @_;
	my $dir = $self->outdir();
	if ($singlemode &&  $newsinglemode) {
		$self->set_extension(".mtx");
		$self->set_extension_cm(".txt");
	}
	elsif ($singlemode && ! $newsinglemode) {
		$self->set_extension("_".$cur.".mtx");
		$self->set_extension_cm("_".$cur.".txt");
	} 
	$self->SUPER::write($mode,$cur,$valuetab);
}
sub adhoc_create {
	my $class = shift;
	my $NewConstraintRegistry = SimpleObjectRegistry->create();
	my $NewActivityRegistry = SimpleObjectRegistry->create();
	
	my $self = eval ($class."->new(10,1)");
	MpmasInputfile->set_global_references("adhoc", "/var/tmp/", "/var/tmp/", undef, undef, undef, undef, undef, undef,undef,undef,undef,undef,undef, undef, undef, undef, undef);  
	$self->set_constraint_registry( $NewConstraintRegistry);
	$self->set_activity_registry($NewActivityRegistry);

	require MpmasQLClassDefinition;
	require MpmasQLElementDefinition;
	require MpmasQLFieldDefinition;

	my $defaults = MpmasQLClassDefinition->create("DEFAULT");
	my $default_activity = MpmasQLElementDefinition->create("activity", "activity");
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "name", fieldname => "name",  content  => "N/A", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "unit", fieldname => "unit",  content  => "N/A", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "integ", fieldname => "integ",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "lbound", fieldname => "lbound",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "ubound", fieldname => "ubound",  content  => "10**31", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "orow", fieldname => "orow",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "priority", fieldname => "priority",  content  => "1000", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "pseudo_down", fieldname => "pseudo_down",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "pseudo_up", fieldname => "pseudo_up",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "type", fieldname => "type",  content  => "default", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "xnproduct", fieldname => "xnproduct",  content  => "-1", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "xnlink", fieldname => "xnlink",  content  => "-1", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "consfix", fieldname => "consfix",  content  => "0", type    => "scalar"));
	$default_activity->add_field(MpmasQLFieldDefinition->create(id => "landfix", fieldname => "landfix",  content  => "0", type    => "scalar"));

	$defaults->add_elements($default_activity );
	my $default_constraint = MpmasQLElementDefinition->create("constraint", "constraint");
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "name", fieldname => "name",  content  => "N/A", type    => "scalar"));
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "unit", fieldname => "unit",  content  => "N/A", type    => "scalar"));
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "eqtype", fieldname => "eqtype",  content  => 1, type    => "scalar"));
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "value", fieldname => "value",  content  => 0, type    => "scalar"));
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "type", fieldname => "type",  content  => "default", type    => "scalar"));
	$default_constraint->add_field(MpmasQLFieldDefinition->create(id => "xnproduct", fieldname => "xnproduct",  content  => "-1", type    => "scalar"));
	$defaults->add_elements($default_constraint );
	MpmasQLClassDefinition->set_defaults($defaults);
	$self->{adhoc_solve_rm} = "No solution attempt yet!";
	$self->{imported_lhs} = 0;
	$self->{imported_slacks} = 0;
	return $self;
}
sub adhoc_add_constraint {
	my ($self, $id, $fields) = @_;
	my $new = MpmasMILPConstraint->create($id, $fields,undef, undef ,0);
	$new->set_lp_row($self->get_constraint_registry->counter );
	$self->add_constraints($new);
	return $new;
}
sub adhoc_add_activity {
	my ($self, $id, $fields) = @_;
	my $new = MpmasMILPActivity->create($id, $fields,undef, undef ,0);
	$new->set_lp_column($self->get_activity_registry->counter );
	$self->add_activities($new);
	return $new;
}
sub adhoc_solve {
	my $self = shift;
	my $milpcheck = shift;
	my $verbose = shift ||0;

	my @SortedAO = sort {$a->lp_column <=> $b->lp_column} $self->activities;
	my @SortedCO = sort {$a->lp_row <=> $b->lp_row} $self->constraints;
	my @SortedActivity;   
	my $ninteg = 0;
	foreach my $i(@SortedAO) {
		  push @SortedActivity , $i->id;   
		  $ninteg++ if ($i->get_attribute("integ") == 1);
	};
	my @SortedConstraint;   
	foreach my $i(@SortedCO) {push @SortedConstraint , $i->id  } ;
	$self->set_sorted_constraints(\@SortedConstraint);
	$self->set_sorted_activities(\@SortedActivity);
	
	my $milpinfo = {(
				NumCol => $self->get_activity_registry->counter,
				NumRow => $self->get_constraint_registry->counter,
				ZeroAct => 0,
				ZeroCon => 0,		
				IntegAct => $ninteg,
				Digs => 4
			)};
	$self->set_milpinfo($milpinfo);

	$self->blank_file;
	$self->prepare_special(1, "_", {});
	$self->write(1, "_",{},1) if $verbose;
	my $fn = $self->write(0, "_",{},1);
	
	$fn =~ s/.mtx$//;
	my $string = $milpcheck." ".$fn." -seq 4";
	$fn .= ".pri";
	unlink($fn);
	
	my $rm = qx($string);
	$self->{adhoc_solve_rm} = $rm;
	my $rv = 1;
	$rv = 0 if ($rm =~ /Unbounded/ or $rm =~ /Infeasible/) ;   
	eval {
		local $SIG{__WARN__}=sub{}; #temporarily suppress warnings
		$self->import_solution_vector($fn);
	};
	if ($@) {
	      croak "$@";
	      return 0;
	}
	else {
		#print "Solution has been imported.\n"
	  return $rv;
	}
}
sub adhoc_status_message {
    return $_[0]->{adhoc_solve_rm};
}
sub getLhsStatus {
	return 	$_[0]->{imported_lhs};
}
sub getSlacksStatus {
	return 	$_[0]->{imported_slacks};
}
1;

