
//	Title:		SystemModel.cpp
//	Purpose:	Contains all SystemModel related code.
//	Authors:	IntEng gp.
//	Source:		C++ texts & IntEng gp.
//	Date:		28-8-2008 (start), 1-12-08 (updated)

#include "stdafx.h"		// rqd. for precompiled header info.
#include <fstream>		// rqd. since WriteSystemModelDataToFile() uses fin and fout file stream objs.
#include <iostream> 
#include <string.h>
#include <math.h>		// rqd. for sqrt() and pow() in AddConnection()
#include <vector>
#include <list>
using namespace std;
#include "SystemModel.h"
#include "DiagramEngDoc.h"			// rqd. since GetDocumentGlobalFn() is called in CSystemModel::AddConnection()
#include "NumericalSolverDialog.h"	// rqd. since a CNumericalSolverDialog obj. used in DlgWndParameterInput()
#include "NewtonMethod.h"			// rqd. since a CNewtonSolver obj. is used in NewtonSolver().


CSystemModel::CSystemModel(void)
{
	// Init. 
	m_iDrawAnnotationFlag = 1;
	m_strModelName = "system_model";
	
	// Time parameters
	m_dTimeStart = 0.0;
	m_dTimeStop = 1.0;
	m_dTimeStepSize = 0.1;
	m_strTimeStepType = "Fixed-step";

	// Integration parameters
	m_dATOL = 0.0001;
	m_dRTOL = 0.0001;
	m_strIntegrationMethod = "Euler (1st Order)"; // "Euler (1st Order)" or "Runge-Kutta (4th Order)"
	m_strWarning = "OK";
	m_iModelStatusFlag = 0;

	// -- Message Box
	/*CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	sMsgTemp.Format("\n CSystemModel::CSystemModel()\n");
	sMsg += sMsgTemp;
	AfxMessageBox(sMsg, nType, nIDhelp);
	*/

	// -- Block list
	/*CBlock *p_block1 = new CBlock;			// new returns & of mem. loc. to hold a CBlock obj.
	list<CBlock*>::iterator it_blk;			// iterator for list of CBlock-ptrs

	m_lstBlock.push_front(p_block1);		// insert a new element at the beginning of the list (a ptr-to-CBlock)
	*/
	// -- Check Block List
	/*for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		sMsgTemp.Format(" CSystemModel::CSystemModel(), m_lstBlock item name = %s\n",(*it_blk)->GetName());
		sMsg += sMsgTemp;
	}*/

	// -- Connection list
	/*CConnection *p_conn = new CConnection;	// new returns & of mem. loc. to hold a CConnection obj.
	list<CConnection*>::iterator it_conn;	// iterator for list of CConnection-ptrs

	m_lstConnection.push_front(p_conn);		// insert a new element at the beginning of the list (a ptr-to-CConnection)
	*/
	// -- Check Connection List
	/*for(it_conn = m_lstConnection.begin(); it_conn != m_lstConnection.end(); it_conn++)
	{
		sMsgTemp.Format(" CSystemModel::CSystemModel(), signal_name = %s\n",(*it_conn)->GetSignal()->GetName());
		sMsg += sMsgTemp;
	}
	AfxMessageBox(sMsg, nType, nIDhelp);
	*/
}

CSystemModel::~CSystemModel(void)
{
	// Delete block list
	DeleteBlockList();

	// Delete connection list
	DeleteConnectionList();

	// Delete source-to-output-block and source-to-loop-repeated-block lists
	DeleteSourceOutputLoopLists();

	// Delete annotation list
	DeleteAnnotationList();
}

CString CSystemModel::GetModelName(void) const
{
	//AfxMessageBox("\n CSystemModel::GetModelName()\n", MB_OK, 0);

	return m_strModelName;
}

void CSystemModel::SetModelName(CString name)
{
	m_strModelName = name;
}

list<CBlock*>& CSystemModel::GetBlockList()
{
	return m_lstBlock;
}

void CSystemModel::DrawSystemModel(CDC *pDC)
{
	int pen_color = RGB(0,0,0);				// White = RGB(255,255,255), Black = RGB(0,0,0)
	int pen_width = 1;						// thin = 1
	CString sMsg;							// main msg string
	UINT nType = MB_OK;						// style of msg. box
	UINT nIDhelp = 0;						// help context ID for the msg.
	list<CAnnotation*>::iterator it_an;		// m_lstAnnotation iterator
	list<CBlock*>::iterator it_blk;			// m_lstBlock iterator
	list<CConnection*>::iterator it_con;	// m_lstConnection iterator

	// Iterate through the SystemModel lists drawing the stored entities.

	// -- SET UP THE PEN AND pDC ----------------
	// Create the pen
	CPen lpen(PS_SOLID, pen_width, pen_color);

	// Select the pen as the drawing obj. 
	// The value returned by SelectObject() is a ptr to the obj. being replaced, i.e. an old-pen-ptr.
	CPen *pOldPen = pDC->SelectObject(&lpen);

	// -- DRAW BLOCKS ---------------------------
	list<CBlock*> blk_list = GetBlockList();	// local block list

	// Check size of block list
	//sMsg.Format("\n CSystemModel::DrawSystemModel(), size = %d\n", blk_list.size());
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Iterate through the block list
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		(*it_blk)->DrawBlock(pDC);		// DrawBlock() called on the ptr-to-CBlock
	}

	// -- DRAW CONNECTIONS ----------------------
	list<CConnection*> con_list = GetConnectionList();			// local connection list
	list<CConnection*> con_list_e = GetConnectionListError();	// local connection list in error (disconnected connections)

	// Iterate through the connection list
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		(*it_con)->DrawConnection(pDC);
	}

	// Prepare the device context for the disconnected connections.
	ModelErrorPenColor(&pen_color);
	CPen lpen2(PS_SOLID, pen_width, pen_color);
	pDC->SelectObject(&lpen2);

	// Iterate through the connection list in error (i.e. disconnected connections)
	for(it_con = con_list_e.begin(); it_con != con_list_e.end(); it_con++)
	{
		(*it_con)->DrawConnection(pDC);
	}

	// -- DRAW ANNOTATIONS ----------------------
	// Iterate through the annotation list
	if(m_iDrawAnnotationFlag == 1)
	{
		for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
		{
			(*it_an)->DrawAnnotation(pDC);
		}
	}

	// -- RESET THE PREV. PEN -------------------
	pDC->SelectObject(pOldPen);
}

int CSystemModel::AddConnection(CPoint ptFrom, CPoint ptTo)
{
	int valid = 0;			// validity of connection: (0) not long enough, (1) long enough
	double con_length;		// length of connection
	double dDeltaLength;	// std def delta length
	double min_length;		// min connection length
	CString sMsg;			// main msg string
	CString sMsgTemp;		// temp msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// Print msg.
	//sMsg.Format("\n CSystemModel::AddConnection(), from (%d,%d) to (%d,%d)\n", ptFrom.x, ptFrom.y, ptTo.x, ptTo.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Set min length
	dDeltaLength = GetDocumentGlobalFn()->GetDeltaLength();
	min_length = 0.5*dDeltaLength;

	// CScrollView-related change: nullify negative coordinate components
	if(ptTo.x < 0)
	{
		ptTo.x = 0;
	}
	if(ptTo.y < 0)
	{
		ptTo.y = 0;
	}

	// Check to make sure that connection is greater than the rqd. min. connection length.
	con_length = sqrt(pow((ptFrom.x - ptTo.x),2) + pow((ptFrom.y - ptTo.y),2));
	if(con_length < min_length)
	{
		sMsgTemp.Format("\n CSystemModel::AddConnection()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Connection line from point (%d, %d) to point (%d, %d).\n", ptFrom.x, ptFrom.y, ptTo.x, ptTo.y);
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Connection length = %lf < %lf = min_length.       \n", con_length, min_length);
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Add new connection >= min_length.\n");
		sMsg += sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);

		valid = 0;		// connection not long enough
		return valid;
	}
	else
	{
		valid = 1;		// connection long enough
	
		// Create a new CConnection obj.
		CConnection *pCon = new CConnection(ptFrom, ptTo);
	
		// Add the new connection to the connection list
		m_lstConnection.push_back(pCon);

		// Get a ptr. to the doc. to call the CDocument member fn. SetModifiedFlag(), and mark the doc. as modified (TRUE).
		GetDocumentGlobalFn()->SetModifiedFlag(TRUE);
	}

	return valid;
}

list<CConnection*> &CSystemModel::GetConnectionList()
{
	// Return the connection list by reference
	return m_lstConnection;
}

list<CConnection*> &CSystemModel::GetConnectionListError()
{
	// Return the connection list by reference
	return m_lstConnectionError;
}

void CSystemModel::UpdateConnectionPointHead(CPort *port)
{
	list<CConnection*>::iterator it_con;
	
	// Iterate through the list of connections to check for connection end pt. updating due to port associations
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		if((*it_con)->GetRefToPort() == port)	// if m_pRefToPort = current port address
		{
			(*it_con)->SetConnectionPointHead(port->GetPortPosition());
		}
	}
}

void CSystemModel::UpdateConnectionPointTail(CPort *port)
{
	list<CConnection*>::iterator it_con;
	
	// Iterate through the list of connections to check for connection end pt. updating due to port associations
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		if((*it_con)->GetRefFromPort() == port)	// if m_pRefFromPort = current port address
		{
			(*it_con)->SetConnectionPointTail(port->GetPortPosition());
		}
	}
}

void CSystemModel::UpdateConnectionPointTailToBendPoint(CPoint *bend_pt)
{
	list<CConnection*>::iterator it_con;
	
	// Iterate through the list of connections 
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		// If current connection tail point is associated with another connection's bend point
		if((*it_con)->GetRefFromPoint() == bend_pt)	
		{
			// Update the tail point (member variable) with the new bend point position.
			(*it_con)->SetConnectionPointTail(*bend_pt);
		}
	}
}

void CSystemModel::DlgWndParameterInput()
{
	int input = 0;			// flag denoting correct input
	CString	sMsg;			// string to be displayed
	CString	sMsgTemp;		// temp string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// Create a dialog object. of class CNumericalSolverDialog : public CDialog
	CNumericalSolverDialog oDlg;	

	// Set the dialog class vars using the CSystemModel class vars
	oDlg.m_dTimeStart = m_dTimeStart;
	oDlg.m_dTimeStop = m_dTimeStop;
	oDlg.m_dTimeStepSize = m_dTimeStepSize;
	oDlg.m_strTimeStepType = m_strTimeStepType;
	oDlg.m_strIntegrationMethod = m_strIntegrationMethod;
	oDlg.m_dATOL = m_dATOL;
	oDlg.m_dRTOL = m_dRTOL;
	oDlg.m_strWarning = m_strWarning;

	// While not correct input
	while(input == 0)
	{
		// Return val of DoModal() fn of ancestor class CDialog is checked to determine which btn was clicked.
		if(oDlg.DoModal() == IDOK) 
		{	
			// Assign CNumericalSolverDialog variable values to CSystemModel variable values.
			m_dTimeStart = oDlg.m_dTimeStart;
			m_dTimeStop = oDlg.m_dTimeStop;
			m_dTimeStepSize = oDlg.m_dTimeStepSize;
			m_strTimeStepType = oDlg.m_strTimeStepType;
			m_strIntegrationMethod = oDlg.m_strIntegrationMethod;
			m_dATOL = oDlg.m_dATOL;
			m_dRTOL = oDlg.m_dRTOL;
			m_strWarning = oDlg.m_strWarning;

			// Print msg with variable value.
			//sMsg.Format("\n CSystemModel::DlgWndParameterInput(), m_strIntegrationMethod = %s\n", m_strIntegrationMethod);
			//AfxMessageBox(sMsg, nType, nIDhelp);
		}
		
		// Check consistency of time-based paras.
		if((m_dTimeStart >= 0) && (m_dTimeStart < m_dTimeStop) 
			&& (m_dTimeStepSize > 0) && (m_dTimeStepSize <= (m_dTimeStop - m_dTimeStart)))
		{
			input = 1;
		}
		else
		{
			input = 0;
			// Print error msg.
			sMsg.Format("");	// reset the msg since within a while loop
			sMsgTemp.Format("\n CSystemModel::DlgWndParameterInput()\n\n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" Input error! Require the following: \n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" 0 <= t_init < t_final \n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" and \n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" 0 < delta_t <= (t_final - t_init) \n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" Please re-enter the correct time-based values.\n");
			sMsg += sMsgTemp;
			AfxMessageBox(sMsg, nType, nIDhelp);
		}
	} // end while
}

int CSystemModel::ValidateModel()
{
	int i;	
	int error1 = 0;			// error in model blocks: (0) no error, (1) error
	int error2 = 0;			// error in model ports: (0) no error, (1) error
	int error3 = 0;			// error in mode connections: (0) no error, (1) error
	int error_flag = 0;		// error flag returned if there are any build errors
	int n_flashes = 3;		// no. of times red flashes after black
	int n_its;				// no. of loop iterations as a fn of n_flashes
	BOOL bkgd_erase = TRUE;	// specifies whether the background is to be erased
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp string
	DWORD nMilliSec = 200;	// 32 bit unsigned integer: no. of milliseconds
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg 
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn();	// declare pDoc to be a ptr to CDiagramEngDoc.

	// -- REORDER BLOCK LIST: Source, I/O, Output
	OrderBlockList();

	// -- VALIDATE SYSTEM MODEL
	error1 = ValidateModelBlocks();
	error2 = ValidateModelBlockPorts();
	error3 = ValidateModelConnections();
	
	// -- INFORM USER OF END OF BUILD MODEL STAGE
	if((error1 == 1) || (error2 == 1) || (error3 == 1))
	{
		error_flag = 1;

		// Afx msg.
		sMsgTemp.Format("\n CSystemModel::ValidateModel().  \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n WARNING!: Build model process ending.  \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Please take note of errors flashing in red.  \n");
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);

		// -- FLASH MODEL BUILD ERRORS 
		n_its = n_flashes*2 + 1;

		for(i=1; i<n_its; i++)
		{
			if(i%2 == 1)
			{
				SetModelStatusFlag(0);	// results in ModelErrorPenColor() leaving pen_color unchanged: black by default.
			}
			else if(i%2 == 0)
			{
				SetModelStatusFlag(1);	// results in ModelErrorPenColor() setting pen_color to red.
			}
			
			// Get the position of the first view in the list of views for the current doc.
			POSITION posn = pDoc->GetFirstViewPosition();

			// Get a pointer to the view identified by posn.
			CView *pView = pDoc->GetNextView(posn);

			// Invalidate the entire client area of CWnd using the pView pointer-to-CView.
			pView->Invalidate(bkgd_erase);

			// Update the client by sending a WM_PAINT msg. if the update region is not empty.
			pView->UpdateWindow();
			
			// Suspend execution of the current thread for no. of milliseconds specified.
			Sleep(nMilliSec);	

		}// end for
	}
	else
	{
		//sMsgTemp.Format("\n CSystemModel::ValidateModel()    \n");
		//sMsg += sMsgTemp;
		//sMsgTemp.Format("\n No build errors.  \n");
		//sMsg += sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);

		// Delete any saved source-to-output or source-to-loop tour block lists
		DeleteSourceOutputLoopLists();

		// Determine if the model has feedback loops, i.e. source-to-loop tours
		m_iNSourceLoopTours = 0;	
		m_iNSourceOutputTours = 0;
		DetermineModelFeedbackLoops();

		if(m_iNSourceLoopTours > 0)
		{
			//sMsg.Format("\n Note! The number of source-to-loop tours in the model is: %d.   \n", m_iNSourceLoopTours);
			//AfxMessageBox(sMsg, nType, nIDhelp);
		}
	}
	
	// -- CLEAR CONNECTIONS IN ERROR
	m_lstConnectionError.clear(); // Note: if this is not cleared, then grouped item deletion will result in a crash.

	// -- RESET MODEL STATUS AND REDRAW
	SetModelStatusFlag(0);
	pDoc->SetModifiedFlag(TRUE);					// prompt the user to save 
	pDoc->UpdateAllViews(NULL);						// this fn calls OnDraw to redraw the system model.

	return error_flag;
}

int CSystemModel::ValidateModelBlocks()
{
	int error = 0;			// error in model
	int n_source = 0;		// no. of model source blocks
	int n_output = 0;		// no. of model output blocks
	int screen_out = 0;		// flag to screen out non-functional blocks
	CString blk_name;		// blk name
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp msg.
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn(); // declare pDoc to be a ptr to CDiagramEngDoc.
	list<CBlock*>::iterator it_blk;

	// CHECK THE NO. OF SOURCE BLOCKS AND OUTPUT BLOCKS IN THE MODEL
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		blk_name = (*it_blk)->GetBlockName();

		if((blk_name == "constant_block") || (blk_name == "linear_fn_block") || (blk_name == "signal_generator_block"))
		{
			n_source++;
		}

		if(blk_name == "output_block")
		{
			n_output++;
		}
	}

	if((n_source == 0) || (n_output == 0))
	{
		// Print msg with variable value.
		sMsgTemp.Format("\n CSystemModel::ValidateModelBlocks()    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n WARNING!: Block error.   \n");
		sMsg += sMsgTemp;

		if(n_source == 0)
		{
			sMsgTemp.Format("\n At least 1 Source block should be present: inserting automatically.  ");
			sMsg += sMsgTemp;
			pDoc->ConstructLinearFnBlock();		// automatically insert a LinearFn block (as an example)
		}
		if(n_output == 0)
		{
			sMsgTemp.Format("\n At least 1 Output block should be present: inserting automatically.  \n");
			sMsg += sMsgTemp;
			pDoc->ConstructOutputBlock();		// automatically insert an Output block (as an example)
		}
		AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box

		// Mark the document as changed, and then redraw given the insertion of new blocks above.
		pDoc->SetModifiedFlag(TRUE);			// prompt the user to save 
		pDoc->UpdateAllViews(NULL);				// this fn calls OnDraw to redraw the system model.
		
		error = 1;	// flag indicates that model has a block error
	}

	// DELETE BLOCKS FOR WHICH FUNCTIONALITY DOES NOT EXIST
	it_blk = m_lstBlock.begin();
	while(it_blk != m_lstBlock.end())
	{
		// Get block name to screen out relevant non-functional blocks
		blk_name = (*it_blk)->GetBlockName();

		if((blk_name == "subsystem_block") || (blk_name == "subsystem_in_block") || (blk_name == "subsystem_out_block") || (blk_name == "transfer_fn_block"))
		{
			// Print message on first occurrence only.
			if(screen_out == 0)
			{
				sMsg.Format("");
				sMsgTemp.Format("\n CSystemModel::ValidateModelBlocks()    \n");
				sMsg += sMsgTemp;
				sMsgTemp.Format("\n Deleting non-functional blocks.   \n");
				sMsg += sMsgTemp;
				AfxMessageBox(sMsg, nType, nIDhelp);
				screen_out = 1;
			}

			// Delete block
			delete *it_blk;						// delete actual block pointed to by it_blk
			it_blk = m_lstBlock.erase(it_blk);	// delete element at offset it_blk in list (that held the block)
		}
		else // only increment the iterator if there were no deletion
		{
			it_blk++;
		}	
	}

	return error;
}

int CSystemModel::ValidateModelBlockPorts()
{
	int error = 0;			// error in model
	int n_in_dc = 0;		// no. of disconnected inputs
	int n_out_dc = 0;		// no. of disconnected outputs
	int port_connected;		// port connection status (0) disconnected, (1) connected
	int print_msg = 1;		// print msg. flag
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp msg.
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;					// block iterator
	list<CConnection*>::iterator it_con;			// connection iterator
	vector<CPort*>::iterator it_port;				// port iterator
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn();	// declare pDoc to be a ptr to CDiagramEngDoc.

	// -- ITERATE THROUGH BLOCK LIST
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		// Get vecs of input and output ports
		vector<CPort*> vec_input_ports = (*it_blk)->GetVectorOfInputPorts();
		vector<CPort*> vec_output_ports = (*it_blk)->GetVectorOfOutputPorts();

		// -- CHECK CONNECTIVITY OF INPUT PORTS 
		for(it_port = vec_input_ports.begin(); it_port != vec_input_ports.end(); it_port++)
		{
			port_connected = 0;

			for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
			{
				if((*it_port) == (*it_con)->GetRefToPort())
				{
					port_connected = 1;
					break;
				}
			}
			if(port_connected == 0)
			{
				error = 1;				// flag indicates that model has a block port error
				n_in_dc++;				// increment the no. of disconnected inputs
			}
		}// end port

		// -- CHECK CONNECTIVITY OF OUTPUT PORTS 
		for(it_port = vec_output_ports.begin(); it_port != vec_output_ports.end(); it_port++)
		{
			port_connected = 0;

			for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
			{
				if((*it_port) == (*it_con)->GetRefFromPort())
				{
					port_connected = 1;
					break;
				}
			}
			if(port_connected == 0)
			{
				error = 1;				// flag indicates that model has a block port error
				n_out_dc++;				// increment the no. of disconnected outputs
			}
		}// end port
	}// end block


	// -- SET THE MODEL STATUS FLAG AND REDRAW DOC
	if(error == 1)
	{
		SetModelStatusFlag(error);			// model status flag = 1, => a port disconnection error
		pDoc->SetModifiedFlag(TRUE);		// prompt the user to save 
		pDoc->UpdateAllViews(NULL);			// this fn calls OnDraw to redraw the system model.
	}

	// -- PRINT ERROR MSG.
	if((error == 1) && (print_msg == 1))
	{
		sMsgTemp.Format("\n CSystemModel::ValidateModelBlockPorts()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n WARNING!: Block port error.\n");
		sMsg += sMsgTemp;
		if(n_in_dc > 0)
		{
			sMsgTemp.Format("\n Total no. of disconnected input ports = %d.  ", n_in_dc);
			sMsg += sMsgTemp;
		}
		if(n_out_dc > 0)
		{
			sMsgTemp.Format("\n Total no. of disconnected output ports = %d.  ", n_out_dc);
			sMsg += sMsgTemp;
		}
		AfxMessageBox(sMsg, nType, nIDhelp);
	}

	return error;
}

int CSystemModel::GetModelStatusFlag() const
{
	return m_iModelStatusFlag;
}

int CSystemModel::ValidateModelConnections()
{
	int error = 0;			// error in model
	int n_discon = 0;		// no. of disconnected connections
	int head_connected = 0;	// head connection status (0) disconnected, (1) connected
	int print_msg = 1;		// print msg. flag
	int	tail_connected = 0;	// tail connection status (0) disconnected, (1) connected
	CPoint tail_pt;			// connection tail point
	CPort *from_port = NULL;// port from which connection emanates
	CPort *to_port = NULL;	// port to which connection is connected
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp msg.
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;					// block iterator
	list<CConnection*>::iterator it_con;			// connection iterator
	list<CConnection*>::iterator it_con2;			// connection iterator of nested loop
	list<CPoint>::iterator it_pt;					// point iterator
	vector<CPort*>::iterator it_port;				// port iterator
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn();	// declare pDoc to be a ptr to CDiagramEngDoc.

	// -- CLEAR CONNECTION LIST THAT RECORDS ERRORS, BUT DON'T DELETE WHAT THE CONTAINED POINTERS POINT AT.
	m_lstConnectionError.clear();

	// -- ITERATE THROUGH ALL CONNECTIONS
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		// Init. the connected state, for the current connection, to disconnected.
		head_connected = 0;
		tail_connected = 0;

		// Get connection reference from and to ports
		from_port = (*it_con)->GetRefFromPort();
		to_port = (*it_con)->GetRefToPort();
	
		// -- ITERATE THROUGH ALL BLOCKS
		for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
		{
			// Get vector of input and output ports
			vector<CPort*> vec_input_ports = (*it_blk)->GetVectorOfInputPorts();
			vector<CPort*> vec_output_ports = (*it_blk)->GetVectorOfOutputPorts();

			// -- ITERATE THROUGH VECTORS OF PORTS
			for(it_port = vec_input_ports.begin(); it_port != vec_input_ports.end(); it_port++)
			{
				if(to_port == (*it_port))
				{
					head_connected = 1;
					break;	// break from it_port
				}
			}// end it_port
			
			for(it_port = vec_output_ports.begin(); it_port != vec_output_ports.end(); it_port++)
			{
				if(from_port == (*it_port))
				{
					tail_connected = 1;
					break;	// break from it_port
				}
			}// end it_port

			if((head_connected == 1) && (tail_connected == 1))
			{
				break;	// break from it_blk
			}
		}// end it_blk
		

		// -- CHECK THAT THE CONNECTION TAIL POINT IS CONNECTED TO ANOTHER CONNECTION'S BEND POINT
		tail_pt = (*it_con)->GetConnectionPointTail();	// current connection's tail point

		for(it_con2 = m_lstConnection.begin(); it_con2 != m_lstConnection.end(); it_con2++)
		{
			// Other connection's bend points list
			list<CPoint> &bend_pts_list = (*it_con2)->GetConnectionBendPointsList();

			for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
			{
				if(tail_pt == *it_pt)	// if primary connection's tail point is on another connection's bend point
				{
					tail_connected = 1;
					break;	// break from it_pt
				}
			}// end it_pt

			if(tail_connected == 1)
			{
				break;	// break from NESTED it_con2
			}
		}// end it_con (nested)
		
		
		// -- IF CONNECTION DISCONNECTED, ADD IT TO THE ERROR LIST
		if((head_connected == 0) || (tail_connected == 0))
		{
			// Set error flag, cnt, and record
			error = 1;
			n_discon++;
			m_lstConnectionError.push_back(*it_con);
		}
	}// end it_con


	// -- SET THE MODEL STATUS FLAG AND REDRAW DOC
	if(error == 1)
	{
		// Mark the document as changed, and then redraw.
		SetModelStatusFlag(1);			// model status flag = 1, => a port disconnection error
		pDoc->SetModifiedFlag(TRUE);	// prompt the user to save 
		pDoc->UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
	}


	// -- PRINT ERROR MSG.
	if((print_msg == 1) && (n_discon > 0))
	{
		sMsgTemp.Format("\n CSystemModel::ValidateModelConnections() \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n WARNING!: Connection error.\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Total no. of disconnected connections = %d.  ", n_discon);
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);
	}

	return error;
}

void CSystemModel::ModelErrorPenColor(int *pen_color)
{
	// Note: incoming argument &pen_color passed

	// If the model build resulted in an error
	if(m_iModelStatusFlag == 1)
	{
		*pen_color = RGB(255,0,0);	// White = RGB(255,255,255), Black = RGB(0,0,0)	
	}
}

void CSystemModel::SetModelStatusFlag(int status)
{
	m_iModelStatusFlag = status;
}

void CSystemModel::DetermineModelFeedbackLoops()
{
	int i;							// row index
	int j;							// col index
	int ncols;						// number of columns of block connectivity matrix
	int nrows;						// number of rows of block connectivity matrix
	int **BlockConMatrix = NULL;	// block connectivity matrix
	CPort *from_port = NULL;		// port from which a connection emanates
	CPort *to_port = NULL;			// port to which a connection is attached
	list<CBlock*>::iterator it_blk;	
	list<CBlock*> &blk_list = GetBlockList();
	list<CConnection*>::iterator it_con;
	list<CPoint>::iterator it_pt;
	vector<CPort*>::iterator it_port;
	
	// Get the size of the block connectivity matrix
	nrows = blk_list.size();
	ncols = nrows;

	// MEMORY ALLOC
	BlockConMatrix = new int *[nrows];
	for(i=0; i<nrows; i++)
	{
		BlockConMatrix[i] = new int[ncols];		// allocate an array of ints, return & to BlockConMatrix[i]
	}

	// Init. block connectivity matrix
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			BlockConMatrix[i][j] = 0;
		}
	}
	
	// Set reference from port of connections that are connected to bend points
	SetRefFromPortOfConnectionFromBendPoint();

	// CONSTRUCT BLOCK-TO-BLOCK CONNECTIVITY MATRIX
	// Iterate over all blocks
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		vector<CPort*> &vec_of_output_ports = (*it_blk)->GetVectorOfOutputPorts();	

		// Iterate over output ports
		for(it_port = vec_of_output_ports.begin(); it_port != vec_of_output_ports.end(); it_port++)
		{
			// Iterate over all connections
			for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
			{
				// Get the reference from port of the current connection
				from_port = (*it_con)->GetRefFromPort();			
			
				// If the connection's from_port is the block's output port, 
				// i.e. if the connection is attached to the block DIRECTLY, or INDIRECTLY via a bend pt.
				if(from_port == *it_port)				
				{
					// Get the input port to which the connection is attached
					to_port = (*it_con)->GetRefToPort();			

					// Get the reference to block of this port
					CBlock &to_block = to_port->GetRefToBlock();		

					// Assign matrix values if from-block is connected to to-block
					AssignBlockConnectivityMatrixValues(*it_blk, &to_block, BlockConMatrix);
				}
			}
		}		
	}

	// Print the block connectivity matrix
	//PrintMatrix(BlockConMatrix, nrows, ncols);

	// Before DetermineModelFeedbackLoops() was called, all ref-from-ports of connections attached to bend pts
	// were NULL, so now reset them to NULL, to leave the program state as it was previously.
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		if((*it_con)->GetRefFromPoint() != NULL)	// if connection attached to a bend POINT
		{
			(*it_con)->SetRefFromPort(NULL);		// set its ref-from-port to NULL (since not attached to a block)
		}
	}

	// Check the block connectivity matrix for feedback loops
	DetermineLoopsFromConnectivityMatrix(BlockConMatrix, nrows, ncols);

	// MEMORY DELETE
	if(BlockConMatrix != NULL)
	{
		for(i=0; i<nrows; i++)
		{
			delete [] BlockConMatrix[i];
		}
	}
	delete [] BlockConMatrix;

	return;
}

void CSystemModel::SetRefFromPortOfConnectionFromBendPoint()
{
	int ref_from_port_flag;			// flag indicating whether m_pRefFromPort was set
	CPort *from_port = NULL;		// port from which primiary connection emanates
	list<CPoint>::iterator it_pt;
	list<CConnection*>::iterator it_con;
	list<CConnection*>::iterator it_con_bend_pt;


	// Put connections emanating from bend points into a list
	list<CConnection*> connections_from_bend_pt_list;
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		if((*it_con)->GetRefFromPoint() != NULL)	// if there is a ref-from POINT for this connection
		{
			connections_from_bend_pt_list.push_back(*it_con);
		}
	}

	// Set the reference-from-port for connections emanating from bend points.
	it_con_bend_pt = connections_from_bend_pt_list.begin();
	while(connections_from_bend_pt_list.size() > 0)
	{
		// Init. ref_from_port_flag: (0) no m_pRefFromPort was set, (1) m_pRefFromPort was set.
		ref_from_port_flag = 0;

		// Iterate over PRIMARY connection list to find bend points with SECONDARY connections attached to them
		for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
		{
			list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();	// get the bend_pts_list
			
			// Iterate over bend points list of the current primary connection
			for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
			{
				// Determine connectivity of a secondary connection's tail pt. to a primary connection's bend pt.
				if(((*it_con_bend_pt)->GetConnectionPointTail().x == (*it_pt).x) && ((*it_con_bend_pt)->GetConnectionPointTail().y == (*it_pt).y))
				{
					from_port = (*it_con)->GetRefFromPort();	// get the primary connection's ref-from-port

					// A ref-from-port of a PRIMARY connection is NULL if the primary connection is not directly
					// connected to a block port.
					// A ref-from-port of a TERTIARY connection is NULL if it is connected to a bend pt. of a
					// SECONDARY connection that has not yet had its m_pRefFromPort assigned (to be that of its 
					// primary connection).
					if(from_port != NULL)
					{
						// Set the m_pRefFromPort of the SECONDARY connection that is attached to a PRIMARY
						// connection's bend point, to be the same as the PRIMARY connection's m_pRefFromPort.
						(*it_con_bend_pt)->SetRefFromPort(from_port);
						
						// Erase the connection from the connections_from_bend_pt_list since its m_pRefFromPort
						// has now been set to that of its PARENT (primary) connection.
						connections_from_bend_pt_list.erase(it_con_bend_pt);
						
						ref_from_port_flag = 1;	// m_pRefFromPort has now been set.
						
						break;	// break from it_pt (bend pts of primary connections)
					}
				}
			}// end for it_pt

			if(ref_from_port_flag == 1)
			{
				break;			// break from it_con (primary connections)
			}
		}// end for it_con

		// If a ref-from-port was assigned to a secondary connection emanating from a bend pt., then this connection
		// was erased (as above), so set the starting pt. of iteration to the beginning of the now newly reduced list.
		if(ref_from_port_flag == 1)
		{
			it_con_bend_pt = connections_from_bend_pt_list.begin();
		}
		else	// else increment the iterator of the connections_from_bend_pt_list.
		{	
			it_con_bend_pt++;
		}
	}// end while
}

void CSystemModel::AssignBlockConnectivityMatrixValues(CBlock *from_block, CBlock *to_block, int **BlockConMatrix)
{
	int i = 0;		// row index: "from" block
	int j = 0;		// col index: "to" block
	list <CBlock *>::iterator it_blk;
	list<CBlock*> &blk_list = GetBlockList();

	// Obtain the row index, i.e. the "from" block
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		if(from_block == (*it_blk))
		{
			break;
		}
		i++;	// increment the i-th "from" block row index
	}

	// Obtain the column index, i.e. the "to" block
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		if(to_block == (*it_blk))
		{
			break;
		}
		j++;	// increment the j-th "to" block col index
	}

	// The connection from the "from" block to the "to" block is denoted by unity.
	BlockConMatrix[i][j] = 1;	
}

void CSystemModel::DetermineLoopsFromConnectivityMatrix(int **A, int nrows, int ncols)
{
	int i;					// index of i-th column of vector
	int n_nodes;			// number of initial nodes in node_vec, i.e., source nodes only, NOT ALL NODES.
	int source_flag;		// flag indicating the presence of source nodes
	int *node_vec = NULL;	// node vector that contains only the source node(s)
	int *tour_vec = NULL;	// vec that holds node-to-node tours, of size total number of nodes = ncols.
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp msg.
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// MEMORY ALLOC
	node_vec = new int[ncols];	// allocate enough space for all nodes, but only emplace source nodes.
	tour_vec = new int[ncols];	// allocate enough space for all nodes, since max tour length contains all nodes

	// Get the initial vector of source blocks/nodes
	source_flag = DetermineInitialNodeVector(A, nrows, ncols, node_vec, n_nodes);

	if(source_flag == -1)
	{
		sMsgTemp.Format("\n DetermineLoopsFromConnectivityMatrix(), node_vec error.\n");
		sMsg+=sMsgTemp;
		sMsgTemp.Format("\n No source nodes found: source_flag = %d\n", source_flag);
		sMsg+=sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);
	}
	else if(source_flag == 0)
	{
		//PrintVector(node_vec, ncols);
	}

	// Setup the initial tour vector by assigning unique initial negative values to the node entries
	// to the tour vector indicating no feasible tour found yet.
	for(i=0; i<ncols; i++)
	{
		tour_vec[i] = -(i+1);	
	}
	//PrintVector(tour_vec, ncols);

	// Construct node-arc tours
	if(source_flag == 0)
	{
		BuildTour(A, nrows, ncols, node_vec, n_nodes, tour_vec);
	}

	// MEMORY DELETE
	delete [] node_vec;
	delete [] tour_vec;

	return;
}

int CSystemModel::DetermineInitialNodeVector(int **A, int nrows, int ncols, int *node_vec, int &n_nodes)
{
	int i;
	int j;
	int offset = 0;		// node_vec offset 
	int col_sum = 0;	// sum of col elements

	// A node on row i, is connected to a node on col j. 
	// So if there is a col of zeros, then that col number represents a source node,
	// since no node is connected to it in the forward-direction sense.

	// Assign initial negative values 
	for(i=0; i<ncols; i++)
	{
		node_vec[i] = -1;
	}

	// Iterate across the cols and down the rows
	for(j=0; j<ncols; j++)
	{
		col_sum = 0;
		for(i=0; i<nrows; i++)
		{
			col_sum = col_sum + A[i][j];	// col sum

		}
		if(col_sum == 0)					
		{
			node_vec[offset] = j;	// place source node in node_vec
			offset++;
		}
	}

	// Assign the number of source nodes in the node_vec
	n_nodes = offset;

	// Return a flag indicating whether a row of zeros was found
	if(offset == 0)
	{
		return -1;	// error since no col of zeros, and hence no initial source node
	}
	else
	{
		return 0;	// a col of zeros was found, hence there exists a source node
	}
}

void CSystemModel::BuildTour(int **A, int nrows, int ncols, int *node_vec, int n_nodes, int *in_tour_vec)
{
	int i;
	int j;
	int posn;					// posn of nodes in tour vector
	int n_branch_nodes;			// number of branch nodes present on a partic. row
	int node;					// node number = row number = col number
	int tour_end = 0;			// flag identifying whether current tour has been found
	int *branch_node_vec = NULL;// vec of branch nodes to which current node can progress
	int *tour_vec = NULL;		// vec of nodes comprising the tour
	//char t_string[20];		// time string
	//time_t seconds;			// time var used to aid user when pressing return bw. iterations of for-node loop
	CString	sMsg;				// string to be displayed
	CString sMsgTemp;			// temp msg.
	UINT nType = MB_OK;			// style of msg. box
	UINT nIDhelp = 0;			// help context ID for the msg.

	// -- MEMORY ALLOC
	tour_vec = new int[ncols];

	// Copy the incoming tour vec into the local tour vec
	for(j=0; j<ncols; j++)
	{	
		tour_vec[j] = in_tour_vec[j];
	}
	
	//PrintVector(tour_vec, ncols);
	//PrintVector(node_vec, ncols);


	// -- ITERATE OVER ONLY THE NODES IN THE NODE VECTOR FROM WHICH CONNECTIONS TO OTHER NODES ARE PREMISSABLE
	for(node=0; node<n_nodes; node++)
	{
		// Get the offset position in the tour vector into which the next node should be placed
		posn = FindCurrentTourPosition(in_tour_vec, ncols);
		
		// Get starting node, i.e. a node in the node vector (of source nodes or branch nodes)
		i = node_vec[node];
		tour_vec[posn] = i;
		tour_end = 0;
		//PrintVector(tour_vec, ncols);

		// Check for repeated entries here, after the new node addition to the tour_vec: if there is a repeat, end tour.
		if(CheckRepeatedEntries(tour_vec, ncols) > 0)
		{
			tour_end = 1;
			SaveTourVector(tour_vec, ncols);	// save tour_vec since tour has had an entry added but a loop exists
		}

		// -- WHILE TOUR NOT COMPLETE
		while(tour_end == 0)
		{
			// Get the offset position in the EVOLVING TOUR VECTOR into which the next node should be placed
			posn = FindCurrentTourPosition(tour_vec, ncols);

			if(posn >= ncols)	// if the posn is after the end of the array, then the tour vector is filled.
			{
				tour_end = 1;
				SaveTourVector(tour_vec, ncols);	// save tour_vec since tour has ended due to no further connections
				break;			// break from while loop
			}

			// Find the number of connections i.e. branch nodes to the current node
			n_branch_nodes = NumberOfOnesOnMatrixRow(A, i, ncols);

			// If there are no 1's in the cols then there are no connecting nodes
			if(n_branch_nodes == 0)	
			{
				tour_vec[posn] = -1;				// -1 indicates no fwd connection exists from the current node
				tour_end = 1;						// end tour
				SaveTourVector(tour_vec, ncols);	// save tour_vec since tour has ended due to no further connections
			}
			// If there is one 1, in the cols, there there is one connecting node
			else if(n_branch_nodes == 1)	
			{
				for(j=0; j<ncols; j++)			// iterate over cols
				{
					if(A[i][j] == 1)			// if a col contains a 1, then, node on row i, is connected to node in col j.
					{
						tour_vec[posn] = j;		// new node traversed is that in col j, connected to that in row i
						i = j;					// update row i with col j to continue to the next connection
						break;					// break from for loop
					}	
				}
	
				//PrintVector(tour_vec, ncols);
			}
			// If there is > one 1, in the cols, then there is > 1 connecting node
			else if(n_branch_nodes > 1)
			{
				// MEMORY ALLOC
				branch_node_vec = new int[ncols];

				// Find all nodes that are connected/branched to the current node
				FindBranchNodes(A, i, ncols, branch_node_vec, n_branch_nodes);
				
				//PrintVector(branch_node_vec, ncols);	
		
				// RECURSIVELY CALL THE BuildTour() FUNCTION WITH THE NEW NODE VECTOR AND TOUR VECTOR
				BuildTour(A, nrows, ncols, branch_node_vec, n_branch_nodes, tour_vec);
				
				tour_end = 1;
	
				// MEMORY DELETE
				delete [] branch_node_vec;	
			}

			// Check for repeated entries here, after the if-else-based node addition to the tour_vec.
			if(CheckRepeatedEntries(tour_vec, ncols) > 0)
			{
				tour_end = 1;
				SaveTourVector(tour_vec, ncols);	// save tour_vec since tour has had an entry added but a loop exists
			}

			// Reset the tour_vec to be that of the incoming tour vector
			if(tour_end == 1)
			{
				ResetTourVector(tour_vec, in_tour_vec, ncols);
			}
		}// end while

		// Get the time of the end of this loop
		//seconds = time(NULL);										// get seconds ellapsed since 1-1-1970
		//strftime(t_string, 20, "%H:%M:%S", localtime(&seconds));	// get the local time as a string
		
		// Print msg. end of current node/block
		//sMsgTemp.Format("\n BuildTour()\n");
		//sMsg+=sMsgTemp;
		//sMsgTemp.Format("\n End of for node = %d\n time: %s\n", node, t_string);
		//sMsg+=sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);

	}// end for

	// MEMORY DELETE
	delete [] tour_vec;

	return;
}

int CSystemModel::FindCurrentTourPosition(int *tour_vec, int ncols)
{
	int i;
	
	// Iterate through the vector to find the first negative, non-node-filled, entry.
	for(i=0; i<ncols; i++)
	{
		if(tour_vec[i] < 0)
		{
			return i;
		}
	}

	// If no -ve value found then return the size of the array "ncols"
	return ncols;
}

int CSystemModel::FindBranchNodes(int **A, int i, int ncols, int *branch_node_vec, int &n_branch_nodes)
{
	int j;			// col index
	int cnt = 0;	// branch_node_vec[] offset index


	// Negate the branch_node_vec
	for(j=0; j<ncols; j++)
	{
		branch_node_vec[j] = -1;
	}

	// Iterate over the cols of A for the current row (i), recording those with one's (1), indicating node connections.
	for(j=0; j<ncols; j++)
	{
		if(A[i][j] == 1)
		{
			branch_node_vec[cnt] = j;
			cnt++;
		}
	}
	n_branch_nodes = cnt;		// update the number of connecting/branch nodes

	return 0;
}

int CSystemModel::NumberOfOnesOnMatrixRow(int **A, int row, int ncols)
{
	int j;
	int n_ones = 0;

	// Count the number of ones on the partic. row.
	for(j=0; j<ncols; j++)
	{
		if(A[row][j] == 1)
		{
			n_ones++;
		}
	}

	return n_ones;
}

int CSystemModel::CheckRepeatedEntries(int *tour_vec, int ncols)
{
	int i;
	int j;
	int n_repeats = 0;

	// Check tour vector for repeated entries (implying a loop)
	for(i=0; i<(ncols-1); i++)
	{
		for(j=(i+1); j<ncols; j++)
		{
			if(tour_vec[j] == tour_vec[i])
			{
				n_repeats++;
			}
		}
	}

	return n_repeats;
}

void CSystemModel::ResetTourVector(int *tour_vec, int *in_tour_vec, int ncols)
{
	int j;	

	// Reassign in_tour_vec to tour_vec
	for(j=0; j<ncols; j++)
	{
		tour_vec[j] = in_tour_vec[j];
	}
}

void CSystemModel::SaveTourVector(int *tour_vec, int ncols)
{
	int i;
	int n_repeats = 0;			// number of repeated entries
	int *tour_vec_copy = NULL;	// copy of the incoming tour_vec
	CString	sMsg;				// string to be displayed
	CString sMsgTemp;			// temp msg.
	UINT nType = MB_OK;			// style of msg. box
	UINT nIDhelp = 0;			// help context ID for the msg.
	
	// MEMORY NEW
	tour_vec_copy = new int[ncols];

	// Copy the incoming tour_vec to the tour_vec_copy
	// tour_vec is deleted in calling environment so can't be added to the list
	// tour_vec_copy is deleted via a call to DeleteSourceOutputLoopLists()
	for(i=0; i<ncols; i++)
	{
		tour_vec_copy[i] = tour_vec[i];		
	}	
	
	// Check tour vector for repeated entries (implying a node-to-node loop)
	n_repeats = CheckRepeatedEntries(tour_vec, ncols);

	// Notify method of tour termination
	//sMsgTemp.Format("\n SaveTourVector()\n");
	//sMsg+=sMsgTemp;
	if(n_repeats == 0)
	{
		m_iNSourceOutputTours++;		// record number of source-to-output block tours
		//sMsgTemp.Format(" tour_vec saved (output block):\n");
		m_lstSourceOutputTour.push_back(tour_vec_copy);
	}
	else if(n_repeats > 0)
	{
		m_iNSourceLoopTours++;			// record the number of source-to-loop-repeated block tours
		//sMsgTemp.Format(" tour_vec saved (loop):\n");
		m_lstSourceLoopTour.push_back(tour_vec_copy);
	}
	//sMsg+=sMsgTemp;
	//AfxMessageBox(sMsg, nType, nIDhelp);

	//PrintVector(tour_vec, ncols);

	return;
}

int CSystemModel::GetNFeedbackLoops() const
{
	// Return the number of source-to-loop tours in the model
	return m_iNSourceLoopTours;
}

void CSystemModel::SignalPropagationDirect()
{
	int n_TimeSteps;					// total no. of time steps for numerical integration
	int out_blk_state = 0;				// flag denoting output-block operation state
	int std_blk_state = 0;				// flag denoting standard-block operation state
	int t_cnt;							// time counter
	bool bStop = FALSE;					// stop simulation
	clock_t tv1;						// init time for execution time calc.
	clock_t tv2;						// final time for execution time calc.
	double delta_t = m_dTimeStepSize;	// time step size of system model simulation
	double t;							// i-th time point t_i = t
	double t_execution;					// execution time
	double t_init = m_dTimeStart;		// time init of system model simulation
	CPort *ref_from_port = NULL;		// output port from which connection emanates
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.


	// Get a ptr-to-CDiagramEngDoc to call UpdateCommonOpsToolbar().
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn();

	// Get the actual connection list by reference
	list<CConnection*> &con_list = GetConnectionList();		// get the connection list

	// SET REF-FROM-PORTS OF CONNECTIONS THAT EMANATE FROM BEND POINTS.
	// These ref-from-ports are then RESET to NULL at the end of the fn (since they don't actually emanate from a PORT).
	SetRefFromPortOfConnectionFromBendPoint();

	// Get a copy of the connection list
	list<CConnection*> con_list_copy = GetConnectionList();	// get a copy of the connection list (NOT BY REFERENCE)


	// -- MAIN ITERATION TIME LOOP	
	n_TimeSteps = DetermineNTimeSteps();	// no. of time steps

	// -- EXECUTION TIME START
	tv1 = clock();

	// -- Loop for n_TimeSteps + 1, from t_0 = 0, UP TO AND INCLUDING the last time point t_n
	for(t_cnt = 0; t_cnt <= n_TimeSteps; t_cnt++)
	{
		t = t_init + t_cnt*delta_t;			// t cnt, i.e. i-th time point t_i, or simply t.
		
		//sMsg.Format("");
		//sMsgTemp.Format("\n SignalPropagationDirect(), t_cnt = %d, t = %lf", t_cnt, t);
		//sMsg+=sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
		
		// -- ITERATE OVER CONNECTION LIST TO DETERMINE CONNECTION'S SIGNAL DATA
		while(con_list_copy.size() > 0)
		{	
			for(it_con = con_list_copy.begin(); it_con != con_list_copy.end(); it_con++)
			{
				// Get port and then block
				ref_from_port = (*it_con)->GetRefFromPort();			// output port of this connection		
				CBlock &ref_to_block = ref_from_port->GetRefToBlock();	// block of this (output) port	

				// Perform block operation
				std_blk_state = ref_to_block.OperateOnData(t_cnt, t, delta_t);	// Overriding fn in CBlock derived classes

				// -- CHECK OPERATION
				if(std_blk_state == -1)			// OPERATIONAL ERROR: ABORT SIGNAL PROPAGATION
				{
					sMsg.Format("");
					sMsgTemp.Format("\n SignalPropagationDirect()\n");
					sMsg+=sMsgTemp;
					sMsgTemp.Format("\n WARNING! Unsuccessful (non-Output) block operation!\n");
					sMsg+=sMsgTemp;
					sMsgTemp.Format(" Aborting signal propagation!\n");
					sMsg+=sMsgTemp;
					AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
					break;	// break from for loop
				}
				else if(std_blk_state == 0)		// NO SIGNAL DATA AVAILABLE FOR CURRENT CONNECTION, HENCE KEEP ITERATING
				{
					// No action necessary: keep iterating until signal data available upon connection
				}
				else if(std_blk_state == 1)		// DELETE LOCAL CONNECTION IF OPERATION SUCCESSFUL
				{
					con_list_copy.erase(it_con);
					break;	// break from for loop
				}
			}// end for

			if(std_blk_state == -1)
			{
				break;	// break from while loop
			}
		}// end while


		// -- EXECUTE ALL OUTPUT BLOCKS 
		if(std_blk_state == 1)				// if all std block operations were successful
		{
			for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
			{
				if((*it_blk)->GetBlockName() == "output_block")
				{
					// Perform block operation
					out_blk_state = (*it_blk)->OperateOnData(t_cnt, t, delta_t);	

					if(out_blk_state == 0)	// Read/write error => out_blk_state = 0, NOT -1
					{
						sMsg.Format("");
						sMsgTemp.Format("\n SignalPropagationDirect()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Unsuccessful Output block operation!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Output block unable to read input signal!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
						break;
					}
				}		
			}// end it_blk
		}

		// -- RESET ALL CONNECTION-HELD SIGNAL DATA
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			(*it_con)->GetSignal()->ResetSignalData();
		}

		// RE-OBTAIN A COPY OF THE CONNECTION LIST AT EACH TIME STEP 
		// This copy of the connection list needs to be re-obtained since the previous copy was iterated over and
		// its elements erased after successful block operation (OperateOnData()).
		con_list_copy = GetConnectionList();

		// If a std block operation failed or if there was a read/write error for the output block, then break.
		if((std_blk_state == -1) || (out_blk_state == 0))
		{
			break;	// break from for loop
		}

		// Update the Edit box on the CommonOps toolbar with the simulation time value	
		sMsg.Empty();
		sMsg.Format("t = %e / %g (s)", t, m_dTimeStop);	// %f = float, %g is the shorter of e-format or f-format
		pDoc->UpdateCommonOpsToolbar(sMsg);

		// Check if stop button pressed
		bStop = CheckStopButtonPressed();
		if(bStop)
		{
			break;
		}

	}// end (time)

	// -- EXECUTION TIME STOP 
	tv2 = clock();
	t_execution = (double)(tv2 - tv1)/(double)CLOCKS_PER_SEC;
	sMsg.Empty();			
	sMsg.Format("t_exe = %e (s)", t_execution);	
	pDoc->UpdateCommonOpsToolbar(sMsg);

	// -- RESET ALL BLOCKS THAT HAVE MEMORY, I.E. RETAIN PAST VALUES
	ResetMemoryBasedBlockData();

	// -- RESET ALL REF-FROM-PORTS OF CONNECTIONS EMANATING FROM BEND POINTS BACK TO NULL
	// Before SignalPropagationDirect() was called the ref-from-ports of connections emanating from bend points
	// were NULL. Now they need to be reset to NULL, to leave the program state as it was previously.

	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		if((*it_con)->GetRefFromPoint() != NULL)	// if connection attached to a BEND POINT
		{	
			(*it_con)->SetRefFromPort(NULL);		// set ref-from-port to NULL (since not attached to a block)
		}
	}

	return;
}

int CSystemModel::DetermineNTimeSteps()
{
	int n_TimeSteps;					// no. of integer time steps
	double delta_t = m_dTimeStepSize;	// time step size of system model simulation
	double t_init = m_dTimeStart;		// time init of system model simulation
	double t_final = m_dTimeStop;		// time final of system model simulation
	double t_final_revised;				// time final revised given integer no. of time steps	
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.

	// -- DETERMINE TIME BASED PARAMETERS
	if(t_final >= t_init)
	{
		n_TimeSteps = int((t_final - t_init)/delta_t);	// calculate no. of time steps
	}
	else	
	{
		sMsgTemp.Format("\n CSystemModel::DetermineNTimeSteps()    \n");
		sMsg+=sMsgTemp;
		sMsgTemp.Format(" WARNING! Time error!\n");
		sMsg+=sMsgTemp;
		sMsgTemp.Format("\n t_final should be greater than t_initial.    \n");
		sMsg+=sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
		return 0;
	}

	// Recompute final time based on integer no. of time steps
	t_final_revised = t_init + n_TimeSteps*delta_t;

	sMsgTemp.Format("\n CSystemModel::DetermineNTimeSteps()    \n");
	sMsg+=sMsgTemp;
	sMsgTemp.Format("\n t_init = %-10.3e, delta_t = %-10.3e, t_final_revised = %-10.3e, n_TimeSteps = %d\n", t_init, delta_t, t_final_revised, n_TimeSteps);
	sMsg+=sMsgTemp;
	//AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box

	return n_TimeSteps;
}

void CSystemModel::SignalPropagationSystemOfEquations()
{
	int n_TimeSteps;					// total no. of time steps for system computation/simulation
	int ncols;							// total no. of cols of system equations vector x and fn vec f(x) (1 col)
	int nrows;							// total no. of rows of system equations vector x and fn vec f(x)
	int t_cnt;							// time counter
	int valid = 0;						// validity of function operation and of x_vec (x) or f_vec (f(x)) vector construction
	bool bStop = FALSE;					// stop simulation
	clock_t tv1;						// init time for execution time calc.
	clock_t tv2;						// final time for execution time calc.
	double delta_t = m_dTimeStepSize;	// time step size of system model simulation
	double t;							// i-th time point t_i = t
	double t_execution;					// execution time
	double t_init = m_dTimeStart;		// time init of system model simulation
	double *x_vec = NULL;				// system equations vector x
	CString blk_name;					// block name
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.

	// Get a ptr-to-CDiagramEngDoc to call UpdateCommonOpsToolbar().
	CDiagramEngDoc *pDoc = GetDocumentGlobalFn();

	// Get the actual connection list by reference
	list<CConnection*> &con_list = GetConnectionList();	// get the connection list

	// SET REF-FROM PORTS OF CONNECTIONS THAT EMANATE FROM BEND POINTS.
	// These ref-from-ports are then RESET to NULL at the end of the fn (since they don't actually emanate from a PORT).
	SetRefFromPortOfConnectionFromBendPoint();

	// PERFORM A PRELIMINARY SIGNAL PROPAGATION TO DETERMINE AN INITIAL GUESS OF SIGNAL DATA AND DIMENSIONS
	// If the system model's connection-based signal data is not already set, then set/propagate preliminary signal values.
	if(CheckSystemSignalValidity() != 1)
	{
		valid = PreliminarySignalPropagation();
		// If PreliminarySignalPropagation() in error then reset ref-from ports and return.
		if(valid == 0)
		{
			// -- RESET ALL REF-FROM PORTS OF CONNECTIONS EMANATING FROM BEND POINTS BACK TO NULL
			// Before SignalPropagationSystemOfEquations() was called the ref-from-ports of connections emanating from bend points
			// were NULL. Now they need to be reset to NULL, to leave the program state as it was previously.
			for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
			{
				if((*it_con)->GetRefFromPoint() != NULL)	// if connection attached to a BEND POINT
				{	
					(*it_con)->SetRefFromPort(NULL);	// set ref-from-port to NULL (since not attached to a block)
				}
			}
			return;	// abort since fn in error
		}
	}

	// DETERMINE TOTAL DATA DIMENSIONS FOR SIGNAL VECTOR x_vec (x) AND FUNCTION VECTOR f_vec (f(x))
	valid = DetermineSystemEquationsDataDimensions(nrows, ncols);
	
	// If dimensionality error reset ref-from ports and return.
	if(valid == 0)
	{
		// -- RESET ALL CONNECTION-HELD SIGNAL DATA TO NULL
		// Setting all connection-held signal data to null enforces PreliminarySignalPropagation() to be rerun to
		// generate new signal data after any alterations to the model by the user.
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			(*it_con)->GetSignal()->ResetSignalData();
		}

		// -- RESET ALL REF-FROM-PORTS OF CONNECTIONS EMANATING FROM BEND POINTS BACK TO NULL
		// Before SignalPropagationSystemOfEquations() was called the ref-from-ports of connections emanating from bend points
		// were NULL. Now they need to be reset to NULL, to leave the program state as it was previously.
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			if((*it_con)->GetRefFromPoint() != NULL)	// if connection attached to a BEND POINT
			{	
				(*it_con)->SetRefFromPort(NULL);	// set ref-from-port to NULL (since not attached to a block)
			}
		}
		return;	// abort since fn in error
	}

	// MEMORY ALLOCATION
	x_vec = new double[nrows];	// memory alloc for system equations vector x = x_vec
	
	// -- MAIN ITERATION TIME LOOP	
	n_TimeSteps = DetermineNTimeSteps();		// no. of time steps
	
	// -- EXECUTION TIME START
	tv1 = clock();

	// -- Loop for n_TimeSteps + 1, from t_0 = 0, UP TO AND INCLUDING the last time point t_n
	for(t_cnt = 0; t_cnt <= n_TimeSteps; t_cnt++)
	{
		t = t_init + t_cnt*delta_t;		// t cnt, i.e. i-th time point t_i, or simply t.
		
		//sMsg.Format("");				// clear the msg.
		//sMsgTemp.Format("\n SignalPropagationSystemOfEquations(), t_cnt = %d, t = %lf", t_cnt, t);
		//sMsg+=sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);	
		
		// DETERMINE SYSTEM EQUATION VECTOR x_vec (x)
		// Retrieve the output signals and assemble into one large output signal vector x_vec (x) representing the system
		// x_vec = x = [x_0, x_1, ... , x_s-1], where s = no. of blocks that generate output signals. 
		valid = DetermineSystemEquationsSignalVector(x_vec); 
		if(valid == 0)
		{
			break;	// break from "for t_cnt"
		}
		
		// PERFORM NEWTON ITERATION TO DETERMINE THE VECTOR x_vec, i.e. x, THAT SATISFIES F(x) = x - f(x) = 0
		valid = NewtonSolver(nrows, x_vec, t_cnt, t, delta_t);

		// If the NewtonSolver() function is in error, print a msg. and break from the time-based loop.
		if(valid == 0)
		{
			sMsg.Format("");
			sMsgTemp.Format("\n SignalPropagationSystemOfEquations()\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format("\n WARNING! Unsuccessful NewtonSolver() operation!\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format(" Check all numerical values before restarting the simulation!\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format(" Aborting simulation!\n");
			sMsg+=sMsgTemp;
			AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
			break;	// break from "for t_cnt"
		}

		//sMsgTemp.Format("\n After NewtonSolver(), x_vec:\n");	
		//sMsg+=sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);

		// Check the x_vec
		//PrintVector(x_vec, nrows);

		// ASSIGN THE RESULTANT SIGNAL VECTOR x_vec TO THE BLOCK OUTPUT CONNECTION-BASED SIGNAL DATA DIRECTLY
		valid = AssignSystemEquationsSignalVector(x_vec);

		// -- EXECUTE ALL OUTPUT BLOCKS 
		if(valid == 1)				// if all block output-connection-based signal data correctly assigned
		{
			for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
			{
				if((*it_blk)->GetBlockName() == "output_block")
				{
					// Perform block operation
					valid = (*it_blk)->OperateOnData(t_cnt, t, delta_t);	

					if(valid == 0)	
					{
						sMsg.Format("");
						sMsgTemp.Format("\n SignalPropagationSystemOfEquations()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Unsuccessful Output block operation!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Output block unable to read input signal!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
						break;
					}
				}		
			}
		}

		// If a signal data writing operation failed or if there was a read/write error for the output block, then break.
		if(valid == 0)
		{
			break;	// break from "for t_cnt"
		}

		// Update the Edit box on the CommonOps toolbar with the simulation time value	
		sMsg.Empty();
		sMsg.Format("t = %e / %g (s)", t, m_dTimeStop);	// %f = float, %g is the shorter of e-format or f-format
		pDoc->UpdateCommonOpsToolbar(sMsg);

		// Check if stop button pressed
		bStop = CheckStopButtonPressed();
		if(bStop)
		{
			break;
		}

	}// end (time)

	// -- EXECUTION TIME STOP
	tv2 = clock();
	t_execution = (double)(tv2 - tv1)/(double)CLOCKS_PER_SEC;
	sMsg.Empty();			
	sMsg.Format("t_exe = %e (s)", t_execution);	
	pDoc->UpdateCommonOpsToolbar(sMsg);
	
	// -- RESET ALL BLOCKS THAT HAVE MEMORY, I.E. RETAIN PAST VALUES
	ResetMemoryBasedBlockData();

	// -- RESET ALL CONNECTION-HELD SIGNAL DATA 
	// Resetting the signal data at the end of each simulation, implies that when a new simulation is started, 
	// the preliminary signal data need to be regenerated using PreliminarySignalPropagation(), and are based upon 
	// the block dialog window parameter input data.
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		(*it_con)->GetSignal()->ResetSignalData();
	}

	// -- RESET ALL REF-FROM PORTS OF CONNECTIONS EMANATING FROM BEND POINTS BACK TO NULL
	// Before SignalPropagationSystemOfEquations() was called the ref-from-ports of connections emanating from bend points
	// were NULL. Now they need to be reset to NULL, to leave the program state as it was previously.
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		if((*it_con)->GetRefFromPoint() != NULL)	// if connection attached to a BEND POINT
		{	
			(*it_con)->SetRefFromPort(NULL);	// set ref-from-port to NULL (since not attached to a block)
		}
	}

	// MEMORY DELETE	
	delete [] x_vec;
	x_vec = NULL;

	return;
}

CString CSystemModel::GetIntegrationMethod() const
{
	return m_strIntegrationMethod;
}

void CSystemModel::GetTimeParameters(double &t_init, double &t_final, double &delta_t) const
{
	// Assign initial time (t0), final time (tn) and time step size (delta_t): note pass-by-reference
	t_init = m_dTimeStart;
	t_final = m_dTimeStop;
	delta_t = m_dTimeStepSize;
}

void CSystemModel::OrderBlockList()
{
	int k;
	int chk_list = 0;		// flag to check blocks in block list
	CString blk_name;		// block name
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg 
	list <CBlock *>::iterator it_blk;				// block list iterator
	list <CBlock *>::reverse_iterator it_blk_r;		// reverse iterator
	list <CBlock *>::iterator it_tmp;				// temporary iterator

	// Determine number of Source and Output blocks
	m_iNSourceBlocks = 0;
	m_iNOutputBlocks = 0;
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		blk_name = (*it_blk)->GetBlockName();
		if((blk_name == "constant_block") || (blk_name == "linear_fn_block") || (blk_name == "signal_generator_block")) 
		{
			m_iNSourceBlocks++;
		}
		
		if(blk_name == "output_block") 	
		{
			m_iNOutputBlocks++;
		}
	}
	
	// Place the Source blocks at the front of the list (retaining their original placement order)
	for(k=0; k<m_iNSourceBlocks; k++)
	{
		for(it_blk_r = m_lstBlock.rbegin(); it_blk_r != m_lstBlock.rend(); it_blk_r++)
		{
			blk_name = (*it_blk_r)->GetBlockName();
			if((blk_name == "constant_block") || (blk_name == "linear_fn_block") || (blk_name == "signal_generator_block")) 
			{	
				// Move element element at source posn, of the source list, to the front of the list (destination posn)
				// Argument order: destination position, list, source position
				it_tmp = it_blk_r.base();
				it_tmp--;
				m_lstBlock.splice(m_lstBlock.begin(), m_lstBlock, it_tmp);	
				break;
			}
		}
	}

	// Check blocks
	if(chk_list == 1)
	{
		sMsgTemp.Format("\n CSystemModel::OrderBlockList().  \n");
		sMsg += sMsgTemp;
		for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
		{
			blk_name = (*it_blk)->GetBlockName();
			sMsgTemp.Format(" %s  \n", blk_name);
			sMsg += sMsgTemp;
		}
		//AfxMessageBox(sMsg, nType, nIDhelp);
	}

	// Place the Output blocks at the back of the list (retaining their original placement order)
	for(k=0; k<m_iNOutputBlocks; k++)
	{
		for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
		{
			if((*it_blk)->GetBlockName() == "output_block")
			{	
				// Move element element at source posn, of the source list, to the end of the list (destination posn)
				// Argument order: destination position, list, source position
				it_tmp = it_blk;
				m_lstBlock.splice(m_lstBlock.end(), m_lstBlock, it_tmp);	
				break;
			}
		}
	}

	// Check blocks
	if(chk_list == 2)
	{
		sMsgTemp.Format("\n CSystemModel::OrderBlockList().  \n");
		sMsg += sMsgTemp;
		for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
		{
			blk_name = (*it_blk)->GetBlockName();
			sMsgTemp.Format(" %s  \n", blk_name);
			sMsg += sMsgTemp;
		}
		AfxMessageBox(sMsg, nType, nIDhelp);
	}

	return;
}

void CSystemModel::DeleteBlockList()
{
	list<CBlock*>::iterator it_blk;

	// Delete block list
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		delete (*it_blk);	// delete what it_blk is pointing to: i.e. deref the it_blk ptr and delete the ptr-to-CBlock.
	}
	m_lstBlock.clear();
}

void CSystemModel::DeleteConnectionList()
{
	list<CConnection*>::iterator it_con;

	// Delete connection list
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		delete (*it_con);	// delete what it_conn is pointing to: i.e. deref the it_conn ptr and delete the ptr-to-CConnection.
	}
	m_lstConnection.clear();
}

void CSystemModel::DeleteSourceOutputLoopLists()
{
	list <int *>::iterator it_int;

	// Delete contents of m_lstSourceOutputTour
	for(it_int = m_lstSourceOutputTour.begin(); it_int != m_lstSourceOutputTour.end(); it_int++)
	{
		delete [] *it_int;
	}
	m_lstSourceOutputTour.clear();

	// Delete contents of m_lstSourceLoopTour
	for(it_int = m_lstSourceLoopTour.begin(); it_int != m_lstSourceLoopTour.end(); it_int++)
	{
		delete [] *it_int;
	}	
	m_lstSourceLoopTour.clear();

	return;
}

int *CSystemModel::GetTourVector(int tour_type, int i)
{
	int cnt;
	list<int *>::iterator it_int;

	// Get the right type of tour: (0) source-to-output tour, (1) source-to-loop tour
	if(tour_type == 0)
	{
		if((i >= 0) && (i < m_iNSourceOutputTours))
		{
			cnt = 0;
			for(it_int = m_lstSourceOutputTour.begin(); it_int != m_lstSourceOutputTour.end(); it_int++)
			{
				if(cnt == i)
				{
					// Dereference the iterator to return the integer array held in the tour list
					return (*it_int);
				}
				cnt++;
			}
		}
	}
	else if(tour_type == 1)
	{
		if((i >= 0) && (i < m_iNSourceLoopTours))
		{
			cnt = 0;
			for(it_int = m_lstSourceLoopTour.begin(); it_int != m_lstSourceLoopTour.end(); it_int++)
			{
				if(cnt == i)
				{
					// Dereference the iterator to return the integer array held in the tour list
					return (*it_int);
				}
				cnt++;
			}
		}	
	}
	
	// Return a null pointer denoting an error regarding tour_type.
	return NULL;
}

void CSystemModel::ConvertIntegerTourToBlockList(int *tour_vec, list<CBlock*> &tour_blk_list)
{
	int i;
	int chk_list = 0;
	int cnt;
	int index_stop;
	int n_blks = m_lstBlock.size();	
	CString blk_name;				// block name
	CString	sMsg;					// string to be displayed
	CString sMsgTemp;				// temp string
	UINT nType = MB_OK;				// style of msg. box
	UINT nIDhelp = 0;				// help context ID for the msg 
	list <CBlock *>::iterator it_blk;

	// Clear the tour_blk_list of any pre-existing items
	tour_blk_list.clear();

	// Get the offset at which the tour ends 
	// The tour ends at either: 
	// 1) the first negative number or 
	// 2) (n_blks - 1) if the tour length is equal to the number of blocks
	
	index_stop = n_blks - 1;	// default stopping index 
	
	for(i=0; i<n_blks; i++)
	{
		if(tour_vec[i] < 0)
		{
			index_stop = i-1;	// stopping index at one prior to the current negative number.
			break;
		}
	}

	// Iterate up to the offset at which the tour ends, i.e. iterate through the tour.
	for(i=0; i <= index_stop; i++)
	{
		// Traverse the block list and pick out the appropriately numbered blocks as recorded in the tour_vec.
		cnt = 0;
		for(it_blk= m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
		{
			if(tour_vec[i] == cnt)
			{
				tour_blk_list.push_back(*it_blk);
				break;
			}
			cnt++;
		}
	}

	// Check blocks
	if(chk_list == 1)
	{	
		sMsgTemp.Format("\n CSystemModel::ConvertIntegerTourToBlockList().  \n");
		sMsg += sMsgTemp;
		for(it_blk = tour_blk_list.begin(); it_blk != tour_blk_list.end(); it_blk++)
		{
			blk_name = (*it_blk)->GetBlockName();
			sMsgTemp.Format(" %s  \n", blk_name);
			sMsg += sMsgTemp;
		}
		AfxMessageBox(sMsg, nType, nIDhelp);
	}

	return;
}

int CSystemModel::CheckSystemSignalValidity()
{
	int valid = 1;
	list<CConnection*>::iterator it_con;

	// Iterate through all connections checking signal data
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		valid = (*it_con)->GetSignal()->GetSignalValidity();
		if(valid == 0)	// if invalid, return valid = 0
		{
			return valid;
		}			
	}

	return valid;		// if valid, return valid = 1
}

int CSystemModel::PreliminarySignalPropagation()
{
	int i;
	int j;
	int ncols;							// ncols of matrix
	int nrows;							// nrows of matrix
	int valid = 0;						// valid: (0) invalid, (1) valid
	int t_cnt = 0;						// time count
	int tour_type;						// (0) source-to-output, (1) source-to-loop
	int *tour_vec = NULL;				// tour_vec ptr-to-int to receive the tour_vec stored in a list
	double delta_t = m_dTimeStepSize;	// time step size delta_t = h.
	double t = 0.00;					// current time point.
	double t_init = m_dTimeStart;		// time initial
	double **matrix = NULL;				// matrix returned from GetInitialSignalMatrix()
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*> tour_blk_list;		// list of ptrs-to-block within a block tour
	list<CBlock*>::iterator it_blk;		// blk list iterator
	list<CBlock*>::iterator prev_blk;	// blk list iterator of the previous block in the list

	//sMsgTemp.Format("\n PreliminarySignalPropagation()\n");
	//sMsg += sMsgTemp;

	// ITERATE THROUGH ALL SOURCE-TO-OUTPUT-BLOCK TOURS
	tour_type = 0;
	for(i=0; i<m_iNSourceOutputTours; i++)
	{
		// Get the tour vector and convert it into a block list
		tour_vec = GetTourVector(tour_type, i);
		ConvertIntegerTourToBlockList(tour_vec, tour_blk_list);

		it_blk = tour_blk_list.begin();		// init. iterator to be at the start of the list

		// ITERATE THROUGH THE LIST A NUMBER OF TIMES = size - 1.
		for(j = 1; j < tour_blk_list.size(); j++)
		{
			valid = (*it_blk)->CheckBlockOutputSignalDataValidity();
			
			//sMsgTemp.Format(" %d, %d, %s, valid = %d\n", tour_type, i, (*it_blk)->GetBlockName(), valid);
			//sMsg += sMsgTemp;
		
			if(!valid)	// if there does not exist output signal data
			{
				if((*it_blk)->GetBlockName() == "sum_block")	// skip sum blocks but transfer signals directly
				{
					prev_blk = it_blk;
					prev_blk--;
					TransferSignalDataToAnotherConnection(*prev_blk, *it_blk);
					valid = 1;
				}
				else if((*it_blk)->GetBlockName() == "divide_block")	// skip divide blocks since output signal data already provided by user
				{
					// CHECK IF THERE IS AN INITIAL SIGNAL AVAIL IN THE CDivideBlock CLASS AND WRITE IT DIRECTLY
					matrix = ((CDivideBlock*)(*it_blk))->GetInitialSignalMatrix(nrows, ncols);
					if((matrix != NULL) && (nrows > 0) && (ncols > 0))
					{
						valid = ((CDivideBlock*)(*it_blk))->WriteDataToOutputSignal(matrix, nrows, ncols);
					}

					// IF SIGNAL INVALID THEN ENTER VIA DLG WND
					if(valid == 0)
					{
						valid = 0;
						sMsgTemp.Format("\n PreliminarySignalPropagation()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Divide block has unspecified output signal!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Please manually enter initial block output signal data!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
						return valid;	// abort fn	
					}
				}
				else	// Perform OperateOnData() to generate the output signal data
				{
					t = t_init + t_cnt*delta_t;
					valid = (*it_blk)->OperateOnData(t_cnt, t, delta_t);
					
					if(valid <= 0)			// OPERATIONAL ERROR: ABORT SIGNAL PROPAGATION
					{
						sMsgTemp.Format("\n PreliminarySignalPropagation()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Unsuccessful (non-Output) block operation!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
						valid = 0;	
						return valid;	// abort fn
					}
				}
			}// end valid

			it_blk++; 	// increment block list iterator at end of "for j" loop

		}// end j
	}// end i
	
	// ITERATE THROUGH ALL SOURCE-TO-LOOP-REPEATED-BLOCK TOURS
	tour_type = 1;
	for(i=0; i<m_iNSourceLoopTours; i++)
	{
		// Get the tour vector and convert it into a block list
		tour_vec = GetTourVector(tour_type, i);
		ConvertIntegerTourToBlockList(tour_vec, tour_blk_list);

		it_blk = tour_blk_list.begin();		// init. iterator to be at the start of the list

		// ITERATE THROUGH THE LIST A NUMBER OF TIMES = size - 1.
		for(j = 1; j < tour_blk_list.size(); j++)
		{
			valid = (*it_blk)->CheckBlockOutputSignalDataValidity();

			//sMsgTemp.Format(" %d, %d, %s, valid = %d\n", tour_type, i, (*it_blk)->GetBlockName(), valid);
			//sMsg += sMsgTemp;

			if(!valid)	// if there does not exist output signal data
			{
				if((*it_blk)->GetBlockName() == "sum_block")	// skip sum blocks but transfer signals directly
				{
					prev_blk = it_blk;
					prev_blk--;
					TransferSignalDataToAnotherConnection(*prev_blk, *it_blk);
					valid = 1;
				}
				else if((*it_blk)->GetBlockName() == "divide_block")	// skip divide blocks since output signal data already provided by user
				{
					// CHECK IF THERE IS AN INITIAL SIGNAL AVAIL IN THE CDivideBlock CLASS AND WRITE IT DIRECTLY
					matrix = ((CDivideBlock*)(*it_blk))->GetInitialSignalMatrix(nrows, ncols);
					if((matrix != NULL) && (nrows > 0) && (ncols > 0))
					{
						valid = ((CDivideBlock*)(*it_blk))->WriteDataToOutputSignal(matrix, nrows, ncols);
					}

					// IF SIGNAL INVALID THEN ENTER VIA DLG WND
					if(valid == 0)
					{
						sMsgTemp.Format("\n PreliminarySignalPropagation()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Divide block has unspecified output signal!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Please manually enter initial block output signal data!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
						return valid;	// abort fn
					}
				}
				else	// Perform OperateOnData() to generate the output signal data
				{
					t = t_init + t_cnt*delta_t;
					valid = (*it_blk)->OperateOnData(t_cnt, t, delta_t);
					
					if(valid <= 0)			// OPERATIONAL ERROR: ABORT SIGNAL PROPAGATION
					{
						sMsgTemp.Format("\n PreliminarySignalPropagation()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! Unsuccessful (non-Output) block operation!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting signal propagation!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
						valid = 0;	
						return valid;	// abort fn
					}
				}
			}// end valid

			it_blk++; 	// increment block list iterator at end of "for j" loop		

		}// end j
	}// end i

	// Check preliminary signal propagation 
	//AfxMessageBox(sMsg, nType, nIDhelp);	

	return valid;
}

int CSystemModel::TransferSignalDataToAnotherConnection(CBlock *ptr_blk1, CBlock *ptr_blk2)
{
	int ncols;							// ncols of matrix data
	int nrows;							// nrows of matrix data
	int valid = 0;						// validity of writing output data signal: (0) invalid, (1) valid
	double **matrix_data = NULL;		// matrix signal data
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.
	vector<CPort*>::iterator it_port;	// iterator for vector of CPort ptrs.	
	vector<CPort*> vec_output_ports_blk1 = ptr_blk1->GetVectorOfOutputPorts();

	// Get the output matrix signal data of block 1
	for(it_port = vec_output_ports_blk1.begin(); it_port != vec_output_ports_blk1.end(); it_port++)
	{
		for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
		{
			if((*it_con)->GetRefFromPort() == (*it_port))	// the output connection from the output port
			{
				matrix_data = (*it_con)->GetSignal()->GetSignalData(&nrows, &ncols);	// get signal data from output connection
				break;
			}
		}		
	}

	// Transfer the output matrix signal data of block 1 to the output signal of block 2.
	valid = ptr_blk2->WriteDataToOutputSignal(matrix_data, nrows, ncols);
	
	return valid;
}

int CSystemModel::DetermineSystemEquationsDataDimensions(int &nrows_total, int &ncols_total)
{
	int ncols = 0;						// ncols of system equations vector x or fn f(x) (should be 1 col only)
	int nrows = 0;						// nrows of system equations vector x or fn f(x)
	int valid = 0;						// validity of data dimensions
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*> equ_blk_list;			// block list of non-output blocks
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.	
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.
	vector<CPort*>::iterator it_port;	// iterator for vector of CPort ptrs.

	// Get the system equations block list, i.e. all non-output blocks.
	DetermineSystemEquationsBlockList(equ_blk_list);

	// Initialize dimensions
	nrows_total = 0;
	ncols_total = 0;

	// Iterate over the equations-based block list
	for(it_blk = equ_blk_list.begin(); it_blk != equ_blk_list.end(); it_blk++)
	{	
		// Iterate over the vector of output ports
		vector<CPort*> vector_of_output_ports = (*it_blk)->GetVectorOfOutputPorts(); // vector of output ports		

		for(it_port = vector_of_output_ports.begin(); it_port != vector_of_output_ports.end(); it_port++)	
		{
			// Iterate over the list of connections
			for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)	
			{
				// If connection attached to output port: either directly or INDIRECTLY through a bend point
				if((*it_con)->GetRefFromPort() == (*it_port))
				{
					// Get signal data dimensions to make sure signal output is a column vector
					(*it_con)->GetSignal()->GetSignalDataDimensions(&nrows, &ncols);
										
					if((nrows >= 1) && (ncols == 1))
					{
						nrows_total += nrows;	
						ncols_total += ncols;

						// There is only one signal vector for an output-port-based-connection object.
						// Although other connection objects share the same ref-from-port, the signal (vector or scalar) 
						// associated with these connections is the same. Hence, break from the "for it_con" loop,
						// as soon as the first output-port connection is found and its signal data dimensions obtained.
						valid = 1;
						break;	// break from "for it_con"
					}
					else
					{
						valid = 0;
						sMsgTemp.Format("\n DetermineSystemEquationsDataDimensions()\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format("\n WARNING! All signals should be column vectors!\n");
						sMsg+=sMsgTemp;
						sMsgTemp.Format(" Aborting function!\n");
						sMsg+=sMsgTemp;
						AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box
						return valid;
					}
				}
			}// end it_con
			if(valid == 1)
			{
				break;	// break from "for it_port"
			}
		}// end it_port
	}// end it_blk
					
	return valid;
}

void CSystemModel::DetermineSystemEquationsBlockList(list<CBlock*> &equ_blk_list)
{
	int chk_list = 0;				// flag to control msg.
	CString blk_name;				// block name
	CString	sMsg;					// string to be displayed
	CString sMsgTemp;				// temp string
	UINT nType = MB_OK;				// style of msg. box
	UINT nIDhelp = 0;				// help context ID for the msg 
	list<CBlock*>::iterator it_blk;	// iterator for list of CBlock ptrs.

	// Iterate through the system model block list extracting only non-Output blocks 	
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		if((*it_blk)->GetBlockName() != "output_block")
		{
			equ_blk_list.push_back(*it_blk);
		}
	}

	// Check blocks
	if(chk_list == 1)
	{
		sMsgTemp.Format("\n CSystemModel::DetermineSystemEquationsBlockList().  \n");
		sMsg += sMsgTemp;
		for(it_blk = equ_blk_list.begin(); it_blk != equ_blk_list.end(); it_blk++)
		{
			blk_name = (*it_blk)->GetBlockName();
			sMsgTemp.Format(" %s  \n", blk_name);
			sMsg += sMsgTemp;
		}
		AfxMessageBox(sMsg, nType, nIDhelp);
	}
}

int CSystemModel::DetermineSystemEquationsSignalVector(double *x_vec)
{
	int i;			
	int cnt = 0;						// counter used in the building of the x_vec
	int ncols = 0;						// number of cols of data signal
	int nrows = 0;						// number of rows of data signal
	int valid = 0;						// validity of the connection-based signal data
	double **data_matrix = NULL;		// signal data matrix
	list<CBlock*> equ_blk_list;			// a list of ptrs to the Blocks used to form the simultaneous equations
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.
	vector<CPort*>::iterator it_port;	// iterator for vector of CPort ptrs.

	// Get the list of blocks concerning the simultaneous equations
	DetermineSystemEquationsBlockList(equ_blk_list);

	// Get the actual connection list by reference
	list<CConnection*> &con_list = GetConnectionList();		// get the connection list

	// FOR ALL BLOCKS IN THE BLOCK LIST USED TO CONSTRUCT THE SYSTEM OF EQUATIONS (i.e. all non-Output blocks)
	for(it_blk = equ_blk_list.begin(); it_blk != equ_blk_list.end(); it_blk++)
	{
		// GET THE EXISTING OUTPUT SIGNAL DATA
		vector<CPort*> vector_of_output_ports = (*it_blk)->GetVectorOfOutputPorts(); 	// vector of output ports		

		for(it_port = vector_of_output_ports.begin(); it_port != vector_of_output_ports.end(); it_port++)	
		{
			// Iterate over list of connections
			for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)	
			{
				// If connection attached to output port: either directly or INDIRECTLY through a bend point
				if((*it_con)->GetRefFromPort() == (*it_port))
				{
					// Check signal validity and get signal data
					valid = (*it_con)->GetSignal()->GetSignalValidity();
					if(valid == 0)
					{
						return valid;
					}
					else
					{
						// Get signal data dimensions to make sure signal output is a column vector
						(*it_con)->GetSignal()->GetSignalDataDimensions(&nrows, &ncols);

						if((nrows >= 1) && (ncols == 1))
						{
							// The signal data is that of the current output connection
							data_matrix = (*it_con)->GetSignal()->GetSignalData(&nrows, &ncols);

							// Build up the total system signal vector x_vec (x)
							// x_vec = x = [x_0, x_1, ... , x_s], where s is the no. of blocks generating output signals
							for(i=0; i<nrows; i++)
							{
								x_vec[cnt] = data_matrix[i][0];
								cnt++;
							}
						}	
						else
						{
							valid = 0;
							return valid;	
						}					

						// Only one signal data matrix exists for the output connection although more than one connection
						// may emanate from the same output port due to ref-from port updating, in which case the signal
						// data is duplicated, and hence only one copy of the signal is required.
						break;	// break from "for it_con"			
					}
				}
			}// end it_con
			if(valid == 1)
			{
				break;	// break from "for it_port"
			}
		}// end it_port
					
	}// end it_blk

	return valid;
}

int CSystemModel::DetermineSystemEquationsFunctionOperationVector(int t_cnt, double t, double delta_t, double *x_vec, double *f_vec)
{
	int i;		
	int cnt = 0;						// counter used to build up f_vec
	int ncols = 0;						// number of cols of data signal
	int nrows = 0;						// number of rows of data signal
	int valid = 0;						// validity of block operation
	double **data_matrix = NULL;		// signal data matrix
	CString	sMsg;						// string to be displayed
	CString sMsgTemp;					// temp msg.
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*> equ_blk_list;			// a list of ptrs to the Blocks used to form the simultaneous equations
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.
	vector<CPort*>::iterator it_port;	// iterator for vector of CPort ptrs.

	// Get the list of blocks concerning the simultaneous equations
	DetermineSystemEquationsBlockList(equ_blk_list);

	// Get the actual connection list by reference
	list<CConnection*> &con_list = GetConnectionList();		// get the connection list

	// FOR ALL BLOCKS IN THE BLOCK LIST USED TO CONSTRUCT THE SYSTEM OF EQUATIONS (i.e. all non-Output blocks)
	for(it_blk = equ_blk_list.begin(); it_blk != equ_blk_list.end(); it_blk++)
	{
		// ASSIGN THE INCOMING SIGNAL VECTOR x (equivalently x_vec) TO THE BLOCK OUTPUT CONNECTION-BASED SIGNAL DATA DIRECTLY
		valid = AssignSystemEquationsSignalVector(x_vec);
		if(valid == 0)
		{
			sMsgTemp.Format("\n DetermineSystemEquationsFunctionOperationVector()\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format("\n WARNING! Unsuccessful assignment of x to signal data!\n");
			sMsg+=sMsgTemp;
			AfxMessageBox(sMsg, nType, nIDhelp);
			return valid;
		}

		// EXECUTE THE BLOCK OPERATION FUNCTION OperateOnData() WHICH CALLS WriteDataToOutputSignal()  
		valid = (*it_blk)->OperateOnData(t_cnt, t, delta_t);

		// If operational validity < 1 then operation in error and abort function vector f(x) determination
		if(valid < 1)			
		{
			valid = 0;
			sMsgTemp.Format("\n DetermineSystemEquationsFunctionOperationVector()\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format("\n WARNING! Unsuccessful (non-Output) block operation!\n");
			sMsg+=sMsgTemp;
			sMsgTemp.Format(" Aborting f(x) vector construction!\n");
			sMsg+=sMsgTemp;
			AfxMessageBox(sMsg, nType, nIDhelp);	// msg. box	
			return valid;
		}

		// GET THE OUTPUT AS A RESULT OF THE BLOCK FUNCTIONAL OPERATION
		vector<CPort*> vector_of_output_ports = (*it_blk)->GetVectorOfOutputPorts(); 	// vector of output ports		
		for(it_port = vector_of_output_ports.begin(); it_port != vector_of_output_ports.end(); it_port++)	
		{
			// Iterate over list of connections
			for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)	
			{
				// If connection attached to output port: either directly or INDIRECTLY through a bend point
				if((*it_con)->GetRefFromPort() == (*it_port))
				{
					// Check signal validity and get signal data
					valid = (*it_con)->GetSignal()->GetSignalValidity();
					if(valid == 0)
					{
						return valid;
					}
					else
					{
						// Get signal data dimensions to make sure signal output is a column vector
						(*it_con)->GetSignal()->GetSignalDataDimensions(&nrows, &ncols);

						if((nrows >= 1) && (ncols == 1))
						{
							// The signal data is that generated by OperateOnData() which called WriteDataToOutputSignal()
							data_matrix = (*it_con)->GetSignal()->GetSignalData(&nrows, &ncols);

							// Build up the total functional operation vector
							for(i=0; i<nrows; i++)
							{
								f_vec[cnt] = data_matrix[i][0];
								cnt++;
							}
						}	
						else
						{
							valid = 0;
							return valid;	
						}					

						// Only one block operation concerning the output connection
						break;	// break from "for it_con"			
					}
				}
			}// end it_con
			if(valid == 1)
			{
				break;	// break from "for it_port"
			}
		}// end it_port
					
	}// end it_blk

	return valid;
}

int CSystemModel::NewtonSolver(int nrows, double *x_vec, int t_cnt, double t, double delta_t)
{
	int i;
	int check;
	int valid = 1;
	double *x = NULL;
	CNewtonMethod probObj(t_cnt, t, delta_t);	// a problem object upon which newt() is called

	// MEMORY ALLOC
	x = new double[nrows];
	if(!x)
	{
		valid = 0;
		return valid;
	}

	// Prepare x for newt() using x_vec
	for(i=0; i<nrows; i++)
	{
		x[i] = x_vec[i];		
	}

	// Check input to newt()
	//probObj.PrintVector(x_vec, nrows);

	// CALL newt() TO OBTAIN THE VECTOR x THAT SATISFIES F(x) = x - f(x) (= 0).
	valid = probObj.newt(x, nrows, &check);

	if(valid != 1)				// if valid != 1 then newt() is in error
	{
		valid = 0;
		//probObj.PrintVector(x, nrows);
	}

	// Prepare the solution vector x_vec from the vector x returned by newt() if newt() not in error.
	if(valid == 1)
	{
		for(i=0; i<nrows; i++)
		{
			x_vec[i] = x[i];	
		}
	}

	// Check output from newt()
	//probObj.PrintVector(x_vec, nrows);	// print the vector x_vec of type double
	
	// MEMORY DELETE
	if(x != NULL)
	{
		delete [] x;
		x = NULL;
	}

	return valid;
}

int CSystemModel::AssignSystemEquationsSignalVector(double *x_vec)
{
	int i;
	int cnt = 0;						// counter used to decompose system x_vec into cmpt vector signals x_0, x_1, ... , x_s.
	int ncols = 0;						// number of cols of data signal
	int nrows = 0;						// number of rows of data signal
	int valid = 0;						// validity of operation
	double **data_matrix = NULL;		// signal data matrix
	list<CBlock*> equ_blk_list;			// a list of ptrs to the Blocks used to form the simultaneous equations
	list<CBlock*>::iterator it_blk;		// iterator for list of CBlock ptrs.
	list<CConnection*>::iterator it_con;// iterator for list of CConnection ptrs.
	vector<CPort*>::iterator it_port;	// iterator for vector of CPort ptrs.

	// Get the list of blocks concerning the simultaneous equations
	DetermineSystemEquationsBlockList(equ_blk_list);

	// Get the actual connection list by reference
	list<CConnection*> &con_list = GetConnectionList();		// get the connection list

	// FOR ALL BLOCKS IN THE BLOCK LIST USED TO CONSTRUCT THE SYSTEM OF EQUATIONS (i.e. all non-Output blocks)
	for(it_blk = equ_blk_list.begin(); it_blk != equ_blk_list.end(); it_blk++)
	{
		// Iterate over list of output ports
		vector<CPort*> vector_of_output_ports = (*it_blk)->GetVectorOfOutputPorts(); // vector of output ports		
		for(it_port = vector_of_output_ports.begin(); it_port != vector_of_output_ports.end(); it_port++)	
		{
			// Iterate over list of connections
			for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)	
			{
				// If connection attached to output port: either directly or INDIRECTLY through a bend point
				if((*it_con)->GetRefFromPort() == (*it_port))
				{
					// Get signal data dimensions to make sure signal output is a column vector
					(*it_con)->GetSignal()->GetSignalDataDimensions(&nrows, &ncols);

					// GET THE EXISTING OUTPUT SIGNAL DATA
					if((nrows >= 1) && (ncols == 1))
					{	
						// MEMORY NEW
						data_matrix = new double *[nrows];	
						for(i=0; i<nrows; i++)
						{
							data_matrix[i] = new double[ncols];	
						}

						// BUILD UP THE COMPONENT VECTOR x_i from system signal vector x_vec = x = [x_0, x_1, ... , x_s]
						for(i=0; i<nrows; i++)
						{
							data_matrix[i][0] = x_vec[cnt];
							cnt++;
						}

						//PrintMatrix(data_matrix, nrows, ncols);

						// WRITE DATA TO THE OUTPUT SIGNAL
						(*it_con)->GetSignal()->ResetSignalData();				// erase existing signal
						valid = (*it_blk)->WriteDataToOutputSignal(data_matrix, nrows, ncols);	// write new signal data
			
						// MEMORY DELETE
						if(data_matrix != NULL)				// only delete memory if it was in fact allocated
						{
							for(i=0; i<nrows; i++)
							{
								delete [] data_matrix[i];	// delete the row arrays
							}
							delete [] data_matrix;			// delete the array (column vector) holding the row arrays	
							data_matrix = NULL;
						}
	
						if(valid == 0)
						{
							return valid;
						}						
					}	
					else
					{
						valid = 0;
						return valid;	
					}					
					// Only one signal data matrix exists for the output connection although more than one connection
					// may emanate from the same output port due to ref-port updating, in which case the signal data
					// is duplicated for all relevant connections as performed in WriteDataToOutputSignal().
					break;	// break from "for it_con"			
				}
			}// end it_con
			if(valid == 1)
			{
				break;	// break from "for it_port"
			}
		}// end it_port
					
	}// end it_blk

	return valid;
}

void CSystemModel::ResetMemoryBasedBlockData()
{
	CString blk_name;				// block name
	list<CBlock*>::iterator it_blk;	// block iterator

	// Reset block data of those blocks that have memory, i.e. remember previous values
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		blk_name = (*it_blk)->GetBlockName();
	
		if((blk_name == "derivative_block") || (blk_name == "integrator_block")) 
		{
			(*it_blk)->ResetBlockData();	
		}
	}

	return;
}

void CSystemModel::WriteSystemModelDataToFile(ofstream &fout)
{
	char strVar[L_FILE_IO_STR];				// local string var
	list<CAnnotation*>::iterator it_an;		// annotation list iterator
	list<CBlock*>::iterator it_blk;			// blk list iterator
	list<CConnection*>::iterator it_con;	// connection list iterator

	//AfxMessageBox("\n CSystemModel::WriteSystemModelDataToFile()\n", MB_OK, 0);

	// Write member data to file.
	strcpy(strVar, m_strModelName);				
	fout << strVar << endl;						

	fout << m_dATOL << endl;
	fout << m_dRTOL << endl;
	fout << m_dTimeStart << endl;
	fout << m_dTimeStepSize << endl;
	fout << m_dTimeStop << endl;

	strcpy(strVar, m_strIntegrationMethod);
	fout << strVar << endl;

	strcpy(strVar, m_strTimeStepType);
	fout << strVar << endl;

	strcpy(strVar, m_strWarning);
	fout << strVar << endl;

	// Iterate through the block list calling WriteBlockDataToFile()
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		(*it_blk)->WriteBlockDataToFile(fout);
	}

	// Iterate through the connection list calling WriteConnectionDataToFile()
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		(*it_con)->WriteConnectionDataToFile(fout);
	}

	// Iterate through the annotation list calling WriteAnnotationDataToFile()
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		(*it_an)->WriteAnnotationDataToFile(fout);
	}
}

void CSystemModel::ReadSystemModelDataFromFile(ifstream &fin)
{
	int cnt = 0;					// counter to discern correctness of first line of file
	int error_cnt = 0;				// counter to discern an inf. loop			
	int max_cnt = 10000;			// max cnt for error detection
	char strLine[L_FILE_IO_STR];	// current line as a character array
	char strVar[L_FILE_IO_STR];		// character array var to work with "fin >>"
	CString sMsg;					// msg string
	CString sMsgTemp;				// temp msg string
	streampos stream_posn;			// position in the stream
	
	//AfxMessageBox("\n CSystemModel::ReadSystemModelDataFromFile()\n", MB_OK, 0);

	// READ THROUGH CONTENTS OF FILE
	while(!fin.eof())
	{
		// Get current stream posn
		stream_posn = fin.tellg();
		
		// Get current line (NOTE: getline() did not work as expected here and returned an empty line)
		fin >> strLine;
	
		// Now go back to the start of the line, such that the first data element can be read in.
		fin.seekg(stream_posn);

		// IF NOT EMPTY STR, THEN CONTINUE
		// If the comparison of "strLine" with the empty string equals zero, then "strLine" is the empty string.
		// If the comparison of "strLine" with the empty string != zero, then "strLine" is not the empty string.
		if(strcmp(strLine, "") != 0)						
		{
			// SYSTEM MODEL 
			// Check that this is a model_data.txt file with "system_model" as the first line

			// If first readable line is not "system_model" then abort file i/o
			if((strcmp(strLine, "system_model") != 0) && (cnt == 0))		
			{
				sMsg.Format("");
				sMsgTemp.Format("\n CSystemModel::ReadSystemModelDataFromFile()    \n");
				sMsg += sMsgTemp;
				sMsgTemp.Format(" File incorrectly formatted.\n");
				sMsg += sMsgTemp;
				sMsgTemp.Format(" Aborting file i/o.\n");
				sMsg += sMsgTemp;
				AfxMessageBox(sMsg, MB_OK | MB_ICONSTOP, 0);
				return;
			}
			cnt++;	
				
			// If strLine is "system_model" then read in the data
			if(strcmp(strLine, "system_model") == 0)			
			{
				fin >> strVar;
				m_strModelName = strVar;
				fin >> m_dATOL;
				fin >> m_dRTOL;
				fin >> m_dTimeStart;
				fin >> m_dTimeStepSize;
				fin >> m_dTimeStop;

				// Integration method
				while(fin.getline(strVar, L_FILE_IO_STR, '\n'))
				{
					// If string not empty then assume string on line is correct
					if(strcmp(strVar, "") != 0)		
					{
						m_strIntegrationMethod = strVar;
						break;
					}
				}
				
				// Time-step type
				while(fin.getline(strVar, L_FILE_IO_STR, '\n'))
				{
					// If string not empty then assume string on line is correct
					if(strcmp(strVar, "") != 0)		
					{
						m_strTimeStepType = strVar;
						break;
					}
				}
				
				// Warning msg.
				while(fin.getline(strVar, L_FILE_IO_STR, '\n'))
				{
					// If string not empty then assume string on line is correct
					if(strcmp(strVar, "") != 0)		
					{
						m_strWarning = strVar;
						break;
					}
				}
			}// end system model
			
			// ANNOTATION
			if(strcmp(strLine, "annotation") == 0)			
			{
				CAnnotation *pAn = new CAnnotation();	
				pAn->ReadAnnotationDataFromFile(fin);								// read and assign annotation member data
				m_lstAnnotation.push_back(pAn);										// add annotation to annotation list
			}

			// CONNECTION
			if(strcmp(strLine, "connection") == 0)			
			{
				CConnection *pCon = new CConnection(CPoint(0,0), CPoint(100,100));	// pass in dummy connection head and tail pts.
				pCon->ReadConnectionDataFromFile(fin);								// read and assign the connections member data
				m_lstConnection.push_back(pCon);									// add the connection to the connection list
			}

			// CONSTANT BLOCK
			if(strcmp(strLine, "constant_block") == 0)
			{
				CBlock *p_block = new CConstantBlock(this, CPoint(100,100), e_rectangle);	// pass in &system_model, a dummy block posn and shape
				p_block->ReadBlockDataFromFile(fin);										// read and assign the block member data
				m_lstBlock.push_back(p_block);												// add the block to the block list
			}

			// DERIVATIVE BLOCK
			if(strcmp(strLine, "derivative_block") == 0)
			{
				CBlock *p_block = new CDerivativeBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

			// DIVIDE BLOCK
			if(strcmp(strLine, "divide_block") == 0)
			{
				CBlock *p_block = new CDivideBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);								
				m_lstBlock.push_back(p_block);										
			}

			// GAIN BLOCK
			if(strcmp(strLine, "gain_block") == 0)
			{
				CBlock *p_block = new CGainBlock(this, CPoint(100,100), e_triangle);		
				p_block->ReadBlockDataFromFile(fin);								
				m_lstBlock.push_back(p_block);										
			}

			// INTEGRATOR BLOCK
			if(strcmp(strLine, "integrator_block") == 0)
			{
				CBlock *p_block = new CIntegratorBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

			// LINEAR FN BLOCK
			if(strcmp(strLine, "linear_fn_block") == 0)
			{
				CBlock *p_block = new CLinearFnBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);								
				m_lstBlock.push_back(p_block);										
			}

			// OUTPUT BLOCK
			if(strcmp(strLine, "output_block") == 0)
			{
				CBlock *p_block = new COutputBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);								
				m_lstBlock.push_back(p_block);									
			}

			// SIGNAL GENERATOR BLOCK
			if(strcmp(strLine, "signal_generator_block") == 0)
			{
				CBlock *p_block = new CSignalGeneratorBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);										
				m_lstBlock.push_back(p_block);												
			}

			// SUBSYSTEM BLOCK
			if(strcmp(strLine, "subsystem_block") == 0)
			{
				CBlock *p_block = new CSubsystemBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);								
				m_lstBlock.push_back(p_block);											
			}

			// SUBSYSTEM IN BLOCK
			if(strcmp(strLine, "subsystem_in_block") == 0)
			{
				CBlock *p_block = new CSubsystemInBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

			// SUBSYSTEM OUT BLOCK
			if(strcmp(strLine, "subsystem_out_block") == 0)
			{
				CBlock *p_block = new CSubsystemOutBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

			// SUM BLOCK
			if(strcmp(strLine, "sum_block") == 0)
			{
				CBlock *p_block = new CSumBlock(this, CPoint(100,100), e_ellipse);			
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

			// TRANSFER FN BLOCK
			if(strcmp(strLine, "transfer_fn_block") == 0)
			{
				CBlock *p_block = new CTransferFnBlock(this, CPoint(100,100), e_rectangle);	
				p_block->ReadBlockDataFromFile(fin);									
				m_lstBlock.push_back(p_block);											
			}

		}// end strcmp()

		// CHECK FOR FILE READ ERROR I.E. AN INF. LOOP
		error_cnt++;

		if(error_cnt > max_cnt)
		{
			sMsg.Format("");
			sMsgTemp.Format("\n CSystemModel::ReadSystemModelDataFromFile()    \n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" File incorrectly formatted.\n");
			sMsg += sMsgTemp;
			sMsgTemp.Format(" Aborting file i/o.\n");
			sMsg += sMsgTemp;
			AfxMessageBox(sMsg, MB_OK | MB_ICONSTOP, 0);
			return;
		}

	}// end while
}

CPoint CSystemModel::DetermineDiagramCoordinateExtrema(void)
{
	int pt_rad = 5;						// radius of a bend point (assume fixed and small)
	double blk_height;					// block width
	double blk_width;					// block height
	CPoint ptAnPosn;					// annotation position
	CPoint ptBend;						// bend point
	CPoint ptBlkPosn;					// block position
	CPoint ptHead;						// connection head point
	CPoint ptMax;						// pseudo extreme point (extreme coords may be from diff. elements)
	CPoint ptTail;						// connection tail point
	CPoint ptTemp;						// temporary point
	CSize an_text_size;					// annotation text size
	list<CAnnotation*>::iterator it_an;	// annotation iterator
	list<CBlock*>::iterator it_blk;		// block iterator
	list<CConnection*>::iterator it_con;// connection iterator
	list<CPoint>::iterator it_pt;		// bend points iterator
	
	// Init extreme point to the origin.
	ptMax.x = 0;
	ptMax.y = 0;

	// ITERATE THROUGH BLOCK LIST
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		ptBlkPosn = (*it_blk)->GetBlockPosition();
		blk_height = (*it_blk)->GetBlockShape().GetBlockHeight();
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		ptTemp.x = ptBlkPosn.x + int(0.5*blk_width);
		ptTemp.y = ptBlkPosn.y + int(0.5*blk_height);

		if(ptTemp.x > ptMax.x)
		{
			ptMax.x = ptTemp.x;
		}
		if(ptTemp.y > ptMax.y)
		{
			ptMax.y = ptTemp.y;
		}
	}

	// ITERATE THROUGH ALL CONNECTIONS
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		// Compare head point
		ptHead = (*it_con)->GetConnectionPointHead();

		if(ptHead.x > ptMax.x)
		{
			ptMax.x = ptHead.x;
		}
		if(ptHead.y > ptMax.y)
		{
			ptMax.y = ptHead.y;
		}

		// Compare tail point
		ptTail = (*it_con)->GetConnectionPointTail();

		if(ptTail.x > ptMax.x)
		{
			ptMax.x = ptTail.x;
		}
		if(ptTail.y > ptMax.y)
		{
			ptMax.y = ptTail.y;
		}

		// Compare connection's bend points
		list<CPoint> &lstBendPoints = (*it_con)->GetConnectionBendPointsList();

		for(it_pt = lstBendPoints.begin(); it_pt != lstBendPoints.end(); it_pt++)
		{
			// Ellipse surrounds the bend point with radius r (pt_rad).
			ptTemp.x = (*it_pt).x + pt_rad;		
			ptTemp.y = (*it_pt).y + pt_rad;

			if(ptTemp.x > ptMax.x)	
			{
				ptMax.x = ptTemp.x;
			}
			if(ptTemp.y > ptMax.y)
			{
				ptMax.y = ptTemp.y;
			}
		}
	}

	// ITERATE THROUGH ANNOTATION LIST
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		// Dimensions
		ptAnPosn = (*it_an)->GetPosition();
		an_text_size = (*it_an)->GetOutputTextSize();

		// Annotation string extreme point coords
		ptTemp.x = ptAnPosn.x + an_text_size.cx;
		ptTemp.y = ptAnPosn.y + an_text_size.cy;

		if(ptTemp.x > ptMax.x)
		{
			ptMax.x = ptTemp.x;
		}
		if(ptTemp.y > ptMax.y)
		{
			ptMax.y = ptTemp.y;
		}
	}

	return ptMax;
}

int CSystemModel::NullifyNegativeCoordinates()
{
	CPoint ptAn;							// annotation position
	CPoint ptBlk;							// block position
	CPoint ptCon;							// connection-based point location
	list<CAnnotation*>::iterator it_an;		// annotation iterator
	list<CBlock*>::iterator it_blk;			// block iterator
	list<CConnection*>::iterator it_con;	// connection iterator
	list<CPoint>::iterator it_pt;			// point iterator

	// CHECK BLOCK COORDINATES
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		ptBlk = (*it_blk)->GetBlockPosition();
		if(ptBlk.x < 0)
		{	
			ptBlk.x = 0;
		}
		if(ptBlk.y < 0)
		{	
			ptBlk.y = 0;
		}
		(*it_blk)->SetBlockPosition(ptBlk);
	}

	// CHECK CONNECTION COORDINATES	
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		// HEAD POINT
		ptCon = (*it_con)->GetConnectionPointHead();
		if(ptCon.x < 0)
		{	
			ptCon.x = 0;
		}
		if(ptCon.y < 0)
		{	
			ptCon.y = 0;
		}
		(*it_con)->SetConnectionPointHead(ptCon);
		
		// TAIL POINT
		ptCon = (*it_con)->GetConnectionPointTail();
		if(ptCon.x < 0)
		{	
			ptCon.x = 0;
		}
		if(ptCon.y < 0)
		{	
			ptCon.y = 0;
		}
		(*it_con)->SetConnectionPointTail(ptCon);
		
		// BEND POINTS
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
		{
			if((*it_pt).x < 0)
			{
				(*it_pt).x = 0;
			}

			if((*it_pt).y < 0)
			{
				(*it_pt).y = 0;
			}
		}
	}

	// CHECK ANNOTATION COORDINATES
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		ptAn = (*it_an)->GetPosition();
		if(ptAn.x < 0)
		{	
			ptAn.x = 0;
		}
		if(ptAn.y < 0)
		{	
			ptAn.y = 0;
		}
		(*it_an)->SetPosition(ptAn);
	}

	return 0;
}

void CSystemModel::ScaleSystemModel(double scale_factor)
{
	int size;							// annotation size
	double height;						// block height
	double width;						// block width
	CPoint ptAn;						// annotation position
	CPoint ptBend;						// connection-based bend point
	CPoint ptBlk;						// block position
	CPoint ptHead;						// head point
	CPoint ptTail;						// tail point
	list<CAnnotation*>::iterator it_an;	// annotation iterator
	list<CBlock*>::iterator it_blk;		// block iterator
	list<CConnection*>::iterator it_con;// connection iterator
	list<CPoint>::iterator it_pt;		// bend point iterator

	// Note: order of operations is important to ensure that items remain connected.
	// Connection end points if attached should remain attached.
	// Block ports should scale correctly with block scaling.

	// SCALE CONNECTION-RELATED VARS
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{	
		// Bend points
		list<CPoint> &lstBendPts = (*it_con)->GetConnectionBendPointsList();
		for(it_pt = lstBendPts.begin(); it_pt != lstBendPts.end(); it_pt++)
		{
			ptBend = (*it_pt);
			ptBend.x = scale_factor*ptBend.x;
			ptBend.y = scale_factor*ptBend.y;
			(*it_pt) = ptBend;
			UpdateConnectionPointTailToBendPoint(&(*it_pt));	// update any tail pt connected to bend pt.
		}

		// Head point - scale if head point not connected to an input port
		ptHead = (*it_con)->GetConnectionPointHead();
		if((*it_con)->GetRefToPort() == NULL)					
		{
			ptHead.x = scale_factor*ptHead.x;
			ptHead.y = scale_factor*ptHead.y;
			(*it_con)->SetConnectionPointHead(ptHead);
		}

		// Tail point - scale if tail point not connected to an output port or bend point
		ptTail = (*it_con)->GetConnectionPointTail();	
		if(((*it_con)->GetRefFromPort() == NULL) && ((*it_con)->GetRefFromPoint() == NULL))
		{
			ptTail.x = scale_factor*ptTail.x;
			ptTail.y = scale_factor*ptTail.y;
			(*it_con)->SetConnectionPointTail(ptTail);
		}
	}

	// SCALE BLOCK-RELATED VARS
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		// Block width
		width = (*it_blk)->GetBlockShape().GetBlockWidth();
		width = scale_factor*width;
		(*it_blk)->GetBlockShape().SetBlockWidth(width);

		// Block height
		height = (*it_blk)->GetBlockShape().GetBlockHeight();
		height = scale_factor*height;
		(*it_blk)->GetBlockShape().SetBlockHeight(height);

		// Block posn
		ptBlk = (*it_blk)->GetBlockPosition();	
		ptBlk.x = scale_factor*ptBlk.x;
		ptBlk.y = scale_factor*ptBlk.y;
		(*it_blk)->SetBlockPosition(ptBlk);
	}

	// SCALE ANNOTATION-RELATED VARS
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		// Annotation size
		size = (*it_an)->GetSize();
		size = scale_factor*size;
		(*it_an)->SetSize(size);
		(*it_an)->CreateAnnotationFont((*it_an)->GetAnnotationFont());

		// Annotation position
		ptAn = (*it_an)->GetPosition();
		ptAn.x = scale_factor*ptAn.x;
		ptAn.y = scale_factor*ptAn.y;
		(*it_an)->SetPosition(ptAn);
	}
}

CPoint CSystemModel::DetermineDiagramCoordinateMinima()
{
	int pt_rad = 5;						// radius of a bend point (assume fixed and small)
	int int_max = 2147483647;			// INT_MAX = 2147483647.
	double blk_height;					// block width
	double blk_width;					// block height
	CPoint ptAnPosn;					// annotation position
	CPoint ptBend;						// bend point
	CPoint ptBlkPosn;					// block position
	CPoint ptHead;						// connection head point
	CPoint ptMin;						// pseudo minimum point (minimum coords may be from diff. elements)
	CPoint ptTail;						// connection tail point
	CPoint ptTemp;						// temporary point
	list<CAnnotation*>::iterator it_an;	// annotation iterator
	list<CBlock*>::iterator it_blk;		// block iterator
	list<CConnection*>::iterator it_con;// connection iterator
	list<CPoint>::iterator it_pt;		// bend points iterator
	
	// Init minimum point to INT_MAX
	ptMin.x = int_max;
	ptMin.y = int_max;

	// ITERATE THROUGH BLOCK LIST
	for(it_blk = m_lstBlock.begin(); it_blk != m_lstBlock.end(); it_blk++)
	{
		ptBlkPosn = (*it_blk)->GetBlockPosition();
		blk_height = (*it_blk)->GetBlockShape().GetBlockHeight();
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		ptTemp.x = ptBlkPosn.x - int(0.5*blk_width);
		ptTemp.y = ptBlkPosn.y - int(0.5*blk_height);

		if(ptTemp.x < ptMin.x)
		{
			ptMin.x = ptTemp.x;
		}
		if(ptTemp.y < ptMin.y)
		{
			ptMin.y = ptTemp.y;
		}
	}

	// ITERATE THROUGH ALL CONNECTIONS
	for(it_con = m_lstConnection.begin(); it_con != m_lstConnection.end(); it_con++)
	{
		// Compare head point
		ptHead = (*it_con)->GetConnectionPointHead();

		if(ptHead.x < ptMin.x)
		{
			ptMin.x = ptHead.x;
		}
		if(ptHead.y < ptMin.y)
		{
			ptMin.y = ptHead.y;
		}

		// Compare tail point
		ptTail = (*it_con)->GetConnectionPointTail();

		if(ptTail.x < ptMin.x)
		{
			ptMin.x = ptTail.x;
		}
		if(ptTail.y < ptMin.y)
		{
			ptMin.y = ptTail.y;
		}

		// Compare connection's bend points
		list<CPoint> &lstBendPoints = (*it_con)->GetConnectionBendPointsList();

		for(it_pt = lstBendPoints.begin(); it_pt != lstBendPoints.end(); it_pt++)
		{
			// Ellipse surrounds the bend point with radius r (pt_rad).
			ptTemp.x = (*it_pt).x - pt_rad;		
			ptTemp.y = (*it_pt).y - pt_rad;

			if(ptTemp.x < ptMin.x)	
			{
				ptMin.x = ptTemp.x;
			}
			if(ptTemp.y < ptMin.y)
			{
				ptMin.y = ptTemp.y;
			}
		}
	}

	// ITERATE THROUGH ANNOTATION LIST
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		// Dimensions
		ptAnPosn = (*it_an)->GetPosition();
		ptTemp = ptAnPosn;					// upper left corner is min pt and also annotation posn

		if(ptTemp.x < ptMin.x)
		{
			ptMin.x = ptTemp.x;
		}
		if(ptTemp.y < ptMin.y)
		{
			ptMin.y = ptTemp.y;
		}
	}

	return ptMin;
}

CSystemModel::CSystemModel(const CSystemModel &sm)
{
	int i = 0;
	int ncols = 0;
	int *tour_vec_copy = NULL;
	CAnnotation *pAn = NULL;
	CBlock *pBlock = NULL;
	CConnection *pCon = NULL;
	list<CAnnotation*>::const_iterator it_an;
	list<CBlock*>::const_iterator it_blk;
	list<CConnection*>::const_iterator it_con;
	list<int*>::const_iterator it_tr;	

	// INTEGER DATA 
	m_iDrawAnnotationFlag = sm.GetDrawAnnotationFlag();
	m_iModelStatusFlag = sm.GetModelStatusFlag();
	m_iNOutputBlocks = sm.GetNOutputBlocks();
	m_iNSourceBlocks = sm.GetNSourceBlocks();
	m_iNSourceLoopTours = sm.GetNSourceLoopTours();
	m_iNSourceOutputTours = sm.GetNSourceOutputTours();

	// DOUBLE DATA
	m_dATOL = sm.GetATOL();
	m_dRTOL = sm.GetRTOL();	
	m_dTimeStart = sm.GetTimeStart();
	m_dTimeStepSize = sm.GetTimeStepSize();
	m_dTimeStop = sm.GetTimeStop();
	
	// CString DATA
	m_strIntegrationMethod = sm.GetIntegrationMethod();
	m_strModelName = sm.GetModelName();
	m_strTimeStepType = sm.GetTimeStepType();
	m_strWarning = sm.GetWarning();

	// LIST DATA
	// Copy Block List
	const list<CBlock*> *plstBlock = sm.GetBlockListCopy();

	for(it_blk = (*plstBlock).begin(); it_blk != (*plstBlock).end(); it_blk++)
	{
		pBlock = (*it_blk)->CopyBlockData();
		m_lstBlock.push_back(pBlock);
	}

	// Copy Connection List
	const list<CConnection*> *plstConnection = sm.GetConnectionListCopy();

	for(it_con = (*plstConnection).begin(); it_con != (*plstConnection).end(); it_con++)
	{
		pCon = new CConnection(**it_con);
		m_lstConnection.push_back(pCon);
	}
	
	// Copy Source Loop Tour List
	const list<int*> *plstSLT = sm.GetSourceLoopTourListCopy();

	for(it_tr = (*plstSLT).begin(); it_tr != (*plstSLT).end(); it_tr++)
	{
		ncols = sizeof(*it_tr)/sizeof(int);			// get size of array
		tour_vec_copy = new int[ncols];				// allocate memory
		for(i=0; i<ncols; i++)
		{
			tour_vec_copy[i] = (*it_tr)[i];
		}
		m_lstSourceLoopTour.push_back(tour_vec_copy);
	}

	// Copy Source Output Tour List
	const list<int*> *plstSOT = sm.GetSourceOutputTourListCopy();

	for(it_tr = (*plstSOT).begin(); it_tr != (*plstSOT).end(); it_tr++)
	{
		ncols = sizeof(*it_tr)/sizeof(int);			// get size of array
		tour_vec_copy = new int[ncols];				// allocate memory
		for(i=0; i<ncols; i++)
		{
			tour_vec_copy[i] = (*it_tr)[i];
		}
		m_lstSourceOutputTour.push_back(tour_vec_copy);
	}

	// Copy Annotation List
	const list<CAnnotation*> *plstAnnotation = sm.GetAnnotationListCopy();

	for(it_an = (*plstAnnotation).begin(); it_an != (*plstAnnotation).end(); it_an++)
	{
		pAn = new CAnnotation(**it_an);
		m_lstAnnotation.push_back(pAn);
	}
}

int CSystemModel::GetNOutputBlocks() const
{
	return m_iNOutputBlocks;
}

int CSystemModel::GetNSourceBlocks() const
{
	return m_iNSourceBlocks;
}

int CSystemModel::GetNSourceLoopTours() const
{
	return m_iNSourceLoopTours;
}

int CSystemModel::GetNSourceOutputTours() const
{
	return m_iNSourceOutputTours;
}

double CSystemModel::GetATOL() const
{
	return m_dATOL;
}

double CSystemModel::GetRTOL() const
{
	return m_dRTOL;
}

double CSystemModel::GetTimeStart() const
{
	return m_dTimeStart;
}

double CSystemModel::GetTimeStepSize() const
{
	return m_dTimeStepSize;
}

double CSystemModel::GetTimeStop() const
{
	return m_dTimeStop;
}

CString CSystemModel::GetTimeStepType() const
{
	return m_strTimeStepType;
}

CString CSystemModel::GetWarning() const
{
	return m_strWarning;
}

const list<CAnnotation*> *CSystemModel::GetAnnotationListCopy() const
{
	// For the fn. to be const, a pointer-to-const type is returned.
	return &m_lstAnnotation;
}

const list<CBlock*> *CSystemModel::GetBlockListCopy() const
{
	// For the fn. to be const, a pointer-to-const type is returned.
	return &m_lstBlock;
}

const list<CConnection*> *CSystemModel::GetConnectionListCopy() const
{
	// For the fn. to be const, a pointer-to-const type is returned.
	return &m_lstConnection;
}

const list<int*> *CSystemModel::GetSourceLoopTourListCopy() const
{
	// For the fn. to be const, a pointer-to-const type is returned.
	return &m_lstSourceLoopTour;
}

const list<int*> *CSystemModel::GetSourceOutputTourListCopy() const
{
	// For the fn. to be const, a pointer-to-const type is returned.
	return &m_lstSourceOutputTour;
}

CSystemModel &CSystemModel::operator=(const CSystemModel &sm)
{
	int i = 0;
	int ncols = 0;
	int *tour_vec_copy = NULL;
	CAnnotation *pAn = NULL;
	CBlock *pBlock = NULL;
	CConnection *pCon = NULL;	
	list<CAnnotation*>::const_iterator it_an;
	list<CBlock*>::const_iterator it_blk;
	list<CConnection*>::const_iterator it_con;
	list<int*>::const_iterator it_tr;

	// CHECK FOR SELF-ASSIGNMENT 
	if(this == &sm)					// if the address of the current object, "this", is the address of the incoming obj.
	{
		return *this;				// deref the "this" pointer to return the obj.
	}

	// DELETE SYSTEM MODEL LISTS
	DeleteBlockList();				// Delete block list
	DeleteConnectionList();			// Delete connection list
	DeleteSourceOutputLoopLists();	// Delete source-to-output-block and source-to-loop-repeated-block lists
	DeleteAnnotationList();			// Delete annotation list

	// INTEGER DATA 
	m_iDrawAnnotationFlag = sm.GetDrawAnnotationFlag();
	m_iModelStatusFlag = sm.GetModelStatusFlag();
	m_iNOutputBlocks = sm.GetNOutputBlocks();
	m_iNSourceBlocks = sm.GetNSourceBlocks();
	m_iNSourceLoopTours = sm.GetNSourceLoopTours();
	m_iNSourceOutputTours = sm.GetNSourceOutputTours();

	// DOUBLE DATA
	m_dATOL = sm.GetATOL();
	m_dRTOL = sm.GetRTOL();	
	m_dTimeStart = sm.GetTimeStart();
	m_dTimeStepSize = sm.GetTimeStepSize();
	m_dTimeStop = sm.GetTimeStop();
	
	// CString DATA
	m_strIntegrationMethod = sm.GetIntegrationMethod();
	m_strModelName = sm.GetModelName();
	m_strTimeStepType = sm.GetTimeStepType();
	m_strWarning = sm.GetWarning();

	// LIST DATA
	// Copy Block List
	const list<CBlock*> *plstBlock = sm.GetBlockListCopy();

	for(it_blk = (*plstBlock).begin(); it_blk != (*plstBlock).end(); it_blk++)
	{
		pBlock = (*it_blk)->CopyBlockData();	// calls the appropriate copy constructor and returns a ptr-to-CBlock
		m_lstBlock.push_back(pBlock);
	}

	// Copy Connection List
	const list<CConnection*> *plstConnection = sm.GetConnectionListCopy();

	for(it_con = (*plstConnection).begin(); it_con != (*plstConnection).end(); it_con++)
	{
		pCon = new CConnection(**it_con);	// deref the iterator and then the ptr-to-CConnection to pass the CConnection obj.
		m_lstConnection.push_back(pCon);
	}
	
	// Copy Source Loop Tour List
	const list<int*> *plstSLT = sm.GetSourceLoopTourListCopy();

	for(it_tr = (*plstSLT).begin(); it_tr != (*plstSLT).end(); it_tr++)
	{
		ncols = sizeof(*it_tr)/sizeof(int);			// get size of array
		tour_vec_copy = new int[ncols];				// allocate memory
		for(i=0; i<ncols; i++)
		{
			tour_vec_copy[i] = (*it_tr)[i];
		}
		m_lstSourceLoopTour.push_back(tour_vec_copy);
	}

	// Copy Source Output Tour List
	const list<int*> *plstSOT = sm.GetSourceOutputTourListCopy();

	for(it_tr = (*plstSOT).begin(); it_tr != (*plstSOT).end(); it_tr++)
	{
		ncols = sizeof(*it_tr)/sizeof(int);			// get size of array
		tour_vec_copy = new int[ncols];				// allocate memory
		for(i=0; i<ncols; i++)
		{
			tour_vec_copy[i] = (*it_tr)[i];
		}
		m_lstSourceOutputTour.push_back(tour_vec_copy);
	}

	// Copy Annotation List
	const list<CAnnotation*> *plstAnnotation = sm.GetAnnotationListCopy();

	for(it_an = (*plstAnnotation).begin(); it_an != (*plstAnnotation).end(); it_an++)
	{
		pAn = new CAnnotation(**it_an);
		m_lstAnnotation.push_back(pAn);
	}

	// RETURN THE CURRENT OBJECT
	return *this;			// deref the "this" pointer to return the underlying obj.
}

list<CAnnotation*> &CSystemModel::GetAnnotationList()
{
	// Return the actual annotation list by reference (non constant fn).
	return m_lstAnnotation;
}

void CSystemModel::DeleteAnnotationList()
{
	list<CAnnotation*>::iterator it_an;

	// Delete annotation list
	for(it_an = m_lstAnnotation.begin(); it_an != m_lstAnnotation.end(); it_an++)
	{
		delete (*it_an);	// delete what it_an is pointing to: i.e. deref the it_an ptr and delete the ptr-to-CAnnotation
	}
	m_lstAnnotation.clear();
}

int CSystemModel::GetDrawAnnotationFlag() const
{
	return m_iDrawAnnotationFlag;
}

void CSystemModel::SetDrawAnnotationFlag(int draw)
{	
	m_iDrawAnnotationFlag = draw;
}

bool CSystemModel::CheckStopButtonPressed()
{
	bool bStop = FALSE;				// btn pressed => bStop = ture
	HWND hWnd = NULL;				// handle to window
	MSG msg;						// MSG structure containing msg info from thread's msg queue
	UINT wMsgFilterMin = 0;			// first msg filter
	UINT wMsgFilterMax = 0;			// last msg filter
	UINT wRemovalMsg = PM_NOREMOVE;	// removal flag: PM_REMOVE or PM_NOREMOVE
	CString sMsg;					// AfxMessageBox() string

	// CHECK MESSAGES
	// PeekMessage() checks a thread msg queue for a msg and places the msg (if any) in msg structure [MSDN].
	while(::PeekMessage(&msg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemovalMsg))
	{
		// PumpMessage() contains the thread's msg. loop.: called directly to force msgs to be processed
		AfxGetApp()->PumpMessage();
		
		// Filter WM_COMMAND event msg's only
		if(msg.message == WM_COMMAND)
		{			
			WORD wNotifyCode = HIWORD(msg.wParam); // notification code: menu or toolbar = 0
			WORD wID = LOWORD(msg.wParam);         // item, control, or accelerator identifier ID_SIM_STOP
		
			//sMsg.Format(" message = %d, wNotifyCode = %d, wID = %d, ID_SIM_STOP = %d\n", msg.message, wNotifyCode, wID, ID_SIM_STOP);
			//AfxMessageBox(sMsg, MB_OK, 0);

			// If menu entry or toolbar button with ID, ID_SIM_STOP selected
			if(wID == ID_SIM_STOP)
			{
				//sMsg.Format(" Stopping simulation ... \n");
				//AfxMessageBox(sMsg, MB_OK, 0);

				bStop = TRUE;
			}
		}
	}

	return bStop;
}
