package MpmasQl3EqnElements;
use strict;
use warnings;

sub shortError {
	my $in = $_[0];
	#my $pos = index $in, "\n at ";
	#$in = substr $in, 0, $pos;
	$in =~ s/^(.+)\n at .+\.pm line \d+.*/$1/;

	return $in;
}


package MpmasQl3EqnElements::context;
use Storable qw(dclone);
use Carp qw(croak);
sub new {
	my ($class, $hashref) = @_;
	croak if (ref $class);
	my $self = {};
	$self = $hashref if (defined $hashref and ref($hashref) eq 'HASH');
	bless $self,  $class;
	return $self;
};
sub add {
	#my ($self, $name, $value) = @_;
	croak if (not ref $_[0]);
	$_[0]->{$_[1]} = $_[2];
};
sub get {
	return $_[0]->{$_[1]};
};
sub exists {
	return exists $_[0]->{$_[1]};
};
sub copy {
	my $new = dclone($_[0]);
	bless $new, ref($_[0]);
};
sub append {
	my ($self, $other) = @_;
	my $merge = Hash::Merge->new('LEFT_PRECEDENT');
	my $temp  =   $merge->merge ( $other, $self );
	$self = $temp;
};

sub new_from_merge {
	my ($class, $self, $other) = @_;
	my $merge = Hash::Merge->new('LEFT_PRECEDENT');
	my $temp  =   $merge->merge (  $self, $other );
	bless $temp, $class;
	return $temp;
};

###INI file
package MpmasQl3EqnElements::iniTableDefinition;
use Carp qw(croak);
use MpmasQLTableDefinition4;
use Data::Dumper qw/Dumper/;

our $keys_of_INI_tables = {(
	SCENDEF => 2,
	SCENLIST => 1,
	RHSDEF => 2,
	RHSLIST => 1,
	RHS_SOILTAB => 2,
	COMMON => 1,
	PARAMETER_SCENDEF => 2,
	PARAMETER_SCENLIST => 1
	
)};
sub name {
	return $_[0]->[0];
}
sub _compatibility_make_inihash {
	my ($self, $database, $dbh) = @_;
	my $name = $_[0]->[0];
	if (exists $database->{INIHASH}->{$name} ) {
		print STDERR "Error: .ini table  $name has been defined twice\n";
		exit(1);
	}
	my $type = $_[0]->[1];
	my $numkeys = $keys_of_INI_tables->{$name};
	my $content;
	if ($type eq "set" or  $type eq "list") {
		my @temp = $_[0]->[2]->evaluate($database);
		$content = \@temp;
	}
	else {
		$content = $_[0]->[2]->evaluate($database);
	}	my $tableDef = MpmasQLTableDefinition4->new($type, $numkeys, $content, $name);

	$database->{INIHASH}->{$name} = $tableDef;
}
sub transform {
	my ($self, $database, $dbh) = @_;
	my $name = $_[0]->[0];
	if (exists $database->{INITABLES}->{$name} ) {
		print STDERR "Error: .ini table  $name has been defined twice\n";
		exit(1);
	}
	my $type = $_[0]->[1];
	my $numkeys = $keys_of_INI_tables->{$name};
	my $content;
	if ($type eq "set" or  $type eq "list") {
		my @temp = $_[0]->[2]->evaluate($database);
		$content = \@temp;
	}
	else {
		$content = $_[0]->[2]->evaluate($database);
	}
	
	my $tableDef = MpmasQLTableDefinition4->new($type, $numkeys, $content, $name);
	my $table;
	eval  {
		  $table = $tableDef->transform($dbh);
	};
    croak "Error: when creating table ".$self->name.":\n".MpmasQl3EqnElements::shortError($@)."\n"  if $@;
    $database->{INITABLES}->{$name} = $table;
    return $table;
}
package MpmasQl3EqnElements::iniEntryDefinition;
use Carp qw(croak carp );
sub transform {
	my ($self, $database) = @_;
	my $name = $_[0]->[0];

	if (exists($database->{INIHASH}->{$name}) ) {
		carp ("Warning: .ini-file parameter $name redefined\n"); 

	}
	if (exists $keys_of_INI_tables->{$name}) {
			croak ("Warning: .ini-file parameter $name should be table\n"); 
	}
	$database->{INIHASH}->{$name} = $_[0]->[1]->evaluate($database);
}
package MpmasQl3EqnElements::iniSetDefinition;
use Carp qw(croak carp);
sub transform {
	my ($self, $database) = @_;
	my $name = $_[0]->[0];

	if (exists($database->{INIHASH}->{$name}) ) {
		carp("Warning: .ini-file parameter $name redefined\n"); 

	}
	my @array = $_[0]->[1]->evaluate($database);
	$database->{INIHASH}->{$name} = \@array;
}

package MpmasQl3EqnElements::iniParameter;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database) = @_;
	my $name = $_[0]->[0];
	my $parameter;
	if (exists($_[1]->{INIHASH}->{$name}) ) {
		$parameter = $_[1]->{INIHASH}->{$name};
	}
	else  {
		croak ("Error: $name not defined\n"); 
	}
	 
	return $parameter;	
}



###MPM, PRM, DAT files
#info content and sectioning
package MpmasQl3EqnElements::sectionHeading;
use Carp qw(croak);
sub name {
	return $_[0]->[0];
}
sub transform {
}
sub to_latex_string {
	return "\\". $_[0]->[0] ."{". $_[0]->[1]."}";
}
package MpmasQl3EqnElements::infoContent;
use Carp qw(croak);
sub name {
	return $_[0]->[0];
}
sub transform {
}	
sub to_latex_string {
	return $_[0]->[1];
}


package MpmasQl3EqnElements::equationOrderDefinition;
use Carp qw(croak);
sub transform {
	my ($self, $baseconfigs) = @_;
	
	my $ctstring = join(",", @$self);
	if (exists $baseconfigs->{CONTYPEORDER} ) {
		$baseconfigs->{CONTYPEORDER} =~ s/###/$ctstring/;
	}
	else {
		  $baseconfigs->{CONTYPEORDER} = $ctstring;
	}
	$baseconfigs->{CONTYPEORDER} = [ split (/\s*,\s*/,  $baseconfigs->{CONTYPEORDER})];
}	

package MpmasQl3EqnElements::decisionVariableOrderDefinition;
use Carp qw(croak);
sub transform {
	my ($self, $baseconfigs) = @_;
	my $atstring = join(",", @$self);
	if (exists $baseconfigs->{ACTTYPEORDER} ) {
		$baseconfigs->{ACTTYPEORDER} =~ s/###/$atstring/;
	}
	else {
		  $baseconfigs->{ACTTYPEORDER} = $atstring;
	}
	$baseconfigs->{ACTTYPEORDER} = [ split (/\s*,\s*/,  $baseconfigs->{ACTTYPEORDER})];
}	
sub to_latex_string {
	return "";
}

#CFG file entries
package MpmasQl3EqnElements::configEntryDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database, $scenario) = @_;
	my $name = $_[0]->[0];

	#if (exists $database->{CONFIG}->{$name} ) {
		#print STDERR "Error: configuration entry $name has been defined twice\n";
		#exit(1);
#	}
	
	my $value ;
	if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
	{
		eval {
			$value = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database);
		};
	}
	else {
		eval {
			$value = $_[0]->[1]->evaluate($database);
		};
	}	
	if ($@) {
			print STDERR "Error in configuration entry: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
	}
	$database->{CONFIG}->{$name} = $value;
}
sub to_latex_string {
	return "";
}
package MpmasQl3EqnElements::configListDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database, $scenario) = @_;
	my $name = $_[0]->[0];

#		if (exists $database->{CONFIG}->{$name} ) {
#			print STDERR "Error: configuration entry $name has been defined twice\n";
#			exit(1);
#		}
		
		my $context = MpmasQl3EqnElements::context->new(); #needed for all ... where, to ensure context is not empty
		my @set;
		if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
		{
			eval {
				@set = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database, $context);
			};
		}
		else {
			eval {
				@set = $_[0]->[1]->evaluate($database, $context);
			};		
		}
		if ($@) {
			print STDERR "Error in configuration entry: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}
		
		if (ref($_[0]->[1]) ne "MpmasQl3EqnElements::expressionList") { #preserve ordering for simple manually specified lists, lists that result from functions, set operations loose ordering, so then they are sorted alphabetically
			@set = sort {  $a =~ /[a-z_A-Z]/ ?  ($a cmp $b)  : ($a <=> $b) } @set;
		}
		$database->{CONFIG}->{$name} = \@set;
}
sub to_latex_string {
	return "";
}
##Simulation run parameters
package MpmasQl3EqnElements::simrunParameterDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database, $scenario) = @_;
	my $name = $self->[0];
	#print "SP: $name\n";
	if (exists $database->{SIMRUN_PARAMETERS}->{$name} ) {
		print STDERR "Error: SimulationRun parameter $name has been defined twice\n";
		exit(1);
	}
	
	my $value ;
	eval {
		$value = $self->[1]->evaluate($database);
	};
	
	if ($@) {
			print STDERR "Error in configuration entry: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
	}
	$database->{SIMRUN_PARAMETERS}->{$name}->{default_value} = $value;
	$database->{SIMRUN_PARAMETERS}->{$name}->{id} = $database->{simrun_parameter_count}++;
}
sub to_latex_string {
	return "";
}


##Simulation run parameters
package MpmasQl3EqnElements::simrunParameterDefinitionIndexed;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database, $scenario) = @_;
	my $stub = $self->[0];
	#print "SPI: $stub\n";
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($database); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in SimulationRun parameter definition $stub: iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}

	if (scalar(@$iteratorOrder) > 0) {
			for my $context (@$contextList) {
						
							my @keyArray;
							for my $it (@$iteratorOrder) {
									print STDERR "Error in SimulationRun parameter definition $stub (iterations): $it not defined in context\n" and exit(1) unless (defined $context->get($it));			
									push @keyArray, $context->get($it);
							}
							my $name = $stub.join("_", @keyArray);
							my $value;
							eval {
								$value = $_[0]->[2]->evaluate($database, $context);#anyExpression
							};
							if ($@) {
								print STDERR "Error in SimulationRun parameter definition $name (value expression):\n".MpmasQl3EqnElements::shortError($@)."\n";
								exit(1);
							}

							if (exists $database->{SIMRUN_PARAMETERS}->{$name} ) {
								print STDERR "Error: SimulationRun parameter $name has been defined twice\n";
								exit(1);
							}
							$database->{SIMRUN_PARAMETERS}->{$name}->{default_value} = $value;
							$database->{SIMRUN_PARAMETERS}->{$name}->{id} = $database->{simrun_parameter_count}++;
		}
	}
	else {	
		my $name = $stub;
		my $value ;
		eval {
			$value = $_[0]->[2]->evaluate($database);
		};
		if ($@) {
				print STDERR "Error in SimulationRun definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
		}
		if (exists $database->{SIMRUN_PARAMETERS}->{$name} ) {
									print STDERR "Error: SimulationRun parameter $name has been defined twice\n";
									exit(1);
		}
		$database->{SIMRUN_PARAMETERS}->{$name}->{default_value} = $value;
		$database->{SIMRUN_PARAMETERS}->{$name}->{id} = $database->{simrun_parameter_count}++;
	}
}
sub to_latex_string {
	return "";
}




##GLOBALS
package MpmasQl3EqnElements::globalDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database, $scenario) = @_;
	my $name = $_[0]->[0];

	if (exists $database->{GLOBALS}->{$name} ) {
		print STDERR "Error: configuration entry $name has been defined twice\n";
		exit(1);
	}
	
	my $value ;
	
	if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
	{
		eval {
			$value = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database);
		};
	}
	else {
		eval {
			$value = $_[0]->[1]->evaluate($database);
		};
	}
	if ($@) {
			print STDERR "Error in configuration entry: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
	}
	$database->{GLOBALS}->{$name} = $value;
}
sub to_latex_string {
	return "";
}
package MpmasQl3EqnElements::globalSetDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[1] = $new_content;
}
sub transform {
	my ($self, $database,  $scenario, $dbh) = @_;
	my $name = $_[0]->[0];

		if (exists $database->{GLOBAL_SETS}->{$name} ) {
			print STDERR "Error: configuration entry $name has been defined twice\n";
			exit(1);
		}
		
		my $context = MpmasQl3EqnElements::context->new(); #needed for all ... where, to ensure context is not empty
		my @set;

		
		if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
		{
			eval {
				@set = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database, $context);
			};
		}
		else {
			eval {
				@set = $_[0]->[1]->evaluate($database, $context);
			};		
		}

		
		if ($@) {
			print STDERR "Error in configuration entry: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}
		$database->{GLOBAL_SETS}->{$name} = \@set;
}
sub to_latex_string {
	return "";
}
##Table definitions
package MpmasQl3EqnElements::tableDefinition;
use Carp qw(croak);
use MpmasQLTableDefinition4;
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub type {
	return $_[0]->[1];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[3] = $new_content;
}
sub transform {
	my ($self, $database,  $scenario, $dbh, $notInPlace) = @_;
	my $name = $_[0]->[0];
	if (defined $database->{TABLES}->get_object_by_id($name) ) {
		print STDERR "Error: table definition $name has been defined twice\n";
		exit(1);
	}
	my $type = $_[0]->[1];
	my $numkeys = $_[0]->[2];
	my $content;

	if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
	{
		eval {
			$content = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database);
		};
	    	print STDERR "Error: when creating table ".$self->name.":\n".MpmasQl3EqnElements::shortError($@)."\n" and exit(1) if $@;
	}
	else {
		eval {
			$content = $_[0]->[3]->evaluate($database);
		};		
	    	print STDERR "Error: when creating table ".$self->name.":\n".MpmasQl3EqnElements::shortError($@)."\n"  and exit(1) if $@;
	}
		
	my $tableDef = MpmasQLTableDefinition4->new($type, $numkeys, $content, $name);
	my $table;
	eval  {
		  $table = $tableDef->transform($dbh, undef,$database->{TABLES} );
	};
    	print STDERR "Error: when creating table ".$self->name.":\n".MpmasQl3EqnElements::shortError($@)."\n"  and exit(1) if $@;
   	if ($notInPlace) {
		return $table;
	}
	else {
		$database->{TABLES}->add_objects($table);
	}
}
sub to_latex_string {
	return "";
}

package MpmasQl3EqnElements::mpmasTableCollection;
#only for marking this as a table collection rather than a table
use Carp qw(croak);
sub list_of_subtable_names {
    return keys %{$_[0]};
} 
sub get_subtable {
    return $_[0]->{$_[1]};
} 
sub transform {
    return $_[0];
}
sub printme_with_header_sorted {
    print "Table collection: \n";
    for my $i ( keys %{$_[0]}) {
        $_[0]->{$i}->printme_with_header_sorted();
        print "\n";
    }
}


package MpmasQl3EqnElements::mpmasTableDefinitionIterated;
use Carp qw(croak);
use MpmasQLTableDefinition4;
use MpmasQLRegistry;
use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub type {
	return $_[0]->[2];
}

sub transform {
	my ($self, $database,  $scenario, $dbh, $notInPlace) = @_;
	my $stub = $_[0]->[0];
	
	my $type = $_[0]->[2];
	my $numkeys = $MpmasQLRegistry::mpmas_tables{$stub};
	
	
	
	#keys %MpmasQLRegistry::mpmas_tables
	
	
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($database); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in MPMAS Table $stub: iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}

	if (scalar(@$iteratorOrder) > 0) {
		for my $context (@$contextList) {
					
			my @keyArray;
			for my $it (@$iteratorOrder) {
					print STDERR "Error in MPMAS Table $stub (iterations): $it not defined in context\n" and exit(1) unless (defined $context->get($it));			
					push @keyArray, $context->get($it);
			}
			my $name = $stub."_".join("_", @keyArray);
			#print "Transforming $name ($type, $numkeys): ",Dumper($_[0]->[3]),"\n";
			my $content;
			eval {
				$content = $_[0]->[3]->evaluate($database, $context);#anyExpression
			};
			if ($@) {
				print STDERR "Error in MPMAS Table $name (value expression):\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
			}

            if (exists $database->{MPMAS_TABLES}->{$stub}->{$name} ) {
                print STDERR "Error: mpmas table $name has been defined twice\n";
                exit(1);
            }
            
            my $tableDef = MpmasQLTableDefinition4->new($type, $numkeys, $content, $name);
            my $table;
            eval  {
                  $table = $tableDef->transform($dbh, undef ,$database->{TABLES});
            };	
            croak "Error: when creating table ".$self->name.":\n$@"  if $@;
            croak "Error: undefined table ".$self->name.":\n$@"  unless (defined $table);
            #print "MERGE2: \n"; $table->printme_with_header_sorted();
            if ($notInPlace) {
                return $table;
                bless $table, "MpmasQl3EqnElements::mpmasTableCollection";
            }
            else {
                $database->{MPMAS_TABLES}->{$stub}->{$name} = $table;
                bless $database->{MPMAS_TABLES}->{$stub}, "MpmasQl3EqnElements::mpmasTableCollection";
            }
		}
	}
	else {
	
		print STDERR "Error in MPMAS Table $stub: recognized as iterated MPMAS table, but no iterators found.\n".MpmasQl3EqnElements::shortError($@)."\n";
    	exit(1);
	
	}


}

package MpmasQl3EqnElements::mpmasTableDefinition;
use Carp qw(croak);
use MpmasQLTableDefinition4;
use MpmasQLRegistry;
#use Data::Dumper qw/Dumper/;


sub name {
	return $_[0]->[0];
}
sub type {
	return $_[0]->[1];
}
sub set_content {
	my ($self, $new_content) = @_;
	$_[0]->[2] = $new_content;
}
sub transform {
	my ($self, $database,  $scenario, $dbh, $notInPlace) = @_;
	my $name = $_[0]->[0];
	if (exists $database->{MPMAS_TABLES}->{$name} ) {
		print STDERR "Error: mpmas table definition $name has been defined twice\n";
		exit(1);
	}
	
	my $type = $_[0]->[1];
	my $numkeys = $MpmasQLRegistry::mpmas_tables{$name};
	
	#print "Transforming $name ($type, $numkeys): ",Dumper($_[0]->[2]),"\n";

	
	my $content;
	if (exists $database->{CHANGED}->{$self->name()}->{$scenario}) 
	{
		eval {
			$content = $database->{CHANGED}->{$self->name()}->{$scenario}->evaluate($database);
		};
	#	print "Transforming from scenario $name ($type, $numkeys): ",$content,"\n";

	}
	else {
		eval {
			$content = $_[0]->[2]->evaluate($database);
		};		
	#		print "Transforming from file $name ($type, $numkeys): ",$content,"\n";

	}
	if ($@) {
		print STDERR "Error in MPMAS table definition $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}
	my $tableDef = MpmasQLTableDefinition4->new($type, $numkeys, $content, $name);
	my $table;
	eval  {
		  $table = $tableDef->transform($dbh, undef ,$database->{TABLES});
	};	
    croak "Error: when creating table ".$self->name.":\n$@"  if $@;
    croak "Error: undefined table ".$self->name.":\n$@"  unless (defined $table);
    #print "MERGE2: \n"; $table->printme_with_header_sorted();
    if ($notInPlace) {
		return $table;
	}
	else {
		$database->{MPMAS_TABLES}->{$name} = $table;
	}
}
sub to_latex_string {
	return "";
}
##Set definitions
package MpmasQl3EqnElements::setDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub transform {
	#my ($self, $database) = @_;
	my $name = $_[0]->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in set definition: $name, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}
	
	if (scalar(@$iteratorOrder) > 0) {
		unless (exists $_[1]->{SETS}->{$name} ) {
				#create new indexed set
			#print "\n\nCreating: $name\n";
			my $hash = {};
			
			#create indexed set
			for my $context (@$contextList) {
			
				my @keyArray;
				for my $it (@$iteratorOrder) {
						croak ("Error: $it not defined in context\n") unless (defined $context->get($it));			
						push @keyArray, $context->get($it);
				}
				
				my @set;
				eval {
					@set = $_[0]->[2]->evaluate($_[1], $context);#setDefinition (Future: may add inserters to subelements)
				};
					
				if ($@) {
					print STDERR "Error in set definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
					exit(1);
				}
				#print ">> @keyArray : @set\n";
				#transform array into hash
				my %newhash;
				if (@set && defined($set[0]) ) {
					%newhash = map { $_ => defined($_) ? [1]  : croak("Error: Undefined value in set: $name:\n") } @set;
				}
				#print Dumper(\%newhash);;
			
				#iteratively dive into hash until reference points to innermost elements
				my $rt = $hash;	
				my $lastkey = pop @keyArray;		
				for my $key (@keyArray) {
					if (not exists($rt->{$key})) {
						$rt->{$key} = {};
					}
					$rt = $rt->{$key};
				}
				#insert hash at deepest position  reached adding another depth to the hash
				$rt->{$lastkey} = \%newhash;
			}
			#print Dumper($hash);;
			$_[1]->{SETS}->{$name} = IndexedTable->create_from_hash($hash, scalar(@$iteratorOrder) + 1, $name, [(@$iteratorOrder, $name, "dummy")]);
		}
		else { #set exists
			
			unless (ref ($_[1]->{SETS}->{$name}) eq 'IndexedTable' or ref ($_[1]->{SETS}->{$name}) eq 'IndexedTableLink') {
					print STDERR "Error: Indexed set $name redefined with different index size\n";
					exit(1);
			}
			unless ($_[1]->{SETS}->{$name}->numkeys() == scalar(@$iteratorOrder) + 1) {
					print STDERR "Error: Indexed set $name redefined with different index size\n";
					exit(1);
			}
			#add to existing table
			my @RowsToAdd;
			for my $context (@$contextList) {
				my @newrow;
				
				for my $it (@$iteratorOrder) {
						croak ("Error: $it not defined in context\n") unless (defined $context->get($it));			
						push @newrow, $context->get($it);
				}
				my @set;
				eval {
					@set = $_[0]->[2]->evaluate($_[1], $context);#anyExpression
				};
				if ($@) {
					print STDERR "Error in set definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
					exit(1);
				}
				foreach my $x (@set) {
					my @fullrow =  (@newrow, $x);
					push @RowsToAdd, \@fullrow;
				}
			}	
			$_[1]->{SETS}->{$name}->add_rows(\@RowsToAdd);
		}
	}
	else {
		if (exists $_[1]->{Sets}->{$name} ) {
			print STDERR "Error: Set $name has been defined twice\n";
			exit(1);
		}
		
		my $context = MpmasQl3EqnElements::context->new(); #needed for all ... where, to ensure context is not empty
		my @set;
		eval {
			@set = $_[0]->[2]->evaluate($_[1], $context);
		};
		if ($@) {
			print STDERR "Error in set definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}
		$_[1]->{SETS}->{$name} = \@set;
	}
}
sub to_latex_string {
	return "";
}
##Parameter definitions
package MpmasQl3EqnElements::parameterDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub name {
	return $_[0]->[0];
}
sub transform {
	#my ($self, $database) = @_;
	my $name = $_[0]->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in parameter definition $name: iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}
	if (scalar(@$iteratorOrder) > 0) {
		unless (exists $_[1]->{PARAMETERS}->{$name} ) {
			#create new indexed parameter
			

			my $hash = {};
			#print "\n\nCreating: $name\n";
			#create indexed parameter
			for my $context (@$contextList) {
			
				my @keyArray;
				for my $it (@$iteratorOrder) {
						print STDERR "Error in parameter definition $name (iterations): $it not defined in context\n" and exit(1) unless (defined $context->get($it));			
						push @keyArray, $context->get($it);
				}
			
				my $value;
				eval {
					$value = $_[0]->[2]->evaluate($_[1], $context);#anyExpression
				};
				if ($@) {
					print STDERR "Error in parameter definition $name (value expression):\n".MpmasQl3EqnElements::shortError($@)."\n";
					exit(1);
				}
				
				#print ">> @keyArray : $value\n";
				my $rt = $hash;
				my $lastkey = pop @keyArray;
				for my $key (@keyArray) {
					if (not exists($rt->{$key})) {
						$rt->{$key} = {};
					}
					$rt = $rt->{$key};
				}
				$rt->{$lastkey} = [$value];
			}
			#print Dumper($hash);;
			$_[1]->{PARAMETERS}->{$name} = IndexedTable->create_from_hash($hash, scalar(@$iteratorOrder), $name, [(@$iteratorOrder, $name)]);
		}
		else {
			unless (ref ($_[1]->{PARAMETERS}->{$name}) eq 'IndexedTable' or ref ($_[1]->{PARAMETERS}->{$name}) eq 'IndexedTableLink') {
					print STDERR "Error: Indexed parameter $name redefined with different index size\n";
					exit(1);
			}
			unless ($_[1]->{PARAMETERS}->{$name}->numkeys() == scalar(@$iteratorOrder)) {
					print STDERR "Error: Indexed parameter $name redefined with different index size\n";
					exit(1);
			}
			#add to existing table
			my @RowsToAdd;
			for my $context (@$contextList) {
				my @newrow;
				
				for my $it (@$iteratorOrder) {
						croak ("Error: $it not defined in context\n") unless (defined $context->get($it));			
						push @newrow, $context->get($it);
				}
				my $value;
				eval {
					$value = $_[0]->[2]->evaluate($_[1], $context);#anyExpression
				};
				if ($@) {
					print STDERR "Error in parameter definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
					exit(1);
				}
				push @newrow, $value;
				push @RowsToAdd, \@newrow;
			}	
			$_[1]->{PARAMETERS}->{$name}->add_rows(\@RowsToAdd);
		}
	}
	else {
		if (exists $_[1]->{PARAMETERS}->{$name} ) {
			print STDERR "Error: Parameter $name has been defined twice\n";
			exit(1);
		}
		
		
		my $value ;
		eval {
			$value = $_[0]->[2]->evaluate($_[1]);
		};
		if ($@) {
				print STDERR "Error in parameter definition: $name:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
		}
		$_[1]->{PARAMETERS}->{$name} = $value;
	}
}
sub to_latex_string {
	return "";
}
##Asset definitions
package MpmasQl3EqnElements::assetDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;

sub transform {
	#my ($self, $database, $milplist) = @_;
		

	my $nameStub = $_[0]->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in asset definition: $nameStub, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}
	#print "Iterator order:" .Dumper($iteratorOrder)."\n";
	#print "Context list:" . Dumper($contextList). "\n";
	
	#MpmasNetworkObject->switch_off_defaults();
	
	my @assetList;
	
	for my $context (@$contextList) {
		my $actName = $nameStub;
		for my $it (@$iteratorOrder) {
				(print STDERR "Error in asset definition $actName: $it not defined in context\n" and exit(1)) unless (defined $context->get($it));
				
				$actName .= "_". $context->get($it);
		}
		my $subelements = {};
		my $fields = {};
		eval {
			$fields = $_[0]->[2]->evaluate($_[1], $context, $subelements);#attribute definition list (Future: may add inserters to subelements)
		};
		if ($@) {
				print STDERR "Error in asset definition: $actName:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
		}
		

		my $newAsset = MpmasNetworkObject->create($actName, $fields, $subelements);
		#$newAsset->set_standard_milp($_[2]->[0]);
		push @assetList, $newAsset;
		
	}
	return @assetList,;
}
sub to_latex_string {
	return "";
}
###Decision variable definition
package MpmasQl3EqnElements::decisionVariableDefinition;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;

sub transform_basics {
    my $nameStub = $_[0]->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in decision variable definition: $nameStub, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	
	#print "Iterator order:" .Dumper($iteratorOrder)."\n";
	#print "Context list:" . Dumper($contextList). "\n";
	
	#MpmasMILPActivity->switch_off_defaults();
	
	my @activityList;
	my @readerList;
	my @inserterList;
	
	for my $context (@$contextList) {
		my $actName = $nameStub;
		for my $it (@$iteratorOrder) {
				(print STDERR "Error in decision variable $actName: $it not defined in context\n" and exit(1)) unless (defined $context->get($it));
			
				$actName .= "_". $context->get($it);
		}
		my $fields = {};
		my $subelements = { };
		my $actType;
		eval {
			$actType = $_[0]->[2]->evaluate_specific($_[1], $context, $subelements, "type"); 
		};
		if ($@) {
			print STDERR "Error in decision variable definition: $actName, type attribute:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}	
		my $actMperiod;
		eval {
			$actMperiod = $_[0]->[2]->evaluate_specific($_[1], $context, $subelements, "mperiod"); 
		};
		if ($@) {
			print STDERR "Error in decision variable definition: $actName, mperiod attribute:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}
		
		if (not defined $actMperiod) {
			my $standardIterator = $_[1]->{CONVERSION}->{mperiodIterator};
			my $temp = $context->get($standardIterator);
			#(print STDERR "Error for decision variable $actName: Standard mperiod indicator '$standardIterator' not set in context and attribute mperiod not explicitly given.\n" ) unless (defined $mperiod);

			$actMperiod = $temp if (defined $temp);
		}
		if (defined $actMperiod) {
		    $fields->{mperiod} = $actMperiod; 
		}
		if (defined $actType) {
		    $fields->{type} = $actType; 
		}
		my $newAct = MpmasMILPActivity->create($actName, $fields); #, $subelements);
		#$newAct->set_standard_milp($_[2]->[0]);
		push @activityList, $newAct;
		
	}
	return (\@activityList);

}
sub transform_specific {
	#my ($self, $database, $context, $attr, $actName) = @_;
    my $context = $_[2];
	my @readerList;
	my @inserterList;
	
	my $value;
	my $subelements = { };

	eval {
		$value = $_[0]->[2]->evaluate_specific($_[1], $context, $subelements, $_[3]); 
	};
	if ($@) {
		print STDERR "Error in decision variable definition: ",$_[4]," attr ",$_[3],":\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	

	return $value;
}


sub transform {
	#my ($self, $database, $milplist) = @_;
		
	my $nameStub = $_[0]->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in decision variable definition: $nameStub, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	
	#print "Iterator order:" .Dumper($iteratorOrder)."\n";
	#print "Context list:" . Dumper($contextList). "\n";
	
	#MpmasMILPActivity->switch_off_defaults();
	
	my @activityList;
	my @readerList;
	my @inserterList;
	
	for my $context (@$contextList) {
		my $actName = $nameStub;
		for my $it (@$iteratorOrder) {
				(print STDERR "Error in decision variable $actName: $it not defined in context\n" and exit(1)) unless (defined $context->get($it));
			
				$actName .= "_". $context->get($it);
		}
		my $subelements = { };
		my $fields = {};
		eval {
			$fields = $_[0]->[2]->evaluate($_[1], $context, $subelements);#attribute definition list, may add inserters to subelements
		};
		if ($@) {
			print STDERR "Error in decision variable definition: $actName, attributes:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}	
		eval {
			$_[0]->[3]->evaluate($_[1], $context, $subelements);#reader definition list, may add readers to subelements
		};
		if ($@) {
			print STDERR "Error in decision variable definition: $actName, mpmas solution readers:\n".MpmasQl3EqnElements::shortError($@)."\n";
			exit(1);
		}
		if (not exists $fields->{mperiod}) {
			my $standardIterator = $_[1]->{CONVERSION}->{mperiodIterator};
			my $mperiod = $context->get($standardIterator);
			#(print STDERR "Error for decision variable $actName: Standard mperiod indicator '$standardIterator' not set in context and attribute mperiod not explicitly given.\n" ) unless (defined $mperiod);
			$fields->{mperiod} = $mperiod if (defined $mperiod);
		}
		
		my $newAct = MpmasMILPActivity->create($actName, $fields); #, $subelements);
		#$newAct->set_standard_milp($_[2]->[0]);
		push @activityList, $newAct;
		
#		for my $iFunc ( keys %{$subelements->{"I"} } ) {
#			for my $iInfo ( @{$subelements->{"I"}->{$iFunc} } ) {
#				$iInfo->{ao} = $newAct;
#				push @{ $inserterList{$iFunc} }, $iInfo;
#			}
#		}

		for my $iFunc ( 0 .. $#{$subelements->{"I"} } ) {
		    next unless (defined $subelements->{"I"}->[$iFunc] );
			for my $iInfo ( @{$subelements->{"I"}->[$iFunc] } ) {
				$iInfo->{ao} = $newAct;
				push @{ $inserterList[$iFunc] }, $iInfo;
			}
		}


		
		for my $rFunc ( 0..$#{$subelements->{"R"} } ) {
		    next unless (defined $subelements->{"R"}->[$rFunc] );
			for my $rInfo ( @{$subelements->{"R"}->[$rFunc] } ) {
				$rInfo->{ao} = $newAct;
				push @{ $readerList[$rFunc] }, $rInfo;
			}
		}
		
		
	}
	return (\@activityList, \@readerList, \@inserterList);
}
sub to_latex_string {
	return "";
}


##Constraint definitions

package MpmasQl3EqnElements::equationDefinition;
use Carp qw(croak);
use Data::Dumper qw/Dumper/;

sub transform {
	my ($self, $database, $ActivityRegistry, $milplist, $parallelInst, $parallelInstThreshold) = @_;
		

	my $nameStub = $self->[0];
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $self->[1]->evaluate($database); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in equation definition: $nameStub, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	

	#MpmasMILPConstraint->switch_off_defaults();
	
	my @constraintList;
	my @inserterList;
	
	if ($parallelInst and scalar ( @$contextList  ) > $parallelInstThreshold) {
			my $iter = Parallel::Iterator::iterate( 
				{  adaptive => 1, 
					workers => $parallelInst}
					,
				sub { 
						my ( $id, $job ) = @_;
						my $context = $job;
						my $conName = $nameStub;
						for my $it (@$iteratorOrder) {
								(print STDERR "Error in equation $conName: $it not defined in context\n" and exit(1) ) unless (defined $context->get($it));
								$conName .= "_". $context->get($it);
						}
						my $subelements = {};
						my $fields = {};
						eval {
							$fields = $self->[2]->evaluate($database, $context, $subelements);#attribute definition list ()should not add anything to subelements as inserters are not allowed in constraint attributes
						};
						if ($@) {
							print STDERR "Error in equation definition: $conName, attributes:\n".MpmasQl3EqnElements::shortError($@)."\n";
							exit(1);
						}	
						unless ( MpmasMILPConstraint->lazy_constraint() ) {
						    eval {
							    $self->[3]->evaluate($database, $context, $ActivityRegistry, $conName, $fields, $subelements); #LHS, note: , MpmasMILPActivity->has_coeffWithConstraints() determines whether coefficients are recorded with each activity, or constraint (fields/subelements passed by reference here!!!)
						    };
						    if ($@) {
							    print STDERR "Error in equation definition: $conName, LHS:\n".MpmasQl3EqnElements::shortError($@)."\n";
							    exit(1);
						    }
						}
						eval {
						    my $temp = $self->[4]->evaluate($database, $context); #relation sign
						    if (defined $temp && $temp != 1) {
							    $fields->{eqtype} = $temp; #1 is the default value and not recorded to save memory
							}
						};
						if ($@) {
							print STDERR "Error in equation definition: $conName, equation sign:\n".MpmasQl3EqnElements::shortError($@)."\n";
							exit(1);
						}
						eval {
							my $temp =  $self->[5]->evaluate($database, $context, $subelements); #RHS, may add mpmas insert value to subelements
							if (defined $temp && $temp != 0) {
    							$fields->{value} = $temp; #0 is the default value and not recorded to save memory
    						}
						};
						if ($@) {
							print STDERR "Error in equation definition: $conName, RHS:\n".MpmasQl3EqnElements::shortError($@)."\n";
							exit(1);
						}
						
						if (not exists $fields->{mperiod}) {
							my $standardIterator = $database->{CONVERSION}->{mperiodIterator};
							my $mperiod = $context->get($standardIterator);
							#(print STDERR "Error for constraint $conName: Standard mperiod indicator '$standardIterator' not set in context and attribute mperiod not explicitly given.\n" and exit(1) ) unless (defined $mperiod);
			              $fields->{mperiod} = $mperiod if (defined $mperiod);
						}
								
						my $newCon;
						if (MpmasMILPCOnstraint->lazy_constraint() ) {
						
		    				$newCon = MpmasMILPConstraint->create($conName, $fields, $subelements, undef, undef, $milplist->[0], $self,);
						
						}
						else { 
						    $newCon = MpmasMILPConstraint->create($conName, $fields, $subelements, undef, undef, $milplist->[0]);
					    }
						
						#$newCon->set_standard_milp($milplist->[0]);
	
						return [ $newCon ];
				}
				,	$contextList  
			);
		
			while ( my ( $index, $value ) = $iter->() ) {
				push @constraintList, @{$value};
			}
	}
	else {
		for my $context (@$contextList) {
			my $conName = $nameStub;
			for my $it (@$iteratorOrder) {
					(print STDERR "Error in equation $conName: $it not defined in context\n" and exit(1) ) unless (defined $context->get($it));
					$conName .= "_". $context->get($it);
			}
			my $subelements = {};
			my $fields = {};
			eval {
				$fields = $_[0]->[2]->evaluate($database, $context, $subelements);#attribute definition list ()should not add anything to subelements as inserters are not allowed in constraint attributes
			};
			if ($@) {
				print STDERR "Error in equation definition: $conName, attributes:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
			}	
			unless ( MpmasMILPConstraint->lazy_constraint() ) {
			    eval {
				    $_[0]->[3]->evaluate($database, $context, $ActivityRegistry, $conName, $fields, $subelements); #LHS, note: , MpmasMILPActivity->has_coeffWithConstraints() determines whether coefficients are recorded with each activity, or constraint (fields/subelements passed by reference here!!!)
			    };
			    if ($@) {
				    print STDERR "Error in equation definition: $conName, LHS:\n".MpmasQl3EqnElements::shortError($@)."\n";
				    exit(1);
			    }
			}
			eval {
				$fields->{eqtype} = $_[0]->[4]->evaluate($database, $context); #relation sign
			};
			if ($@) {
				print STDERR "Error in equation definition: $conName, equation sign:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
			}
			eval {
				$fields->{value} = $_[0]->[5]->evaluate($database, $context, $subelements, $fields->{othermilp} ); #RHS, may add mpmas insert value to subelements
			};
			if ($@) {
				print STDERR "Error in equation definition: $conName, RHS:\n".MpmasQl3EqnElements::shortError($@)."\n";
				exit(1);
			}
			
			if (not exists $fields->{mperiod}) {
				my $standardIterator = $database->{CONVERSION}->{mperiodIterator};
				my $mperiod = $context->get($standardIterator);
				#(print STDERR "Error for constraint $conName: Standard mperiod indicator '$standardIterator' not set in context and attribute mperiod not explicitly given.\n" and exit(1) ) unless (defined $mperiod);
				$fields->{mperiod} = $mperiod if (defined $mperiod);
			}
			
			
		
			my $newCon;
			if (MpmasMILPConstraint->lazy_constraint() ) {
				$newCon = MpmasMILPConstraint->create($conName, $fields, {}, undef, undef, $milplist->[0], \$self, $context);
			}
			else { 
			    $newCon = MpmasMILPConstraint->create($conName, $fields, {}, undef, undef, $milplist->[0]);
		    }
			
			
			#$newCon->set_standard_milp($milplist->[0]);
			push @constraintList, $newCon ;
			
	#		print "eqDefEnd subelements: ",$newCon->id(),":\n", Dumper($subelements);
			for my $iFunc ( 0..$#{$subelements->{"I"} } ) {
			    next unless (defined $subelements->{"I"}->[$iFunc] );
				for my $iInfo ( @{$subelements->{"I"}->[$iFunc] } ) {
					$iInfo->{co} = $newCon;
					push @{ $inserterList[$iFunc] }, $iInfo;
				}
			}
		#	print "eqDefEnd: ",$newCon->id(),":\n", Dumper(\%inserterList);

		}
	}
	#print "eqDefEnd: $nameStub:\n", Dumper(\%inserterList);

	return (\@constraintList,\@inserterList);	
}
sub transform_coefficients {
    #my ($self, $database, $ActivityRegistry, $context, $conName, $field_ref, $constraintOb) = @_;


    my $subelements = {};
 
    eval {
	     $_[0]->[3]->evaluate($_[1], $_[3], $_[2], $_[4], $_[5], $subelements); #LHS, note: , MpmasMILPActivity->has_coeffWithConstraints() determines whether coefficients are recorded with each activity, or constraint (fields/subelements passed by reference here!!!)
    };
    if ($@) {
	    print STDERR "Error in equation definition:  $_[4], LHS:\n".MpmasQl3EqnElements::shortError($@)."\n";
	   #print STDERR "Error in equation definition:  $_[4], LHS:\n".$@."\n";
	    exit(1);
    }
    #print "Subs:\n", Dumper($subelements);
   	my @inserterList;
	for my $iFunc (0.. $#{$subelements->{"I"} } ) {
		for my $iInfo ( @{$subelements->{"I"}->[$iFunc] } ) {
			$iInfo->{row} =  $_[6]->lp_row();
			push @{ $inserterList[$iFunc] }, $iInfo;
		}
	}
    return \@inserterList;
    #return $subelements->{"I"};
}


sub to_latex_string {
	my ($self) = @_;
		
	

	my $nameStub = $_[0]->[0];
	
	my $forallString = $_[0]->[1]->to_latex_string();
	
	if ($@) {
		print STDERR "Error in equation definition: $nameStub, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	

	
	my $lhs = $_[0]->[3]->to_latex_string();
	my $sign = $_[0]->[4]->to_latex_string();
	my $rhs = $_[0]->[5]->to_latex_string();
	
	my $outstring = " \\begin{equation}\n\\label{eq:$nameStub}\n";
	
	$outstring .= "$lhs $sign $rhs";
	
	$outstring .= $forallString;
	
	$outstring .= "\\end{equation}";
	return $outstring;
}
package MpmasQl3EqnElements::LHS;
use Carp qw(croak);
sub evaluate {
	#my ($self,$database, $context, $ActivityRegistry, $conName, $fields, $subelements) = @_;
	
	for my $i (@{$_[0]}) {
		#eval  { #eval { //captured at base level in equationDefinition
			$i->evaluate($_[1], $_[2], $_[3], $_[4], $_[5], $_[6]);
		#};
		#if ($@) {
			#croak "In term: " . Dumper($_[0]->[1])."\n". MpmasQl3EqnElements::shortError($@);
		#	croak MpmasQl3EqnElements::shortError($@);
		#}
	}
	return; #does not return anything
}
sub to_latex_string {
	my $string = "";
	for my $i (@{$_[0]}) {
		 $string .= " ". $i->to_latex_string() if (defined $i);
	}
	return $string;
}
package MpmasQl3EqnElements::lhsTerm;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub evaluate {
	my ($self,$database, $context, $ActivityRegistry, $conName, $fields, $subelements) = @_;
	
	my $sign = ($_[0]->[0] eq '-') ? -1: 1;  # plus or minus linkage to other functions
	
	#eval { //captured at base level in equationDefinition
		$_[0]->[1]->evaluate($_[1], $_[2], $ActivityRegistry, $conName, $sign, $fields, $subelements);
	#};
	#if ($@) {
	#	#croak "In term: " . Dumper($_[0]->[1])."\n". MpmasQl3EqnElements::shortError($@);
	#	croak MpmasQl3EqnElements::shortError($@);
	#}
	return; #does not return anything
}
package MpmasQl3EqnElements::sumExpression;
use Carp qw(croak);

sub evaluate {
	
	#my ($self,$database, $origContext, $ActivityRegistry, $conName, $sign, $fields, $subelements) = @_;
	
	my $contextList = $_[0]->[0]->evaluate($_[1], $_[2]); #iterator definition
	
	for my $innerContext (@$contextList) {
		$_[0]->[1]->evaluate($_[1], $innerContext, $_[3], $_[4], $_[5], $_[6], $_[7]);
	}
	
	return; #does not return anything
}
package MpmasQl3EqnElements::simpleLhsTerm;
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub evaluate {
	#my ($self, $database, $context, $ActivityRegistry, $conName, $sign, $fields, $subelements) = @_;

	#first determine decision variable
	my $actName = $_[0]->[1]->evaluate($_[1], $_[2]); 
	
	#lookup activity in registry to get corresponding object
	my $ao = $_[3]->get_object_by_id($actName);	
	if (not defined $ao) {
		print STDERR ("Info: Skipped non-existing activity $actName marked with '?'\n" ) if  (defined  $_[0]->[2] and $_[0]->[2] eq '?') and return(); #return if explicitly marked as possible exception
		croak ("No such activity $actName\n") unless (defined $ao); # else error
	}
	#evaluate coefficient expression
	my $newSubelements = {"I" => [], "R" => [] };
	my $attname = $actName;
	unless ( MpmasMILPActivity->has_coeffWithConstraints()) {
		$attname = $_[4];
	}
	my $coefficient = $_[0]->[0]->evaluate($_[1], $_[2], $newSubelements, $attname, $_[5]); #may fill subelements if mpmas insert expression is encountered
	
	if (not defined $coefficient) {
		print "Warning: Undefined coefficient for activity '$actName' in equation '",$_[4],"'\nwill be treated as zero. Check and revise.\n";
		$coefficient = 0;
	}
	{	use warnings FATAL => qw/uninitialized numeric/;
		$coefficient *=  $_[5]; #* sign
	}
	if (MpmasMILPActivity->has_coeffWithConstraints() ){
		$_[6]->{coefficients}->{$actName} += $coefficient;
		#print "coeffEnd: ",$actName,"/", $_[4], ":\n", Dumper($newSubelements);

		for my $i (keys %$newSubelements) {
		    #print "HERE: $i ", ref($i) ,"\n" unless ref($i) eq "ARRAY";
			for my $j (0..$#{$newSubelements->{$i}} ) {
                next unless (defined $newSubelements->{$i}[$j]);
			    unless (defined $_[7]->{$i}->[$j]) {
    			    $_[7]->{$i}->[$j] = [];
			    }
				push @{$_[7]->{$i}->[$j]}, @{$newSubelements->{$i}[$j] };
			}
		}
	}
	else {
		$ao->set_coeff($_[4], $coefficient);
		for my $i (keys %$newSubelements) {
			for my $j (keys %{$newSubelements->{$i}} ) {
				$ao->add_subelement_field($i,$j, $newSubelements->{$i}{$j});
			}
		}
	}
	return;
}
package MpmasQl3EqnElements::marketSpecificLhsTerm;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context, $ActivityRegistry, $conName, $sign, $fields, $subelements) = @_;

	#first determine decision variable
	my $actName = $_[0]->[2]->evaluate($_[1], $_[2]); 
	
	#lookup activity in registry to get corresponding object
	my $ao = $_[3]->get_object_by_id($actName);	
	if (not defined $ao) {
		print STDERR ("Info: Skipped non-existing activity $actName marked with '?'\n" ) if  (defined  $_[0]->[3] and $_[0]->[3] eq '?') and       return(); #return if explicitly marked as possible exception
		croak ("No such activity $actName\n") unless (defined $ao); # else error
	}
	# loop over markets
	
	for my $market (0 ..  $_[1]->{CONFIG}->{MARKETS} - 1) {	
	
		my $innerContext = $_[2]->copy();
		$innerContext->add($_[0]->[0]->name(), $market);
		my $milpid =  sprintf("%01d%01d" , 1 , $market);
		
		#evaluate coefficient expression
		my $newSubelements = {"I" => [], "R" => [] };
		my $attname = $actName;
		unless ( MpmasMILPActivity->has_coeffWithConstraints()) {
			$attname = $_[4];
		}
		my $coefficient = $_[0]->[1]->evaluate($_[1], $innerContext, $newSubelements, $attname, $_[5]); #may fill subelements if mpmas insert expression is encountered
		
		if (not defined $coefficient) {
			print STDERR "Warning: Undefined coefficient for activity '$actName' in equation '",$_[4],"'\nwill be treated as zero. Check and revise.\n";
			$coefficient = 0;
		}
		$coefficient *=  $_[5]; #* sign
		
		if (MpmasMILPActivity->has_coeffWithConstraints() ){
			if ($market == 0) {
				$_[6]->{coefficients}->{$actName} += $coefficient;
			}
			else {
				$_[6]->{othermilp}->{$milpid}->{$actName} += $coefficient ;
			}
			for my $i (keys %$newSubelements) {
				for my $j (0..$#{$newSubelements->{$i}} ) {
#					$_[7]->{$i}->[$j] = $newSubelements->{$i}[$j];
                    next unless (defined $newSubelements->{$i}[$j]);    
			        unless (defined $_[7]->{$i}->[$j]) {
        			    $_[7]->{$i}->[$j] = [];
			        }
				    push @{$_[7]->{$i}->[$j]}, @{$newSubelements->{$i}[$j] };


				}
			}
		}
		else {
			$ao->set_coeff($_[4], $coefficient, $milpid);
			for my $i (keys %$newSubelements) {
				for my $j (keys %{$newSubelements->{$i}} ) {
					$ao->add_subelement_field($i,$j, $newSubelements->{$i}{$j});
				}
			}
		}
	}
	return;
}

package MpmasQl3EqnElements::relsign;
use Carp qw(croak);
sub evaluate {
	#my ($self,$database, $context) = @_;
	return 3 if ($_[0]->[0] eq '==');
	return 2 if ($_[0]->[0] eq '>=');
	return 1 if ($_[0]->[0] eq '<=');
}
sub to_latex_string {
	#my ($self,$database, $context) = @_;
	return "=" if ($_[0]->[0] eq '==');
	return "\\geq" if ($_[0]->[0] eq '>=');
	return "\\leq" if ($_[0]->[0] eq '<=');
}


package MpmasQl3EqnElements::RHS;
use Carp qw(croak);
sub evaluate {
	#my ($self,$database, $context, $subelements, $othermilp) = @_;
	return $_[0]->[0]->evaluate($_[1], $_[2], $_[3], 'RHS',1);#may add mpmas insert value to subelements
}
sub to_latex_string {
		return $_[0]->[0]->to_latex_string();
}
package MpmasQl3EqnElements::marketSpecificRHS;
use Carp qw(croak);
sub evaluate {
	#my ($self,$database, $context, $subelements, $othermilp) = @_;
	my $standardvalue;
	for my $market (0 ..  $_[1]->{CONFIG}->{MARKETS} - 1) {	
	
		my $innerContext = $_[2]->copy();
		$innerContext->add($_[0]->[0]->name(), $market);
		my $milpid =  sprintf("%01d%01d" , 1 , $market);
	
		if ($market == 0) {
			$standardvalue = $_[0]->[0]->evaluate($_[1], $innerContext, $_[3], 'RHS',1);
		}
		else {
			$_[4]->{$milpid}->{value} = $_[0]->[1]->evaluate($_[1], $innerContext, $_[3], 'RHS',1); ;
		}
	}
	return $standardvalue;#may add mpmas insert value to subelements
}
sub to_latex_string {
		return $_[0]->[0]->to_latex_string();
}




##Definitions of iterators and attributes

#iteratorDefinitionList
package MpmasQl3EqnElements::iteratorDefinitionList;
use Storable qw(dclone);
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub evaluate {
	#my ($self, $database, $origContext) = @_;
	#print "Evaluating iteratorDefinitionList\n";
	#print Dumper($self)."\n";
	
	#print "Orig context: ". Dumper($origContext)."\n" if (defined $origContext);
	my $origContext = $_[2];
	$origContext = MpmasQl3EqnElements::context->new() unless (defined $origContext);
	
	my $contextList = [( $origContext )];
	my @iteratorOrder = ();
	
	#empty 
	if (scalar (@{$_[0]}) == 0){
		return (\@iteratorOrder, $contextList);
	}
	for my $i (@{$_[0]}) { #iterate over iterator definitions
		my @crossContextList =();	
		push @iteratorOrder, $i->name();
		for my $c (@$contextList) {
			
			#print "Now context: ". Dumper($c)."\n";
			my $newContexts = $i->evaluate($_[1], $c);
			push @crossContextList, @$newContexts if (scalar @$newContexts);
		}
		$contextList = dclone(\@crossContextList);
	}
	return (\@iteratorOrder, $contextList);
}
sub to_latex_string {
	my ($self) = @_;
	
	my $count = 0;
	my $string = "\\forall {";
	for my $i (@$self) { #iterate over iterator definitions
		if ($count++ > 0) {
			$string .= "} \\times {";
		}
		$string .= $i->to_latex_string();
	}
	$string .= "}";
	return $string;
}
package MpmasQl3EqnElements::iteratorDefinitionRestrictedList; #Same as iterator definition, but for making sure that it only contains iterators restricted to alphanumeric characters
use Storable qw(dclone);
use Carp qw(croak);
#use Data::Dumper qw/Dumper/;
sub evaluate {
	#my ($self, $database, $origContext) = @_;
	#print "Evaluating iteratorDefinitionList\n";
	#print Dumper($self)."\n";
	
	#print "Orig context: ". Dumper($origContext)."\n" if (defined $origContext);
	my $origContext = $_[2];
	$origContext = MpmasQl3EqnElements::context->new() unless (defined $origContext);
	
	my $contextList = [( $origContext )];
	my @iteratorOrder = ();
	
	#empty 
	if (scalar (@{$_[0]}) == 0){
		return (\@iteratorOrder, $contextList);
	}
	for my $i (@{$_[0]}) { #iterate over iterator definitions
		my @crossContextList =();	
		push @iteratorOrder, $i->name();
		for my $c (@$contextList) {
			
			#print "Now context: ". Dumper($c)."\n";
			my $newContexts = $i->evaluate($_[1], $c);
			push @crossContextList, @$newContexts if (scalar @$newContexts);
		}
		$contextList = dclone(\@crossContextList);
	}
	return (\@iteratorOrder, $contextList);
}
sub to_latex_string {
	my ($self) = @_;
	
	my $count = 0;
	my $string = "\\forall {";
	for my $i (@$self) { #iterate over iterator definitions
		if ($count++ > 0) {
			$string .= "} \\times {";
		}
		$string .= $i->to_latex_string();
	}
	$string .= "}";
	return $string;
}


#iteratorDefinition
package MpmasQl3EqnElements::iteratorDefinition;
use Carp qw(croak);
sub name {
	my ($self) = @_;
	return $_[0]->[0]->name();
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	return undef() if (not defined $_[2]); #if context is empty return empty context, only happens if a set defining the iterators in a previous loop was empty
    my $itName = $_[0]->[0]->name();#iterator name
	my @set = $_[0]->[1]->evaluate($_[1], $_[2]);	
	
	my @newContexts;
	for my $x (@set) {
		 my $nc = $_[2]->copy();
#		 if ($x =~ /[\W\s]/) {
#    		 print STDERR "Error: Value '$x' from set '",$_[0]->[1]->name(),"' contains non-alphanumeric characters other than '_' and hence cannot be used for iterator $itName\n";
#    		 exit(1);
#		 }
		 $nc->add($itName, $x);
		 push @newContexts,  $nc; 
	}
	return \@newContexts;
}
sub to_latex_string {
	my ($self) = @_;
	
	my $string = $_[0]->[0]->to_latex_string(). " \\in ". $_[0]->[1]->to_latex_string();

	
	return $string;
}

#iteratorDefinitionRestricted
package MpmasQl3EqnElements::iteratorDefinitionRestricted;
use Carp qw(croak);
sub name {
	my ($self) = @_;
	return $_[0]->[0]->name();
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	return undef() if (not defined $_[2]); #if context is empty return empty context, only happens if a set defining the iterators in a previous loop was empty
    my $itName = $_[0]->[0]->name();#iterator name
	my @set = $_[0]->[1]->evaluate($_[1], $_[2]);	
	
	my @newContexts;
	for my $x (@set) {
		 my $nc = $_[2]->copy();
		 if ($x =~ /[\W\s]/) {
    		 print STDERR "Error: Value '$x' from set '",$_[0]->[1]->name(),"' contains non-alphanumeric characters other than '_' and hence cannot be used for iterator $itName\n";
    		 exit(1);
		 }
		 $nc->add($itName, $x);
		 push @newContexts,  $nc; 
	}
	return \@newContexts;
}
sub to_latex_string {
	my ($self) = @_;
	
	my $string = $_[0]->[0]->to_latex_string(). " \\in ". $_[0]->[1]->to_latex_string();

	
	return $string;
}




package MpmasQl3EqnElements::attributeList;
use Carp qw(croak);
sub evaluate {
	##my ($self, $database, $context, $subelements) = @_;
	my $fields = {};
	for my $i (@{$_[0]}) {
		my ($attname, $attvalue) = $i->evaluate($_[1], $_[2], $_[3]);
		$fields->{$attname} = $attvalue;
	}
	return $fields;
}
sub evaluate_selected {
	##my ($self, $database, $context, $subelements, $selected) = @_;
	my $fields = {};
	for my $i (@{$_[0]}) {
	    if ($i->[0] eq $_[4]) {
		    my ($attname, $attvalue) = $i->evaluate($_[1], $_[2], $_[3]);
		    return $attvalue;
		}
	}
	return undef;
}



package MpmasQl3EqnElements::attribute;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context, $subelements) = @_;
	my $attname = $_[0]->[0];
	my $attvalue = $_[0]->[1]->evaluate($_[1], $_[2], $_[3], $attname, 1); #sign is generally positive in attributes
	return ($attname, $attvalue);
}
package MpmasQl3EqnElements::hashAttribute;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $origContext, $subelements) = @_;
	my $attname = $_[0]->[0];
	
	my ($iteratorOrder, $contextList);
	eval {
		($iteratorOrder, $contextList)= $_[0]->[1]->evaluate($_[1], $_[2]); #iterator definition list	
	};
	if ($@) {
		print STDERR "Error in attribute: $attname, iterator definition list:\n".MpmasQl3EqnElements::shortError($@)."\n";
		exit(1);
	}	
	
	my %hash;
	for my $innerContext (@$contextList) {
		my $key =  $_[0]->[2]->evaluate($_[1], $innerContext, $_[3], $attname, 1); #sign is generally positive in attributes
		my $value =  $_[0]->[3]->evaluate($_[1], $innerContext, $_[3], $attname, 1); #sign is generally positive in attributes
		$hash{$key} = $value;
	}
	return ($attname, \%hash);
}


package MpmasQl3EqnElements::attributeContentList;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context, $subelements, $attname, $sign) = @_; # subelements not needed since not allowed in string argument
	
	my $result = join("", map{$_->evaluate($_[1], $_[2])} @{$_[0]});
	return $result;
}
package MpmasQl3EqnElements::stringContentList;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_; 
	
	my $result = join("", map{$_->evaluate($_[1], $_[2])} @{$_[0]});
	return $result;
}


##Expression elements

#iterator
package MpmasQl3EqnElements::iterator;
use Carp qw(croak);
#local $SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
sub name {
	#my ($self) = (@_);

	return $_[0]->[0];
}
sub evaluate {
	#my ($self, $database, $context) = @_;#need database here to have general interface compatible with parameter calls even if only context is used
	croak ("Error: Iterator ".$_[0]->[0]." not defined in this context\n") unless ($_[2]->exists($_[0]->[0]));
	my $value = $_[2]->get($_[0]->[0]);
	return $value if (defined  $value);
	croak ("Error: Iterator ".$_[0]->[0]." not defined in this context\n");
}
sub to_latex_string {
	return $_[0]->[0];
}

#index
package MpmasQl3EqnElements::index;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	
	my @indexArray = map { $_->evaluate($_[1], $_[2]) } @{$_[0]};
	return \@indexArray;
}
#decVar
package MpmasQl3EqnElements::decVar;

use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	return $_[0]->[0];
}
package MpmasQl3EqnElements::decVarIndexed;

use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $actName = join("_", $_[0]->[0], @{$_[0]->[1]->evaluate($_[1], $_[2])});
	return $actName;
}
package MpmasQl3EqnElements::assetReference;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $ao = $_[1]->{ASSETS}->get_object_by_id( $_[0]->[0]);
	croak ("Error: Asset ". $_[0]->[0] ." not defined\n") unless (defined $ao);
	return  $ao->attr($_[0]->[1]); 
	
}
sub to_latex_string {
	my ($self) = @_;
	return  $_[0]->[0]. ".". $_[0]->[1]; 
}
package MpmasQl3EqnElements::indexedAssetReference;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $id = join("_", $_[0]->[0], @{$_[0]->[1]->evaluate($_[1], $_[2])} );
	my $ao = $_[1]->{ASSETS}->get_object_by_id( $id);
	croak ("Error: Asset ". $id ." not defined\n(Note: Asset references cannot be used when defining sets or parameters.\nThey can only be used in decision_variable and equation definitions and subsequent asset definitions.)\n") unless (defined $ao);
	return  $ao->attr($_[0]->[2]); 
	
}
sub to_latex_string {
	my ($self) = @_;
	return  $_[0]->[0]. "[". $_[0]->[1]->to_latex_string() ."]".".". $_[0]->[2]; 
}
package MpmasQl3EqnElements::assetReferenceIdFast;
#to allow referring to other assets within asset definitions
#no check possible whether ASSET exists
#because they have not been created yet. Only use of ID possible, since
#this can be generated as string
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	return  $_[0]->[0]; 
	
}
sub to_latex_string {
	my ($self) = @_;
	return  $_[0]->[0]; 
	
}
package MpmasQl3EqnElements::indexedAssetReferenceIdFast;
#to allow referring to other assets within asset definitions
#no check possible whether ASSET exists
#because they have not been created yet. Only use of ID possible, since
#this can be generated as string
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $id = join("_", $_[0]->[0], @{$_[0]->[1]->evaluate($_[1], $_[2])} );
	return  $id; 
}
sub to_latex_string {
	my ($self) = @_;
	return  $_[0]->[0]. "[". $_[0]->[1]->to_latex_string() ."]"; 
}
package MpmasQl3EqnElements::assetList;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $id = join("_", $_[0]->[0], @{$_[0]->[1]->evaluate($_[1], $_[2])} );
	my $ao = $_[1]->{ASSETS}->get_object_by_id( $id);
	croak ("Error: Asset ". $id ." not defined\n") unless (defined $ao);
	return  $ao->attr($_[0]->[2]); 
	
}
sub to_latex_string {
	return "";
}
#set
package MpmasQl3EqnElements::parameter;
use Carp qw(croak);

#Idea ( not yet implemented)
#Note: $_[0]->[1] is used as cache for a constant value
#Note: $_[0]->[2] is used as cache signal for a constant value
#Due to the ifs in the beginning cache is checked only on first call
#Problem: Cache needs to be cleared for new scenario (?); Challenge: How to access all parameters to do that call???
#sub clear_cache {
#	$#{ $_[0] } = 1; 
#}
#Alternative: Use closures/subroutine references
sub evaluate {
	#my ($self, $database, $context) = @_;
	
	#return $_[0]->[1] if ( @{$_[0]} == 2);
	#return $_[2]->get($_[0]->[1]) if ( @{$_[0]} == 3);
	my $name = $_[0]->[0];
	my $parameter;
	if (exists($_[1]->{PARAMETERS}->{$name}) ) {
		$parameter = $_[1]->{PARAMETERS}->{$name};
		#$_[0]->[1] = $parameter;
	}
	elsif (exists($_[1]->{GLOBALS}->{$name}) ) {
		$parameter = $_[1]->{GLOBALS}->{$name};
		#$_[0]->[1] = $parameter;
	}
	elsif (exists($_[1]->{CONFIG}->{$name}) ) {
		$parameter = $_[1]->{CONFIG}->{$name};
		#$_[0]->[1] = $parameter;
	}
	else  {
		#croak ("Error: Parameter or iterator ".$name." does not exist\n") unless ( $_[2]->exists($name)) ;
		$parameter = $_[2]->get($name);
		croak ("Error: Parameter or iterator ".$name." not defined\n") unless (defined $parameter); 
		#$_[0]->[2] = 1;
	}
	 

	if (ref($parameter) eq 'IndexedTable' or ref($parameter) eq 'IndexedTableLink') {
		croak ("Parameter $name is an indexed parameter, but you did not provide an index!\n");	
	}
	return $parameter;	
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."}";
}
package MpmasQl3EqnElements::globalParameter;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $name = $_[0]->[0];
	my $parameter;
	if (exists($_[1]->{GLOBALS}->{$name}) ) {
		$parameter = $_[1]->{GLOBALS}->{$name};
	}
	elsif (exists($_[1]->{CONFIG}->{$name}) ) {
		$parameter = $_[1]->{CONFIG}->{$name};
	}
	elsif (defined $_[2])  {
		$parameter = $_[2]->get($name);
		croak ("Error: Global parameter or iterator ".$name." not defined\n") unless (defined $parameter); 
	}
	else {
		croak ("Error: Global parameter or iterator ".$name." not defined\n") unless (defined $parameter); 
	}

	if (ref($parameter) eq 'IndexedTable' or ref($parameter) eq 'IndexedTableLink') {
		croak ("Parameter $name is an indexed parameter, but you did not provide an index!\n");	
	}
	return $parameter;	
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."}";
}
package MpmasQl3EqnElements::indexedParameter;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	#my $name = $_[0]->[0];
		
	croak ("Error: Parameter ".$_[0]->[0]." not defined\n") if (not exists($_[1]->{PARAMETERS}->{ $_[0]->[0]  }) ); ;
	
	my $parameter = $_[1]->{PARAMETERS}->{ $_[0]->[0]  };
	
	croak ("Parameter ",$_[0]->[0], "is not an indexed parameter!\n") if (not ref($parameter) eq 'IndexedTable' and not ref($parameter) eq 'IndexedTableLink');
	
	my $indexArray = $_[0]->[1]->evaluate($_[1], $_[2]); #evaluate the index
	if (scalar(@$indexArray) != $parameter->numkeys()) {
		croak ("Parameter ",$_[0]->[0],"[". join(",", @$indexArray). "]: Index has the wrong number of elements!\n");		
	}
	my $value = $parameter->query($indexArray, $parameter->numkeys() +1 );
	#+print "Parameter evaluation $name [". join(",", @$indexArray)."]: $value \n"; 
	
	return $value;
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."}_{".$_[0]->[1]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::tableReference;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0];
	my $table = $_[0]->[1];
	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);
	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;

	my $indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	if ($tabo->numkeys() >= $tabo->column_by_name_keys_allowed($column) ) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Column $column is a key column and thus cannot be used in a parameter reference!\nMaybe you wanted to create a set rather than a parameter?\n");		
	}
	
	if (scalar(@$indexArray) != $tabo->numkeys()) {
		croak ("Table reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has the wrong number of elements!\n");		
	}
	my $value = $tabo->query_by_columnname($indexArray, $column );
	$value = 0 unless (defined $value); # replace by zero, if nothing found
	return $value;
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::tableReferenceVarCol;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $table = $_[0]->[1];
	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);
	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;

	my $indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	if ($tabo->numkeys() >= $tabo->column_by_name_keys_allowed($column) ) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Column $column is a key column and thus cannot be used in a parameter reference!\nMaybe you wanted to create a set rather than a parameter?\n");		
	}
	
	if (scalar(@$indexArray) != $tabo->numkeys()) {
		croak ("Table reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has the wrong number of elements!\n");		
	}
	my $value = $tabo->query_by_columnname($indexArray, $column );
	$value = 0 unless (defined $value); # replace by zero, if nothing found
	return $value;
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::tableSetReference;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0];
	my $table = $_[0]->[1];

	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);

	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;
	

	my $indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	if ($tabo->numkeys() < $tabo->column_by_name_keys_allowed($column) ) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Column $column is not a key column and thus cannot be used in a set reference!\n");		
	}
	if (scalar(@$indexArray) >= $tabo->numkeys()) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has too many elements for a set reference!\n");		
	}
	if (scalar(@$indexArray) > $tabo->column_by_name_keys_allowed($column) -1 ) {
		croak ("Error in  table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has an inconsistent number of elements for column $column!\n");		
	}
	
	return $tabo->query($indexArray, 0);
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::tableSetReferenceDistinct;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0];
	my $table = $_[0]->[1];

	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);

	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;
	
	my $indexArray = [];
	if (defined $_[0]->[2]) {
		$indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	}
	$tabo->distinct_values_for_columnname($indexArray, $column);
	
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}

package MpmasQl3EqnElements::tableSetReferenceVarCol;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $table = $_[0]->[1];

	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);

	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;
	

	my $indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	if ($tabo->numkeys() < $tabo->column_by_name_keys_allowed($column) ) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Column $column is not a key column and thus cannot be used in a set reference!\n");		
	}
	if (scalar(@$indexArray) >= $tabo->numkeys()) {
		croak ("Error in table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has too many elements for a set reference!\n");		
	}
	if (scalar(@$indexArray) > $tabo->column_by_name_keys_allowed($column) -1 ) {
		croak ("Error in  table set reference  ".$column."@".$table."[". join(",", @$indexArray). "]: Index has an inconsistent number of elements for column $column!\n");		
	}
	
	return $tabo->query($indexArray, 0);
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::tableSetReferenceVarColDistinct;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $column = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $table = $_[0]->[1];

	my $tabo = $_[1]->{TABLES}->get_object_by_id($table);

	if (not defined $tabo ) {
		$tabo = $_[1]->{MPMAS_TABLES}->{$table};
	}
	croak ("Error: Table ".$table." not defined\n") if (not defined $tabo ); ;
	
	my $indexArray = [];
	if (defined $_[0]->[2]) {
		$indexArray = $_[0]->[2]->evaluate($_[1], $_[2]); #evaluate the index
	}
	$tabo->distinct_values_for_columnname($indexArray, $column);
	
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."\@" .$_[0]->[1]."}_{".$_[0]->[2]->to_latex_string()  . "}";
}
package MpmasQl3EqnElements::indexedSet;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $name = $_[0]->[0];
	
	croak ("Error: Set ".$_[0]->[0]." not defined\n") if (not exists($_[1]->{SETS}->{$name}) ); ;
	
	my $set = $_[1]->{SETS}->{$name};
	
	croak ("Set $name is not an indexed set!\n") if (not ref($set) eq 'IndexedTable' and not ref($set) eq 'IndexedTableLink');
	
	my $indexArray = $_[0]->[1]->evaluate($_[1], $_[2]); #evaluate the index
	if (scalar(@$indexArray) >= $set->numkeys()) {
		croak ("Set $name [".join(",", @$indexArray) . "]: Index has too many elements!\n");		
	}
	return $set->query($indexArray, 0);
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."}_{".$_[0]->[1]->to_latex_string()  . "}";
}

package MpmasQl3EqnElements::set;
use Carp qw(croak);
sub name {
    #my ($self, $database, $context) = @_;
    return $_[0]->[0];
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $name = $_[0]->[0];
	my $set;
	if (exists($_[1]->{SETS}->{$name})) {
		$set = $_[1]->{SETS}->{$name};
	}
	elsif ( exists($_[1]->{GLOBAL_SETS}->{$name} ))
	{
		$set = $_[1]->{GLOBAL_SETS}->{$name};
	}
	elsif (exists($_[1]->{CONFIG}->{$name}) and ref($_[1]->{CONFIG}->{$name}) and ref($_[1]->{CONFIG}->{$name}) eq 'ARRAY' ) {
		$set = $_[1]->{CONFIG}->{$name};
	}
	else {
		croak ("Error: Set ".$_[0]->[0]." not defined\n")	
	}

	if (ref($set) eq 'IndexedTable') {
		croak ("Set $name is an indexed set, but you did not provide an index!\n");	
	}
	elsif (ref($set) eq 'IndexedTableLink') {
		croak ("Set $name is an indexed set, but you did not provide an index!\n") if ($set->numkeys() > 0);	
		return $set->query([],1);
	}
	return @$set;	
}
sub to_latex_string {
	return "\\mathit{".$_[0]->[0]."}";
}

package MpmasQl3EqnElements::expressionList;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	return map { $_->evaluate($_[1], $_[2]) } @{$_[0]};
}
sub to_latex_string {
	my ($self) = @_;
	return "\\left{". join(", ", map { $_->to_latex_string() } @$self)."\\right}";
}
package MpmasQl3EqnElements::barewordList;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	return @{$_[0]};
}
#~ package MpmasQl3EqnElements::min;
#~ use Carp qw(croak);
#~ sub evaluate {
	#~ #my ($self, $database, $context) = @_;
#~ 
	#~ my $smallest; 
	#~ foreach  my $i (@$self) {
		#~ my $new = $i->evaluate($database, $context);
		#~ if ( not defined($smallest) or $smallest > $new){
		 #~ $smallest = $new;
		#~ }
	#~ }
	#~ return  $smallest ;
#~ }
#~ package MpmasQl3EqnElements::max;
#~ use Carp qw(croak);
#~ sub evaluate {
	#~ #my ($self, $database, $context) = @_;
#~ 
	#~ my $largest;
	#~ foreach  my $i (@$self) {
		#~ my $new = $i->evaluate($database, $context);
		#~ if ( not defined($largest) or $largest < $new){
		 #~ $largest = $new;
		#~ }
	#~ }
	#~ return  $largest ;
#~ }


package MpmasQl3EqnElements::conditionThenListElement;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $condition = $_[0]->[0]->evaluate($_[1], $_[2]);
	
	if ($condition) {
		my $value = $_[0]->[1]->evaluate($_[1], $_[2]);;
		return ($condition, $value);
	}
	return ($condition, undef);
}
sub to_latex_string {
	my ($self) = @_;
	return $_[0]->[1]->to_latex_string().",& ".$_[0]->[0]->to_latex_string();
}
package MpmasQl3EqnElements::conditionThenSetListElement;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $condition = $_[0]->[0]->evaluate($_[1], $_[2]);
	
	if ($condition) {
		my @value = $_[0]->[1]->evaluate($_[1], $_[2]);;
		return ($condition, \@value);
	}
	return ($condition, [()]);
}
sub to_latex_string {
	my ($self) = @_;
	return $_[0]->[1]->to_latex_string().",& ".$_[0]->[0]->to_latex_string();
}

package MpmasQl3EqnElements::min;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my $smallest = $_[0]->[0]->evaluate($_[1], $_[2]); 
	my @set = $_[0]->[1]->evaluate($_[1], $_[2]);

	
	foreach  my $new (@set) {
		#my $new = $i->evaluate($_[1], $_[2]);
		if ( not defined($smallest) or $smallest > $new){
		 $smallest = $new;
		}
	}
	return  $smallest ;
}
sub to_latex_string {
	my ($self) = @_;
	return "\\mathrm{min}\\left(". join(", ", map { $_->to_latex_string() } @$self)."\\right)";
}
package MpmasQl3EqnElements::max;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my $largest = $_[0]->[0]->evaluate($_[1], $_[2]); 
	my @set = $_[0]->[1]->evaluate($_[1], $_[2]);

	foreach  my $new (@set) {
		#my $new = $i->evaluate($_[1], $_[2]);
		if ( not defined($largest) or $largest < $new){
		 $largest = $new;
		}
	}
	return  $largest ;
}
sub to_latex_string {
	my ($self) = @_;
	return "\\mathrm{max}\\left(". join(", ", map { $_->to_latex_string() } @$self)."\\right)";
}

package MpmasQl3EqnElements::minSet;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my $smallest;
	my @set = $_[0]->[0]->evaluate($_[1], $_[2]);

	foreach  my $new (@set) {

		if ( not defined($smallest) or $smallest > $new){
		 $smallest = $new;
		}
	}
	return  $smallest ;
}
sub to_latex_string {
	my ($self) = @_;
	return "\\mathrm{min}\\left(".  $_[0]->[0]->to_latex_string() ."\\right)";
}	
package MpmasQl3EqnElements::maxSet;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my $largest;
	my @set = $_[0]->[0]->evaluate($_[1], $_[2]);

	foreach  my $new (@set) {
		if ( not defined($largest) or $largest < $new){
		 $largest = $new;
		}
	}
	return  $largest ;
}
sub to_latex_string {
	my ($self) = @_;
	return "\\mathrm{max}\\left(".  $_[0]->[0]->to_latex_string() ."\\right)";
}	
package MpmasQl3EqnElements::union;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my %union;
	foreach  my $i (@{$_[0]} ) {
		 my @newset = $i->evaluate($_[1], $_[2]);
		 foreach (@newset) { $union{$_} = 1 };
	}
	return keys %union ;
}
sub to_latex_string {
	my ($self) = @_;
	return join(" \\cup ", map { $_->to_latex_string() } @$self);
}



package MpmasQl3EqnElements::intersection;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my %count;
	my $n = 0;
	foreach  my $i (@{$_[0]}) {
		 $n++;
		 my @newset = $i->evaluate($_[1], $_[2]);
		 foreach (@newset) { $count{$_}++ };
	}
	my @intersection = grep {$count{$_} == $n } keys %count;
	return  @intersection ;
}
sub to_latex_string {
	my ($self) = @_;
	return join(" \\cap ", map { $_->to_latex_string() } @$self);
}
package MpmasQl3EqnElements::setDifference;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my @set = $_[0]->[0]->evaluate($_[1], $_[2]);
	my %toExclude =  map {  $_ => 1  } $_[0]->[1]->evaluate($_[1], $_[2]);
	#croak("Empty set to the left of except\n") unless ((scalar @set) && defined($set[0]));
	return @set unless ((scalar @set) && defined($set[0]));
	@set = grep {!$toExclude{$_} } @set;
	return @set;
}
sub to_latex_string  {
	return	 $_[0]->[0]->to_latex_string() . " \\setminus ". $_[0]->[1]->to_latex_string();
}


package MpmasQl3EqnElements::cartesian_concat;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my @result;
	my $n = 0;
	foreach  my $ex (@{$_[0]}) {
		 $n++;
		 my @newset = $ex->evaluate($_[1], $_[2]);
		 my @temp;
		 
		 if ( $n == 1) {
			 @result = @newset;
		 }
		 else {
			 foreach my $i ( @result) {
				 foreach my $j (@newset) { 
						push @temp, $i . $j;
				 }
			 }
			 @result = @temp;
		 }
	}
	
	return  @result ;
}
sub to_latex_string {
	my ($self) = @_;
}

package MpmasQl3EqnElements::cartesian_power_concat;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	my @result;
	
	my @newset = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $n = $_[0]->[1]->evaluate($_[1], $_[2]);
	foreach my $k (1..$n)   {
		 my @temp;		 
		 if ( $k == 1) {
			 @result = @newset;
		 }
		 else {
			 foreach my $i ( @result) {
				 foreach my $j (@newset) { 
						push @temp, $i . $j;
				 }
			 }
			 @result = @temp;
		 }
	}
	
	return  @result ;
}
sub to_latex_string {
	my ($self) = @_;
}





package MpmasQl3EqnElements::emptySet;
use Carp qw(croak);
sub evaluate {
#	my ($self, $database, $origContext) = @_;
		return [];
}
sub to_latex_string {
	my ($self) = @_;
	return "\\emptyset";
}
package MpmasQl3EqnElements::subset;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $origContext) = @_;

	my $contextList = $_[0]->[0]->evaluate($_[1], $_[2]); #iterator definition
	my $itname = $_[0]->[0]->name();
	my @set;
	for my $innerContext (@$contextList) {
		if ($_[0]->[1]->evaluate($_[1], $innerContext) ) {
			push @set, $innerContext->get( $itname);
		}
	}
	return @set;
}	
sub to_latex_string {
	my ($self) = @_;
	return "\\left{ ". $_[0]->[0]->to_latex_string(). " : ". $_[0]->[1]->to_latex_string()."\\right}";
}
### MPMAS insertions
package MpmasQl3EqnElements::mpmasInsertExpression;
use Carp qw(croak);
use Data::Dumper qw/Dumper/;
sub evaluate {
	#my ($self, $database, $context, $subelements, $attname, $sign,  $expressionLink, $termPositionInExpression) = @_;
	
	my ($mpmasInsertFunction, $indexArray) = $_[0]->[0]->evaluate($_[1], $_[2]); #mpmasInserter or mpmasInserterIndexed
	
	my $multiplier = $_[0]->[1]->evaluate($_[1], $_[2]);
	print STDERR "Empty value in $_[4] : Multiplier of $mpmasInsertFunction \n" and exit(1) unless (defined $multiplier);
	$multiplier *= $_[5] ;
	
	my $summand = $_[0]->[2]->evaluate($_[1], $_[2]);
	print STDERR "Empty value in $_[4] : Summand of $mpmasInsertFunction \n" and exit(1) unless (defined $summand);
	$summand *= $_[5];
	

	
	my $insertInfo = {  
						ar => $indexArray
	};
	if ($_[4] eq "orow") {
    	$insertInfo->{row} = -1;
	}
	elsif ($_[4] eq "lbound") {
    	$insertInfo->{row} = -2;
	}
	elsif ($_[4] eq "ubound") {
    	$insertInfo->{row} = -3;
	}
	elsif ($_[4] eq "RHS") {
    	$insertInfo->{col} = -1;
	}
	else {
    	$insertInfo->{at} = $_[4];
    }
	
	$insertInfo->{m}  = $multiplier if ($multiplier != 1);
	$insertInfo->{s}  =  $summand if ($summand != 0);
	$insertInfo->{px} =   $_[6] if  defined ($_[6]) ; 
	$insertInfo->{po} = $_[7] if (defined $_[7] );
	

	
	my $mpmasInsertFunctionId = MpmasMILPMultiperiod->get_index_of_inserter($mpmasInsertFunction);
	
	unless (defined $_[3]->{"I"}->[$mpmasInsertFunctionId]) {
    	$_[3]->{"I"}->[$mpmasInsertFunctionId] = [];
	}
	
	push @{ $_[3]->{"I"}->[$mpmasInsertFunctionId] }, $insertInfo;
	
	#	print $mpmasInsertFunction, ": ", $mpmasInsertFunctionId,"\n" if ($mpmasInsertFunction eq "markets.price");
	#print Dumper($_[3]->{"I"});
	
	return 0; #returns zero as insertions are recorded in subelements 
			  #and not returned as they are not initially written into the matrix 
			  #(default values for the matrix used if no insertion takes place could originally be set with the || operator)
}
sub to_latex_string {
	my ($self) = @_;
	my $string =  $_[0]->[0]->to_latex_string();
	my $m = $_[0]->[1]->to_latex_string();
	$string .= "\\cdot $m" if ($m != 1);
	my $s = $_[0]->[2]->to_latex_string();
	if ($s != 0) {
		$string = "\\left(". $string  ."+ $s". "\\right)";
	}
	return $string;
}

package MpmasQl3EqnElements::mpmasInsertExpressionCompound;
use Carp qw(croak);
#se Data::Dumper qw/Dumper/;;
sub evaluate {
	#my ($self, $database, $context, $subelements, $attname, $sign, $expressionLink, $termPositionInExpression) = @_;
	
##	my ($mpmasInsertFunction, $indexArray) = $_[0]->[0]->evaluate($_[1], $_[2]); #mpmasInserter or mpmasInserterIndexed
	
##	my $multiplier = $_[0]->[1]->evaluate($_[1], $_[2]);
##	print STDERR "Empty value in $_[4] : Multiplier of $mpmasInsertFunction \n" and exit(1) unless (defined $multiplier);
##	$multiplier *= $_[5] ;
	
##	my $summand = $_[0]->[2]->evaluate($_[1], $_[2]);
##	print STDERR "Empty value in $_[4] : Summand of $mpmasInsertFunction \n" and exit(1) unless (defined $summand);
##	$summand *= $_[5];
	
	#my $string = $mpmasInsertFunction. "(". join(",", @$indexArray).").multiply($multiplier).add($summand)";
	
	
    my $expressionID = $_[3]->{'X'}->[0][0]++; #expression counter for each element (constraint, activity)

	#evaluate the operands		
	my $rt1 = $_[0]->[0]->evaluate($_[1], $_[2], $_[3], $_[4],  1, $expressionID, 1);
	my $rt2 = $_[0]->[2]->evaluate($_[1], $_[2], $_[3], $_[4],  1, $expressionID, 2);
	my $x = 0;
	my $nonInserterExp = 0;
	if( not ref ($_[0]->[0]) eq "MpmasQl3EqnElements::mpmasInsertExpressionCompound" ||  ref($_[0]->[0]) eq "MpmasQl3EqnElements::mpmasInsertExpression") {
		$x =  $rt1;
		$nonInserterExp = 1;
	}
	elsif (not (ref ($_[0]->[2]) eq "MpmasQl3EqnElements::mpmasInsertExpressionCompound" ||  ref($_[0]->[2]) eq "MpmasQl3EqnElements::mpmasInsertExpression")) {
		$x =  $rt2;
		$nonInserterExp = 2;
	}

	my $insertInfo = {  #inserter  => $mpmasInsertFunction,
							ar => [($expressionID, $_[0]->[1]), $nonInserterExp ], #expressionID,  operator

						};
	if ($_[4] eq "orow") {
    	$insertInfo->{row} = -1;
	}
	elsif ($_[4] eq "lbound") {
    	$insertInfo->{row} = -2;
	}
	elsif ($_[4] eq "ubound") {
    	$insertInfo->{row} = -3;
	}
	elsif ($_[4] eq "RHS") {
    	$insertInfo->{col} = -1;
	}
	else {
    	$insertInfo->{at} = $_[4];
    }

	$insertInfo->{m} = $_[5] if ($_[5] != 1);
	$insertInfo->{s}  =  $x if ($x != 0);
	$insertInfo->{px} =   $_[6] if  defined ($_[6]) ; 
	$insertInfo->{po} = $_[7] if (defined $_[7] );
	
	##  MpmasMILPMultiperiod->get_index_of_inserter("expression") -> defined to be zero!
	unless (defined $_[3]->{"I"}->[0]) {
    	$_[3]->{"I"}->[0] = [];
	}
	
    push @{ $_[3]->{"I"}->[0] }, $insertInfo;

	#print Dumper($insertInfo);
	#print Dumper($_[0]);
	
	
	return 0; #returns zero as insertions are recorded in subelements 
			  #and not returned as they are not initially written into the matrix 
			  #(default values for the matrix used if no insertion takes place can be set with the || operator)
}

sub to_latex_string {
	my ($self) = @_;
	
	my $string = join("\\cdot", map {$_->to_latex_string()} @$_[0]);
	
	return $string;
}



package MpmasQl3EqnElements::mpmasInserter;
use Carp qw(croak);
sub evaluate {
	my ($self , $database, $context) = @_;
	return ($_[0]->[0], []);
}
sub to_latex_string {
	my ($self) = @_;
	return " \\mathit{".$_[0]->[0]."} ";
}
package MpmasQl3EqnElements::mpmasInserterIndexed;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return ($_[0]->[0], $_[0]->[2]->evaluate ($_[1], $_[2]) ); # note: index is saved at position 2, position 1 should be undef after merging subcat list
}
sub to_latex_string {
	my ($self) = @_;
	return " \\mathit{".$_[0]->[0]."}_{".$_[0]->[1]->to_latex_string()."} ";
}
### Mpmas readers
package MpmasQl3EqnElements::mpmasReaderList;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context, $subelements) = @_;

	for my $i (@{$_[0]}) {
		$i->evaluate($_[1], $_[2], $_[3]); #result recorded in subelements
	}
	return ();#returns nothing explicitly as readers are recorded in subelements 
}
package MpmasQl3EqnElements::mpmasReaderDefinition;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context, $subelements) = @_;
	
	my $when = $_[0]->[1];
	
	return () if (defined ($when) and not $when->evaluate($_[1], $_[2]) ); #bypass reader creation if at condition evaluates to false
	
	my ($mpmasReaderName, $indexArray) = $_[0]->[2]->evaluate($_[1], $_[2]); #mpmasReader or mpmasReaderIndexed
	
	my $what = $_[0]->[0];
	my $objval = 0;
	if ($what eq "solution") {
		$objval = 0;
	}
	elsif ($what =~ /solution\s*\*\s*orow/) {
		$objval = 1;
	}
	elsif ($what =~ /solution\s*\*\s*objective/) {
		$objval = 1;
	}
	else {
		croak ("Error undefined source $what in mpmasReader $when $mpmasReaderName : Index ".join(",", @$indexArray)."\n");
	}
	
	
	my @additionalArguments;
	@additionalArguments = $_[0]->[3]->evaluate($_[1], $_[2]) if (defined $_[0]->[3]);
	
	croak ("Error undefined element in mpmasReader $what $when $mpmasReaderName : Index ".join(",", @$indexArray)."\n")    if (grep  {!defined $_}  @$indexArray );
	croak ("Error undefined element in mpmasReader $what $when $mpmasReaderName : FurtherArguments ".join(",", @additionalArguments)."\n")    if (grep  {!defined $_}  @additionalArguments );
	
	#my $string = $mpmasReaderName. "(". join(",", @$indexArray, @additionalArguments).")";
	#$_[3]->{"R"}->{$string} = $what ;
	my $mpmasReaderId = MpmasMILPMultiperiod->get_index_of_reader($mpmasReaderName);
	
	push @ { $_[3]->{"R"}->[$mpmasReaderId]} , { 
			#string => $string,
			ob => $objval,
			ar => [(@$indexArray,@additionalArguments)]
		}   ;
	
	
	return (); #returns nothing as readers are recorded in subelements 
}

package MpmasQl3EqnElements::mpmasReader;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return ($_[0]->[0], []);
}
package MpmasQl3EqnElements::mpmasReaderIndexed;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return ($_[0]->[0], $_[0]->[2]->evaluate ($_[1], $_[2]) ); # not index is saved at position 2, position 1 should be undef after merging subcat list
}


###Expressions and operators
package MpmasQl3EqnElements::String;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	my $result = $_[0]->[0];
	$result =~ s/^['"]//;
	$result =~ s/['"]$//;
	$result =~ s/(?<!\\)\\"/"/g; #?
	return $result;
}
sub to_latex_string {
	my $result = $_[0]->[0];
	$result =~ s/^['"]//;
	$result =~ s/['"]$//;
	$result =~ s/(?<!\\)\\"/"/g; #?
	return $result;
}

package MpmasQl3EqnElements::Expression;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	if (ref $_[0]->[0]) {
		return $_[0]->[0]->evaluate($_[1], $_[2]);
	}
	return $_[0]->[0];
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ;
}
package MpmasQl3EqnElements::number;
use Carp qw(croak);
sub evaluate {
	return $_[0]->[0];
}
sub to_latex_string {
		return  $_[0]->[0] ;
}
package MpmasQl3EqnElements::negation;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return -1 * $_[0]->[0]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return "-". $_[0]->[0]->to_latex_string() ;
}
package MpmasQl3EqnElements::exponentiation;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	my $r =  $_[0]->[0]->evaluate($_[1], $_[2]) ** $_[0]->[1]->evaluate($_[1], $_[2]);
	
	croak "Error: Exponentiation returns '$r' (maybe due to negative exponential for negative number?)\n"if ($r eq "NaN" or $r eq "Inf"or $r eq "nan"or $r eq "inf");
	return $r;
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ."^{". $_[0]->[1]->to_latex_string() ."}" ;
}
package MpmasQl3EqnElements::multiplication;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  $_[0]->[0]->evaluate($_[1], $_[2]) * $_[0]->[1]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\cdot ". $_[0]->[1]->to_latex_string()  ;
}
package MpmasQl3EqnElements::division;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;

	return  $_[0]->[0]->evaluate($_[1], $_[2]) / $_[0]->[1]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return  "\\frac{". $_[0]->[0]->to_latex_string() ." }{ ". $_[0]->[1]->to_latex_string()  ."}";
}

package MpmasQl3EqnElements::modulo;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  $_[0]->[0]->evaluate($_[1], $_[2]) % $_[0]->[1]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return  " ". $_[0]->[0]->to_latex_string() ." \\mathrm{mod} ". $_[0]->[1]->to_latex_string()  ." ";
}

package MpmasQl3EqnElements::addition;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  $_[0]->[0]->evaluate($_[1], $_[2]) + $_[0]->[1]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." + ". $_[0]->[1]->to_latex_string()  ;
}
package MpmasQl3EqnElements::subtraction;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  $_[0]->[0]->evaluate($_[1], $_[2]) - $_[0]->[1]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." - ". $_[0]->[1]->to_latex_string()  ;
}
package MpmasQl3EqnElements::And;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) && $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\wedge ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::Or;
use Carp qw(croak);
sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) || $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\vee ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::OrInserter;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context, $subelements, $attname, $sign) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2], $_[3], $_[4], $_[5]) || $_[0]->[1]->evaluate($_[1], $_[2])   ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\vee ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::Not;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ((! $_[0]->[0]->evaluate($_[1], $_[2]) ) ? 1 : 0 );
}
sub to_latex_string {
		return  " \\neg " .$_[0]->[0]->to_latex_string() ;
}
package MpmasQl3EqnElements::equal;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) == $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." = ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::not_equal;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) != $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\neq ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::greater_equal;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) >= $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\geq ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::less_equal;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) <= $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." \\leq ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::greater;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) > $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." > ". $_[0]->[1]->to_latex_string();
}
package MpmasQl3EqnElements::less;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context) = @_;
	return  ($_[0]->[0]->evaluate($_[1], $_[2]) < $_[0]->[1]->evaluate($_[1], $_[2])? 1 : 0);
}
sub to_latex_string {
		return  $_[0]->[0]->to_latex_string() ." < ". $_[0]->[1]->to_latex_string();
}

###FUNCTIONS 
###Set creating functions
package MpmasQl3EqnElements::sequence;
use Carp qw(croak);
sub  evaluate {
  #my ($self, $database, $context) = @_;
  my $start = $_[0]->[0]->evaluate($_[1], $_[2]); 
  my $end =  $_[0]->[1]->evaluate($_[1], $_[2]);
  my $step =  $_[0]->[2]->evaluate($_[1], $_[2]);
    croak "Error: invalid arguments for function #steps: $start, $end, $step\n" if ( $step == 0  );
    #or ($start <=> $end) != (0 <=> $step ) #Not a problem actually, will just never start
    my @array;
    my $i = $start;
    while ($i <= $end) {
      push @array, $i;
      $i += $step;
    }
  return @array;
}
sub to_latex_string {
	return " \\mathrm{TODO: sequence} ";
}
package MpmasQl3EqnElements::fromMaxXYtoZ;
use Carp qw(croak);
sub  evaluate {
  #my ($self, $database, $context) = @_;
  my $x = $_[0]->[0]->evaluate($_[1], $_[2]);
  my $y = $_[0]->[1]->evaluate($_[1], $_[2]);
  my $z = $_[0]->[2]->evaluate($_[1], $_[2]); # actual function argument is expressionList, which is evaluated into an array

  my $start = ($x > $y ? $x : $y );
  
  return ($start..$z);  
}
sub to_latex_string {
	return " \\mathrm{TODO: fromMaxXYtoZ} ";
}
package MpmasQl3EqnElements::mperiodsThrough;
use Carp qw(croak);
sub  evaluate {
  #my ($self, $database, $context) = @_;
  my ($minFirst, $maxBack, $now) = $_[0]->[0]->evaluate($_[1], $_[2]); # actual function argument is expressionList, which is evaluated into an array
  croak ("Error. Invalid arguments to mperiodsThrough\n") unless (defined $now);
  $minFirst = 0 unless(defined $minFirst);
  my $start;
  if (not defined $maxBack)  {
		$start = $minFirst;
  } 
  else  {
	$start = ($minFirst > ($now - $maxBack + 1) ? $minFirst : ($now - $maxBack + 1) );
  }
  return ($start..$now);  
}
sub to_latex_string {
	return " \\mathrm{TODO: mperiodsThrough} ";
}
package MpmasQl3EqnElements::mperiodsAndAfter;
use Carp qw(croak);
sub  evaluate {
  #my ($self, $database, $context) = @_;
  my $now = $_[0]->[0]->evaluate($_[1], $_[2]); 
  my $maxFor = $_[0]->[1]->evaluate($_[1], $_[2]); 
  my $maxLast = $_[0]->[2]->evaluate($_[1], $_[2]); 
  croak ("Error. Invalid arguments to mperiodsAndAfter\n") unless (defined $now and defined $maxFor and defined $maxLast);
  my $end = $maxFor < $maxLast ? $maxFor : $maxLast;
  
  return ($now..$end);  
}
sub to_latex_string {
	return " \\mathrm{TODO: mperiodsAndAfter} ";
}
package MpmasQl3EqnElements::fromXtoMinYZ;
use Carp qw(croak);
sub  evaluate {
  #my ($self, $database, $context) = @_;
  my $x = $_[0]->[0]->evaluate($_[1], $_[2]); 
  my $y = $_[0]->[1]->evaluate($_[1], $_[2]); 
  my $z = $_[0]->[2]->evaluate($_[1], $_[2]); 
  my $end = $y < $z ? $y : $z;

  return ($x..$end);  
}
sub to_latex_string {
	return " \\mathrm{TODO: fromXtoMinYZ} ";
}

package MpmasQl3EqnElements::setMappingFunction;
use Carp qw(croak);

sub evaluate {
	
	#my ($self,$database, $origContext) = @_;
	
	my $contextList = $_[0]->[0]->evaluate($_[1], $_[2]); #iterator definition
	
	my @ret;
	for my $innerContext (@$contextList) {
		push @ret, $_[0]->[1]->evaluate($_[1], $innerContext);
	}
	return @ret; 
}
#sub to_latex_string {
#	
#	#my ($self) = @_;
#	
#	my $itDef = $_[0]->[0]->to_latex_string(); #iterator definition
#	my $cont =  $_[0]->[1]->to_latex_string();
#	
#	return "\\prod_{$itDef} $cont "; 
#}


#Scalar-returning functions
package MpmasQl3EqnElements::if;
use Carp qw(croak);
sub evaluate {
  #my ($self, $database, $context) = @_;
  my $condition  = $_[0]->[0]->evaluate($_[1], $_[2]);
  if ($condition) {
	return $_[0]->[1]->evaluate($_[1], $_[2]);
  }
  else {
	return $_[0]->[2]->evaluate($_[1], $_[2]);
  }
}
sub to_latex_string {
	#my ($self, $database, $context) = @_;
	my $condition  = $_[0]->[0]->to_latex_string();
	my $string = "\\begin{cases}\n";

	$string .= $_[0]->[1]-> to_latex_string() . " , & ".$condition ." \\ \n"; 
	$string .= $_[0]->[2]-> to_latex_string() . " , & \text{otherwise} \\ \n"; 
	$string .= "\n\\end{cases}";
	return $string;
}

package MpmasQl3EqnElements::ifnull;
use Carp qw(croak);
sub evaluate {
  #my ($self, $database, $context) = @_;
  my $condition  = $_[0]->[0]->evaluate($_[1], $_[2]);
  if (not defined($condition)) {
	return $_[0]->[1]->evaluate($_[1], $_[2]);
  }
  else {
	return $condition
  }
}
sub to_latex_string {
	#my ($self, $database, $context) = @_;
	my $condition  = $_[0]->[0]->to_latex_string();
	my $string = "\\begin{cases}\n";

	$string .= $_[0]->[1]-> to_latex_string() . " , & \\text{otherwise} \\ \n"; 
	$string .= "\n\\end{cases}";
	return $string;
}


package MpmasQl3EqnElements::ifInserter;
use Carp qw(croak);
sub evaluate {
	#my ($self , $database, $context, $subelements, $attname, $sign) = @_;
	if (   $_[0]->[0]->evaluate($_[1], $_[2]) ) {
		return $_[0]->[1]->evaluate($_[1], $_[2], $_[3], $_[4], $_[5]);
	}
	else {
		return $_[0]->[2]->evaluate($_[1], $_[2], $_[3], $_[4], $_[5]);
	}
}
sub to_latex_string {
	#my ($self, $database, $context) = @_;
	my $condition  = $_[0]->[0]->to_latex_string();
	my $string = "\\begin{cases}\n";

	$string .= $_[0]->[1]-> to_latex_string() . " , & ".$condition ." \\ \n"; 
	$string .= $_[0]->[2]-> to_latex_string() . " , & \text{otherwise} \\ \n"; 
	$string .= "\n\\end{cases}";
	return $string;
}


package MpmasQl3EqnElements::isEqual;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
    my  $alt1 = $_[0]->[0]->evaluate($_[1], $_[2]);
    my  $alt2 = $_[0]->[1]->evaluate($_[1], $_[2]);
	return ($alt1 eq $alt2 ? 1 : 0);
}
sub to_latex_string {
	#my ($self) = @_;
	return $_[0]->[0]->to_latex_string() . " = ". $_[0]->[1]->to_latex_string();
}

package MpmasQl3EqnElements::isNotEqual;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
    my  $alt1 = $_[0]->[0]->evaluate($_[1], $_[2]);
    my  $alt2 = $_[0]->[1]->evaluate($_[1], $_[2]);
	return ($alt1 ne $alt2 ? 1 : 0);
}
sub to_latex_string {
	my ($self) = @_;
	return $_[0]->[0]->to_latex_string() . " \neq ". $_[0]->[1]->to_latex_string();
}

package MpmasQl3EqnElements::isIn;
use Carp qw(croak);
use List::Util qw/first/;
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $test = $_[0]->[0]->evaluate($_[1], $_[2]);
	croak ("Error: undefined element in is_element_of /is_part_of\n") unless (defined $test);
	my @array = $_[0]->[1]->evaluate($_[1], $_[2]);
	#croak ("Error: empty set in is_element_of /is_part_of\n (".join(",", @array).")\n") unless ((scalar @array) &&  defined $array[0]);
	return 0 unless ((scalar @array) &&  defined $array[0]);
#	return defined( (not defined $test or $test =~ /\D/) ?  first { $test eq $_ } @array  :  first {   $test == $_ } @array  ) ? 1 : 0;
	return defined( first { $test eq $_ || (not ($_ =~ /\D/) && not($test =~ /\D/ ) && $test == $_) } @array   ) ? 1 : 0;
}
sub to_latex_string {
	my ($self) = @_;
	return $_[0]->[0]->to_latex_string() . " \\in ". $_[0]->[1]->to_latex_string();
}

package MpmasQl3EqnElements::classifyToMultiple;
use Carp qw(croak);
use POSIX qw(ceil);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $value  = $_[0]->[0]->evaluate($_[1], $_[2]);
    my $base = $_[0]->[1]->evaluate($_[1], $_[2]);
	return &ceil($value / $base)  * $base; 
}
package MpmasQl3EqnElements::e;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $value = $_[0]->[0]->evaluate($_[1], $_[2]);
	return exp($value);
}
sub to_latex_string {
	my ($self) = @_;
	return "e^{ ". $_[0]->[0]->to_latex_string()."}" ;
}
package MpmasQl3EqnElements::ln;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $value = $_[0]->[0]->evaluate($_[1], $_[2]);
	return log($value);
}
sub to_latex_string {
	#my ($self) = @_;
	return "\\ln^{ ". $_[0]->[0]->to_latex_string()."}" ;
}
package MpmasQl3EqnElements::concatenate;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my @args = map { $_->evaluate($_[1], $_[2]) } @{$_[0]};
    croak("Trying to concatenate unitialized value\n") if ( not defined ($args[0]));
	return join("", @args);
}
sub to_latex_string {
	#my ($self) = @_;
	return join( "", map {$_->to_latex_string()} @{$_[0]}); ;
}
package MpmasQl3EqnElements::concatOp;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $arg1 = $_[0]->[0]->evaluate($_[1], $_[2]);
    my $arg2 = $_[0]->[1]->evaluate($_[1], $_[2]);
	return join("", $arg1, $arg2);
}
sub to_latex_string {
	#my ($self) = @_;
	return join( "", map {$_->to_latex_string()} @{$_[0]}); ;
}
package MpmasQl3EqnElements::partOfText;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $text = $_[0]->[0]->evaluate($_[1], $_[2]);
    my $start = $_[0]->[1]->evaluate($_[1], $_[2]);
    my $end = $_[0]->[2]->evaluate($_[1], $_[2]);
    
	return substr($text, $start-1, $end-$start+1);
}
sub to_latex_string {
	#my ($self) = @_;
#	return substr($text, $start-1, $end-$start+1)
}

package MpmasQl3EqnElements::lengthOfText;
use Carp qw(croak);
sub  evaluate{
	#my ($self, $database, $context) = @_;
    my $text = $_[0]->[0]->evaluate($_[1], $_[2]);
    
    
	return length($text);
}
sub to_latex_string {
	#my ($self) = @_;
#	return length($text);
}



package MpmasQl3EqnElements::financeFrmRate;
use Carp qw(croak);
sub direct {
	my ($amount_financed, $irate,  $totalTime )= @_;
	return $amount_financed * MpmasQl3EqnElements::annuityFactor::direct($irate,  $totalTime); 
}
sub  evaluate {
	#my ($self, $database, $context) = @_;
	my $amount_financed = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $irate = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $totalTime = $_[0]->[2]->evaluate($_[1], $_[2]);
	return $amount_financed * MpmasQl3EqnElements::annuityFactor::direct($irate,  $totalTime); 
}
sub to_latex_string {
	return " \\mathrm{TODO: financeFrmRate} ";
}
package MpmasQl3EqnElements::annuityFactor;
use Carp qw(croak);
sub direct {
	my ($irate, $totalTime) = @_;
	return (((1 + $irate) ** $totalTime) * $irate) / (((1 + $irate) ** $totalTime) -1);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $irate = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $totalTime = $_[0]->[1]->evaluate($_[1], $_[2]);
	return (((1 + $irate) ** $totalTime) * $irate) / (((1 + $irate) ** $totalTime) -1);
}
sub to_latex_string {
	#my ($self) = @_;
  	my $irate  = $_[0]->[0]->to_latex_string();
	my $totalTime  = $_[0]->[1]->to_latex_string();
	
	#if ($financialFunctionsResolved) {
#		return " $anCapital \\cdot \\frac{\l\eft( 1 + $anInterest \r\ight) ^ {$anLifespan \\cdot $anInterest }}{\\left(1 + $anInterest ^{$anLifespan} -1}";
#	}
#	else {
		return "\\mathrm{annuityFactor}\\left($irate, $totalTime\\right)";
#	}
	
}

package MpmasQl3EqnElements::financeFrmPrincipalPaidAtAge;
use Carp qw(croak);
sub direct {
	my ($amount_financed, $irate, $age, $totalTime )= @_;
	return $amount_financed * ((1 + $irate)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irate, $totalTime ) - $irate);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $amount_financed = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $irate = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $age = $_[0]->[2]->evaluate($_[1], $_[2]);
	my $totalTime = $_[0]->[3]->evaluate($_[1], $_[2]);
	return $amount_financed * ((1 + $irate)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irate, $totalTime ) - $irate);
}
package MpmasQl3EqnElements::financeFrmDepreciationAtAge;
use Carp qw(croak);
sub direct {
	my ($amount_equity, $irateEq, $amount_financed, $irateLoan, $age, $totalTime )= @_;
	return $amount_financed * ((1 + $irateLoan)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irateLoan, $totalTime ) - $irateLoan)
	       + $amount_equity * ((1 + $irateEq)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irateEq, $totalTime ) - $irateEq);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $amount_equity = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $irateEq = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $amount_financed = $_[0]->[2]->evaluate($_[1], $_[2]);
	my $irateLoan = $_[0]->[3]->evaluate($_[1], $_[2]);
	my $age = $_[0]->[4]->evaluate($_[1], $_[2]);
	my $totalTime = $_[0]->[5]->evaluate($_[1], $_[2]);
	return $amount_financed * ((1 + $irateLoan)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irateLoan, $totalTime ) - $irateLoan)
	       + $amount_equity * ((1 + $irateEq)**$age) * (MpmasQl3EqnElements::annuityFactor::direct($irateEq, $totalTime ) - $irateEq);
}
package MpmasQl3EqnElements::financeFrmInterestPaidAtAge ;
use Carp qw(croak);
sub direct {
	my ($amount_financed, $irate, $age, $totalTime )= @_;
	if ($age > 0) {
		return $irate * $amount_financed * MpmasQl3EqnElements::financeFrmTimeValueFactor::direct( $totalTime, $age, $irate);
	}
	else {
		return $irate * $amount_financed;
	}
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $amount_financed = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $irate = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $age = $_[0]->[2]->evaluate($_[1], $_[2]);
	my $totalTime = $_[0]->[3]->evaluate($_[1], $_[2]);
	if ($age > 0) {
		return $irate * $amount_financed * MpmasQl3EqnElements::financeFrmTimeValueFactor::direct( $totalTime, $age, $irate);
	}
	else {
		return $irate * $amount_financed;
	}
}
package MpmasQl3EqnElements::financeFrmTimeValueFactor;
use Carp qw(croak);
sub direct {
	my ( $totalTime, $age, $irate) = @_;
	return 1 if ($age == 0);
	#return 1.0 - ((1.0 + $irate)** $age) * (MpmasQl3EqnElements::annuityFactor::direct($irate, $totalTime) - $irate) / MpmasQl3EqnElements::annuityFactor::direct($irate, $age);
	return  ((1.0 + $irate) **$totalTime - (1.0 + $irate)** $age) / ((1.0 + $irate) **$totalTime - 1.0 );
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $totalTime  = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $age  = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $irate = $_[0]->[2]->evaluate($_[1], $_[2]);
	return 1 if ($age == 0);
	return ((1.0 + $irate) **$totalTime - (1.0 + $irate)** $age) / ((1.0 + $irate) **$totalTime - 1.0 );
}
package MpmasQl3EqnElements::financePVofAnnuityFactor;
use Carp qw(croak);
sub direct {
	my (  $irate, $totalTime) = @_;
	return ( (1  / $irate) - 1 /  ( $irate * (1+$irate) ** $totalTime  ) )  ;
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $irate = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $totalTime  = $_[0]->[1]->evaluate($_[1], $_[2]);
	return ( (1  / $irate) - 1 /  ( $irate * (1+$irate) ** $totalTime  ) )  ;
}
package MpmasQl3EqnElements::round_to;
use Carp qw(croak);
sub direct {
	return sprintf("%.$_[1]f",$_[0]);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	return sprintf("%.".$_[0]->[1]->evaluate($_[1], $_[2])."f",$_[0]->[0]->evaluate($_[1], $_[2]) );
}	
package MpmasQl3EqnElements::floor;
use Carp qw(croak);
sub direct {
	$_[0] =~ /^(-?\d+).?/;
	defined $1 ? $1 : 0;
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $x = $_[0]->[0]->evaluate($_[1], $_[2]);
	$x =~ /^(-?\d+).?/;
	defined $1 ? $1 : 0;
}

package MpmasQl3EqnElements::if_elsif;
use Carp qw(croak);
sub direct {
  while (@_) {
    my $condition = shift @_;
    my $value = shift @_;
    return $value if ($condition);
  }
  return 0;
}
sub evaluate {
  #my ($self, $database, $context) = @_;
	for my $i (@{$_[0]}) {
    my ($condition, $result) =  $i->evaluate($_[1], $_[2]);#conditionThenElement
    if ($condition) {
		return $result;
	}
  }
  return 0;
}
package MpmasQl3EqnElements::if_elsif_set;
use Carp qw(croak);
sub direct {
  while (@_) {
    my $condition = shift @_;
    my $value = shift @_;
    return $value if ($condition);
  }
  return 0;
}
sub evaluate {
  #my ($self, $database, $context) = @_;
   for my $i (@{$_[0]}) {
    my ($condition, $result) =  $i->evaluate($_[1], $_[2]);#conditionThenSetElement
    if ($condition) {
		return @$result;
	}
  }
  return ();
}



package MpmasQl3EqnElements::annuity;
use Carp qw(croak);
sub direct {
    my ($anInterest, $anCapital, $anLifespan) = @_;
    return $anCapital * (( 1 + $anInterest)**$anLifespan *$anInterest) / ((1 +$anInterest)**$anLifespan -1);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
  	my $anInterest  = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $anCapital  = $_[0]->[1]->evaluate($_[1], $_[2]);
	my $anLifespan = $_[0]->[2]->evaluate($_[1], $_[2]);
	return $anCapital * (( 1 + $anInterest)**$anLifespan *$anInterest) / ((1 +$anInterest)**$anLifespan -1);
}
sub to_latex_string {
	#my ($self) = @_;
  	my $anInterest  = $_[0]->[0]->to_latex_string();
	my $anCapital  = $_[0]->[1]->to_latex_string();
	my $anLifespan = $_[0]->[2]->to_latex_string();
	
	#if ($financialFunctionsResolved) {
#		return " $anCapital \\cdot \\frac{\\left( 1 + $anInterest \\right) ^ {$anLifespan \\cdot $anInterest }}{ \\left(1 + $anInterest ^{$anLifespan} -1}";
#	}
#	else {
		return "\\mathrm{annuity}\\left($anInterest, $anCapital, $anLifespan\\right)";
#	}
	
}
package MpmasQl3EqnElements::fvalue;
use Carp qw(croak);
sub direct {
  my $i = $_[0]; #interest
  my $t = $_[1]; #lifetime
  my $q =  1.0 + $i;
  my $fvalue =   $q **$t / ($q ** $t - 1.0) - (1.0 / ($t * ($q - 1.0)));
  return $fvalue;
}
sub evaluate {
	#my ($self, $database, $context) = @_;
  	my $i  = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $t  = $_[0]->[1]->evaluate($_[1], $_[2]);
  my $q =  1.0 + $i;
  my $fvalue =   $q **$t / ($q ** $t - 1.0) - (1.0 / ($t * ($q - 1.0)));
  return $fvalue;
}
sub to_latex_string {
	#my ($self) = @_;
  	my $i  = $_[0]->[0]->to_latex_string();
	my $t  = $_[0]->[1]->to_latex_string();
  
  return " fvalue\\left($i, $t\\right)";
}
package MpmasQl3EqnElements::count;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
  	my @list  = $_[0]->[0]->evaluate($_[1], $_[2]); #set
    return scalar @list;
}

sub to_latex_string {
		#my ($self) = @_;
		return " card\\left(".$_[0]->[0]->to_latex_string()."\\right) ";
}
package MpmasQl3EqnElements::random_number;
use Scalar::Util qw(looks_like_number);
use Carp qw(croak);	
sub direct {
      croak  "Error: Argument ($_[0]) to random_number invalid\n " unless (looks_like_number($_[0]));
      return rand($_[0]);
}
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $x  = $_[0]->[0]->evaluate($_[1], $_[2]);
	croak  "Error: Argument ($_[0]) to random_number invalid\n " unless (looks_like_number($x));
    return rand($x);
}
sub to_latex_string {
	#my ($self) = @_;
	my $x  = $_[0]->[0]->to_latex_string();
    return "rnd\\left($x\\right)";
}
package MpmasQl3EqnElements::var;
use Carp qw(croak);	
sub evaluate {
	#my ($self, $database, $context) = @_;
	return $_[0]->[0]->evaluate($_[1], $_[2]);
}
sub to_latex_string {
	#my ($self) = @_;
	my $x  = $_[0]->[0]->to_latex_string();
    return $x;
}
package MpmasQl3EqnElements::regexp;
use Carp qw(croak);	
sub evaluate {
	#my ($self, $database, $context) = @_;
	return $_[0]->[0]->evaluate($_[1], $_[2]) =~ $_[0]->[1]->evaluate($_[1], $_[2]) ? 1 : 0;
}
sub to_latex_string {
	#my ($self) = @_;
	return $_[0]->[0]->to_latex_string() ." ~ " . $_[0]->[1]->to_latex_string();
}

package MpmasQl3EqnElements::list;
use Carp qw(croak);	
sub evaluate {
	#my ($self, $database, $context) = @_;
	my @list = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $qlist = join (", ", @list);
	return $qlist;	
}
package MpmasQl3EqnElements::qlist;
use Carp qw(croak);	
sub evaluate {
	#my ($self, $database, $context) = @_;
	my @list = $_[0]->[0]->evaluate($_[1], $_[2]);
	my $qlist = "'".join("', '", @list)  ."'"; 
	return $qlist;	
}
package MpmasQl3EqnElements::userWarning;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	print STDERR "#croak: ", $_[0]->[0]->evaluate($_[1], $_[2]), "\n";  
	return 0;
}
sub to_latex_string {
	return "";
}
package MpmasQl3EqnElements::userError;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	print STDERR "#error: ", $_[0]->[0]->evaluate($_[1], $_[2]), "\n";  
	exit(1)
}
sub to_latex_string {
	return "";
}
package MpmasQl3EqnElements::userReport;
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;
	my $result = $_[0]->[1]->evaluate($_[1], $_[2]);
	print STDERR "#report ", $_[0]->[0]->evaluate($_[1], $_[2])," : ",$result, "\n";  
	return $result;
}
sub to_latex_string {
	return "";
}
package MpmasQl3EqnElements::userReportUndef; # only for testing undef proof
use Carp qw(croak);
sub evaluate {
	#my ($self, $database, $context) = @_;

	print STDERR "#report ", $_[0]->[0]->evaluate($_[1], $_[2])," : UNDEF\n";  
	return undef;
}
sub to_latex_string {
	return "";
}

package MpmasQl3EqnElements::sumFunction;
use Carp qw(croak);

sub evaluate {
	use warnings FATAL => qw/uninitialized numeric/;
	#local $SIG{ __DIE__ } = sub { Carp::confess( @_ ) };

	#my ($self,$database, $origContext) = @_;
	
	my $contextList = $_[0]->[0]->evaluate($_[1], $_[2]); #iterator definition
	
	my $sum = 0;
	for my $innerContext (@$contextList) {
		$sum += $_[0]->[1]->evaluate($_[1], $innerContext);
	}
	return $sum; 
}
sub to_latex_string {
	
	#my ($self) = @_;
	
	my $itDef = $_[0]->[0]->to_latex_string(); #iterator definition
	my $cont =  $_[0]->[1]->to_latex_string();
	
	return "\\sum_{$itDef} $cont "; 
}

package MpmasQl3EqnElements::productFunction;
use Carp qw(croak);

sub evaluate {
	
	#my ($self,$database, $origContext) = @_;
	
	my $contextList = $_[0]->[0]->evaluate($_[1], $_[2]); #iterator definition
	
	my $pr = 1;
	for my $innerContext (@$contextList) {
		$pr *= $_[0]->[1]->evaluate($_[1], $innerContext);
	}
	return $pr; 
}
sub to_latex_string {
	
	#my ($self) = @_;
	
	my $itDef = $_[0]->[0]->to_latex_string(); #iterator definition
	my $cont =  $_[0]->[1]->to_latex_string();
	
	return "\\prod_{$itDef} $cont "; 
}


###ACTIONS PERFORMED DIRECTLY DURING MARPA PARSING
package MpmasQl3EqnActions;
use Carp qw(croak);
sub new {
	#somehow necessary
}


#functions for LHS
sub preponePlus {
	#my ($perparse, $self) = @_;
	unshift @{$_[1]}, '+';
	return $_[1];
}
sub mergeFirstLhsTerm {
	#my ($perparse, $self) = @_;
	my $temp = pop @{$_[1]}; #second element is arrayref of second and further lhsTerms
	push @{$_[1]}, @$temp; #merge array with self 
	return $_[1];
}

sub replace_by_one { 
	return bless([1], 'MpmasQl3EqnElements::number');
}

#function for sorting mpmasInsertExpressions into standard order (IMA)
sub mpmasInsertSortMI {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in place swapping to keep blessing of array ref
	my $temp = $mpmasInsertExpArray->[0];
	$mpmasInsertExpArray->[0] = $mpmasInsertExpArray->[1];
	$mpmasInsertExpArray->[1] = $temp;
	$mpmasInsertExpArray->[2] = bless([0], 'MpmasQl3EqnElements::number'); #set to neutral element
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertSortMIA {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in place swapping to keep blessing of array ref
	my $temp = $mpmasInsertExpArray->[0];
	$mpmasInsertExpArray->[0] = $mpmasInsertExpArray->[1];
	$mpmasInsertExpArray->[1] = $temp;
 	return $mpmasInsertExpArray; 
}

sub mpmasInsertSortIM {
	my ($perparse, $mpmasInsertExpArray) =@_;
	#correct order, only add third element
	$mpmasInsertExpArray->[2] = bless([0], 'MpmasQl3EqnElements::number');#set to neutral element
 	return $mpmasInsertExpArray; 
}
#sub mpmasInsertSortIMA { } already correct order

sub mpmasInsertSortAMI {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in place swapping to keep blessing of array ref
	my $temp = $mpmasInsertExpArray->[0];
	$mpmasInsertExpArray->[0] = $mpmasInsertExpArray->[2];
	$mpmasInsertExpArray->[2] = $temp;
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertSortAIM {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in place swapping to keep blessing of array ref
	my $temp = $mpmasInsertExpArray->[0];
	$mpmasInsertExpArray->[0] = $mpmasInsertExpArray->[1];
	$mpmasInsertExpArray->[1] = $mpmasInsertExpArray->[2];
	$mpmasInsertExpArray->[2] = $temp;
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertSortAI {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in-place swapping to keep blessing of array ref
	my $temp = $mpmasInsertExpArray->[0];
	$mpmasInsertExpArray->[0] = $mpmasInsertExpArray->[1];
	$mpmasInsertExpArray->[1] = bless([1], 'MpmasQl3EqnElements::number'); #set to neutral element
	$mpmasInsertExpArray->[2] = $temp; 
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertSortIA {
	my ($perparse, $mpmasInsertExpArray) =@_;
	
	#do in place swapping to keep blessing of array ref
	$mpmasInsertExpArray->[2] = $mpmasInsertExpArray->[1]; 
	$mpmasInsertExpArray->[1] = bless([1], 'MpmasQl3EqnElements::number'); #set to neutral element
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertSortI {
	my ($perparse, $mpmasInsertExpArray) =@_;
	$mpmasInsertExpArray->[1] = bless([1], 'MpmasQl3EqnElements::number'); #set to neutral element
	$mpmasInsertExpArray->[2] = bless([0], 'MpmasQl3EqnElements::number');  #set to neutral element
 	return $mpmasInsertExpArray; 
}
sub mpmasInsertJoinName {
	my ($perparse, $mpmasInserterArray) =@_;
	#join main category and subcategories to one string
	my $string = join(".", $mpmasInserterArray->[0], @{$mpmasInserterArray->[1]});
	$mpmasInserterArray->[0] = $string; #reuse old array to preserve blessing
	$mpmasInserterArray->[1] = undef;
	#delete old subcategory entry, note that the index (if indexed) is and remains at [2]
	return $mpmasInserterArray; 
}

sub mpmasQlInternalAttributeInteger { return "integ";}
sub mpmasQlInternalAttributeObjective { return "orow";}
sub mpmasQlInternalAttributeDescription { return "name";}
sub mpmasQlInternalAttributeDivisible { return "div";}
sub mpmasQlInternalReaderProduct { return "solution * orow";}
1;
