//
//  EXPModel.m
//  dde
//
//  Created by Ashley on 21/07/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "EXPModel.h"
#import "EXPParser.h"
#import "EXPElement.h"
#import "EXPParameterElement.h"
#import "EXPAuxiliaryElement.h"
#import "EXPStateElement.h"
#import "EXPBlockElement.h"
#import "EXPSymbolReference.h"
#import "EXPExpression.h"
#import "EXPUnaryOp.h"
#import "EXPBinOp.h"
#import "EXPAssignmentOp.h"
#import "EXPSymbolTable.h"
#import "EXPExpression.h"
#import "EXPDimensionElement.h"
#import "EXPFunctionElement.h"
#import "EXPOutputElement.h"
#import "EXPPrintElement.h"
#import "EXPPlotElement.h"
#import "EXPSwitchElement.h"
#import "EXPTableElement.h"
#import "EXPError.h"
#import "PLTMatrix.h"
#import "GPTGraph.h"

@implementation EXPModel

- (id) initWithMemory:(unsigned int)newMemorySize
{
	self = [super initWithMemory:newMemorySize];
	if (self!=nil) {
		_name = nil;
		
		_block = [[EXPBlockElement alloc] init];
		_printHeadings = NO;
		_options = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 
					@"0.0", @"tstart",
					@"10.0", @"tstop",
					@"1.0e-2", @"tstep",
					@"1.0e-08", @"epsilon",
					@"1.0e-1", @"outstep",
					@"2000", @"histsize", 
					@"aqt", @"device",
					@"", @"plotfile", 
					@"No", @"printheader", 
				    nil];
		
//		_file = fopen("/Users/Ashley/Projects/Objective-C/MathsProgs/DDESolver/dde/output.lst", "w");
/*		NSString *dirName = [@"~/Projects/Objective-C/MathsProgs/DDESolver/dde-examples" stringByExpandingTildeInPath];
		NSFileManager *fileManager = [NSFileManager defaultManager];
		BOOL isDir;
		if ([fileManager fileExistsAtPath:dirName isDirectory:&isDir] && isDir) {
			NSString *pathName = [dirName stringByAppendingPathComponent:@"output.lst"];
			_file = fopen([pathName UTF8String], "w");
		} else {
			_file = NULL;
		} */
		
		_listfileName = nil;
		_matrix = nil;
		_history = nil;
		_delays = nil;
		_switchAddresses = nil;
		_mapAddresses = nil;
		
		_outputValues = nil;
		_nEvaluations = 0;

	}
	return self;
}

- (void) setSymbolTable:(id)symbolTable
{
	[symbolTable retain];
	[_symbolTable release];
	_symbolTable = symbolTable;
}

- (id) symbolTable
{
	return _symbolTable;
}

- (void) setBlock:(id)block
{
	[block retain];
	[_block release];
	_block = block;
}

- (id) block
{
	return _block;
}

- (void) setName:(NSString *)name
{
	[name retain];
	[_name release];
	_name = name;
}

- (NSString *)name
{
	return _name;
}

- (void) setPrintAll:(BOOL)printAll
{
	_printAll = printAll;
}

- (BOOL) printAll
{
	return _printAll;
}

- (void) setPrintHeadings:(BOOL)printHeadings
{
	_printHeadings = printHeadings;
}

- (BOOL) printHeadings
{
	return _printHeadings;
}

- (void) setOutputFormat:(id)outputFormat
{
	[outputFormat retain];
	[_outputFormat release];
	_outputFormat = outputFormat;
}

- (id) outputFormat
{
	return _outputFormat;
}

- (void) setOutputMatrix:(id)matrix
{
	[matrix retain];
	[_matrix release];
	_matrix = matrix;
}

- (id) outputMatrix
{
	return _matrix;
}

- (void) setRowCounter:(int)row
{
	_outputRowCounter = row;
}

- (int) rowCounter
{
	return _outputRowCounter;
}

- (void) setListFilename:(id)name
{
	[name retain];
	[_listfileName release];
	_listfileName = name;
}

- (id) listFilename
{
	return _listfileName;
}

- (void) setVariableNames:(id)variableNames
{
	[variableNames retain];
	[_variableNames release];
	_variableNames = variableNames;
}

- (id) variableNames
{
	return _variableNames;
}

- (void) setAuxiliaryNames:(id)auxiliaryNames
{
	[auxiliaryNames retain];
	[_auxiliaryNames release];
	_auxiliaryNames = auxiliaryNames;
}

- (id) auxiliaryNames
{
	return _auxiliaryNames;
}

- (id) option:(NSString *)name
{
	id option = [_options valueForKey:name];
	return option;
}

- (void) setOption:(id) value forName:(NSString *)name
{
	if ([_options valueForKey:name]!=nil) {
		[_options setValue:value forKey:name];
	}
}

- (void) setConstants:(NSDictionary *)constants
{
	NSEnumerator *enumerator = [constants keyEnumerator];
	NSString *key;
	EXPSymbolTable *table = [self symbolTable];
//	EXPVirtualMachine *machine = [self machine];
 
	while ((key = [enumerator nextObject])) {
		NSString *value = [constants objectForKey:key];
		EXPNamedElement *element = [table symbolForName:key];
		if (element!=nil && [[element elementType] isEqualToString:@"parameter"]) {
//			 [element setValue:[value doubleValue]];
			int address = [element address];
			[self setPtr:address + [self gp]];
			[self putDouble:[value doubleValue]];
		}
//		NSLog(@"%@", value);
	}
}

- (int) nEvaluations
{
	return _nEvaluations;
}

- (BOOL) processOption:(EXPParser *)parser
{
	NSString *optionName = [[NSString alloc] initWithString:[parser symbol]];
	[parser readSymbol];

	NSString *optionValue;
	if ([parser testSymbol:@"="]) {
		optionValue = [[NSString alloc] initWithString:[parser symbol]];
		if (!([parser isLiteral] || [parser isStringLiteral])) {
			[optionName release];
			[optionValue release];
			return NO;
		}

		[parser readSymbol];
	} else {
		optionValue = @"";
	}

	BOOL success = YES;
	
	[self setOption:optionValue forName:optionName];
	
	[optionName release];
	[optionValue release];
	
	return success;
}

/*- (BOOL) openFiles
{
	BOOL success = YES;
	EXPVirtualMachine *machine = [self machine];
	NSArray *outputters = [self outputters];
	
	int i;
	for(i=0; i<[outputters count]; i++) {
		EXPPrintElement *outputter = [outputters objectAtIndex:i];
		NSString *filename = [outputter outputFilename];
		if ([[outputter elementType] isEqualToString:@"printer"] && ![filename isEqualToString:@""]) {
			int fileNum = [machine attachFile:filename type:@"w"];
			if (fileNum<0)  {
				success = NO;
				break;
			}
			[outputter setFileNumber:fileNum];
		}
	}
	
	return success;
}

- (BOOL) closeFiles
{
	BOOL success = YES;
	EXPVirtualMachine *machine = [self machine];
	NSArray *outputters = [self outputters];
	
	int i;
	for(i=0; i<[outputters count]; i++) {
//		EXPPrintElement *outputter = [outputters objectAtIndex:i];
		[machine detachFile:i];
	}
	
	return success;
} */

- (int) nConstants
{
	return nConstants;
}

- (void) setNConstants:(int) newNConstants
{
	nConstants = newNConstants;
}

- (int) nAuxiliaries
{
	return nAuxiliaries;
}

- (void) setNAuxiliaries:(int) newNAuxiliaries
{
	nAuxiliaries = newNAuxiliaries;
}

- (int) nVariables
{	
	return nVariables;
}

- (void) setNVariables:(int) newNVariables
{
	nVariables = newNVariables;
}

- (void) setNSwitches:(int)nSwitches
{
	_nSwitches = nSwitches;
}

- (int) nSwitches
{
	return _nSwitches;
}

/*- (void) setTables:(tableStructure **)newTables
{
	tables = newTables;
}

- (tableStructure **)tables
{
	return tables;
} */

- (void) setDelays:(id)delays
{
	[delays retain];
	[_delays release];
	_delays = delays;
}

- (id) delays
{
	return _delays;
} 

/* - (void) setDelays:(id)delays
{
	[delays retain];
	[_delays release];
	_delays = delays;
}

- (id) delays
{
	return _delays;
}  */

- (void) setOutputters:(id)outputters
{
	[outputters retain];
	[_outputters release];
	_outputters = outputters;
}

- (id) outputters
{
	return _outputters;
}

- (void) setSwitchAddresses:(id)switchAddresses
{
	[switchAddresses retain];
	[_switchAddresses release];
	_switchAddresses = switchAddresses;
}

- (id) switchAddresses
{
	return _switchAddresses;
}

- (void) setMapAddresses:(id)mapAddresses
{
	[mapAddresses retain];
	[_mapAddresses release];
	_mapAddresses = mapAddresses;
}

- (id) mapAddresses
{
	return _mapAddresses;
}

/*- (void) setHistory:(id <history, NSObject>)history
{
	_history = history;
}

- (id) history
{
	return _history;
}

- (void) setNHistory:(int)nHistory
{
	_nHistory = nHistory;
}

- (int) nHistory
{
	return _nHistory;
}

- (void) setNSwitches:(int)nSwitches
{
	_nSwitches = nSwitches;
}

- (int) nSwitches
{
//	EXPVirtualMachine *machine = [self machine];
	return [self nSwitches];
} */

- (int) nLag
{
	return 1;
}

- (void) setVariableStartAddress:(int)variableStartAddress
{
	_variableStartAddress = variableStartAddress;
}

- (int) variableStartAddress
{
	return _variableStartAddress;
}

- (void) setGradientStartAddress:(int)gradientStartAddress
{
	_gradientStartAddress = gradientStartAddress;
}

- (int) gradientStartAddress
{
	return _gradientStartAddress;
}

- (void) setParameterStartAddress:(int)parameterStartAddress
{
	_parameterStartAddress = parameterStartAddress;
}

- (int) parameterStartAddress
{
	return _parameterStartAddress;
}

- (void) setAuxiliaryStartAddress:(int)auxiliaryStartAddress
{
	_auxiliaryStartAddress = auxiliaryStartAddress;
}

- (int) auxiliaryStartAddress
{
	return _auxiliaryStartAddress;
}

- (void) setTablesStartAddress:(int)tablesStartAddress
{
	_tablesStartAddress = tablesStartAddress;
}

- (int) tablesStartAddress
{
	return _tablesStartAddress;
}

- (void) setSwitchesExecutionAddress:(int)switchesExecutionAddress
{
	_switchesExecutionAddress = switchesExecutionAddress;
}

- (int) switchesExecutionAddress
{
	return _switchesExecutionAddress;
}

- (void) setSwitchesStartAddress:(int)switchesStartAddress
{
	_switchesStartAddress = switchesStartAddress;
}

- (int) switchesStartAddress
{
	return _switchesStartAddress;
}

- (void) setMapExecutionAddress:(int)mapExecutionAddress
{
	_mapExecutionAddress = mapExecutionAddress;
}

- (int) mapExecutionAddress
{
	return _mapExecutionAddress;
}

- (void) setOutputtersExecutionAddress:(int)outputtersExecutionAddress
{
	_outputtersExecutionAddress = outputtersExecutionAddress;
}

- (int) outputtersExecutionAddress
{
	return _outputtersExecutionAddress;
}

- (void) setOutputCounter:(int)outputCounter
{
	_outputCounter = outputCounter;
}

- (int) outputCounter
{
	return _outputCounter;
}

- (void) setDimensionStartAddress:(int)dimensionStartAddress
{
	_dimensionStartAddress = dimensionStartAddress;
}

- (int) dimensionStartAddress
{
	return _dimensionStartAddress;
}

- (void) setParameterInitialisationAddress:(int)parameterInitialisationAddress
{
	_parameterInitialisationAddress = parameterInitialisationAddress;
}

- (int) parameterInitialisationAddress
{
	return _parameterInitialisationAddress;
}

- (void) setVariableInitialisationAddress:(int)variableInitialisationAddress
{
	_variableInitialisationAddress = variableInitialisationAddress;
}

- (int) variableInitialisationAddress
{
	return _variableInitialisationAddress;
}

- (void)setPrintHeaderAddress:(int)printHeaderAddress
{
	_printHeaderAddress = printHeaderAddress;
}

- (int) printHeaderAddress
{
	return _printHeaderAddress;
}

- (void)setPrintFooterAddress:(int)printFooterAddress
{
	_printFooterAddress = printFooterAddress;
}

- (int) printFooterAddress
{
	return _printFooterAddress;
}

- (void) setAuxiliaryComputationAddress:(int)auxiliaryComputationAddress
{
	_auxiliaryComputationAddress = auxiliaryComputationAddress;
}

- (int) auxiliaryComputationAddress
{
	return _auxiliaryComputationAddress;
}

- (void) setVariableComputationAddress:(int)variableComputationAddress
{
	_variableComputationAddress = variableComputationAddress;
}

- (int) variableComputationAddress
{
	return _variableComputationAddress;
}

- (void) setWashupExecutionAddress:(int)washupExecutionAddress
{
	_washupExecutionAddress = washupExecutionAddress;
}

- (int) washupExecutionAddress
{
	return _washupExecutionAddress;
}

- (void) setDataTop:(int)dataTop
{
	_dataTop = dataTop;
}

- (int) dataTop
{
	return _dataTop;
}

- (void) setCodeTop:(int)codeTop
{
	_codeTop = codeTop;
}

- (int) codeTop
{
	return _codeTop;
}

- (void) setStartTimeAddress:(int)startTimeAddress
{
	_startTimeAddress = startTimeAddress;
}

- (int) startTimeAddress
{
	return _startTimeAddress;
}

- (void) setStopTimeAddress:(int)stopTimeAddress
{
	_stopTimeAddress = stopTimeAddress;
}

- (int) stopTimeAddress
{
	return _stopTimeAddress;
}

- (void) setTimeAddress:(int)timeAddress
{
	_timeAddress = timeAddress;
}

- (int) timeAddress
{
	return _timeAddress;
}

- (void) setTime:(double)time
{
//	unsigned short int *memory = [self memory];
	int address = [self timeAddress];
	*(double *)(&memory[address + gp]) = time;
}

- (double) time
{
	int address = [self timeAddress];
	return *(double *)(&memory[address + gp]);
}

//			case DELAY:
- (id) delayTrap:(id)object machine:(EXPVirtualMachine *)machine
{
	double delayTime;
	double initialValue;
	int markValue;

//	unsigned int addr = *(unsigned int *)(&memory[pc]);
//	pc += M;
	unsigned int addr = acc.i;
	sp -= N;
	markValue = (int)(*(double *)(&memory[sp]));
	sp -= N;
	initialValue = *(double *)(&memory[sp]);
	sp -= N;
	delayTime = *(double *)(&memory[sp]);

//	int address = [self timeAddress];
//	double time = *(double *)(&memory[address + gp]);
//	address = [self startTimeAddress];
//	double startTime = *(double *)(&memory[address + gp]);
	double time = [self time];
	double startTime = [self startTime];
	int returnCode = 0;

	if (delayTime<startTime) {
		acc.x = initialValue;
	} else if (delayTime>time) {
		printf("Oops! Delay time %g is greater than time %g\n", delayTime, time);
		returnCode = -1;
	} else {
		acc.x = [[self history] pastValue:addr time:delayTime mark:markValue];
	}
//				printf("%04x %12.5f %12.5f %12.5f %4d\n", addr, acc.x, initialValue, delayTime, markValue);

	NSNumber *returnValue = [NSNumber numberWithInt:returnCode];
	return returnValue;
}

//			case DELAY1:
- (id) delayTrap1:(id)object machine:(EXPVirtualMachine *)machine
{
	double delayTime;
	double initialValue;
	int markValue;

//	unsigned int addr = *(unsigned int *)(&memory[pc]);
//	pc += M;
	unsigned int addr = acc.i;
	sp -= N;
	markValue = (int)(*(double *)(&memory[sp]));
	sp -= N;
	initialValue = *(double *)(&memory[sp]);
	sp -= N;
	delayTime = *(double *)(&memory[sp]);
	sp -= M;
	addr += *(unsigned int *)(&memory[sp]);

//	int address = [self timeAddress];
//	double time = *(double *)(&memory[address + gp]);
//	address = [self startTimeAddress];
//	double startTime = *(double *)(&memory[address + gp]);
	double time = [self time];
	double startTime = [self startTime];

	int returnCode = 0;
	if (delayTime<startTime) {
		acc.x = initialValue;
	} else if (delayTime>time) {
		printf("Oops! Delay time %g is greater than time %g\n", delayTime, time);
		returnCode = -1;
	} else {
		acc.x = [[self history] pastValue:addr time:delayTime mark:markValue];
	}
//				printf("%04x %12.5f %12.5f %12.5f %4d\n", addr, acc.x, initialValue, delayTime, markValue);

	NSNumber *returnValue = [NSNumber numberWithInt:returnCode];
	return returnValue; 
}

- (void) setStartTime:(double)startTime
{
	int address = [self startTimeAddress];
	*(double *)(&memory[address + gp]) = startTime;
}

- (double) startTime
{
	return 0.0;
}

- (void) setStopTime:(double)stopTime
{
	int address = [self stopTimeAddress];
	*(double *)(&memory[address + gp]) = stopTime;
}

- (double) stopTime
{
	return 0.0;
}

- (void) dumpVariables
{
	int i;
	for(i=[self parameterStartAddress]; i<[self tablesStartAddress]; i+=N) {
		printf("%04x: %g\n", i, *(double *)(&memory[gp+i]));
	}
}

- (BOOL) writeToFile:(NSString *)fileName
{
	BOOL success = YES;

	const char *filename = [fileName UTF8String];
	FILE *f = fopen(filename, "w");
	
	if (f==NULL) {
		return NO;
	}
	
	fprintf(f, "#!/usr/local/bin/dderun\n");
	fprintf(f, "%d %d \n", 1, 0);

	fprintf(f, "%d %d %d \n", [self nConstants], [self nVariables], [self nSwitches]);
	
	EXPBlockElement *block = [self block];
	NSArray *constants = [block parameters];
	int i;
	int numConstants = [constants count];
	if (numConstants>0) {
		fprintf(f, "<constants>\n");
		for(i=0; i<numConstants; i++) {
			fprintf(f, "    <constant>\n");
			EXPParameterElement *constant = [constants objectAtIndex:i];
			fprintf(f, "        <name>%s</name>\n", [[constant name] UTF8String]);
			fprintf(f, "        <address>%04</address>x ", [constant address]);
			NSString *label = [constant label];
			if ([label length]>0) {
				fprintf(f, "        <label>%s</label>\n", [label UTF8String]);
			}
			fprintf(f, "    </constant>\n");
		}
		fprintf(f, "</constants>\n");
	}
	
	NSArray *variables = [block variables];
	for(i=0; i<[variables count]; i++) {
		EXPStateElement *variable = [variables objectAtIndex:i];
		fprintf(f, "%s %1d %08x %08x ", [[variable name] UTF8String], (int)[variable isDelayVariable], [variable address], [variable gradientAddress]);
		NSString *label = [variable label];
		if ([label length]>0) {
			fprintf(f, "\"%s\"\n", [label UTF8String]);
		} else {
			fprintf(f, "\"Variable%d\"", i);
		}
		fprintf(f, "\n");
	}
/*	NSArray *switches = [block switches];
	for(i=0; i<[switches count]; i++) {
	} */
	fprintf(f, "%08x %08x \n", [self switchesExecutionAddress], [self mapExecutionAddress]);
	fprintf(f, "%08x\n", [self outputtersExecutionAddress]);
	fprintf(f, "%08x\n", [self printHeaderAddress]);
	fprintf(f, "%08x\n", [self printFooterAddress]);
	fprintf(f, "%08x\n", [self parameterInitialisationAddress]);
	fprintf(f, "%08x\n", [self variableInitialisationAddress]);
	fprintf(f, "%08x\n", [self auxiliaryComputationAddress]);
	fprintf(f, "%08x\n", [self variableComputationAddress]);
	fprintf(f, "%08x\n", [self washupExecutionAddress]);
	
//	unsigned short int *memory = [self memory];
	int m = [self codeTop];
	int j = 0;
	for(i=0; i<[self codeTop]; i++) {
		if (j==0) {
			fprintf(f, "%01x ", (m>=8)?8:m);
		}
		fprintf(f, "%04x ", memory[i]);
		if ((j++)==7) {
			fprintf(f, "\n");
			j = 0;
		}
		m--;
	}
	
	fprintf(f, "\n");
	fclose (f);
	
	return success;
}

- (void) outputHeader
{
}

//	Print out string to file replacing \n with newline. Doesn't replace any other escape sequences though...
- (void) outputString:(NSString *)string toFile:(FILE *)f
{
	NSMutableString *str = [[NSMutableString alloc] initWithString:string];
	NSRange range = NSMakeRange(0, [str length]);
	const unichar ch = (const unichar)0x0a;
	NSString *rep = [[NSString alloc] initWithCharacters:&ch length:1];
	[str replaceOccurrencesOfString:@"\\n" withString:rep options:(unsigned)0 range:range];
	[rep release];
	fprintf(f, "%s", [str UTF8String]);
	[str release];
}

- (double) pastValue:(int)i time:(double)t mark:(int)markno
{
	PLTMatrix *matrix = [self outputMatrix];
	NSArray *delays = [self delays];
	
	EXPStateElement *var = [delays objectAtIndex:i];
	int variableStartAddress = [self variableStartAddress];
	int addr = [var address];
	int j = (addr - variableStartAddress)/N;
	int nRows = [self rowCounter];

//	Check this!!
	double pastValue = 0.0;
	if (t<=[matrix cellAtRow:0 andCol:0]) {
		pastValue = [matrix cellAtRow:0 andCol:j];
//	} else if () {
	} else {
		int ii = 0;
		int jj = nRows;
		do {
			int k = (ii + jj)/2;
			double tk = [matrix cellAtRow:k andCol:0];
			if (t<tk) jj = k;
			if (t>=tk) ii = k;
		} while (jj>(ii+1));
//	Hermite interpolation??
		pastValue = [matrix cellAtRow:jj andCol:j];
	}
	
	return pastValue;
}

- (BOOL) print
{
	BOOL success = YES;
	
	PLTMatrix *matrix = [self outputMatrix];
	int m;
	int row;
	int n = [self rowCounter];
	int i;
	
	if ([self printAll]) {
//		NSString *outType = @"lst";
//		NSString *outType = @"html";
		NSString *outType = [self outputFormat];
		if ([self printHeadings]) {
			NSArray *headings = [matrix columnHeadings];
			for(i=0; i<[headings count]; i++) {
				printf("%s\t", [[headings objectAtIndex:i] UTF8String]);
			}
			printf("\n");
		}
		NSString *outString = [matrix stringRepresentationUsingType:outType outputPoints:n];
		printf("%s\n", [outString UTF8String]);
	}
	
//	printf("%s\n", [[matrix stringRepresentationUsingType:@"txt" outputPoints:n] UTF8String]);
	FILE *file=NULL;
	NSString *filename = [self listFilename];
	file = fopen([[filename stringByExpandingTildeInPath] UTF8String], "w");

	if (file!=NULL) {
		for(row = 0; row<n; row++) {
			for(i=0; i<=2*[self nVariables]; i++) {
				fprintf(file, "\t%lg", [matrix cellAtRow:row andCol:i]);
			}
	
			fprintf(file, "\n"); 
		}
		fclose(file);
	}
	

	NSArray *outputters = [self outputters];
	double *points;
	for(m=0; m<[outputters count]; m++) {
		EXPOutputElement *outputter = [outputters objectAtIndex:m];
		NSString *outputterType = [outputter elementType];
		int nItems = 0;
		NSArray *expressions = [outputter expressionList];
		int nn;
		for(nn=0; nn<[expressions count]; nn++) {
			int m = 1;
			NSArray *indices = [[expressions objectAtIndex:nn] indices];
			int p;
			for(p=0; p<[indices count]; p++) {
				m *= [[indices objectAtIndex:p] nElements];
			}
			nItems += m;
		}
		FILE *f;
		if ([outputterType isEqualToString:@"printer"]) { 
			EXPPrintElement *printer = (EXPPrintElement *)outputter;
			NSString *filename = [printer outputFilename];
			if (filename==nil) {
				f = stdout;
			} else {
				f = fopen([filename UTF8String], "w");
			}
			if ([printer header]!=nil) {
				[self outputString:[printer header] toFile:f];
				fprintf(f, "\n");
			}
		} else if ([outputterType isEqualToString:@"plotter"]) {
			points = (double *)calloc(nItems*n, sizeof(double));
		}
		
		[self setHistory:self];
		
		for(row = 0; row<n; row++) {
			double t = [matrix cellAtRow:row andCol:0];
			[self setTime:t];
	
			int variableBaseAddress = [self variableStartAddress];
			int addr = variableBaseAddress;
//			int nVariables = [self nVariables];
			for(i=0; i<nVariables; i++) {
				double *p = (double *)(&memory[addr + [self gp]]);
				double state = [matrix cellAtRow:row andCol:(i+1)];
				*p = state;
				addr += (sizeof(double)/sizeof(unsigned short int));
			}

			int variableComputationAddress = [self variableComputationAddress];
			[self setPc:variableComputationAddress];
			[self runFromPc];
			if ([self exitCode]!=0) {
				success = NO;
				break;
			}
//			[self dumpDoubles:variableBaseAddress to:variableBaseAddress + nVariables*N];

			int oldSp = [self sp];
			int outputtersExecutionAddress = [outputter address];
			[self setPc:outputtersExecutionAddress];
			[self runFromPc];
			if ([self exitCode]!=0) {
				success = NO;
				break;
			}
			int pp = oldSp;
			int p;

			if ([outputterType isEqualToString:@"printer"]) {
				EXPPrintElement *printer = (EXPPrintElement *)outputter;
				if ([printer leader]!=nil) {
//					fprintf(f, "%s", [[printer leader] UTF8String]);
					[self outputString:[printer leader] toFile:f];
				}
				for(p=0; p<nItems; p++) {
					if (p>0) {
						NSString *sep = [printer separator];
						if (sep==nil) {
							fprintf(f, " ");
						} else {
//							fprintf(f, "%s", [sep UTF8String]);
							[self outputString:sep toFile:f];
						}
					}
					double *q = (double *)(&memory[pp]);
					fprintf(f, "%g", *q);
					pp += N;
				}
				if ([printer trailer]!=nil) {
//					fprintf(f, "%s", [[printer trailer] UTF8String]);
					[self outputString:[printer trailer] toFile:f];
				}
				fprintf(f, "\n");
			} else if ([outputterType isEqualToString:@"plotter"]) {
				for(p=0; p<nItems; p++) {
					double *q = (double *)(&memory[pp]);
					points[row*nItems + p] = *q;
					pp += N;
				}
			}
			[self setSp:oldSp];
		}
		if ([outputterType isEqualToString:@"plotter"]) {
			EXPPlotElement *plotter = (EXPPlotElement *)outputter;
			[plotter plot:points nLines:nItems nPoints:n];
			free(points);
		} else if ([outputterType isEqualToString:@"printer"]) {
			EXPPrintElement *printer = (EXPPrintElement *)outputter;
			if ([printer footer]!=nil) {
//				fprintf(f, "%s", [[printer footer] UTF8String]);
				[self outputString:[printer footer] toFile:f];
				fprintf(f, "\n");
			}
			if (f!=stdout) {
				fclose(f);
			}
		}
	}
	
	return success;
}

- (void) output:(double *)s gradients:(double *)g time:(double)t
{
//	printf("output:time: %lx %lx %g\n", (int)s, (int)_file, t);
//	EXPVirtualMachine *machine = [self machine];
//	printf("output:time = %g\n", t);
	
	PLTMatrix *matrix = [self outputMatrix];
	int n = [self nVariables];
	int m = [self nAuxiliaries];
	if (matrix!=nil) {
		int row = [self rowCounter];
		int col = 0;
		[matrix setCell:t atRow:row andCol:col++];
		int i;
		for(i=0; i<n; i++) {
			[matrix setCell:s[i] atRow:row andCol:col++];
		}
/*		for(i=0; i<n; i++) {
			[matrix setCell:g[i] atRow:row andCol:col++];
		} */
		[self setTime:t];
		[self setPc:[self variableComputationAddress]];
		[self runFromPc];
		int address = [self auxiliaryStartAddress];
		unsigned short int *mem = [self memory];
		for(i=1; i<m; i++) {
			address += N;
			double val = *(double *)(&mem[address + gp]);
			[matrix setCell:val atRow:row andCol:col++];
		}
		
		row++;
		[self setRowCounter:row];
	}
	
/*	if ([self printAll]) {
		printf("%g ", t);
		int i;
		for(i=0; i<n; i++) {
			printf("%g ", s[i]);
		}
		printf("\n");
	} */
	
/*	int outputtersExecutionAddress = [self outputtersExecutionAddress];
	[self setPc:outputtersExecutionAddress];
	[self runFromPc]; */

}

- (void) initConstants
{
	double outstep = [[self option:@"outstep"] doubleValue];
	double tstart  = [[self option:@"tstart"] doubleValue];
	double tstop   = [[self option:@"tstop"] doubleValue];
	int nRows = (int)abs(1.25*(tstop - tstart)/outstep);
//	int nCols = 2*[self nVariables] + 1;
//		printf("%g %g %g %d %d\n", outstep, tstart, tstop, nRows, nCols);
	int nCols = [self nVariables] + [self nAuxiliaries]/* + 1*/;
	PLTMatrix *matrix = [[PLTMatrix alloc] initWithRows:nRows andCols:nCols];
	NSArray *colHeaders = [[self variableNames] arrayByAddingObjectsFromArray:[self auxiliaryNames]];
	[matrix setColumnHeadings:colHeaders];
	[self setOutputMatrix:matrix];
	[matrix release];

	[self setTime:0.0];

	[self setPc:[self parameterInitialisationAddress]];
	[self runFromPc];
	
}

- (void) stateScale:(double *)scale
{
/*	NSArray *sheets = [self sheets];
	int nSheets = [sheets count];
	
	int i;
	for(i=0; i<nSheets; i++) {
		EXPSheet *sheet = [sheets objectAtIndex:i];
		NSArray *variables = [sheet variables];
		
		int j;
		for (j=0; j<[variables count]; j++) {
			EXPStateElement *vbl = [variables objectAtIndex:j];
			int addr = [vbl address];
			scale[addr] = [vbl scaleFactor];
		}
	}
	 */
}

- (void) initState:(double *)state
{
	[self setTime:0.0];

	int variableInitialisationAddress = [self variableInitialisationAddress];
	[self setPc:variableInitialisationAddress];
	[self runFromPc];
//	[self dumpVariables];

	int i;
	int addr = [self variableStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + [self gp]]);
		state[i] = *p;
		addr += (sizeof(double)/sizeof(unsigned short int));
	}

	_nEvaluations = 0;
	
}

- (void) switchFunctions:(double *)sw newState:(double *)state time:(double)t
{
//	printf("In switchFunctions:newState:time: at t= %g\n", t);
	
//	EXPVirtualMachine *machine = [self machine];
	[self setTime:t];
	
//	unsigned short int *memory = [self memory];
	int i;
	int addr = [self variableStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + [self gp]]);
		*p = state[i];
		addr += (sizeof(double)/sizeof(unsigned short int));
	}

	[self setPc:[self switchesExecutionAddress]];
	[self runFromPc];
	
	addr = [self switchesStartAddress];
	for(i=0; i<[self nSwitches]; i++) {
		double *p = (double *)(&memory[addr + [self gp]]);
		sw[i] = *p;
//		printf("sw[%d] = %g", i, sw[i]);
		addr += (sizeof(double)/sizeof(unsigned short int));
	}
//	printf("\n");

}

- (void) map:(double *)state time:(double)t switchNo:(int)swno
{
	int mapExecutionAddress = [self mapExecutionAddress];
//	EXPVirtualMachine *machine = [self machine];

//	unsigned short int *memory = [self memory];
	int i;
	int addr = [self variableStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + [self gp]]);
		*p = state[i];
		addr += (sizeof(double)/sizeof(unsigned short int));
	}
	
	[self setAccInt:swno];

	[self setPc:mapExecutionAddress];
	[self runFromPc];

	addr = [self variableStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + [self gp]]);
		state[i] = *p;
		addr += (sizeof(double)/sizeof(unsigned short int));
	}

}

- (BOOL) gradients:(double *)grad forState:(double *)state atTime:(double)t
{
	[self setTime:t];

#ifdef DEBUG	
	printf("Gradients. t = ");
	printf("%12.3f: ", t); 
#endif

	int i;
	int baseAddress = [self gp];
	int addr = [self variableStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + baseAddress]);
		*p = state[i];
		addr += (sizeof(double)/sizeof(unsigned short int));
	}

	[self setPc:[self variableComputationAddress]];
	[self runFromPc];
	if (exitCode!=0) {
		return NO;
	}
	
	addr = [self gradientStartAddress];
	for(i=0; i<[self nVariables]; i++) {
		double *p = (double *)(&memory[addr + baseAddress]);
#ifdef DEBUG	
		printf(" %15.6f", *p);
#endif
		grad[i] = *p;
		addr += (sizeof(double)/sizeof(unsigned short int));
	}
#ifdef DEBUG	
	printf("\n");
#endif

//	printf("%g %g %g\n", t, state[0], grad[0]);

//	[self dumpVariables];

	_nEvaluations++;
	
	BOOL success = [self exitCode]==0;
	return success;
}

- (void) storeHistory:(double *)his gradientsHistory:(double *)ghis gradients:(double *)gradients state:(double *)state time:(double)t
{
	NSArray *delays = [self delays];

	int i;
	for(i=0; i<[delays count]; i++) {
		EXPStateElement *var = [delays objectAtIndex:i];
		int n = [var numberOfElements];
		int addr = [var delayNumber];
		int j;
		for(j=0; j<n; j++) {
			his[addr] = state[addr];
			ghis[addr] = gradients[addr];
			addr++;
		}
	} 

}

- (void) dealloc
{
	[_name release];
	[_matrix release];
	
	[_variableNames release];
	[_auxiliaryNames release];
	
	[_symbolTable release];
	[_block release];
	[_options release]; 
	
	[_switchAddresses release];
	[_mapAddresses release];
	[_delays release];
	[_outputters release];
	
	[_listfileName release];
/*	if (_file!=NULL) {
		fclose(_file);
	} */
	
	[super dealloc];
}

@end