package MpmasMILP;

use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Carp qw(croak);
use Data::Dumper;
use MpmasInputfile;
use MpmasInputBlock;
use MpmasInputBlockWithValueReplace;
use MpmasMILPAbstract;
use IndexedTable;
use SimpleObjectRegistry;
use MpmasMILPActivity;
use MpmasMILPConstraint;
use CtUtilities qw(numbers_robust_to_sorting);
use MpmasQLClassDefinition;
use MpmasQLElementDefinition;
use MpmasQLFieldDefinition;

our @ISA = qw(MpmasMILPAbstract);
my %atcodes = (
	age_household_head 	=> 1,
	age_successor		=> 2,
	last_income			=> 3,
	mark_household_head => 4,
	mark_successor		=> 5,
	#extraChar			=> 6
);
my %aggcodes = (
	avg 	=> 0,
	sum		=> 1,
	med		=> 2,
	min		=> 3,
	max		=> 4,
	medl	=> 5,
	medu	=> 6
);
my %hhatcodes = (
	count => -1,
	age => 0,
	labgrp => 1,
	labprov => 2,
	leaving => 3,
	marrying => 4,
	retiring  => 5,
	succession_priority  => 6,
	minimum_consumption  => 7,
	opportunity_wage  => 8,
	leaving_with_mother  => 9,
	leaving_with_father  => 10,
	_first_nutrient => 11
);

my %hheventcodes = (
	die => -6,
	leave => -5,
	retire => -4,
	become_household_head => -3,
	marry => -2,
	give_birth => -1
);
sub prepare_shared {
	my ($self) = @_;
	
	##Determine Constraints that have been included into the Perennial file and need to go in one block together
	if ($self->config_entry("PERMCROP_MODEL") == 1) {
		for my $i ($self->perennialobjects) {
			for my $j ($i->export_affected_constraints) {
				my $o = $self->constraint($j);
				print STDERR "Constraint $j is referred to in the perennials file, but has not been declared anywhere.\n" and exit (1) unless (defined $o);
				$o->set_attr("type","MASperlab");
			}
		}
	}
	
	$self->make_constraint_order_hash();
    #Adapt watersupply constraints to irrigation season
	my $ConstraintTypes;
	for my $element ($self->constraints){
		my $x =$element->attr("type");
		my $y = $element->id;
		if ($x eq "MASwater") {
			$y =~ /(\d+)$/;
			$x = "MASwater1";
			$x = "MASwater2" if ($1 < $self->config_entry("SEASONSTART"));

		}
		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")) {
			$ConstraintTypes->{$y}[1] = $element->attr("mperiod"); 
		}
		$ConstraintTypes->{$y}[2] = numbers_robust_to_sorting($y)
	}
	#Sort activities and constraints
	$self->sort_constraints($ConstraintTypes);
	
	$self->make_activity_order_hash();
	my %actrank = %{$self->{actrank}};
	
	my $ActivityRanking;	
	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 $ActivityRanking->{$y});
		print STDERR "Error for Activity $y: activity type $x not listed in ACTTYPEORDER\n" and exit(1) unless $actrank{$x};
		
		$ActivityRanking->{$y}[0] = $actrank{$x};
		$ActivityRanking->{$y}[1] = 0;
		$ActivityRanking->{$y}[2] = 0;
		
		if ( ($self->config_entry("CROP_MODEL") == 2 or $self->config_entry("CROP_MODEL") >= 3)  and ($self->config_entry("STATICNRU") != 1 or not $self->config_entry("EXTERNALCGFORMAT0")) and ( $x eq "MAScgact" or $x eq "MASperennial" or $x eq "MASswitch")) {
			print STDERR "Error for activity $y: activity is of type $x and STATICNRU is != 1 or EXTERNALCGFORMAT0 = 0, but no 'external_crop_growth>slot' field has been defined for this activity.\n" and exit(1)  unless defined ($element->subelement_field("external_crop_growth", "slot") );
			print STDERR "Error for activity $y: activity is of type $x and STATICNRU is != 1 or EXTERNALCGFORMAT0 = 0, but no 'external_crop_growth>nru' field has been defined for this activity.\n" and exit(1)  unless defined ($element->subelement_field("external_crop_growth", "nru") );
			$ActivityRanking->{$y}[0] = $actrank{"MAScgact"};
			$ActivityRanking->{$y}[1] = numbers_robust_to_sorting($element->subelement_field("external_crop_growth", "nru") );
			$ActivityRanking->{$y}[2] = numbers_robust_to_sorting($element->subelement_field("external_crop_growth", "slot") );
		}
		elsif ($x eq "MASdisinvest") {
			print STDERR "Error for activity $y: activity is of type $x a, but no 'disinvest>asset' field has been defined for this activity.\n" and exit(1)  unless defined ($element->subelement_field("disinvest", "asset") );
			$ActivityRanking->{$y}[1] = numbers_robust_to_sorting($element->subelement_field("disinvest", "asset"));
		}
		if($self->config_entry("MULTIPERIOD")) {
			$ActivityRanking->{$y}[3] =  $element->attr("mperiod"); 
		}

	}

	my @SortedActivity;
	if($self->config_entry("MULTIPERIOD")) {
		@SortedActivity= sort {$ActivityRanking->{$a}[3] <=> $ActivityRanking->{$b}[3]
			|| $ActivityRanking->{$a}[0] cmp $ActivityRanking->{$b}[0]
			|| $ActivityRanking->{$a}[1] cmp $ActivityRanking->{$b}[1] 
			|| $ActivityRanking->{$a}[2] cmp $ActivityRanking->{$b}[2] 
			|| numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b) 
		  } keys %{$ActivityRanking} ; ##
	}
	else {
		@SortedActivity= sort {$ActivityRanking->{$a}[0] <=> $ActivityRanking->{$b}[0]
		|| $ActivityRanking->{$a}[1] cmp $ActivityRanking->{$b}[1]
		|| $ActivityRanking->{$a}[2] cmp $ActivityRanking->{$b}[2] 
		|| numbers_robust_to_sorting($a) cmp numbers_robust_to_sorting($b) 
		  } keys %{$ActivityRanking} ; ##
	 }
	my $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);


	$self->calculate_milpinfo();

	#look for first labour endowment
	unless ($self->config_entry("MPMASCHECKSOFF"))  {
		my @SortedConstraint = $self->sorted_constraints;
		for my $i (0..$#SortedConstraint) {
			my $t = $self->constraint($SortedConstraint[$i]);
			if (lc($t->attr("type")) eq "maslab"){
				$self->set_config_entry("FIRSTLAB",  $t->lp_row );
				last;
			}
		}
		print STDERR "\nError: No constraint of type 'maslab' found\n"  and exit (1) unless (defined $self->config_entry("FIRSTLAB"));	
	}

    unless ($self->config_entry("MPMASCHECKSOFF"))  {
	      my $masperlab; 
	      my $maspermac;
	      if ($self->config_entry("PERMCROP_MODEL") == 1) {
		  $masperlab = $self->find_special_constraint("masperlab",0);
		  $maspermac = $masperlab + 1;
	      }
	      else {
		  $masperlab = 	$self->config_entry("FIRSTLAB");
		  $maspermac = $masperlab;
	      }
	      my $masforget = $self->find_special_constraint("masforget",0);
		$self->set_milpinfo_entry("MasPerLab", $masperlab);
		$self->set_milpinfo_entry("MasPerMac",$maspermac);
		$self->set_milpinfo_entry("MasForget", $masforget);
      }
}
sub prepare_special {
	my ($self, $singlemode, $curRHS, $rhshash) = @_;	
	
	$self->set_extension(".dat");
	$self->set_extension_cm(".txt");
	if ($self->tableau_compact) {
	    $self->set_extension(".cpt");
	}
	
	$self->consistency_check_activities();
	$self->add_matrix_title($singlemode);
	$self->add_objrow($singlemode);
	$self->add_tableau($singlemode, $curRHS, $rhshash);

	unless ($singlemode) {		
		$self->add_fixed_independent_rhs();
		$self->add_consumption_harvest_block();
		if ( $self->config_entry("PRODUCER_ORGS") > 0) {
			$self->add_fixed_marketing();
		}
		# !! CT: TSPC, do not implement but wait on changes in setup there	
		my $tspc = MpmasInputBlock->new(3,80);			
		$tspc->add_row("8","TSPC INPUT FOR CROPPING ACTIVITIES in NRUs");
		$tspc->add_row("");		
		$tspc->add_row("");			
		$self->add_block($tspc);
	}
	$self->add_type_and_range_of_constraints($singlemode, $curRHS, $rhshash);
	#$self->add_marked_integers($singlemode);
	$self->add_column_bounds($singlemode);
	$self->add_integer_sets();
	$self->add_quadratic_info($singlemode);

	unless ($singlemode) {
		$self->add_epsila();
		$self->add_shadow_price_calc() unless ($self->is_otherFarm_MILP() );
		$self->add_timevariant_coefficients();
		#if ($self->config_entry("COUNTRY") == 5) {
			$self->add_agent_attributes_to_matrix() unless ($self->is_otherFarm_MILP());
			$self->add_landscape_attributes_to_matrix() if ($self->config_entry("NUDEF") > 0 && !$self->is_otherFarm_MILP() ) ;
		#}
		if ($self->config_entry("NUM_PLOT_CAPACITY_LAYERS") > 0 && $self->config_entry("LANDMARKET_MODEL") == 4) {
			$self->add_plot_capacity_constraints() unless ($self->is_otherFarm_MILP() );
		}
		if( $self->config_entry("LANDMARKET_MODEL") > 1 && $self->config_entry("LANDMARKET_MODEL") < 5 ) {
			$self->add_remaining_duration_constraints() unless ($self->is_otherFarm_MILP() );
		}
		if( $self->config_entry("LANDMARKET_MODEL") > 3 && $self->config_entry("MIPSTARTS")) {
			$self->add_plot_offer_slots() unless ($self->is_otherFarm_MILP() );
		}
		if ($self->config_entry("DEMOGRAPHY_MODEL") >= 2) {
			$self->add_household_members_to_matrix() unless ($self->is_otherFarm_MILP());
			$self->add_household_member_events_from_matrix() unless ($self->is_otherFarm_MILP());
			$self->add_land_and_asset_transfers_from_matrix() unless ($self->is_otherFarm_MILP());
		}
		$self->add_farmstead_info()  unless ($self->is_otherFarm_MILP());
		
		if ($self->config_entry("LAND_REDISTRIBUTION_MODEL")  && $self->config_entry("LANDMARKET_MODEL") > 1) {
			$self->add_application_for_land_distribution_cols() unless ($self->is_otherFarm_MILP());
		}
		if ($self->config_entry("PRE_INVESTMENT_EXTERNAL_SCRIPT") ){
			$self->add_asset_applications();
		}
	}		
}
sub add_matrix_title {
	my ($self, $singlemode) = @_;
	#Matrix Header
	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");
	
	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("","","","","","","","","$NumRow","$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("","","","","","","","",$NumRow - $ZeroCon,$NumCol - $ZeroAct, "0.3",   $self->delim("#END_NONZERO"));	
		$matrixtitle->add_row("");
		if ($self->is_otherFarm_MILP() ) {
			$matrixtitle->add_comment_row("1.3","Constraint Indices","","","","","","","Index of first soil constraint");
			#look for constraint index of the first soil endowment"	;
			my $x =  $self->find_special_constraint("massoil", 1);		
			$matrixtitle->add_row("","","","","","","","", $x,   $self->delim("#END_SOIL"));
		} 
		else {
			my  @temp = ("1.3","Constraint Indices","","","","","","","Constraint index of the first labor endowment","Constraint index of the first liquidity endowment");
			if ($self->config_entry("PERMCROP_MODEL") == 1) {
				for my $i ($self->sorted_constraints) {
						my $t = $self->constraint($i);
						push @temp,  $t->attr("name") if (lc($t->attr("type")) eq "masinv");
				}
			}
		#	if ($self->config_entry("COUNTRY") == 5 ) {
						push @temp, "Cash flow constraint (Coeff.)";
						push @temp, "Cash flow constraint (RHS fix cost)";
						push @temp, "Cash flow constraint (RHS min. consumption)";
		#	}
			push @temp, "First soil constraint";
			$matrixtitle->add_comment_row(@temp);
			undef @temp;
			@temp[0..7] = "";
			push @temp ,$self->config_entry("FIRSTLAB");
			push @temp, $self->find_special_constraint("masliq",1);		
		#	if ($self->config_entry("COUNTRY") == 5 ) {
						my $t = -1;
						my $sc = $self->constraint($self->config_entry("CASHFLOW_COEF"));
						$t = $sc->lp_row() if (defined $sc);
						push @temp, $t ;
						$t = -1;
						$sc = $self->constraint($self->config_entry("CASHFLOW_RHS"));
						$t = $sc->lp_row() if (defined $sc) ;
						push @temp, $t;
						$t = -1;
						$sc = $self->constraint($self->config_entry("CASHFLOW_RHS_MINCONS"));
						$t = $sc->lp_row() if (defined $sc);
						push @temp, $t;
		#	}
			
			#look for constraint indices of investment soil constraints
			if ($self->config_entry("PERMCROP_MODEL") == 1) {
				my $mascasfound = 0;
				my $csoil = 0;
				for my $i ($self->sorted_constraints) {
						my $t = $self->constraint($i);
						push @temp,  $t->lp_row and $csoil++ if (lc($t->attr("type")) eq "masinv");
						$mascasfound = 1 if (lc($t->attr("type")) eq "mascas");
				}
				
				print STDERR "Error: Using the permanent crops submodel requires including a investment constraint for each soil type (type: MASinv).\nFound $csoil investment constraints, NSOIL: ".$self->config_entry("NSOIL")."\n" and exit(1) if ($csoil != $self->config_entry("NSOIL"));
				
				print STDERR "Error: Using the permanent crops submodel requires including an additional refinancing cash balance constraint for the gestation period (type: MAScas).\nCould not find any MAScas constraint\n" and exit(1) unless ($mascasfound);

			}
			#look for constraint index of the first soil endowment"	;
			push @temp, $self->find_special_constraint("massoil", 1);		
			$matrixtitle->add_row(@temp,   $self->delim("#END_SOIL"));
		
	
			undef @temp;
					
			$matrixtitle->add_comment_row("1.4","Indices of Special Constraints","","","","","","", "Index of the average investment cash constraint", "Perennials, index of labor constraint nr. 0\tPerennials, index of machinery constraint nr. 0");
			@temp[0..7] = "";
			#look for ongoing liquidity constraint
			push @temp, $self->find_special_constraint("masong",1);
			
			#look cor Constraint index for the first perennials labour use constraint"	;
			if ($self->config_entry("PERMCROP_MODEL") == 1 ) { 
				push @temp, $self->milpinfo("MasPerLab"); 		
				push @temp, $self->milpinfo("MasPerMac");
			}
			else {
				push @temp,("");
			}
			$matrixtitle->add_row(@temp,   $self->delim("#END_LIQPERM"));
			undef @temp;
			
			$matrixtitle->add_comment_row("1.5", "Indices of special activities related to cash flows","","","","","","", "Index of the savings activity","Number of short-term credit sources","Index of the first short-term credit activity",	"Number of short-term deposit destinations",	"Index of first short-term deposit activity")	;

			@temp[0..7]  = "";
			if ($self->config_entry("CONSTYPE") == 1) {
				push @temp, $self->find_special_activity("masrcash",1);#remaining cash activity
			}
			else {
				push @temp , ("","");
			}
			push @temp, $self->find_special_activities("masscred");	# short-term credit
			push @temp, $self->find_special_activities("masdep",1);	# short-term savings
			$matrixtitle->add_row(@temp,  $self->delim("#END_CASHACT"));
			undef @temp;
			
			$matrixtitle->add_comment_row("1.6","Indices of special activities related to labor use","","","","","","","Number of hiring in temporary","Index of first activity", "Number of hiring out temporary","Index of first activity", "Number of permanent hiring in","Index of first activity",	"Number of permanent hiring out","Index of first activity");
			@temp[0..7]  = "";		
			push @temp, $self->find_special_activities("maslabtin");# temporary hiring in
			push @temp, $self->find_special_activities("maslabtout");# temporary hiring out
			push @temp, $self->find_special_activities("maslabpin");# permanent hiring in
			push @temp, $self->find_special_activities("maslabpout");# permanent hiring out
			$matrixtitle->add_row(@temp,   $self->delim("#END_LABACT"));
			undef @temp;
			
			$matrixtitle->add_comment_row("1.7","Labour Switches","","","","","","", "Number of Labor spans", "Number of Cash spans", "Fixing female labor binary [yes/no]", "Female labor switch: activity index if male and female labor","Female labor switch: activity index if only male labor", "Male labor switch: activity index if male and female labor","Male labor switch: activity index if only male labor");

			@temp[0..7]  = "";
			#Num Labour spans set to one!
			#Note: labor spans are not the same as labor groups, theoretically you could have more than one span per labor group, e.g. for seasons, usually you can however solve this within the MILP (with transfer activities), so for now this has not been implemented in mpmasql
			push @temp, 1;
			my ($count, $first) = $self->find_special_constraints("masliq");		#Num Cash spans
			push @temp, $count;
			# !!CT implement	female/male labour switches !!!
			push @temp, 0;
			$matrixtitle->add_row(@temp,  $self->delim("#END_LABSWITCHES"));
			undef @temp;
		}
	}	
	$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,  $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_fixed_marketing {
	my ($self) = @_;
	my $fixmarket = MpmasInputBlock->new(3,60);			
	$fixmarket->add_row("7","FIXING OF COLUMNS IN MARKETING MODE");
	$fixmarket->add_row("");	
	for my $i ($self->sorted_activity_attr("marketfix", $self->argument) ) {
			$fixmarket->add_row("","","",$i);	
	}
	$fixmarket->add_row("","","", $self->delim("#END_FIXMARKET"));	

	$fixmarket->add_row("");	
	$self->add_block($fixmarket);
	return $fixmarket;
}
sub add_consumption_harvest_block {
		my ($self, $singlemode) = @_;
		if ($self->config_entry("CONSTYPE") == 1) {
			my $disinvest = MpmasInputBlock->new(3,50);			
			$disinvest->add_row("5","CONSTRAINTS FOR NUTRIENT DEMANDS OF HOUSEHOLD");
			my ($nnutrdemc, $fnutrdemc) = $self->find_special_constraints("MASnutdemand");
			$disinvest->add_row("", "Index of constr. for hh. energy demand","",$fnutrdemc, $self->delim("#END_CONS")) ;
			$disinvest->add_empty_rows(2);
			$self->add_block($disinvest);
		}	
		#Special adaptations for harvest LP
		my $fixthird = MpmasInputBlock->new(3,60);			
		$fixthird->add_row("6","FIXING OF COLUMNS IN CONSUMPTION/HARVEST");
		$fixthird->add_row("");	
		if ($self->config_entry("CONSTYPE") == 1 or $self->config_entry("HARVEST_DECISION") == 1 ) {
			for my $i ($self->sorted_activity_attr("consfix",$self->argument) ) {
				$fixthird->add_row("","","",$i);	
			}
			$fixthird->add_row("","","", $self->delim("#END_FIXCONS"));	
		}
		$fixthird->add_row("");	
		$self->add_block($fixthird);
		
		my $finetunetop = MpmasInputBlock->new(3,70);			
		$finetunetop->add_row("7","DIFFERENT PARAMETERS FOR CONSUMPTION/HARVEST");
		$finetunetop->add_row("");
		my $numFineTune = 0;
		
		my $finetune = MpmasInputBlock->new(3,71);			
		$finetune->add_comment_row("","ConstraintID","ActivityID", "Index of constraint", "Index of activity","Coef. in harvest stage");
		for my $i ($self->sorted_activities) {
			my $h = $self->activity($i);
			if ($h->has_subelement("harvest") ) {
			    for my $j ($h->subelement_fields("harvest")) {
				$numFineTune++;
				my $c = $self->constraint($j);
				print STDERR "Error: Unknown constraint '$j' referenced in 'harvest' subelement of activity $i\n" and exit(1) if (not defined $c);
				$finetune->add_row("",$j,$i, $c->lp_row(), $h->lp_column,$h->milpspecific_subelement_field("harvest", $j, $self->argument));
			    }
			}
		}

		$finetunetop->add_row("","","Number of coefficients:", $numFineTune);
		$finetune->add_row("","","", $self->delim("#END_FINE"));	
		$finetune->add_row("");	
		$self->add_block($finetunetop);		
		$self->add_block($finetune);				
}
sub add_agent_attributes_to_matrix() {
	my ($self) = @_;
	my $agattribtop = MpmasInputBlock->new(3,160);			
	$agattribtop->add_row("20","AGENT ATTRIBUTES TO BE INSERTED INTO MATRIX");
	$agattribtop->add_row("");
	my $numagattrib = 0;
	
	my @ExtraChars = $self->get_cfg_list_empty_ok("EXTRA_HHMEMBER_CHARACTERISTICS");
	my %ExtraCharCodes;
	for my $i (0..$#ExtraChars) {
		$ExtraCharCodes{$ExtraChars[$i]} = $i;
	}
	
	my $agattrib = MpmasInputBlock->new(3,161);			
	$agattrib->add_comment_row("Attribute", "ActivityID", "ConstraintID", "AttributeID", "SubattributeID", "Index of activity", "Index of constraint" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("agent_attribute_to") ) {
		    for my $j ($h->subelement_fields("agent_attribute_to")) {
				$numagattrib++;
				my $attribute = $h->subelement_field("agent_attribute_to", $j);
				#first check predefined attribute codes
				my $atcode = $atcodes{$attribute};
				#second check extra household member characteristics
				my $atcode2 = -1;
				if (not defined $atcode) {
					if (exists ($ExtraCharCodes{$attribute}) ) {
						$atcode = 6; 
						$atcode2 = $ExtraCharCodes{$attribute};
					}
				}				
				print STDERR "Error: Agent attribute '$attribute' in 'agent_attribute_to' subelement of activity $i unknown or not activated for inclusion into matrix\nActivated attributes are:\n". join(', ', (keys %atcodes), @ExtraChars)."\n" and exit(1) if (not defined $atcode);
				print STDERR "Error: Agent attribute '$attribute' in 'agent_attribute_to' subelement of activity $i only available with advanced demography model\n" and exit(1) if ($atcode <= 2 and not $self->config_entry("DEMOGRAPHY_MODEL") >= 1);
				
				if ($j eq "orow") {
					$agattrib->add_row($attribute,$i, $j, $atcode, $atcode2, $h->lp_column, -1);					
				}
				else {
					my $c = $self->constraint($j);
					print STDERR "Error: Unknown constraint '$j' referenced in 'agent_attribute_to' subelement of activity $i\n" and exit(1) if (not defined $c);

					$agattrib->add_row($attribute,$i, $j, $atcode, $atcode2, $h->lp_column, $c->lp_row());
				}
		    }
		}
	}
	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if ($h->has_subelement("agent_attribute_to") ) {
		    for my $j ($h->subelement_fields("agent_attribute_to")) {
				$numagattrib++;
				my $attribute = $h->subelement_field("agent_attribute_to", $j);
				my $atcode = $atcodes{$attribute};
				#second check extra household member characteristics
				my $atcode2 = -1;
				if (not defined $atcode) {
					if (exists ($ExtraCharCodes{$attribute}) ) {
						$atcode = 6; 
						$atcode2 = $ExtraCharCodes{$attribute};
					}
				}
				print STDERR "Error: Agent attribute '$attribute' in 'agent_attribute_to' subelement of constraint $i unknown or not activated for inclusion into matrix\nActivated attributes are:\n".join(', ', (keys %atcodes), (keys %ExtraCharCodes) )."\n" and exit(1) if (not defined $atcode);
				print STDERR "Error: Agent attribute '$attribute' in 'agent_attribute_to' subelement of constraint $i only available with advanced demography model\n" and exit(1) if ($atcode <= 2 and not $self->config_entry("DEMOGRAPHY_MODEL") >= 1);
				
				if ($j eq "value") {
					$agattrib->add_row($attribute,"RHS",$i, $atcode, $atcode2,  -1, $h->lp_row);					
				}
				else {
						print STDERR "Error in 'agent_attribute_to' subelement of constraint $i: only 'value' field allowed for constraints\n" and exit(1) ;
				}
		    }
		}
	}
	$agattribtop->add_row("","","Number of entries:", $numagattrib);
	$agattrib->add_row("");	
	$agattrib->add_row("","","",  $self->delim("#END_AGSPEC"));
	$self->add_block($agattribtop);		
	$self->add_block($agattrib);	
		
}
sub add_landscape_attributes_to_matrix() {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,170);			
	$lscpattribtop->add_row("21","LANDSCAPE ATTRIBUTES TO BE INSERTED INTO MATRIX");
	$lscpattribtop->add_row("");
	my $numlscpattrib = 0;
	
	my $lscpattrib = MpmasInputBlock->new(3,171);			
	
	
	$lscpattrib->add_comment_row("","", "",  "ParameterID", "Aggregation", "Soil type/NRU", "Column", "Row" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("landscape_attribute_to") ) {
		    for my $j ($h->subelement_fields("landscape_attribute_to")) {
				$numlscpattrib++;
				my $string = $h->subelement_field("landscape_attribute_to", $j);
				$string =~ /(\w+)_S(\d+)_P(\d+)/;
				my $agg = $1;
				my $nru = $2;
				my $param = $3;
				print STDERR "Error: Invalid landscape_attribute_to> for activity $i: $string\n" and exit(1) unless (defined $agg and defined $nru and defined $param);
				my $aggcode = $aggcodes{$agg};
				print STDERR "Error: Aggregation '$agg' in 'landscape_attribute_to' subelement of activity $i unknown\n$string\nAvailable aggregations are:\n". join(', ', (keys %aggcodes) )."\n" and exit(1) if (not defined $aggcode);
				print STDERR "Error: User-defined landscape parameter '$param' in 'landscape_attribute_to' subelement of activity $i inconsistent with number of user-defined parameters (NUDEF = ".$self->config_entry("NUDEF") .")\nNote: Parameters IDs [0,.., NUDEF -1]\n" and exit(1) if ($param >= $self->config_entry("NUDEF") or $param < 0);
				print STDERR "Error: NRU '$nru' in 'landscape_attribute_to' subelement of activity $i inconsistent with number of NRUs (NRU = ".$self->config_entry("NNRU") .")\nNote: NRU IDs [0,.., NNRU -1]\n" and exit(1) if ($nru >= $self->config_entry("NNRU") or $nru < 0);
				
				
				if ($j eq "orow") {
					$lscpattrib->add_row("","", "", $param, $aggcode, $nru, $h->lp_column, -1);					
				}
				else {
					my $c = $self->constraint($j);
					print STDERR "Error: Unknown constraint '$j' referenced in 'landscape_attribute_to' subelement of activity $i\n" and exit(1) if (not defined $c);

					$lscpattrib->add_row("","", "", $param, $aggcode, $nru, $h->lp_column, $c->lp_row());
				}
		    }
		}
	}
	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if ($h->has_subelement("landscape_attribute_to") ) {
		    for my $j ($h->subelement_fields("landscape_attribute_to")) {
				$numlscpattrib++;
				my $string = $h->subelement_field("landscape_attribute_to", $j);
				$string =~ /(\w+)_S(\d+)_P(\d+)/;
				my $agg = $1;
				my $nru = $2;
				my $param = $3;
				print STDERR "Error: Invalid landscape_attribute_to> for constraint $i: $string\n" and exit(1) unless (defined $agg and defined $nru and defined $param);
				my $aggcode = $aggcodes{$agg};
				print STDERR "Error: Aggregation '$agg' in 'landscape_attribute_to' subelement of constraint $i unknown\n$string\nAvailable aggregations are :\n". join(', ', (keys %aggcodes) )."\n" and exit(1) if (not defined $aggcode);
				print STDERR "Error: User-defined landscape parameter '$param' in 'landscape_attribute_to' subelement of constraint $i inconsistent with number of user-defined parameters (NUDEF = ".$self->config_entry("NUDEF") .")\nNote: Parameters IDs [0,.., NUDEF -1]\n" and exit(1) if ($param >= $self->config_entry("NUDEF") or $param < 0);
				print STDERR "Error: NRU '$nru' in 'landscape_attribute_to' subelement of constraint $i inconsistent with number of NRUs (NRU = ".$self->config_entry("NNRU") .")\nNote: NRU IDs [0,.., NNRU -1]\n" and exit(1) if ($nru >= $self->config_entry("NNRU") or $nru < 0);
				
				if ($j eq "value") {
					$lscpattrib->add_row("","","", $param, $aggcode, $nru , -1, $h->lp_row);					
				}
				else {
						print STDERR "Error in 'landscape_attribute_to' subelement of constraint $i: only 'value' field allowed for constraints\n" and exit(1) ;
				}
		    }
		}
	}
	$lscpattribtop->add_row("","","Number of entries:", $numlscpattrib);
	$lscpattrib->add_row("","","", $self->delim("#END_LSCAPE"));
	$lscpattrib->add_row("");	
	$self->add_block($lscpattribtop);		
	$self->add_block($lscpattrib);	
}
sub add_plot_capacity_constraints {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,180);			
	$lscpattribtop->add_row("22","PLOT CAPACITY CONSTRAINTS");
	$lscpattribtop->add_row("");
	my $numlscpattrib = 0;
	
	my $lscpattrib = MpmasInputBlock->new(3,181);			
	$lscpattrib->add_comment_row("","", "",  "Row" , "Layer", "withRented");

	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if (defined $h->attr("plot_capacity_layer") && $h->attr("plot_capacity_layer") > -1 ) {
			print STDERR "Error in constraint $i: plot_capacity_layer ". $h->attr("plot_capacity_layer") ." is larger than NUM_PLOT_CAPACITY_LAYERS -1\n" and exit(1) if ( $h->attr("plot_capacity_layer") > -1 &&  $h->attr("plot_capacity_layer") > $self->config_entry("NUM_PLOT_CAPACITY_LAYERS") - 1 );
			my $s = 0;
			$s = $h->attr("plot_capacity_with_rented") if (defined $h->attr("plot_capacity_with_rented") );
		    $lscpattrib->add_row("","","", $h->lp_row(), $h->attr("plot_capacity_layer"), $s );
		    $numlscpattrib++;
		}
	}
	$lscpattribtop->add_row("","","Number of entries:", $numlscpattrib);
	$lscpattrib->add_row("","","",  $self->delim("#END_PLOTCAP"));
	$lscpattrib->add_row("");	
	$self->add_block($lscpattribtop);		
	$self->add_block($lscpattrib);	
}



sub is_otherFarm_MILP {
	my ($self) = @_;
	$self->argument() =~ /^2/;
}
sub add_remaining_duration_constraints {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,190);			
	$lscpattribtop->add_row("23","REMAINING RENTAL CONTRACT DURATION CONSTRAINTS");
	$lscpattribtop->add_row("");
	my $numlscpattrib = 0;
	
	my $lscpattrib = MpmasInputBlock->new(3,191);			
	$lscpattrib->add_comment_row("","", "",  "soilType" , "minDuration", "LPRow");

	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if (defined $h->subelement("plot_remaining_duration") ) {
				my $s = $h->subelement_field("plot_remaining_duration", "terrain");
				my $minDur = $h->subelement_field("plot_remaining_duration", "minimumDuration");
				print STDERR "Error: Invalid soilType/NRU $s given in 'plot_remaining_contract_duration>terrain' field of constraint ". $h->id(). "\n" and exit(1) if (not defined $s || $s < 0 or $s >= $self->config_entry("NNRU"));
				print STDERR "Error: Invalid minimumDuration $minDur given in 'plot_remaining_contract_duration>minimumDuration' field of constraint ". $h->id(). "\n" and exit(1) if (not defined $minDur || $minDur < -1 );
				$lscpattrib->add_row("","","", $s, $minDur, $h->lp_row()  );
				$numlscpattrib++;
		}
	}
	$lscpattribtop->add_row("","","Number of entries:", $numlscpattrib);
	$lscpattrib->add_row("","","",  $self->delim("#END_REMDUR"));
	$lscpattrib->add_row("");	
	$self->add_block($lscpattribtop);		
	$self->add_block($lscpattrib);	
}
sub add_plot_offer_slots {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,200);			
	$lscpattribtop->add_row("24","ACTIVITIES FOR OFFERED PLOTS");
	$lscpattribtop->add_row("");
	my ($fi, $num) = $self->find_special_activities("masplotoffer", 1);
	
	
	$lscpattribtop->add_row("","","Index of first activity", $fi);
	$lscpattribtop->add_row("","","Number of activities", $num);
	$lscpattribtop->add_row("","","",  $self->delim("#END_PLOT_OFFER_COLUMNS"));

	$lscpattribtop->add_row("");	
	$self->add_block($lscpattribtop);		
	
	
}
sub add_household_members_to_matrix {
	my ($self) = @_;
	my $agattribtop = MpmasInputBlock->new(3,210);			
	$agattribtop->add_row("25","HOUSEHOLD MEMBERS TO BE INSERTED INTO MATRIX");
	$agattribtop->add_row("");
	my $numagattrib = 0;
	
	my @ExtraChars = $self->get_cfg_list_empty_ok("EXTRA_HHMEMBER_CHARACTERISTICS");
	my %ExtraCharCodes;
	for my $i (0..$#ExtraChars) {
		$ExtraCharCodes{$ExtraChars[$i]} = $i;
	}
	
	my @HHNutrients = ();
	my %HHNutrientIds = ();
	if  ($self->config_entry("CONSTYPE") == 1) {
		@HHNutrients = $self->get_cfg_list("HHNUTRIENTS");
		for my $i (0..$#HHNutrients) {
			$HHNutrientIds{$HHNutrients[$i]} = $i;
		}
	}
	
	my $agattrib = MpmasInputBlock->new(3,211);			
	$agattrib->add_comment_row("", "ActivityID", "ConstraintID", "CareerID", "AgeMin", "AgeMax", "Which", "What", "Whoelse", "Index of activity", "Index of constraint" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("household_member_to") ) {
		    for my $j ($h->subelement_fields("household_member_to")) {
				$numagattrib++;
				my $string = $h->subelement_field("household_member_to", $j);
				
				unless ( $string =~ /(\w+)_OF_(ALL|[\d+]st|[\d+]nd|[\d+]rd|[\d+]th)_(\w+)_AGED_(\d+)_TO_(\d+)(_WITH_CHILDREN|_WITH_LEAVING)?(_\d+_YEARS_AHEAD)?/) {
					print STDERR "Error in activity $i: #household_member_to> $j   Household member string $string did not match required format.\n";
					exit(1);
				}
				
				my $what = $1;
				my $which = $2;
				my $career = $3;
				my $ageMin = $4;
				my $ageMax = $5;
				my $whoelse = $6;
				my $ahead = $7;
				
				
				
				unless (defined $ahead) {
					$ahead = 0;
				}
				else {
					$ahead =~ s/^\D+//;
					$ahead =~ s/\D+$//;
				}
				#career
				my $careercode = $self->config_entry("CAREERS")->{$career};
				print STDERR "Error: career $career not defined" and exit(1)  unless (defined $careercode);

				my $whichcode;
				if ($which eq "ALL") {
					$whichcode = -1;
				}
				else {
					$which =~ /(\d+)/;
					$whichcode = $1 - 1;	
				}

				my $whoelsecode = 0;
				if ($whoelse eq "_WITH_CHILDREN") {
					$whoelsecode = 1;
				}
				elsif ($whoelse eq "_WITH_LEAVING") {
					$whoelsecode = 2;
				}
				
				#first check predefined attribute codes
				my $whatcode = $hhatcodes{$what};
				#second check extra household member characteristics

				if (not defined $whatcode) {
					if (exists ($HHNutrientIds{$what}) ) {
						$whatcode = $hhatcodes{"_first_nutrient"} +  $HHNutrientIds{$what};

					}
					elsif (exists ($ExtraCharCodes{$what}) ) {
						 
						$whatcode = $hhatcodes{"_first_nutrient"} + (scalar @HHNutrients) + $ExtraCharCodes{$what};
					}
				}				
				print STDERR "Error: Household member attribute '$what' in 'household_member_to' subelement of activity $i unknown or not activated for inclusion into matrix\nActivated attributes are:\n". join(', ', (keys %hhatcodes), @ExtraChars)."\n" and exit(1) if (not defined $whatcode);
				
				
				if ($j eq "orow") {
					$agattrib->add_row("",$i, $j, $careercode, $ageMin, $ageMax,$whichcode, $whatcode, $whoelsecode, $ahead, $h->lp_column, -1);					
				}
				else {
					my $c = $self->constraint($j);
					print STDERR "Error: Unknown constraint '$j' referenced in 'household_member_to' subelement of activity $i\n" and exit(1) if (not defined $c);

					$agattrib->add_row("",$i, $j, $careercode, $ageMin, $ageMax,$whichcode, $whatcode, $whoelsecode, $ahead, $h->lp_column, $c->lp_row());
				}
		    }
		}
	}
	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if ($h->has_subelement("household_member_to") ) {
		    for my $j ($h->subelement_fields("household_member_to")) {
				$numagattrib++;
				my $string = $h->subelement_field("household_member_to", $j);
				
				unless ($string =~ /(\w+)_OF_(ALL|[\d+]st|[\d+]nd|[\d+]rd|[\d+]th)_(\w+)_AGED_(\d+)_TO_(\d+)(_WITH_CHILDREN|_WITH_LEAVING)?(_\d+_YEARS_AHEAD)?/) {
					print STDERR "Error in activity $i: #household_member_to> $j   Household member string $string did not match required format.\n";
					exit(1);
				}
				
				my $what = $1;
				my $which = $2;
				my $career = $3;
				my $ageMin = $4;
				my $ageMax = $5;
				my $whoelse = $6;
				my $ahead = $7;
				
				unless (defined $ahead) {
					$ahead = 0;
				}
				else {
					$ahead =~ s/^\D+//;
					$ahead =~ s/\D+$//;
				}
				#career
				my $careercode = $self->config_entry("CAREERS")->{$career};
				print STDERR "Error: career $career not defined" and exit(1)  unless (defined $careercode);

				my $whichcode;
				if ($which eq "ALL") {
					$whichcode = -1;
				}
				else {
					$which =~ /(\d+)/;
					$whichcode = $1 - 1;	
				}

				my $whoelsecode;
				if (not defined $whoelse ) {
					$whoelsecode = 0;
				}
				elsif ($whoelse eq "_WITH_CHILDREN") {
					$whoelsecode = 1;
				}
				elsif ($whoelse eq "_WITH_LEAVING") {
					$whoelsecode = 2;
				}
				
				#first check predefined attribute codes
				my $whatcode = $hhatcodes{$what};
				#second check extra household member characteristics

				if (not defined $whatcode) {
					if (exists ($HHNutrientIds{$what}) ) {
						$whatcode = $hhatcodes{"_first_nutrient"} +  $HHNutrientIds{$what};

					}
					elsif (exists ($ExtraCharCodes{$what}) ) {
						 
						$whatcode = $hhatcodes{"_first_nutrient"} + (scalar @HHNutrients) + $ExtraCharCodes{$what};
					}
					elsif(defined $self->config_entry("CAREERS")->{$what})
					{
						$whatcode = -1 * $self->config_entry("CAREERS")->{$what}
					}
				}			
					
				print STDERR "Error: Household member attribute '$what' in 'household_member_to' subelement of constraint $i unknown or not activated for inclusion into matrix\nActivated attributes are:\n". join(', ', (keys %hhatcodes), @ExtraChars)."\n" and exit(1) if (not defined $whatcode);
				
				if ($j eq "value") {
					$agattrib->add_row("", "RHS",$i, $careercode, $ageMin, $ageMax,$whichcode, $whatcode, $whoelsecode, $ahead,  -1, $h->lp_row);					
				}
				else {
						print STDERR "Error in 'household_member_to' subelement of constraint $i: only 'value' field allowed for constraints\n" and exit(1) ;
				}
		    }
		}
	}
	$agattribtop->add_row("","","Number of entries:", $numagattrib);
	$agattrib->add_row("");	
	$agattrib->add_row("","","",  $self->delim("#END_HOUSEHOLD_MEMBERS_IN"));
	$self->add_block($agattribtop);		
	$self->add_block($agattrib);	
		
}
sub add_household_member_events_from_matrix {
	my ($self) = @_;
	my $agattribtop = MpmasInputBlock->new(3,220);			
	$agattribtop->add_row("26","HOUSEHOLD MEMBER EVENTS TO BE READ FROM SOLUTION");
	$agattribtop->add_row("");
	my $numagattrib = 0;
	
	
	my $agattrib = MpmasInputBlock->new(3,221);			
	$agattrib->add_comment_row("", "ActivityID", "", "CareerID", "AgeMin", "AgeMax", "Which", "What", "Whereto","Index of activity" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("indicates_household_member_event") ) {
			
			for my $j ($h->subelement_fields("indicates_household_member_event")) {
				$numagattrib++;
				my $what = $h->subelement_field("indicates_household_member_event", $j);
				
				unless ($j =~ /(ALL|[\d+]st|[\d+]nd|[\d+]rd|[\d+]th)_(\w+)_AGED_(\d+)_TO_(\d+)/ ){
					print STDERR "Error in activity $i: #household_member_to>   Household member string $j  did not match required format.\n";
					exit(1);
				}
								
				my $which = $1;
				my $career = $2;
				my $ageMin = $3;
				my $ageMax = $4;

				my $whereto = -1;


				#career
				my $careercode = $self->config_entry("CAREERS")->{$career};
				print STDERR "Error: career $career not defined" and exit(1)  unless (defined $careercode);

				my $whichcode;
				if ($which eq "ALL") {
					$whichcode = -1;
				}
				else {
					$which =~ /(\d+)/;
					$whichcode = $1 - 1;	
				}
				#first check predefined attribute codes
				my $whatcode = $hheventcodes{$what};

				if ( (not defined $whatcode) && $what =~ /leave_to_new_household_(\d+)/) {
					$whatcode = $hheventcodes{"leave"};
					$whereto = $1;
				}

				if (not defined $whatcode) {
						$whatcode = $self->config_entry("CAREERS")->{$what};
				}
				
				print STDERR "Error: Household member event '$what' in 'indicates_household_member_event' subelement of constraint $i unknown or not activated for inclusion into matrix\nActivated attributes are:\n". join(', ', (keys %hheventcodes), 'leave_to_new_household_X', keys %{ $self->config_entry("CAREERS") } )."\n" and exit(1) if (not defined $whatcode);
				
				$agattrib->add_row("",$i, "", $careercode, $ageMin, $ageMax,$whichcode, $whatcode,  $whereto, $h->lp_column);

			}
			
		}
	}

	$agattribtop->add_row("","","Number of entries:", $numagattrib);
	$agattrib->add_row("");	
	$agattrib->add_row("","","",  $self->delim("#END_HOUSEHOLD_MEMBERS_OUT"));
	$self->add_block($agattribtop);		
	$self->add_block($agattrib);	
}
sub add_land_and_asset_transfers_from_matrix {
	my ($self) = @_;
	my $agattribtop = MpmasInputBlock->new(3,230);			
	$agattribtop->add_row("27","LAND AND ASSET TRANSFERS TO NEW HOUSEHOLDS");
	$agattribtop->add_row("");
	my $numagattrib = 0;
	
	
	my $agattrib = MpmasInputBlock->new(3,231);			
	$agattrib->add_comment_row("", "ActivityID", "", "What", "Which", "Age", "ToWhom","Index of activity" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("transfer_land_to_new_household") ) {
			for my $j ($h->subelement_fields("transfer_land_to_new_household")) {
				$numagattrib++;
				my $what = 1; #land
				my $which =  $j;
				my $age = -1;
				my $whereto = $h->subelement_field("transfer_land_to_new_household", $j); 			

				$agattrib->add_row("",$i, "", $what, $which, $age, $whereto,  $h->lp_column);
			}
		}
		if ($h->has_subelement("transfer_cash_to_new_household") ) {
			
			for my $j ($h->subelement_fields("transfer_cash_to_new_household")) {
				$numagattrib++;
				my $what = 0; #cash
				my $which =  -1;
				my $age = -1;
				my $whereto = $h->subelement_field("transfer_cash_to_new_household", $j); 			
				
				$agattrib->add_row("",$i, "", $what, $which, $age, $whereto,  $h->lp_column);

			}
			
		}
		if ($h->has_subelement("transfer_asset_to_new_household") ) {
			
			for my $j ($h->subelement_fields("transfer_asset_to_new_household")) {
				$numagattrib++;
				my $what = 2; #asset
				
				my ($asset, $age);
				
				if ($j =~ /(\w+)_AGED_(\d+)/) {
					$asset  =  $1;
					$age = $2
				}
				else {
					$asset  = $j;
					$age = -1 
				}
				
				my $ob = $self->networkobject($asset);
				
				print STDERR "Error: Undefined asset '$asset' in #transfer_asset_to_new_household> of activity $i\n" and exit(1) unless (defined $ob);
				my $which = $ob->oid();	
											
				my $whereto = $h->subelement_field("transfer_asset_to_new_household", $j); 			
				
				$agattrib->add_row("",$i, "", $what, $which, $age, $whereto,  $h->lp_column);

			}
			
		}
	}

	$agattribtop->add_row("","","Number of entries:", $numagattrib);
	$agattrib->add_row("");	
	$agattrib->add_row("","","",  $self->delim("#END_HOUSEHOLD_SPLIT"));
	$self->add_block($agattribtop);		
	$self->add_block($agattrib);	
}
sub add_farmstead_info {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,240);			
	$lscpattribtop->add_row("28","FARMSTEAD NRU INTO MATRIX");
	$lscpattribtop->add_row("");
	my $numlscpattrib = 0;
	
	my $lscpattrib = MpmasInputBlock->new(3,241);			
	
	
	$lscpattrib->add_comment_row("","", "",  "Soil type/NRU", "Column", "Row" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("farmstead_is_on_land_type") ) {
		    for my $j ($h->subelement_fields("farmstead_is_on_land_type")) {
				$numlscpattrib++;
				my $nru = $h->subelement_field("farmstead_is_on_nru", $j);

				print STDERR "Error: NRU '$nru' in 'farmstead_is_on_nru' subelement of activity $i inconsistent with number of NRUs (NRU = ".$self->config_entry("NNRU") .")\nNote: NRU IDs [0,.., NNRU -1]\n" and exit(1) if ($nru >= $self->config_entry("NNRU") or $nru < 0);
				
				
				if ($j eq "orow") {
					$lscpattrib->add_row("","", "", $nru, $h->lp_column, -1);					
				}
				else {
					my $c = $self->constraint($j);
					print STDERR "Error: Unknown constraint '$j' referenced in 'farmstead_is_on_nru' subelement of activity $i\n" and exit(1) if (not defined $c);

					$lscpattrib->add_row("","", "", $nru, $h->lp_column, $c->lp_row());
				}
		    }
		}
	}
	for my $i ($self->sorted_constraints) {
		my $h = $self->constraint($i);
		if ($h->has_subelement("farmstead_is_on_nru") ) {
		    for my $j ($h->subelement_fields("farmstead_is_on_nru")) {
				$numlscpattrib++;
				my $nru = $h->subelement_field("farmstead_is_on_nru", $j);
				
				print STDERR "Error: NRU '$nru' in 'farmstead_is_on_nru' subelement of constraint $i inconsistent with number of NRUs (NRU = ".$self->config_entry("NNRU") .")\nNote: NRU IDs [0,.., NNRU -1]\n" and exit(1) if ($nru >= $self->config_entry("NNRU") or $nru < 0);
				
				if ($j eq "value") {
					$lscpattrib->add_row("","","", $nru , -1, $h->lp_row);					
				}
				else {
						print STDERR "Error in 'farmstead_is_on_nru' subelement of constraint $i: only 'value' field allowed for constraints\n" and exit(1) ;
				}
		    }
		}
	}
	$lscpattribtop->add_row("","","Number of entries:", $numlscpattrib);
	$lscpattrib->add_row("","","", $self->delim("#END_FARMSTEAD_NRU"));
	$lscpattrib->add_row("");	
	$self->add_block($lscpattribtop);		
	$self->add_block($lscpattrib);	

}
sub add_application_for_land_distribution_cols {
	my ($self) = @_;
	my $agattribtop = MpmasInputBlock->new(3,250);			
	$agattribtop->add_row("29","APPLICATION FOR LAND REDISTRIBUTION");
	$agattribtop->add_row("");
	my $numagattrib = 0;
	
	
	my $agattrib = MpmasInputBlock->new(3,251);			
	$agattrib->add_comment_row("", "ActivityID", "", "Application No.", "Land/Priority", "NRU/PriorityGroup", "Index of activity" );
	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if ($h->has_subelement("apply_for_land_from_redistribution") ) {
			for my $j ($h->subelement_fields("apply_for_land_from_redistribution")) {
				$numagattrib++;
				
				my $what = $j;
				
				
				
				my $applicationNo;
				if ($what =~ /^(\d+)/) {
					$applicationNo = $1;
				}
				if (!defined ($applicationNo) && $applicationNo < 0) {
					printf STDER "Error: Invalid application number  #apply_for_land_from_redistribution> for activity $i\n";
					exit 1; 
				}
				my $whatcode;
				if ($what =~ /land$/) {
					$whatcode = 0;
				}
				elsif ($what =~ /priority$/) {
					$whatcode = 1;
				}
				else {
					printf STDER "Error: Invalid type of column in  #apply_for_land_from_redistribution> for activity $i. Valid choices are _land, _priority\n";
					exit 1;
				}
				
				my $which = $h->subelement_field("apply_for_land_from_redistribution", $j); 			
				
				$agattrib->add_row("",$i, "", $applicationNo, $whatcode, $which, $h->lp_column);
			}
		}
	}

	$agattribtop->add_row("","","Number of entries:", $numagattrib);
	$agattrib->add_row("");	
	$agattrib->add_row("","","",  $self->delim("#END_LAND_DISTRIBUTION_COLS"));
	$self->add_block($agattribtop);		
	$self->add_block($agattrib);	
}

sub add_asset_applications {
	my ($self) = @_;
	my $lscpattribtop = MpmasInputBlock->new(3,260);			
	$lscpattribtop->add_row("30","SOLUTION VALUES TO SCRIPT PRE-INVESTMENT");
	$lscpattribtop->add_row("");
	my $numlscpattrib = 0;
	
	my $lscpattrib = MpmasInputBlock->new(3,261);			
	$lscpattrib->add_comment_row("","", "",  "Activity" , "Variable");

	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if (defined $h->attr("solution_to_script_pre_investment") ) {			
		    $lscpattrib->add_row("","","", $h->lp_column(), $h->attr("solution_to_script_pre_investment"));
		    $numlscpattrib++;
		}
	}
	$lscpattribtop->add_row("","","Number of entries:", $numlscpattrib);
	$lscpattrib->add_row("","","",  $self->delim("#END_SOL_SCRIPT_PREINV"));
	$lscpattrib->add_row("");	
	$self->add_block($lscpattribtop);		
	$self->add_block($lscpattrib);	
	
	
	
	
	
		my $lscpattribtop2 = MpmasInputBlock->new(3,262);			
	$lscpattribtop2->add_row("","UBOUNDS PRE-INVESTMENT");
	$lscpattribtop2->add_row("");
	my $numlscpattrib2 = 0;
	
	my $lscpattrib2 = MpmasInputBlock->new(3,263);			
	$lscpattrib2->add_comment_row("","", "",  "Activity" , "Ubound");

	for my $i ($self->sorted_activities) {
		my $h = $self->activity($i);
		if (defined $h->attr("ubound_pre_investment") ) {			
		    $lscpattrib2->add_row("","","", $h->lp_column(), $h->attr("ubound_pre_investment"));
		    $numlscpattrib2++;
		}
	}
	$lscpattribtop2->add_row("","","Number of entries:", $numlscpattrib2);
	$lscpattrib2->add_row("","","",  $self->delim("#END_UBOUND_PREINV"));
	$lscpattrib2->add_row("");	
	$self->add_block($lscpattribtop2);		
	$self->add_block($lscpattrib2);
	
	
}



1;
