#include "global.h"
#include "cdatalog.h"
#include "cdatabase.h"
#include "cmemory.h"
#include "csyntactic.h"
#include <stdlib.h>
#include "cgraph.h"
#include "cinfowin.h"
#include "chtml.h"

//#define _PRINTDEBUG

void CDatalog::CreateFromPart(SYNTACTIC_RULE *rule, CMemory *output, DWORD predIndex)
{
	//vytvor klauzulu from do SQL dotazu
	//ak predId != -1, nahrad idb predikat predId seminaivnym
	char tmp2[20];
	bool from = false;
	bool comma = false;
	const CDbStructure *db = app.GetDatabase()->GetStructure();
	for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
	{
		if (rule->atom[atomIndex]->not)
			continue;//negovane nechceme
		DWORD atId = rule->atom[atomIndex]->id;
		SYNTACTIC_ATOM *at = table->atoms[atId];
		switch (at->idb.type)
		{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_EDBATOM:
				if (!from)
				{
					from = true;
					output->AddData(" FROM ", 6);
				}

				if (comma)
					output->AddData(",", 1);
				else
					comma = true;
				break;
		}
		switch (at->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
			{
				if (atomIndex == predIndex)
					output->AddData("demp", 4);
				else
					output->AddData("temp", 4);
				itoa(atId, tmp2, 10); output->AddData(tmp2, strlen(tmp2));
			}
			break;
		case SYNTACTIC_ATOM_EDBATOM:
			{
				output->AddData("public.", 7);//aby sme sa dostali k tabulkam s menom temp[xx]
				char *str = db->tables[atId].name; output->AddData(str, strlen(str));
			}
			break;
		}
		//pridaj alias
		switch (at->idb.type)
		{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_EDBATOM:
				output->AddData(" as t", 5);
				itoa(atomIndex, tmp2, 10); output->AddData(tmp2, strlen(tmp2));
				break;
		}
	}
}

CStringTable **CDatalog::CreateSQLQueries()
{
	//vytvor dotazy pre normalnu "topologicku" evaluaciu

	//zoznam idb predikatov, pre ktore sa pocitaju SQL dotazy - pre vsetky
	DWORD count = 0;
	DWORD *idbPred = _NEW(DWORD[table->nIdb]);

	for (DWORD atomId=0; atomId<table->nAtoms; atomId++)
	{
		SYNTACTIC_ATOM *atom = table->atoms[atomId];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			idbPred[count++] = atomId;
			break;
		}
	}
	CStringTable **ret = CreateSQLQueriesEx(idbPred, count, false);
	_DELETE(idbPred);
	return ret;
}

CStringTable **CDatalog::CreateSQLQueriesEx(DWORD *idbPred, DWORD count, bool seminaive)
{
	//ak je semnaive true, sprav dotazy seminavine, tj. pre kazde pravidlo sprav k dotazov, kde k je pocet IDB predikatov v tele
	//delta tabulky sa volaju demp
	//normalne su temp

	const CDbStructure *db = app.GetDatabase()->GetStructure();

	infoWin->SetText("Vytváram SQL príkazy");

	CMemory *tmp = _NEW(CMemory());
	CMemory *dtmp = _NEW(CMemory());
	char tmp2[20], tmp3[20];

	CStringTable **sqlQueries = _NEW(CStringTable*[table->nAtoms]);
	::ZeroMemory(sqlQueries, 4*table->nAtoms);

	//inser into table select [distinct]??? ...

	for (DWORD ii=0; ii<count; ii++)
	{
		infoWin->NextStep();
		DWORD atomId = idbPred[ii];
		SYNTACTIC_ATOM *atom = table->atoms[atomId];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			sqlQueries[atomId] = _NEW(CStringTable());
			for (DWORD ruleIndex=0; ruleIndex<atom->idb.nRules; ruleIndex++)
			{
				SYNTACTIC_RULE *rule = atom->idb.rules[ruleIndex];
				tmp->DeleteData();

				if (!seminaive)
					tmp->AddData("INSERT INTO temp", 16);
				else
					tmp->AddData("%sINSERT INTO %semp", 19);//nahradi sa "*","d", ak sa nenachadza ziadny IDB)
															//inak sa nahradi "", "e"
				itoa(atomId, tmp2, 10); tmp->AddData(tmp2, strlen(tmp2));

				//vyrob teraz samotny SQL selekt na pridanie dat (teda .......)
				tmp->AddData(" SELECT DISTINCT ", 17);
				//dalej vloz premenne v hlavicke
				for (DWORD argId=0; argId<atom->idb.nArgs; argId++)
				{
					if (argId)
						tmp->AddData(",",1);
					char *str = ExpressionToString(rule, (char*)rule->varNames->Get(argId), true);
					tmp->AddData((void*)str, strlen(str));
					_DELETE(str);
				}

				//vloz kartezsky sucin tabuliek v pravidle (mena edb/idb atomov oddelenych ciarkami)
				//vynechaj negovane

				if (seminaive)
				{
					//pri seminaivnej evaluacii tuto cast budeme nahradzat postupne roznymi verziami
					//v kazdej nahradime jeden IDB predikat jeho delta 
					tmp->AddData("%s", 2);
				}
				else
					//vytvor normalnu from cast
					CreateFromPart(rule, tmp, -1);

				//vloz podmienky do WHERE
				bool where = false;
				bool and = false;

				//vsetky aritmeticke atomy, okrem tych, kde sa vyskytuju agregacne funkcie, prepis pomocou exp2str
				//negovane relacne podciele prepis do ..
				//..NOT EXISTS (SELECT 1 FROM atom as t[atomIndex] WHERE aritm. vyrazy so vsetkymi premennymi v tomto negovanom atome)
				for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
				{
					DWORD atId = rule->atom[atomIndex]->id;
					SYNTACTIC_ATOM *at = table->atoms[atId];
					switch (at->arit.type)
					{
					case SYNTACTIC_ATOM_ARITATOM:
						{
							char *str = ExpressionToString(rule, at->arit.expression, true);
							//over, ci sa vo vyraze nevyskytuje agregacna funkcia (tie sa prepisuju do HAVING klauzuly)
							if (_agrInExp)
							{
								_DELETE(str);
								break;
							}

							if (!where)
							{
								where = true;
								tmp->AddData(" WHERE ",7);
							}
							if (and)
								tmp->AddData(" AND ", 5);
							else
								and = true;

							//negovany aritmeticky vyraz?
							if (rule->atom[atomIndex]->not)
								tmp->AddData("NOT ", 4);

							tmp->AddData(str, strlen(str));
							_DELETE(str);
						}
						break;
					case SYNTACTIC_ATOM_EDBATOM:
					case SYNTACTIC_ATOM_IDBATOM:
						{
							if (!rule->atom[atomIndex]->not)
								continue;//nenegovane nechcem

							if (!where)
							{
								where = true;
								tmp->AddData(" WHERE ", 7);
							}
							if (and)
								tmp->AddData(" AND ", 5);
							else
								and = true;

							tmp->AddData("NOT EXISTS (SELECT 1 FROM ", 26);
							switch (at->idb.type)
							{
							case SYNTACTIC_ATOM_IDBATOM:
								{
									tmp->AddData("temp",4); itoa(atId, tmp2, 10); tmp->AddData(tmp2, strlen(tmp2));
								}
								break;
							case SYNTACTIC_ATOM_EDBATOM:
								{
									tmp->AddData("public.",7);//aby sme sa dostali k tabulkam s menom temp[xx]
									char *str = db->tables[atId].name; tmp->AddData(str, strlen(str));
								}
								break;
							}
							//alias
							tmp->AddData(" as ", 4);
							tmp2[0] = 't';
							itoa(atomIndex, &tmp2[1], 10); tmp->AddData(tmp2, strlen(tmp2));
							//WHERE
							tmp->AddData(" WHERE ", 7);
							for (DWORD argIndex=0; argIndex<rule->atom[atomIndex]->nArgs; argIndex++)
							{
								if (argIndex)
									tmp->AddData(" AND ", 5);
								//alias.c[argIndex]
								tmp->AddData(tmp2, strlen(tmp2));
								switch (at->idb.type)
								{
								case SYNTACTIC_ATOM_IDBATOM:
									{
										tmp->AddData(".c" ,2);
										itoa(argIndex, tmp3, 10); tmp->AddData(tmp3, strlen(tmp3));
									}
									break;
								case SYNTACTIC_ATOM_EDBATOM:
									{
										tmp->AddData(".",1);
										char *str = db->tables[atId].attributes[argIndex].name;
										tmp->AddData(str, strlen(str));
									}
									break;
								}
								//vytvor vyraz '=argument', kde argument je zapisany ako premenna vo vyraze
								tmp3[0]='='; *((DWORD*)&tmp3[1]) = ((rule->atom[atomIndex]->argsId[argIndex])<<8) + DATALOG_EXP_VAR; tmp3[5]=0;
								char *str = ExpressionToString(rule, tmp3, true);
								tmp->AddData(str, strlen(str));
								_DELETE(str);
							}

							tmp->AddData(")", 1);
						}
						break;
					}
				}

				//group by
				//najdi vyskyt vsetkych predikatov groupby a premenne z nich daj do klauzuly GROUP BY
				bool groupby = false; bool comma = false;
				for (atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
				{
					DWORD atId = rule->atom[atomIndex]->id;
					SYNTACTIC_ATOM *at = table->atoms[atId];
					if (at->groupby.type == SYNTACTIC_ATOM_GROUPBYATOM)
					{
						if (!groupby)
						{
							groupby = true;
							tmp->AddData(" GROUP BY ", 10);
						}

						for (DWORD varIndex=0; varIndex<rule->atom[atomIndex]->nArgs; varIndex ++)
						{
							if (comma)
								tmp->AddData(",", 1);
							const char *name = rule->varNames->Get(rule->atom[atomIndex]->argsId[varIndex]);
							tmp->AddData((char*)name, strlen(name));
							comma = true;
						}
					}
				}


				//este HAVING klauzula pre aritmeticke vyrazy s agregacnou funkciou
				bool having = false;
				and = false;
				for (atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
				{
					DWORD atId = rule->atom[atomIndex]->id;
					SYNTACTIC_ATOM *at = table->atoms[atId];
					if (at->arit.type == SYNTACTIC_ATOM_ARITATOM)
					{
						char *str = ExpressionToString(rule, at->arit.expression, true);
						//over, ci sa vo vyraze vyskytuje agregacna funkcia
						if (!_agrInExp)
						{
							_DELETE(str);
							continue;
						}

						if (!having)
						{
							having = true;
							tmp->AddData(" HAVING ",8);
						}
						if (and)
							tmp->AddData(" AND ", 5);
						else
							and = true;

						tmp->AddData(str, strlen(str));
						_DELETE(str);
					}
				}

				tmp->AddData(" EXCEPT (SELECT * FROM ", 23);
				if (!seminaive)
					tmp->AddData("temp", 4);
				else
					tmp->AddData("%semp", 5);
				itoa(atomId, tmp2, 10); tmp->AddData(tmp2, strlen(tmp2));

				//ukonci cely dotaz
				tmp->AddData(");",3);//aj zakonci NULL

				if (seminaive)
				{
					//vytvor teraz rozne verzie tohto jedneho dotazu (lisia sa v IDB predikatoch v tele pravidla)
					bool someidb = false;
					for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
					{
						//ak sa atom tento atom z pravidla nachadza medzi idbPred, nahrad ho delta
						bool replace = false;
						DWORD atomId2 = rule->atom[atomIndex]->id;
						for (DWORD i=0; i<count; i++)
						{
							if (idbPred[i] == atomId2)
							{
								replace = true;
								break;
							}
						}

						if (replace)
						{
							someidb = true;
							CreateFromPart(rule, dtmp, atomIndex);
							char *query = CMemory::printstr((char*)tmp->GetPointer(), "", "e", dtmp->GetPointer(), "e");
							sqlQueries[atomId]->Add(query);
							_DELETE(query);
							dtmp->ClearMemory();
						}
					}

					if (!someidb)
					{
						//ziadny IDB podciel, sprav normalny dotaz
						//na zaciatok hod *, aby sme vedeli identifikovat, ze toto pravidlo obsahuje len edb predikaty alebo
						//len priradenie konstanty
						CreateFromPart(rule, dtmp, -1);
						char *query = CMemory::printstr((char*)tmp->GetPointer(), "*", "d", dtmp->GetPointer(), "d");
						sqlQueries[atomId]->Add(query);
						_DELETE(query);
						dtmp->ClearMemory();
					}

				}
				else
				{
					sqlQueries[atomId]->Add((char*)tmp->GetPointer());
#ifdef _PRINTDEBUG
				//vypis dotaz
				char *tmp2 = CMemory::printstr("DEBUG: %s.%i : %s", atom->idb.name, ruleIndex+1, tmp->GetPointer());
				app.mainWindow->GetOutputWindow()->AddLine(tmp2); _DELETE(tmp2);
#endif
				}
			}
			break;
		}
	}

	_DELETE(tmp);
	_DELETE(dtmp);

	return sqlQueries;
}

void CDatalog::DeleteAllTables(CStringTable *names)
{
	for (DWORD i=0; i<names->GetCount(); i++)
	{
		char *query = CMemory::printstr("DROP TABLE %s;", names->Get(i));
		CSQLresult *res;

		try
		{
			res = app.GetDatabase()->ExecSQL(query);
			if (!res->ok)
			{
#ifdef _PRINTDEBUG
				char *tmp = CMemory::printstr("DEBUG: DeleteIDBTable : SQL %s", res->GetError());
				app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
#endif
				app.mainWindow->GetOutputWindow()->AddLine("Nepodarilo sa zrušiť dočasnú tabuľku! Ukončite aplikáciu a spusťte ju znova.");
			}
			_DELETE(res);
		} catch (int err) {err;}
		_DELETE(query);
	}
}


//docasne tabulky sa vytvaraju v specialnej scheme
CStringTable *CDatalog::CreateAllTables(bool seminaive)
{
	char tmp[20];
	CSQLresult *res = NULL;
	CDatabase *db = app.GetDatabase();
	CMemory *str = _NEW(CMemory());
	CStringTable *tables = _NEW(CStringTable());

	DEBUGPRINT("DEBUG: ----------------------");
	DEBUGPRINT("DEBUG: Creating all tables...");
	infoWin->SetText("Vytváram dočasné tabuľky");

	DWORD atomId;
	try
	{
		for (atomId=0; atomId<table->nAtoms; atomId++)
		{
			if ( (table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_IDBATOM) ||
				(table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_ANSWER))
			{
				//vytvor prikaz pre SQL
				str->AddData("CREATE TEMP TABLE temp", 22);
				itoa(atomId, tmp, 10);//pridaj cislo atomu
				str->AddData(tmp, strlen(tmp));
				str->AddData(" (",2);
				//pridaj nazvy stlpcov
				for (DWORD i=0; i<table->atoms[atomId]->idb.nArgs; i++)
				{
					//pridaj meno a cislo premennej
					if (i)
						str->AddData(",c",2);
					else
						str->AddData("c",1);
					itoa(i, tmp, 10);
					str->AddData(tmp, strlen(tmp));
					str->AddData(" ",1);
					//pridaj typ premennej
					const char *type;
					switch (table->atoms[atomId]->idb.argTypes[i])
					{
					case DATABASE_TYPE_NUMBER:
						type = CDatabase::typeNumeric;
						break;
					case DATABASE_TYPE_STRING:
						type = CDatabase::typeString;
						break;
					case DATABASE_TYPE_DATE:
						type = CDatabase::typeDate;
						break;
					}
					str->AddData((void*)type, strlen(type));
				}
				str->AddData(");",3);

#ifdef _PRINTDEBUG
				char *tmpx = CMemory::printstr("DEBUG: %s: %s", table->atoms[atomId]->idb.name,str->GetPointer());
				app.mainWindow->GetOutputWindow()->AddLine(tmpx); _DELETE(tmpx);
#endif
				res = app.GetDatabase()->ExecSQL((char*)str->GetPointer());
				infoWin->NextStep();
				if (!res->ok)
					throw 1234;
				_DELETE(res);

				char *name = CMemory::printstr("temp%i",atomId); tables->Add(name);_DELETE(name);

				if (seminaive)
				{
					//vytvor este niekolko pomocnych tabuliek
					char *tmp = (char*)str->GetPointer();

					tmp[18]='d';//nie temp ale demp
					res = app.GetDatabase()->ExecSQL(tmp);
					infoWin->NextStep();
					if (!res->ok)
						throw 1234;
					_DELETE(res);
					char *name = CMemory::printstr("demp%i",atomId); tables->Add(name);_DELETE(name);


					tmp[18]='e';
					res = app.GetDatabase()->ExecSQL(tmp);
					infoWin->NextStep();
					if (!res->ok)
						throw 1234;
					_DELETE(res);
					name = CMemory::printstr("eemp%i",atomId); tables->Add(name);_DELETE(name);

				}

				str->DeleteData();
			}
		} //for
	} catch (int err)
	{
		if (err == 1234)//nasa chyba
		{
#ifdef _PRINTDEBUG
			char *tmperr = CMemory::printstr("DEBUG: CreateIDBTable : SQL %s", res->GetError());
			app.mainWindow->GetOutputWindow()->AddLine(tmperr); _DELETE(tmperr);
#else
			app.mainWindow->GetOutputWindow()->AddLine("Nepodarilo sa vytvoriť dočasnú tabuľku!");
#endif
			_DELETE(res);
		}

		_DELETE(str);
		//zmaz doteraz vytvorene tabulky
		DeleteAllTables(tables); _DELETE(tables);
		throw 1;
	}

	DEBUGPRINT("DEBUG: ...done");
	DEBUGPRINT("DEBUG: ----------------------");
	_DELETE(str);

	return tables;
}

/*DWORD CDatalog::GetMaxStratumForVar(SYNTACTIC_RULE *rule, DWORD varId, DWORD *stratum)
{
	DWORD st = 0;
	//prejdi vsetky relacne atomy v pravidle a najdi atom, v ktorom sa viaze premenna varId - zisti jeho stratum
	//hladame max. stratum zo vsetkych
	for (DWORD atomIx=0; atomIx<rule->nAtoms; atomIx++)
	{
		switch (table->atoms[rule->atom[atomIx]->id]->arit.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_EDBATOM:
			//prejdi vsetky premenne v argumentoch a hladaj varId
			for (DWORD varIndex=0; varIndex<rule->atom[atomIx]->nArgs; varIndex++)
			{
				if (rule->atom[atomIx]->argsId[varIndex] == varId)
					//ok, mame predikat, mame jeho strata, ale hladame maximalne
					if (stratum[rule->atom[atomIx]->id]>st)
						st = stratum[rule->atom[atomIx]->id];
			}
			break;
		}
	}
	return st;
}*/

void CDatalog::EvaluateStratum(DWORD *pred, DWORD count)
{
	//rekurzivne vypocita hodnotu predikatov z pola pred dlzky count
	//predpoklad je, ze tie predikaty, ktore pouzivaju, maju stratum <=

	CDatabase *db = app.GetDatabase();

	//vytvor dotazy pre predikaty v pred
	CStringTable **queries = CreateSQLQueriesEx(pred, count, true);

	CSQLresult *res = NULL;

	const char *query;
	char *tmpQuery = NULL;

	try
	{

		//vymazem vsetky P
		for (DWORD i=0;i<count; i++)
		{
			tmpQuery = CMemory::printstr("DELETE FROM temp%i;", pred[i]);
			res = db->ExecSQL(tmpQuery);
			if (!res->ok)
				throw 1234;
			_DELETE(tmpQuery); tmpQuery = NULL;
			_DELETE(res);
		}

		DEBUGPRINT("**********************************");
		DEBUGPRINT("DEBUG: SEMINAIVE EVALUATION");

		//+ vykonaj najskor tie, ktore maju na zaciatku *, tj. tie, v ktorych sa nenachadzaju ziadne IDB predikaty z pola pred
		DEBUGPRINT("DEBUG: compute rules without IDBs");
		for (i=0; i<count; i++)
		{
			if (!queries[pred[i]]) continue;
			for (DWORD q=0;q<queries[pred[i]]->GetCount(); q++)
			{
				query = queries[pred[i]]->Get(q);
				if (query[0]=='*')
				{
					//vykonaj dotaz
					DEBUGPRINT((char*)&query[1]);
					res = db->ExecSQL(&query[1]);
					if (!res->ok)
						throw 1234;
					_DELETE(res); res = NULL;
				}
			}
		}

		//+ opakuj dovtedy, kym deltaP relacie (demp) nie su prazdne
		bool loop = true;
		while (loop)
		{
			infoWin->NextStep();
			loop = false;

			/*************************************************************************************************************/
			//KROK 1
			//su deltaP relacie prazdne?
			/*************************************************************************************************************/
			DEBUGPRINT("DEBUG: STEP 1 - empty deltaP?");
			for (i=0; i<count; i++)
			{
				tmpQuery = CMemory::printstr("SELECT 1 FROM demp%i;", pred[i]);
				res = db->ExecSQL(tmpQuery); _DELETE(tmpQuery); tmpQuery = NULL;
				if (!res->ok)
					throw 1234;
				DWORD n = res->GetnTuples();
				_DELETE(res);
				if (n)
					loop = true;//pokracuj dalej vo vypoctoch, neprazdna relacia
			}

			if (!loop)
			{
				DEBUGPRINT("DEBUG: All deltaP's empty");
				break;//skoncili sme, prazdne relacie
			}

			/*************************************************************************************************************/
			//KROK 2
			//vypocitaj P = P union deltaP
			/*************************************************************************************************************/

			DEBUGPRINT("DEBUG: STEP 2 - count P = P union deltaP");
			for (i=0; i<count; i++)
			{
				tmpQuery = CMemory::printstr("INSERT INTO temp%i SELECT DISTINCT * FROM demp%i EXCEPT (SELECT DISTINCT * FROM temp%i);",
					pred[i], pred[i], pred[i]);
				DEBUGPRINT2("DEBUG: %s", tmpQuery);
				res = db->ExecSQL(tmpQuery);
				if (!res->ok)
					throw 1234;
				_DELETE(tmpQuery); tmpQuery = NULL;
				_DELETE(res);
			}

			/*************************************************************************************************************/
			//KROK 3
			//vypocitaj deltaP' podla pravidiel v queries
			//pravidla prepis, lebo tie odkazuju na deltaP
			/*************************************************************************************************************/
			DEBUGPRINT("DEBUG: STEP 3 - compute deltaP'");
			//najskor zmaz obsah tabuliek deltaP'
			DEBUGPRINT("DEBUG: deleting deltaP'");
			for (DWORD i=0;i<count; i++)
			{
				tmpQuery = CMemory::printstr("DELETE FROM eemp%i;", pred[i]);
				res = db->ExecSQL(tmpQuery);
				if (!res->ok)
					throw 1234;
				_DELETE(tmpQuery); tmpQuery = NULL;
				_DELETE(res);
			}
			//vykonaj teraz dotazy pre deltaP ale prepis ich na deltaP' (vynechaj s *)
			for (i=0;i<count; i++)
			{
				CStringTable *q = queries[pred[i]];
				for (DWORD j=0; j<q->GetCount(); j++)
				{
					char *query = (char*)q->Get(j);
					if (query[0]=='*')
						continue;

					//query[12] = 'e'; DWORD len = strlen(query); query[len-7] = 'e';

					DEBUGPRINT2("DEBUG: deltaP' %s", query);
					res = db->ExecSQL(query);
					if (!res->ok)
						throw 1234;
					_DELETE(res);
				}
			}

			/*************************************************************************************************************/
			//KROK 4
			//vypocitaj deltaP = deltaP' - P
			//a pokracuj v cykle
			/*************************************************************************************************************/
			DEBUGPRINT("DEBUG: STEP 4 - compute deltaP = deltaP' - P");
			//zmaz najskor deltaP
			DEBUGPRINT("DEBUG: deleting deltaP");
			for (i=0;i<count; i++)
			{
				tmpQuery = CMemory::printstr("DELETE FROM demp%i;", pred[i]);
				res = db->ExecSQL(tmpQuery);
				if (!res->ok)
					throw 1234;
				_DELETE(tmpQuery); tmpQuery = NULL;
				_DELETE(res);
			}
			//vypocitaj deltaP = deltaP' - P
			DEBUGPRINT("DEBUG: computing deltaP = deltaP' - P");
			for (i=0;i<count; i++)
			{
				tmpQuery = CMemory::printstr("INSERT INTO demp%i SELECT DISTINCT * FROM eemp%i EXCEPT (SELECT DISTINCT * FROM temp%i);",
					pred[i], pred[i], pred[i]);
				DEBUGPRINT2("DEBUG: %s", tmpQuery);
				res = db->ExecSQL(tmpQuery);
				if (!res->ok)
					throw 1234;
				_DELETE(tmpQuery); tmpQuery = NULL;
				_DELETE(res);
			}
		}//while (loop)
		DEBUGPRINT("DEBUG: END of SEMINAIVE EVALUATION");
		DEBUGPRINT("**********************************");
	} catch (int err)
	{
		if (err == 1234)//nasa chyba, nie chyba v ExecSQL
		{
#ifdef _PRINTDEBUG
			if (tmpQuery)
				query = tmpQuery;
			DEBUGPRINT2("DEBUG: ERROR SQL QUERY: %s", query);
			app.mainWindow->GetOutputWindow()->AddLine(res->GetError());
#endif
			//app.mainWindow->GetOutputWindow()->AddLine("Chyba pri vykonaní SQL dotazu!");
			char *tmp = CMemory::printstr("Chyba pri vykonaní SQL príkazu: %s", res->GetError());
			app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
			app.mainWindow->GetOutputWindow()->AddLine("Evaluácia bude predčasne ukončená.");
			_DELETE(res);
		}

		if (tmpQuery) { _DELETE(tmpQuery);}

		for (DWORD i=0; i<table->nAtoms; i++)
		{
			if (queries[i]) { _DELETE(queries[i]); }
		}
		_DELETE(queries);
		throw 1;
	}

	//ok

	for (DWORD i=0; i<table->nAtoms; i++)
	{
		if (queries[i]) { _DELETE(queries[i]); }
	}
	_DELETE(queries);
}

CSQLresult *CDatalog::EvaluateWithLoop()
{

	DEBUGPRINT("DEBUG: *** Evaluate WITH Loop ***");

	//vypocitaj strata
	infoWin->SetText("Overujem stratifikáciu programu");

	DWORD *stratum = _NEW(DWORD[table->nAtoms]);//strata predikatov

	DWORD stratSize = 1204;
	DWORD *strat = _NEW(DWORD[stratSize]);//strata premennych

	::ZeroMemory(stratum, table->nAtoms*4);

	bool change = true, notstratified = false;
	DWORD maximum = 0;//najvacsie stratum zo strat vsetkych predikatov
	while (change)
	{
		infoWin->NextStep();
		change = false;
		//prejdi vsetky pravidla
		for (DWORD atomId=0; atomId<table->nAtoms; atomId++)
		{
			SYNTACTIC_ATOM *atom = table->atoms[atomId];
			switch (atom->idb.type)
			{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_ANSWER:
				{
					for (DWORD ruleIndex=0; ruleIndex<atom->idb.nRules; ruleIndex++)
					{
						SYNTACTIC_RULE *rule = atom->idb.rules[ruleIndex];

						if (rule->varNames->GetCount() > stratSize)
						{
							//prealokuj pamat
							_DELETE(strat);
							stratSize = rule->varNames->GetCount();
							strat = _NEW(DWORD[stratSize]);
						}

						::ZeroMemory(strat, stratSize);

						//prejdi kazdy podciel a urci pre neho stratum
						//urci z nich maximum a prirad ho ako stratum IDB predikatu na lavej strane

						//problem robia tie veci, kde sa musi dat stratum+1 a to:
						//	negovany relacny podciel
						//	agregacne funkcie
						//	priradenie, ktore nie je v tvare X=Y
						//
						//	premenna v groupby ????? treba zistit, ci to nejako ovplyvni, ale asi nie...??


						//vypocitaj stratum premennych (je to stratum podciela, v ktorom su viazane)

						//najskor urci stratum premennych viazanych v relacnych podcieloch -> je rovne strate podciela
						for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
						{
							DWORD atomId = rule->atom[atomIndex]->id;
							switch (table->atoms[atomId]->idb.type)
							{
							case SYNTACTIC_ATOM_IDBATOM:
							case SYNTACTIC_ATOM_EDBATOM:
								if (!rule->atom[atomIndex]->not)
									for (DWORD argIndex=0; argIndex<rule->atom[atomIndex]->nArgs; argIndex++)
									{
										DWORD varId = rule->atom[atomIndex]->argsId[argIndex];
										if (varId != -1)
											strat[varId] = stratum[atomId];
									}
								break;
							}
						}

						//teraz vypocitaj stratum premennych viazanych v priradeniach, v tran. uzavere
						bool change2 = true;
						while (change2)
						{
							change2 = false;
							for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
							{
								DWORD atomId = rule->atom[atomIndex]->id;
								if (table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_ARITATOM_BINDED)
								{
									const char *exp = table->atoms[atomId]->arit.expression;
									DWORD varId = (*((DWORD*)exp))>>8;//lava strana
									//urci max. zo strat vsetkych premennych
									DWORD i=0;
									DWORD max=0;
									while (exp[i])
									{
										if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
										{
											DWORD varId = (*(DWORD*)&exp[i])>>8;
											if (strat[varId] > max)
												max = strat[varId];
											i+=4;
										}
										else
										{
											if (exp[i] == '\'')
												break;//za retazcom uz premenna v priradeni nebude
											i++;
										}
									}
									//max je maximalne stratum premennych z priradenia

									//je stratum vyrazu na pravej strane > ako stratum premennej na lavej strane?
									if (max > strat[varId])
									{
										strat[varId] = max;
										change2 = true;
									}
								}
							}
						}


						//teraz prejdi vsetky podciele a urci z nich maximalne stratum
						DWORD maxRuleStratum = 0;//najvacsie stratum v pravidle
						for (atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
						{
							DWORD stratumIndex = 0;//vypocitane stratum pre atom atomIndex
							DWORD atomId = rule->atom[atomIndex]->id;
							switch (table->atoms[atomId]->idb.type)
							{
							case SYNTACTIC_ATOM_IDBATOM:
							case SYNTACTIC_ATOM_EDBATOM:
								if (rule->atom[atomIndex]->not)
									stratumIndex = stratum[atomId]+1;
								else
									stratumIndex = stratum[atomId];
								break;
							case SYNTACTIC_ATOM_ARITATOM:
							case SYNTACTIC_ATOM_ARITATOM_BINDED:
								{
									//urci pocet premennych a zisti, ci je vo vyraze nejaka agrfun a operacia
									char *exp = table->atoms[atomId]->arit.expression;
									bool aritop = false, agrfun = false;
									DWORD nVar = 0;//pocet premennych vo vyraze
									DWORD max=0;//najvyssie stratum nejakej premennej vo vyraze
									DWORD i=0;
									while (exp[i])
									{
										if ( (exp[i] == '\'') || (exp[i] == '\"') )
											break;//je to string
										if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
										{
											DWORD varId = (*(DWORD*)&exp[i])>>8;
											nVar++;
											if (strat[varId] > max)
												max = strat[varId];
											if (exp[i] != DATALOG_EXP_VAR)
												agrfun = true;
											i+=4;
										}
										else
										{
											if ( (exp[i] == '+')||(exp[i] == '-')||(exp[i] == '*')||(exp[i] == '/'))
												aritop = true;
											i++;
										}
									}

									if (table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_ARITATOM_BINDED)
									{
										//priradenie
										DWORD varId = (*((DWORD*)exp))>>8;
										stratumIndex = strat[varId];
										//ak priradenie nie je v tvare var=const/var, tak treba zvysit stratum o jedna
										//ako to zistim?
										//pocet premennych vo vyraze musi musi byt >=2
										//ak je = 1, mame premennu len vlavo a na pravo len konstantny vyraz - ok
										//ak je = 2 a vyraz obsahuje aritmeticku operaciu, nie je daneho tvaru
										//	(zatvorky moze, lebo napr. x=(y) je ok)
										if (nVar > 2)
											stratumIndex++;
										else
										if ((nVar == 2) && (aritop || agrfun))
											stratumIndex++;
									}
									else
									{
										//podmienka
										if (agrfun)
											stratumIndex = max+1;
										else
											stratumIndex = max;
									}
								}
								break;
							}

							if (stratumIndex > maxRuleStratum)
								maxRuleStratum = stratumIndex;
						}//for podciele

						//je stratum tela vacsie ako stratum predikatu v hlavicke?
						if (maxRuleStratum > stratum[atomId])
						{
							change = true;
							stratum[atomId] = maxRuleStratum;

							//je stratum atomu v hlavicke (novo vypocitane) najvacsie zo vsetkych predikatov?
							if (maxRuleStratum > maximum)
							{
								maximum = maxRuleStratum;

								if (maximum > table->nIdb)
								{
									//program nie je stratifikovany
									notstratified = true;
									change = false;
									break;//von z foru
								}
							}
						}
					}//for vsetky pravidla
				}
				break;
			}

			if (notstratified)
				break;//vyskoc z cyklu atomov atomId

		}//for vsetky pravidla
	}//while (change)

	_DELETE(strat);

	if (notstratified)
	{
		app.mainWindow->GetOutputWindow()->AddLine("Program nie je stratifikovaný, a preto nemôže byť vyhodnotený.");
		_DELETE(stratum);
		return NULL;
	}


	DEBUGPRINT("DEBUG: Evaluation with stratification");
	DEBUGPRINT2("DEBUG: No. of strata : %i", maximum);

	CStringTable *tableNames = CreateAllTables(true);
	DWORD *idbPred = _NEW(DWORD[table->nIdb]);

	//teraz prejdi cez jednotlive vrstvy (od 0 hore - po maximum) a vypocitaj ich
	infoWin->SetText("Vyhodnocujem program");
	for (DWORD i=0; i< maximum+1; i++)
	{
		DWORD count=0;
		for (DWORD atomId=0; atomId<table->nAtoms; atomId++)
		{
			SYNTACTIC_ATOM *atom = table->atoms[atomId];
			switch (atom->idb.type)
			{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_ANSWER:
				if (stratum[atomId] == i)//pocitame i-tu vrstvu, nachadza sa tu tento atom?
					idbPred[count++] = atomId;
				break;
			}
		}

		try
		{
			EvaluateStratum(idbPred, count);
		} catch (int err)
		{
			DeleteAllTables(tableNames); _DELETE(tableNames);
			_DELETE(idbPred);
			_DELETE(stratum);
			throw err;
		}
	}

	char *tmpQuery = NULL;
	CSQLresult *res = NULL;

	try
	{
		//dotaz pre answer
		infoWin->SetText("Vytváram výsledok");
		tmpQuery = CMemory::printstr("SELECT * FROM temp%i;", table->answerID);
		res = app.GetDatabase()->ExecSQL(tmpQuery); _DELETE(tmpQuery); tmpQuery = NULL;
		if (!res->ok)
			throw 1234;
	} catch (int err)
	{
		if (err == 1234)
		{
			char *tmp = CMemory::printstr("Chyba pri vykonaní SQL príkazu: %s", res->GetError());
			app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
			app.mainWindow->GetOutputWindow()->AddLine("Výsledok nebude zobrazený.");
			app.mainWindow->GetOutputWindow()->AddLine(res->GetError());
			_DELETE(res);
		}
		if (tmpQuery) {_DELETE(tmpQuery);}
		DeleteAllTables(tableNames); _DELETE(tableNames);
		_DELETE(idbPred);
		_DELETE(stratum);
		return NULL;
	}

	//zmaz docasne tabulky
	DeleteAllTables(tableNames); _DELETE(tableNames);

	_DELETE(idbPred);
	_DELETE(stratum);

	return res;//vrat vysledok dotazu
}

CSQLresult *CDatalog::EvaluateWithoutLoop(CMemory *ord_list)
{
	//ord_list je topologicky utriedeny zoznam vrcholov (existuje, lebo nemame cyklus)
	//vrcholy sa musia vyhodnotit od zaciatku zoznamu - na zaciatku sa nachadzaju listy stromu

	DEBUGPRINT("DEBUG: *** Evaluate WITHOUT Loop ***");

	CDatabase *db = app.GetDatabase();
	CSQLresult *res = NULL;
	char *query, *tmpQuery = NULL;
	CStringTable *tableNames = NULL;
	CStringTable **queries = NULL;

	try
	{
		//vytvor docasne tabulky
		tableNames = CreateAllTables(false);
		//vytvor dotazy
		queries = CreateSQLQueries();

		//vyhodnot podla topologicky utriedeneho zoznamu jednotlive IDB predikaty
		infoWin->SetText("Vyhodnocujem program");
		DWORD size = ord_list->GetSize()/4;
		DWORD *list = (DWORD*)ord_list->GetPointer();
		for (DWORD i=0; i<size; i++)
		{
			infoWin->NextStep();
			DWORD atomId = graph->GetVertex(list[i])->id;
			if (queries[atomId])
			{
				for (DWORD queryIndex=0; queryIndex<queries[atomId]->GetCount(); queryIndex++)
				{
					query = (char*)queries[atomId]->Get(queryIndex);
					res = db->ExecSQL(query);
					if (!res->ok)
						throw 1234;
					_DELETE(res); res = NULL;
				}
			}
		}

		//zmaz dotazy
		for (i=0; i<table->nAtoms; i++)
		{
			if (queries[i]) { _DELETE(queries[i]); }
		}
		_DELETE(queries);

		infoWin->SetText("Vytváram výsledok");
		//vytvor dotaz pre answer
		tmpQuery = CMemory::printstr("SELECT * FROM temp%i;", table->answerID);
		res = app.GetDatabase()->ExecSQL(tmpQuery);
		if (!res->ok)
			throw 1234;
		_DELETE(tmpQuery);

		//zmaz docasne tabulky
		DeleteAllTables(tableNames); _DELETE(tableNames);

		return res;//vrat vysledok dotazu

	} catch (int err)
	{
		if (err == 1234)
		{
#ifdef _PRINTDEBUG
			if (tmpQuery)
				query = tmpQuery;
			char *tmp = CMemory::printstr("DEBUG: ERROR SQL QUERY: %s", query);
			app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
			app.mainWindow->GetOutputWindow()->AddLine(res->GetError());
#else
			char *tmp = CMemory::printstr("Chyba pri vykonaní SQL príkazu: %s", res->GetError());
			app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
			app.mainWindow->GetOutputWindow()->AddLine("Evaluácia bude predčasne ukončená.");
#endif
			_DELETE(res);
		}

		if (tmpQuery) {_DELETE(tmpQuery);}

		if (queries)
		{
			//zmaz dotazy
			for (DWORD i=0; i<table->nAtoms; i++)
			{
				if (queries[i]) { _DELETE(queries[i]); }
			}
			_DELETE(queries);
		}

		if (tableNames)
		{
			//zmaz docasne tabulky
			DeleteAllTables(tableNames); _DELETE(tableNames);
		}
		throw 1;
	}
}

/*LRESULT CALLBACK CDatalog::StaticWindowProcViewResult(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
	switch (msg)
	{
	case WM_DESTROY:
		::UnEmbedBrowserObject(hwnd);
		app.mainWindow->hWndViewResult = NULL;
		break;
	}
	return ::DefWindowProc(hwnd,msg,wp,lp);
}
*/
void CDatalog::ViewResult(CSQLresult *res)
{
	//return;
	infoWin->SetText("Generujem výsledok do HTML");
	infoWin->Animate();

/*	static char *s1="<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-2\"><title>answer</title></head><body>";
	static char *s11="<table border=\"1\" width=\"100%\">";
	static char *s2="</body></html>";
	static char *s3="<p>Dotaz nebol vykonaný</p>";
*/
	static char *s1="<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-2\"><title>answer</title></head><body>";
	static char *s11="<table border=\"1\" width=\"100%\">";
	static char *s2="</font></body></html>";
	static char *s3="<p>Dotaz nebol vykonaný</p>";

	char *s4 = CMemory::printstr("<font face=\"%s\" size=\"%i\"><p>Počet záznamov: ",
		app.options->html.fontName, app.options->html.fontSize);
	char *s5 = CMemory::printstr("<font size=\"%i\">", app.options->html.fontSize);
	
	CMemory *html = _NEW(CMemory());
	html->AddData(s1, strlen(s1)); 

	if (!res)
	{
		html->AddData(s3, strlen(s3));
	}
	else
	{
		html->AddData(s4, strlen(s4));
		//pridaj info o pocte riadkov
		char tmp[10]; itoa(res->GetnTuples(), tmp, 10);
		html->AddData(tmp, strlen(tmp));
		html->AddData("</p>",4);

		//tabulka
		html->AddData(s11, strlen(s11));
		//pridaj hlavicku tabulky - nazvy stlpcov
		html->AddData("<p><tr align=\"center\" bgcolor=\"#80A0C0\">", 40);
		SYNTACTIC_ATOM_IDB *answer = (SYNTACTIC_ATOM_IDB*)&table->atoms[table->answerID]->idb;
		for (DWORD i=0; i<answer->nArgs; i++)
		{
			html->AddData("<td>",4);
			html->AddData(s5, strlen(s5));
			const char *name = argsAnswer->Get(i);
			html->AddData((char*)name, strlen(name));
			html->AddData("</font></td>",12);
		}
		html->AddData("</tr>", 5);

		//data
		for (DWORD row=0; row<(DWORD)res->GetnTuples(); row++)
		{
			html->AddData("<tr>", 4);
			for (DWORD i=0; i<answer->nArgs; i++)
			{
				html->AddData("<td>", 4);
				html->AddData(s5, strlen(s5));
				const char *str = res->Value(row, i);
				html->AddData((void*)str, strlen(str));
				html->AddData("</font></td>", 12);
			}
			html->AddData("</tr>", 5);
		}
		html->AddData("</p></table>",12);
	}

	html->AddData(s2, strlen(s2)+1);
	_DELETE(s4);
	_DELETE(s5);
/*
	if (!app.mainWindow->hWndViewResult)
	{
		//vytvor okno
		char classname[] = "viewresultdatalogsql";
		WNDCLASS wc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
		wc.hCursor = ::LoadCursor(0,IDC_ARROW);
		wc.hIcon = NULL;
		wc.hInstance = app.hInstance;
		wc.lpfnWndProc = (WNDPROC)CDatalog::StaticWindowProcViewResult;
		wc.lpszClassName = classname;
		wc.lpszMenuName = 0;
		wc.style = 0;
		::RegisterClass(&wc);

		app.mainWindow->hWndViewResult = ::CreateWindowEx(0, classname, "Výsledok dotazu", WS_OVERLAPPEDWINDOW,
			900, 100, 400, 600, app.mainWindow->GetHandle(), NULL, app.hInstance, 0);
		::EmbedBrowserObject(app.mainWindow->hWndViewResult);
		::ShowWindow(app.mainWindow->hWndViewResult, SW_SHOWNORMAL);
	}
	else
	{
		::UnEmbedBrowserObject(app.mainWindow->hWndViewResult);
		::EmbedBrowserObject(app.mainWindow->hWndViewResult);
	}

	::DisplayHTMLStr(app.mainWindow->hWndViewResult, (char*)html->GetPointer());*/

	app.mainWindow->GetHtmlWindow()->ViewHTML("Výstup", (char*)html->GetPointer());
	//::SetForegroundWindow(app.mainWindow->GetHtmlWindow()->GetHandle());

	infoWin->StopAnimation();

	_DELETE(html);
}

DWORD CDatalog::CanBeDate(char *str)
{
	CSQLresult *res = NULL;
	char *tmp;
	try
	{
		tmp = CMemory::printstr("SELECT date(%s);", str);
		res = app.GetDatabase()->ExecSQL(tmp); _DELETE(tmp);
	} catch (int err)
	{
		_DELETE(tmp);
		throw err;
	}
	if (res->ok)
	{
		_DELETE(res);
		return DATALOG_DATE_YES;
	}
	else
	{
		const char *err = res->GetErrorCode();
		//22007 - zly format datumu
		//22008 - pretecenie datumu
		if (!strcmp(err, "22007"))
		{
			_DELETE(res);
			return DATALOG_DATE_NO;
		}
		if (!strcmp(err, "22008"))
		{
			_DELETE(res);
			return DATALOG_DATE_OVERFLOW;
		}
		else
		{
			char *tmp = CMemory::printstr("Chyba pri vykonaní SQL príkazu: %s", res->GetError());
			app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
			app.mainWindow->GetOutputWindow()->AddLine("Evaluácia bude predčasne ukončená.");
			_DELETE(res);
			throw 1;
		}
	}
}	

BYTE CDatalog::CheckAgregFunType(DWORD funId, SYNTACTIC_RULE *rule, DWORD *ret)
{
	//x je lub. typ
	//avg(numeric), vracia numeric
	//count(x), vracia numeric
	//min(x), vracia x
	//max(x), vracia x
	//sum(numeric), vracia numeric

	DWORD id = funId>>8;

	if (rule->varTypes[id].type == DATABASE_TYPE_NOTYPE)
		return DATABASE_TYPE_NOTYPE;

	switch (funId&0xFF)
	{
	case DATALOG_EXP_AVG:
	case DATALOG_EXP_SUM:
		if (rule->varTypes[id].type != DATABASE_TYPE_NUMBER)
		{
			if (!(rule->varTypes[id].error&DATALOG_ERR_TYPE))
			{
				char *tmp = CMemory::printstr("Premenná ’%s’ typu ’%s’ bola použitá ako argument agregačnej funkcie %s, ktorý musí byť typu ’%s’!",
					rule->varNames->Get(id), CDatabase::GetType(rule->varTypes[id].type), _agrFunctionsName[funId&0xFF], CDatabase::typeNumeric);
				app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
				rule->varTypes[id].error |= DATALOG_ERR_TYPE;
				*ret = DATALOG_ERROR;
			}
		}
		return DATABASE_TYPE_NUMBER;
	case DATALOG_EXP_MIN:
	case DATALOG_EXP_MAX:
		return rule->varTypes[id].type;
	case DATALOG_EXP_COUNT:
		return DATABASE_TYPE_NUMBER;
	}
	//sem nikdy neprideme
	return DATABASE_TYPE_NOTYPE;
}

DWORD CDatalog::CheckSemantic()
{
	DEBUGPRINT("DEBUG: *** SEMANTICS ANALYSIS ***");

	app.mainWindow->GetOutputWindow()->StartBuffering(app.mainWindow->GetDatalogWindow()->GetNumOfLines());

	DWORD ret = DATALOG_OK;
	const CDbStructure *db = app.GetDatabase()->GetStructure();

	//pri semantickej kontrole sa prepisu nazvy premennych
	//kvoli vystupu si uloz nazvy premennych pre answer
	CStringTable *str = table->atoms[table->answerID]->idb.rules[0]->varNames;
	argsAnswer->DeleteAll();
	for (DWORD i=0; i<str->GetCount(); i++)
		argsAnswer->Add((char*)str->Get(i));

	//+ su vsetky premenne v pravidle viazane?
	//+	su navyse premenne v groupby podcieloch a v argumentoch agrfun viazane v relacnych podcieloch?
	//	(nie v prirovnaniach)
	//+ v aritmetickych atomoch oznac priradenia
	//vsetko len v ramci jedneho pravidla, ziadny cyklus (nemusim si preto pamatat, pre ktoru premennu som uz vypisal chybu)

	CMemory *varList = _NEW(CMemory());//kvoli vypise chyby si pamatame, ktore premenne neboli viazane
	DWORD nVarList;//pocet neviazanych premennych (kvoli tvaru slov v chybe)

	for (i=table->nEdb; i<table->nAtoms; i++)//i su atomy
	{
		SYNTACTIC_ATOM *atom = table->atoms[i];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			for (DWORD j=0; j<atom->idb.nRules; j++)//j je pravidlo
			{
				varList->ClearMemory();
				varList->AddData("Premenn%s ’", 11);
				nVarList = 0;

				SYNTACTIC_RULE *rule = atom->idb.rules[j];

				//mame v pravidle nejaku agregacnu funkciu alebo groupby predikat?
				bool agrFunOrGroupby = false;

				//***********************************************************************************************************************
				//ak aritmeticke podciele obsahuju agregacne funkcie alebo v pravidle sa nachadza groupby predikat,
				//	v pravidle sa musi nachadzat len jeden relacny podciel
				DWORD nRelAtoms = 0;//pocet relacnych podcielov
				for (DWORD k=0; k<rule->nAtoms; k++)
				{
					DWORD id = rule->atom[k]->id;//id atomu na pravej strane
					switch (table->atoms[id]->idb.type)
					{
					case SYNTACTIC_ATOM_IDBATOM:
					case SYNTACTIC_ATOM_EDBATOM:
						nRelAtoms++;
						break;
					case SYNTACTIC_ATOM_GROUPBYATOM:
						agrFunOrGroupby = true;
						break;
					case SYNTACTIC_ATOM_ARITATOM:
						{
							if (agrFunOrGroupby)
								break;
							//mame nejaku agregacnu funkciu?
							char *exp = table->atoms[id]->arit.expression;
							DWORD i=0;
							while (exp[i])
							{
								if (exp[i] == DATALOG_EXP_VAR)
									i+=4;
								else
								if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
								{
									agrFunOrGroupby = true;
									break;
								}
								else
								{
									if (exp[i]=='\'')
										break;//tento vyraz obsahuje retazec; agregacna funkcia sa tu nemoze nachadzat
									i++;
								}
							}
						}
						break;
					}
				}
				if (agrFunOrGroupby && (nRelAtoms > 1))
				{
					rule->skip = true;
					app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer("Pravidlo, ktoré obsahuje agregačné funkcie alebo groupby predikát, musí mať len jeden relačný podcieľ!",
						rule->inputLine);
					ret = DATALOG_ERROR;
					continue;//zober dalsie pravidlo
				}
				//***********************************************************************************************************************

				//prejdi najskor nenegovane relacne atomy - premenne v nich su viazane
				for (k=0; k<rule->nAtoms; k++)
				{
					DWORD id = rule->atom[k]->id;//id atomu na pravej strane
					switch (table->atoms[id]->idb.type)
					{
					case SYNTACTIC_ATOM_IDBATOM:
					case SYNTACTIC_ATOM_EDBATOM:
						if (!rule->atom[k]->not)
						{
							//premenne v tychto relaciach su viazane
							for (DWORD l=0; l<rule->atom[k]->nArgs; l++)
							{
								DWORD var = rule->atom[k]->argsId[l];
								if (var!=-1)//nie je to anonymna premenna
									rule->varTypes[var].mark |= DATALOG_MARKED_BINDEDREL;
							}
						}
						break;
					}
				}
				//teraz prejdi aritmeticke atomy tvaru X=... (teda lavou stranou je (len jedna) premenna
				//ak su premenne na pravej strane viazane, tak aj X bude viazana

				//prechadzaj v cykle kvoli tranzitivnemu uzaveru viazanosti

				//kazdy vyraz s novo zviazanou premennou na lavej strane bude priradenim
				//(ak premenna este nebola zviazana, nenachadzala sa ako argument ziadneho relacneho podciela)
				bool change = true;
				while (change)
				{
					change = false;
					for (k=0; k<rule->nAtoms; k++)
					{
						SYNTACTIC_ATOM_ARIT *arit = (SYNTACTIC_ATOM_ARIT *)table->atoms[rule->atom[k]->id];
						if ((arit->type == SYNTACTIC_ATOM_ARITATOM) && (arit->typeOp == SYNTACTIC_ARIT_OPERAND_EQUAL))
						{
							if (rule->atom[k]->not)
								continue;//dalsi atom, negovany nemoze byt prirovnanim

							char *exp = arit->expression;

							//na lavej strane musi byt len jedna premenna a hned za nou operacia =
							if ( (exp[0] == DATALOG_EXP_VAR) && (exp[4] == '=') )
							{
								DWORD id = (*((DWORD*)exp))>>8;//premenna na lavej strane

								if (rule->varTypes[id].mark&(DATALOG_MARKED_BINDEDASS+DATALOG_MARKED_BINDEDREL))
									continue;//uz je viazana; zober dalsi atom

								//teraz prejdi cely vyraz a over, ci vsetky premenne su viazane
								//agregacne funkcie vracaju hodnotu ako konstantu, takze su zviazane
								bool binded = true;//predpokladame, ze je viazana
								DWORD j=5;//zacni az po operaci prirovnania, teda pravou stranou vyrazu
								while (exp[j])
								{
									if (exp[j] == DATALOG_EXP_VAR)
									{
										DWORD id2 = (*((DWORD*)(&exp[j])))>>8;
										if (!(rule->varTypes[id2].mark&(DATALOG_MARKED_BINDEDASS+DATALOG_MARKED_BINDEDREL)))
										{
											//nie je viazana
											binded = false;
											break;
										}
										j+=4;
									}
									else
									if (BYTE(exp[j]) < DATALOG_EXP_SPECIAL)
										j+=4;
									else
										j++;
								}

								if (binded)
								{
									rule->varTypes[id].mark |= DATALOG_MARKED_BINDEDASS;
#ifdef _PRINTDEBUG
									const char *_id = rule->varNames->Get(id);
									char *tmp;
									if (_id)//ma premenna nazov?
										tmp = CMemory::printstr("DEBUG: Premenna/vyraz ’%s’ je v pozicii priradenia", _id);
									else
										tmp = CMemory::printstr("DEBUG: Premenna/vyraz [%i] je v pozicii priradenia", id);
									app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
#endif
									arit->typeOp = SYNTACTIC_ARIT_OPERAND_ASSIGN;//zapis priradenie
									change = true;//dalsi cyklus je potrebny
								}
							}
						}
					}
				} //while change

				//+ over, ci vsetky premenne v tele su viazane
				//premenne v agregacnych funkciach musia byt viazane v relacnych atomoch
				//argumenty (premenne) v groupby predikatoch musia byt viazane v relacnych atomoch
				//	premenne v groupby oznac DATALOG_MARKED_GROUPBY
				//premenne v aritmetickych vyrazoch musia byt viazane
				//premenne v hlavicke, ktore sa nenachadzaju v tele pravidla, este musime otestovat zvlast (lebo nie su v ziadnom podcieli)
				//ostatne premenne (ktore nie su v groupby, aritm. podcieloch, musia byt len v arg. rel.podcieloch, kde su viazane
				//=> viac nemusime kontrolovat


				for (k=0; k<rule->nAtoms; k++)//prejdi postupne vsetky podciele
				{
					DWORD id = rule->atom[k]->id;//id podciela
					switch (table->atoms[id]->idb.type)
					{
					case SYNTACTIC_ATOM_GROUPBYATOM:
						{
							//premenne musia byt viazane v relacnom podcieli
							for (DWORD argIndex=0; argIndex<rule->atom[k]->nArgs; argIndex++)
							{
								DWORD varId = rule->atom[k]->argsId[argIndex];
								//oznac, ze premenna je v groupby
								rule->varTypes[varId].mark |= DATALOG_MARKED_GROUPBY;
								//je viazany v relacnom podcieli>?
								if ( !(rule->varTypes[varId].mark&DATALOG_MARKED_BINDEDREL) && !(rule->varTypes[varId].error&DATALOG_ERR_MUSTREL))
								{
									//premenna nie je viazana v relacnom podcieli
									char *tmp = CMemory::printstr("Premenná ’%s’ v groupby podcieli musí byť viazaná v relačnom podcieli!",
										rule->varNames->Get(varId));
									app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
									rule->varTypes[varId].error |= DATALOG_ERR_MUSTREL;
									ret = DATALOG_ERROR;
								}
							}
						}
						break;
					case SYNTACTIC_ATOM_ARITATOM:
						{
							const char *exp = table->atoms[id]->arit.expression;
							//vsetky premenne vo vyraze musia byt viazane
							//premenne v agrfun musia byt viazane v relacnom podcieli
							DWORD ii=0;
							while (exp[ii])
							{
								if (exp[ii] == DATALOG_EXP_VAR)
								{
									DWORD varId = (*((DWORD*)(&exp[ii])))>>8;//premenna vo vyraze

									//ak mame substituciu, premenna na lavej strane neexistuje v programe, bola vytvorena pocas syntaktickej
									//analyzy, preto ignoruj, ak nie je zviazana.
									//ak nie je zviazana, nejaka ina premenna na pravej strane musi byt nezviazana - vypis chybu len pre nu
									if (!(!ii && table->atoms[id]->arit.substitution))//takze nechceme lavu stranu substitucie
									{
										if (!(rule->varTypes[varId].mark&(DATALOG_MARKED_BINDEDASS+DATALOG_MARKED_BINDEDREL)) &&
												!(rule->varTypes[varId].error&DATALOG_ERR_NOTBINDED))
										{
											//premenna nie je viazana
											char *var = (char*)rule->varNames->Get(varId);

											if (nVarList)
												varList->AddData(",",1);
											varList->AddData(var, strlen(var));
											nVarList++;

											rule->varTypes[varId].error |= DATALOG_ERR_NOTBINDED;
											ret = DATALOG_ERROR;
										}
									}
									else
									{
										//sme na lavej strane substitucie

										//oznac, ze premenne na lavej strane substitucie su "substitucne premenne"
										//neskor budeme vypisovat chybu pre premenne v hlavicke a nechceme, aby sa nam tam tieto
										//substitucie zamiesali
										//v hlavicke chceme vypisat neviazane len tie premenne, ktore su tam zadane uzivatelom
										//=> tuto premennu preto nevypiseme, ze je neviazana (ak teda je)
										rule->varTypes[varId].mark |= DATALOG_MARKED_SUBS;
									}
									ii+=4;
								}
								else
								if  (BYTE(exp[ii]) < DATALOG_EXP_SPECIAL)
								{
									DWORD varId = (*((DWORD*)(&exp[ii])))>>8;
									if ( !(rule->varTypes[varId].mark&DATALOG_MARKED_BINDEDREL) &&
										 !(rule->varTypes[varId].error&DATALOG_ERR_MUSTREL))
									{
										//premenna nie je viazana v relacnom atome
										char *tmp = CMemory::printstr("Premenná ’%s’ v agregačnej funkcii %s musí byť viazaná v relačnom podcieli!",
											rule->varNames->Get(varId), _agrFunctionsName[exp[ii]&0xFF]);
										app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
										rule->varTypes[varId].error |= DATALOG_ERR_MUSTREL;
										ret = DATALOG_ERROR;
									}
									ii+=4;
								}
								else
									ii++;
							}
						}
						break;
					case SYNTACTIC_ATOM_IDBATOM:
					case SYNTACTIC_ATOM_EDBATOM:
						//ak sa premenna nachadza v nenegovanom EDB, IDB
						//1. je tu viazana
						//2. je viazana v inom relacnom predikate (nie v priradeni, lebo inak by bola viazana tu)
						//	-> pocas synt. analyzy sa vytvori nova premenna a nove prirovnanie -> v nom sa otestuje viazanost premennej

						//jedine v negovanych nie je viazana
						if (rule->atom[k]->not)
						{
							for (DWORD argIndex=0; argIndex<rule->atom[k]->nArgs; argIndex++)
							{
								DWORD varId = rule->atom[k]->argsId[argIndex];
								//je viazana?
								if (!(rule->varTypes[varId].mark&(DATALOG_MARKED_BINDEDASS+DATALOG_MARKED_BINDEDREL)) &&
										!(rule->varTypes[varId].error&DATALOG_ERR_NOTBINDED))
								{
									//premenna nie je viazana
									char *var = (char*)rule->varNames->Get(varId);

									if (nVarList)
										varList->AddData(",",1);
									varList->AddData(var, strlen(var));
									nVarList++;

									rule->varTypes[varId].error |= DATALOG_ERR_NOTBINDED;
									ret = DATALOG_ERROR;
								}
							}
						}

						break;
					}//switch
				}//for podciele

				//este otestuj premenne v hlavicke, ci su viazane
				//ak sa premenna nachadza len v hlavicke a nie v tele, potom este nebola vypisana chyba, lebo sme prezerali premenne len v tele
				//nevypisuj premenne substitucie, lebo nemaju nazov a pre premenne na lavej strane sme uz chybu vypisali
				for (DWORD i=0; i<atom->idb.nArgs; i++)
					if (!(rule->varTypes[i].mark&(DATALOG_MARKED_BINDEDASS+DATALOG_MARKED_BINDEDREL))
							&& !(rule->varTypes[i].error&DATALOG_ERR_NOTBINDED)
							&& !(rule->varTypes[i].mark&DATALOG_MARKED_SUBS))
					{
						//premenna nie je viazana
						char *var = (char*)rule->varNames->Get(i);

						if (nVarList)
							varList->AddData(",",1);
						varList->AddData(var, strlen(var));
						nVarList++;

						rule->varTypes[i].error |= DATALOG_ERR_NOTBINDED;
						ret = DATALOG_ERROR;
					}


				//***************************************************************************************************
				//AGREGACNE FUNKCIE
				//***************************************************************************************************

				//ma zmysel vobec nieco uvazovat? tj. mame nejaky groupby podciel alebo nejaku agregacnu funkciu?
				if (agrFunOrGroupby)
				{
					//vo vyrazoch s agrfun v tranzitivnom uzavere mozu vystupovat len premenne z groupby podciela

					//detekuj prirovnania, kde na pravej strane su len groupby premenne, hodnoty agrfun a premenne rovnakeho priradenia
					//  kde su teda v tranzitivnom uzavere groupby premenne a hodnoty agregacnych funkcii
					change = true;
					while (change)
					{
						change = false;
						for (k=0; k<rule->nAtoms; k++)//prejdi postupne vsetky podciele
						{
							DWORD id = rule->atom[k]->id;//id podciela
							if (table->atoms[id]->idb.type == SYNTACTIC_ATOM_ARITATOM)
								if (table->atoms[id]->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
								{
									const char *exp = table->atoms[id]->arit.expression;
									DWORD varId = (*((DWORD*)exp))>>8;
									if (rule->varTypes[varId].mark&DATALOG_MARKED_ASSIGN_ONLY_AGRFUN)
										continue;//zober dalsiu, tato je uz oznacena

									bool assign = true;//predpokladame, ze to je
									bool someagrfun = false;//mame nejaku agregacnu funkciu v tran.uzavere?
									DWORD i=5;//prva premenna urcite nebude este oznacena
									while (exp[i])
									{
										if (exp[i] == DATALOG_EXP_VAR)
										{
											DWORD varId = (*((DWORD*)(&exp[i])))>>8;//premenna vo vyraze
											if (!(rule->varTypes[varId].mark&(DATALOG_MARKED_ASSIGN_ONLY_AGRFUN+DATALOG_MARKED_GROUPBY)))
												assign = false;
											if (rule->varTypes[varId].mark&DATALOG_MARKED_ASSIGN_SOME_AGRFUN)
												someagrfun = true;
											i+=4;
										}
										else
										if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
										{
											//agregacna funkcia
											//assign zostava nezmenene
											someagrfun = true;
											i+=4;
										}
										else
										{
											if (exp[i] == '\'')
												break;//vyraz s retazcom nechceme - obsahuj len jednu premennu (x=string/string=x)
											i++;
										}
									}

									if (assign)
									{
										//premenna este nebola oznacena (vid podmienka
										//	if (rule->varTypes[varId].mark&DATALOG_MARKED_ASSIGN_ONLY_AGRFUN)
										rule->varTypes[varId].mark |= DATALOG_MARKED_ASSIGN_ONLY_AGRFUN;
										if (someagrfun)
											rule->varTypes[varId].mark |= DATALOG_MARKED_ASSIGN_SOME_AGRFUN;
										change = true;
									}
								}
						}//for podciele
					}//while change

					//v kazdom aritmetickom vyraze, ktory obsahuje agregacnu funkciu alebo premennu DATALOG_MARKED_ASSIGN_SOME_AGRFUN,
					//musia byt vsetky premenne DATALOG_MARKED_ASSIGN_ONLY_AGRFUN
					for (k=0; k<rule->nAtoms; k++)//prejdi postupne vsetky podciele
					{
						DWORD id = rule->atom[k]->id;//id podciela
						if (table->atoms[id]->idb.type == SYNTACTIC_ATOM_ARITATOM)
						{
							const char *exp = table->atoms[id]->arit.expression;
							bool someagrfun = false;//obsahuje vyraz nejaku agrfun v tran.uzavare?
							//obsahuje vyraz agregacnu funkciu alebo premennu DATALOG_MARKED_ASSIGN_SOME_AGRFUN -> someagrfun = true
							DWORD i=0;
							while (exp[i])
							{
								if (exp[i] == DATALOG_EXP_VAR)
								{
									DWORD varId = (*((DWORD*)(&exp[i])))>>8;//premenna vo vyraze
									if (rule->varTypes[varId].mark&DATALOG_MARKED_ASSIGN_SOME_AGRFUN)
									{
										someagrfun = true;
										break;
									}
									i+=4;
								}
								else
								if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
								{
									//agregacna funkcia
									someagrfun = true;
									break;
								}
								else
								{
									if (exp[i] == '\'')
										break;//vyraz s retazcom nechceme - obsahuj len jednu premennu (x=string/string=x)
									i++;
								}
							}

	/*						//vystupuje lava strana priradenia v hlavicke? over, ci moze
							if (table->atoms[id]->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
							{
								DWORD varId = (*((DWORD*)exp))>>8;
								if (varId < atom->idb.nArgs)

							}*/
	/*
							if (!someagrfun)
								//vyraz neobsahuje agregacnu funkciu
								continue;//dalsi atom zober
	*/

							if (someagrfun)
							{
								//vyraz obsahuje aspon jednu premennu DATALOG_MARKED_ASSIGN_SOME_AGRFUN, tj. aspon jednu agrfun v tran.uzav.
								//vsetky premenne musia byt DATALOG_MARKED_ASSIGN_ONLY_AGRFUN alebo v groupby
								bool notallagrfun = false;
								i = 0;
								while (exp[i])
								{
									if (exp[i] == DATALOG_EXP_VAR)
									{
										DWORD varId = (*((DWORD*)(&exp[i])))>>8;//premenna vo vyraze
										if (!(rule->varTypes[varId].mark&(DATALOG_MARKED_ASSIGN_ONLY_AGRFUN+DATALOG_MARKED_GROUPBY)))
										{
											notallagrfun = true;
											break;
										}
										i+=4;
									}
									else
									if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
										i+=4;
									else
										i++;
								}

								if (notallagrfun)
								{
									//chyba
									char *str;
									if (table->atoms[id]->arit.substitution)
										str = ExpressionToString(rule, (char*)&exp[5], false);
									else
										str = ExpressionToString(rule, (char*)exp, false);
									char *tmp = CMemory::printstr("Výraz ’%s’ musí obsahovať len agregačné funkcie alebo premenné v groupby podcieli!",
										str);
									app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
									_DELETE(str);
									ret = DATALOG_ERROR;
								}
							}
						}
					}

					for (DWORD i=0; i<atom->idb.nArgs; i++)
						if (!(rule->varTypes[i].mark&(DATALOG_MARKED_GROUPBY+DATALOG_MARKED_ASSIGN_ONLY_AGRFUN)) &&
							!(rule->varTypes[i].error&DATALOG_ERR_INHEAD))
						{
							char *tmp = CMemory::printstr("Výraz ’%s’ sa nesmie nachádzať v hlavičke! Musí obsahovať len premenné uvedené v groupby podcieli alebo hodnotu agregačných funkcií.", rule->varNames->Get(i));
							app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
							rule->varTypes[i].error |= DATALOG_ERR_INHEAD;
							ret = DATALOG_ERROR;
						}

				}//if agrFunOrGroupby

				//vypis chyby (ak treba)
				if (nVarList)
				{
					varList->AddData("’ nie %s viazan%s!", 18);
					char *tmp;
					if (nVarList == 1)
						tmp = CMemory::printstr((char*)varList->GetPointer(), "á", "je", "á");
					else
						tmp = CMemory::printstr((char*)varList->GetPointer(), "é", "sú", "é");
					app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
				}

			}//for pravidla
		}//switch

	} //for atomy

	_DELETE(varList);

	//*************************************************************************************************************************
	//TYPE CHECKING
	//*************************************************************************************************************************

						//AGREGACNE FUNKCIE
						//x je lub. typ
						//avg(numeric), vracia numeric
						//count(x), vracia numeric
						//min(x), vracia x
						//max(x), vracia x
						//sum(numeric), vracia numeric

	//+ urci typ premennych - podla toho, kde boli viazane

	//prejdi vsetky zviazane premenne
	//ak boli zviazane v relacnom podcieli, zisti v ktorom a prirad premennej typ podla argumentu
	//ak je viazana v priradeni, prirad jej typ podla pravej strany; vsetky premenne na pravej strane musia mat typ!
	//premenne, ktore nemaju typ, nie su grounded. vsetky premenne musia byt grounded!


	//prechadzaj definiciu typov v cykle, kvoli tranzitivnemu uzaveru

	bool change = true;
	while (change)
	{
		change = false;

		for (DWORD i=table->nEdb; i<table->nAtoms; i++)//i su atomy
		{
			SYNTACTIC_ATOM *atom = table->atoms[i];
			switch (atom->idb.type)
			{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_ANSWER:
				for (DWORD j=0; j<atom->idb.nRules; j++)//j je pravidlo
				{
					SYNTACTIC_RULE *rule = atom->idb.rules[j];
					if (rule->skip)
						continue;//dalsie pravidlo

					//prejdi vsetky premenne
					for (DWORD varId=0; varId<rule->varNames->GetCount(); varId++)
					{
						if (rule->varTypes[varId].type == DATABASE_TYPE_NOTYPE)//treba premennej definovat typ? nema ho uz definovany?
						{
							if (rule->varTypes[varId].mark&DATALOG_MARKED_BINDEDASS)
							{
								//premenna viazana v priradeni

								//zisti v ktorom priradeni je premenna viazana
								DWORD atomId = GetAssignmentOfBinding(varId, rule);

								const char *exp = table->atoms[atomId]->arit.expression;
								DWORD varId = (*((DWORD*)exp))>>8;//id premennej na lavej strane
								BYTE varType = DATABASE_TYPE_NOTYPE;//typ, aky sa jej priradi
								SYNTACTIC_VAR_TYPE *type = &rule->varTypes[varId];//jej typ

								//ak prava strana obsahuje aritm. operaciu, je to ciselny vyraz ->
								//	typ premennej na lavej strane bude NUMERIC, ale len vtedy, ak vsetky podvyrazy napravo su typu numeric
								bool aritop = false;
								DWORD i=5;
								while (exp[i])
								{
									if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
										i+=4;
									else
									{
										if ( (exp[i] == '+')||(exp[i] == '-')||(exp[i] == '*')||(exp[i] == '/'))
										{
											aritop = true;
											break;
										}
										i++;
									}
								}

								if (aritop)
								{
									//aritmeticky vyraz
									DWORD t = AllSubexpressionMustBeNumbers(atomId, (char*)exp, rule, &ret);
									switch (t)
									{
									case DATABASE_TYPE_NUMBER:
										//vsetky podvyrazy su ciselneho typu, ok
										varType = DATABASE_TYPE_NUMBER;
										break;
									case DATABASE_TYPE_NOTYPE:
										//niektore typy este nie su definovane
										continue;
									default:
										//zly typ
										type->error |= DATALOG_ERR_TYPE;
										ret = DATALOG_ERROR;
										continue;//dalsia premenna
									}
								}
								else
								{
									//urci typ pravej strany, je tam len jeden operand
									//premenna/agr. funkcia/ciselna konstanta
									DWORD i=5;
									varType = GetTypeInExpression((char*)exp, i, rule);
									if (varType == -1)
										continue;//dalsia premenna
								}

								if (varType == DATABASE_TYPE_NOTYPE)
									continue;//este nevieme zistit typ premennej

								//este nedefinovany typ
								type->type = varType;
								change = true;//zmena

							} //if (mark&1)
							else
							if (rule->varTypes[varId].mark&DATALOG_MARKED_BINDEDREL)
							{
								//premenna viazana v relacnom podcieli

								//najdi podciel, v ktorom je viazana
								DWORD index;
								DWORD atomId = GetPredicateOfBinding(varId, rule, &index);
								//zisti typ argumentu z EDB/IDB predikatu
								BYTE argType;
								if (table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_IDBATOM)
									argType = table->atoms[atomId]->idb.argTypes[index];
								else
									argType = db->tables[atomId].attributes[index].type;

								//ma tento atom uz definovany typ tohto argumentu? (EDB maju vsetky, IDB nemusia mat)
								if (!argType)
									continue;//nie

								//prirad premennej typ
								rule->varTypes[varId].type = argType;

								change = true;
							}
							else
								//premenna nie je viazana, nemozeme jej pridelit typ
								;
						}
					}//for premenne


					//prirad argumentom predikatu typ podla zodpovedajucej premennej
					//ale len vtedy, ak nema definovany typ
					//ak ho ma a je rozny, ide o chybu

					for (varId=0; varId<atom->idb.nArgs; varId++)
					{
						BYTE *typeInHead = &atom->idb.argTypes[varId];
						BYTE typeInRule = rule->varTypes[varId].type;

						if (*typeInHead == typeInRule)
							continue;
						if (typeInRule == DATABASE_TYPE_NOTYPE)
							continue;
						if (*typeInHead == DATABASE_TYPE_NOTYPE)
						{
							*typeInHead = typeInRule;
						}
						else
						{
							//typova chyba
							if (rule->varTypes[varId].error&DATALOG_ERR_TYPE)
								continue;//uz sme mali chybu

							char *tmp;
							if (rule->varTypes[varId].mark&DATALOG_MARKED_SUBS)
							{
								//potrebujeme pravu stranu substitucie
								DWORD atomId = GetAssignmentOfBinding(varId, rule);
								const char *exp = table->atoms[atomId]->arit.expression;
								char *exp2 = ExpressionToString(rule, (char*)&exp[5], false);
								tmp = CMemory::printstr("Argument č.%i ’%s’ v hlavičke musí byť typu ’%s’, nie ’%s’!",
									varId+1, exp2, CDatabase::GetType(*typeInHead), CDatabase::GetType(typeInRule));
								_DELETE(exp2);
							}
							else
								tmp = CMemory::printstr("Argument č.%i ’%s’ v hlavičke musí byť typu ’%s’, nie ’%s’!",
									varId+1, rule->varNames->Get(varId), CDatabase::GetType(*typeInHead), CDatabase::GetType(typeInRule));

							app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
							rule->varTypes[varId].error |= DATALOG_ERR_TYPE;
							ret = DATALOG_ERROR;
						}
					}
				}
				break;
			}//switch (typ atomu)
		}//for i (atomy)
	}//while change

	//***************************************************************************************
	//vypis premenne s nazvami, ktore nemaju prideleny typ - premenne
	CMemory *noTypeVars = _NEW(CMemory());
	for (i=table->nEdb; i<table->nAtoms; i++)//i su atomy
	{
		SYNTACTIC_ATOM *atom = table->atoms[i];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			for (DWORD j=0; j<atom->idb.nRules; j++)//j je pravidlo
			{
				SYNTACTIC_RULE *rule = atom->idb.rules[j];
				if (rule->skip)
					continue;

				noTypeVars->ClearMemory();
				noTypeVars->AddData("Premenn%s ’", 11);
				DWORD nNoTypeVars = 0;

				for (DWORD k=0; k<rule->varNames->GetCount();k++)
					//substituciu nechceme
					if (!(rule->varTypes[k].mark&DATALOG_MARKED_SUBS) && rule->varTypes[k].type == DATABASE_TYPE_NOTYPE)
					{
						char *var = (char*)rule->varNames->Get(k);
						if (nNoTypeVars)
							noTypeVars->AddData(",",1);
						nNoTypeVars++;

						noTypeVars->AddData(var, strlen(var));
						ret = DATALOG_ERROR;
					}

				//vypis pripadne chyby
				if (nNoTypeVars)
				{
					noTypeVars->AddData("’ nem%s definovaný typ!", 23);
					char *tmp;
					if (nNoTypeVars == 1)
						tmp = CMemory::printstr((char*)noTypeVars->GetPointer(), "á", "á");
					else
						tmp = CMemory::printstr((char*)noTypeVars->GetPointer(), "é", "ajú");
					app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
				}
			}
			break;
		}
	}
	_DELETE(noTypeVars);

	//+ over typ premennych v aritmetickych atomoch
	for (i=table->nEdb; i<table->nAtoms; i++)//i su atomy
	{
		SYNTACTIC_ATOM *atom = table->atoms[i];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			for (DWORD j=0; j<atom->idb.nRules; j++)//j je pravidlo
			{
				SYNTACTIC_RULE *rule = atom->idb.rules[j];
				if (rule->skip)
					continue;//dalsie pravidlo

				for (DWORD k=0; k<rule->nAtoms; k++)
				{
					DWORD id = rule->atom[k]->id;//id atomu na pravej strane
					switch (table->atoms[id]->idb.type)
					{
					case SYNTACTIC_ATOM_ARITATOM:
						{
							if (table->atoms[id]->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
								//priradenia nemusime typovo kontrolovat, to sme uz spravili
								continue;
/*							if (table->atoms[id]->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
							{
								//priradenia nemusime typovo kontrolovat, to sme uz spravili
							 	
								//ak ide vsak o priradenie do premennej z hlavicky
								//vtedy over, ci typ argumentu v hlavicke je zhodny s typom premennej
								const char *exp = table->atoms[id]->arit.expression;
								DWORD varId = *((DWORD*)exp)>>8;
								if (varId < atom->idb.nArgs)
								{
									BYTE type1 = rule->varTypes[varId].type;
									BYTE type2 = atom->idb.argTypes[varId];
									if (type1 != type2)
									{
										char *tmp;
										if (table->atoms[id]->arit.substitution)
										{
											char *exp2 = ExpressionToString(rule, (char*)&exp[5], false);
											tmp = CMemory::printstr("Argument č.%i ’%s’ v hlavičke musí byť typu ’%s’, nie ’%s’!",
												varId+1, exp2, CDatabase::GetType(type2), CDatabase::GetType(type1));
											_DELETE(exp2);
										}
										else
											tmp = CMemory::printstr("Argument č.%i ’%s’ v hlavičke musí byť typu ’%s’, nie ’%s’!",
												varId+1, rule->varNames->Get(varId), CDatabase::GetType(type2), CDatabase::GetType(type1));
										app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
										ret = DATALOG_ERROR;
									}
								}
								continue;
							}*/

							//mame podmienku
							const char *exp = table->atoms[id]->arit.expression;

							//ak vyraz obsahuje nejaku aritmeticku operaciu, musi byt len ciselny
							bool aritop = false; bool string = false;
							DWORD i=0;
							while (exp[i])
							{
								//retazec? najdi jeho koniec
								if (exp[i] == '\'')
								{
									string = true;
									i++;while (true){while (exp[i]!='\'')i++;if (exp[++i]!='\'')break;i++;}
									if (!exp[i])
										break;
								}
								if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
									i+=4;
								else
								{
									if ( (exp[i] == '+')||(exp[i] == '-')||(exp[i] == '*')||(exp[i] == '/'))
									{
										aritop = true;
										break;
									}
									i++;
								}
							}

							if (aritop)
							{
								if (string)
								{
									app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer("Numerický aritmetický podcieľ nesmie obsahovať reťazec alebo dátum!", rule->inputLine);
									ret = DATALOG_ERROR;
									continue;
								}

								AllSubexpressionMustBeNumbers(id, (char*)exp, rule, &ret);
							}
							else
							{
								//vyraz neobsahuje ziadnu aritmeticku operaciu
								//je tvaru x operand y, kde x a y je string/cislo/premenna, lubovolne ozatvorkovane
								
								//preskoc zatvorky
								DWORD i=0;

								DWORD typeL = GetTypeInExpression((char*)exp, i, rule);
								if (typeL == -1)
									continue;

								//nacitaj operaciu
								if (exp[i]=='!') i++;
								if (exp[i]=='<') i++;
								if (exp[i]=='>') i++;
								if (exp[i]=='=') i++;

								DWORD typeR = GetTypeInExpression((char*)exp, i, rule);
								if (typeR == -1)
									continue;

								if (typeL != typeR)
								{
									char *tmp;
									if (table->atoms[id]->arit.substitution)
									{
										//zisti nazov predikatu, v ktorom je premenna viazana (v ktorom vystupuje ako substitucia)
										DWORD varId = *((DWORD*)exp)>>8;
										char *name;
										DWORD index;
										DWORD id = GetPredicateOfBinding(varId, rule, &index);
										if (table->atoms[id]->idb.type == SYNTACTIC_ATOM_IDBATOM)
											name = table->atoms[id]->idb.name;
										else
											name = db->tables[id].name;
										char *exp2 = ExpressionToString(rule, (char*)&exp[5], false);
										tmp = CMemory::printstr("Argument č.%i ’%s’ podcieľa ’%s’ musí byť typu ’%s’, nie ’%s’!",
											index+1, exp2, name, CDatabase::GetType(typeL), CDatabase::GetType(typeR));
										_DELETE(exp2);
									}
									else
									{
										char *expr = ExpressionToString(rule, (char*)exp, false);
										tmp = CMemory::printstr("Vo výraze ’%s’ nezhoda typov : ’%s’ a ’%s’!", expr,
											CDatabase::GetType(typeL), CDatabase::GetType(typeR));
										_DELETE(expr);
									}
									app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
									ret = DATALOG_ERROR;
								}
							}
						}
						break;
					}
				}
			}
			break;
		}
	}

/*
	//over, ci vsetky premenne v hlavickach maju typ (su grounded)
	//..(ak su vsetky argumenty grounded, grounded je aj atom)
	for (i=table->nEdb; i<table->nAtoms; i++)//i su atomy
	{
		SYNTACTIC_ATOM *atom = table->atoms[i];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			for (DWORD j=0; j<atom->idb.nArgs; j++)
			{
				if (!atom->idb.argTypes[j])
				{
					char *tmp = CMemory::printstr("Argument č.%i v hlavičke atómu '%s' nie je uzemnená", j+1, atom->idb.name);
					app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
					ret = DATALOG_ERROR;
				}
			}
			break;
		}
	}

*/

	app.mainWindow->GetOutputWindow()->WriteBuffer();

	if (ret == DATALOG_ERROR)
	{
		DEBUGPRINT("DEBUG: *** SEMANTICS ANALYSIS done with error ***");
		return DATALOG_ERROR;
	}

	/**********************************************************************************************************************/
	//PREKLAD MIEN
	/**********************************************************************************************************************/
	//prepis nazvy premennych ich hodnotou
	//..ak je viazana v IDB,EDB tak alias.stlpec (alias je poradie vyskytu atomu (v ktorom sa viaze) v pravidle)
	//..ak je viazana priradenim, tak hodnotou priradenia (prava strana)
	//....Tento atom, v ktorom bola prepisana (prvykrat) zmaz - pri prepise do SQL sa kazde viazanie X = vyraz..
	//....prepise na WHERE [vyraz za X] = vyraz - teda kazde dalsie priradenie premennej X = ... sa prepise na..
	//....podmienku rovnosti pravych stran. Prvy vyskyt vsak prepis na SYNTACTIC_ATOM_ARITATOM_BINDED,
	//....aby sme po substitucii nedostali rovnost vyraz = ten isty vyraz
	CMemory *mem = _NEW(CMemory());
	for (i=table->nEdb; i<table->nAtoms; i++)//i su atomy
	{
		SYNTACTIC_ATOM *atom = table->atoms[i];
		switch (atom->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			for (DWORD j=0; j<atom->idb.nRules; j++)//j je pravidlo
			{
				SYNTACTIC_RULE *rule = atom->idb.rules[j];
				DWORD nVars = rule->varNames->GetCount();
				CStringTable *newVarNames = _NEW(CStringTable());

				for (DWORD varId = 0; varId<nVars; varId++)
				{
					//prejdi vsetky atomy a podla prveho vhodneho vyskytu prepis nazov premennej
					bool nextVar = false;
					mem->DeleteData();

					for (DWORD l=0; l<rule->nAtoms; l++)//l je atom na pravej strane pravidla
					{
						if (nextVar) break;//zober dalsiu premennu
						DWORD atomId = rule->atom[l]->id;
						SYNTACTIC_ATOM *atom = table->atoms[atomId];
						switch (atom->idb.type)
						{
						case SYNTACTIC_ATOM_ARITATOM:
							if (atom->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
							{
								char *exp = atom->arit.expression;
								if ((*(DWORD*)exp)>>8 == varId)//priradenie do nasej premennej?
								{
									nextVar = true;
									//nahrad hodnotu premennej pravou stranou
									//ak je premenna typu date, tak hodnotu zapuzdri do date('....') - DISTINCT hadze chybu - datum berie ako text!
									if (rule->varTypes[(*(DWORD*)exp)>>8].type == DATABASE_TYPE_DATE)
									{
										mem->AddData("date(", 5);
										mem->AddData(&exp[5], CDatalog::ExpressionLength(&exp[5]));
										mem->AddData(")",2);
									}
									else
										mem->AddData(&exp[5], CDatalog::ExpressionLength(&exp[5])+1);
									//vymaz tento atom
									atom->idb.type = SYNTACTIC_ATOM_ARITATOM_BINDED;
								}
							}
							break;
						case SYNTACTIC_ATOM_EDBATOM:
						case SYNTACTIC_ATOM_IDBATOM:
						case SYNTACTIC_ATOM_ANSWER:
							if (!rule->atom[l]->not)//negovany atom nechcem
								//prejdi vsetky argumenty tohto atomu a zisti, ci medzi nimi nie je premenna z hlavicky
								for (DWORD m=0; m<rule->atom[l]->nArgs; m++)//m je stlpec tabulky IDB/EDB atomu na pravej strane
								{
									if (rule->atom[l]->argsId[m] == varId)
									{
										nextVar = true;
										char alias[20]; alias[0] = 't'; itoa(l, &alias[1], 10);
										//do vystupu ide tento stlpec ako alias.nazov_stlpca
										if (atom->idb.type == SYNTACTIC_ATOM_EDBATOM)
										{
											char *columnName = db->tables[atomId].attributes[m].name;
											mem->AddData(alias, strlen(alias));
											mem->AddData(".", 1);
											mem->AddData(columnName, strlen(columnName));
										}
										else
										{
											char tmp2[20]; itoa(m, tmp2, 10);
											mem->AddData(alias, strlen(alias));
											mem->AddData(".",1);
											mem->AddData("c",1);mem->AddData(tmp2, strlen(tmp2));
										}
										char c=0; mem->AddData(&c,1);
										break;
									}
								}
							break;
						}
					}

					//pridaj string pre tuto premennu
					newVarNames->nAdd((char*)mem->GetPointer(), mem->GetSize()+1);
				}//for varId


#ifdef _PRINTDEBUG
				//vypis aliasy tabuliek a premennych

				char *tmp = CMemory::printstr("DEBUG: Pravidlo %i.", j+1);
				app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);

				CStringTable *oldVarNames = rule->varNames;
				rule->varNames = newVarNames;
				for (varId = 0; varId<nVars; varId++)
				{
					char *str = ExpressionToString(rule, (char*)newVarNames->Get(varId), false);
					char *tmp = CMemory::printstr("DEBUG: alias %s : ’%s’", oldVarNames->Get(varId), str);
					_DELETE(str);
					app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, rule->inputLine); _DELETE(tmp);
				}
				rule->varNames = oldVarNames;
#endif
				_DELETE(rule->varNames);
				rule->varNames = newVarNames;
			}
			break;
		}
	}

	_DELETE(mem);

	DEBUGPRINT("DEBUG: *** SEMANTICS ANALYSIS done ***");
	return ret;
}

CDatalog::CDatalog(void* input)
{
	graph = NULL; table = NULL;
	argsAnswer = _NEW(CStringTable());
	sa = _NEW(CSyntacticAnalyzer(input));
}

char *CDatalog::StringToExpression(SYNTACTIC_RULE *rule, CMemory *var_used, SYNTACTIC_ATOM_FULLNAME_ARIT* arit)
{
	//v stringu nahrad premenne ich id (pridaj ich do rule), modifikuj var_used a vrat vyraz
	//premenna xx sa nahradi do tvaru DWORD (01 xx xx xx)

	CMemory *mem = _NEW(CMemory());
	
	DWORD start = 0, len;
	for (DWORD i=0; i<arit->nVar; i++)
	{
		len = arit->var[i].start - start;//dlzka textu po najblizsiu premennu
		if (len)
		{
			mem->AddData(&arit->expression[start], len);//zapis text po najblizsiu premennu
			start += len;
		}

		char c = arit->expression[start + arit->var[i].len];
		arit->expression[start + arit->var[i].len] = 0;//len potialto je string

		char *name = &arit->expression[start];
		BYTE type;
		
		if (!strcmp(name,"avg"))
			type = DATALOG_EXP_AVG;
		else
		if (!strcmp(name,"count"))
			type = DATALOG_EXP_COUNT;
		else
		if (!strcmp(name,"min"))
			type = DATALOG_EXP_MIN;
		else
		if (!strcmp(name,"max"))
			type = DATALOG_EXP_MAX;
		else
		if (!strcmp(name,"sum"))
			type = DATALOG_EXP_SUM;
		else
			type = DATALOG_EXP_VAR;

		if (type != DATALOG_EXP_VAR)
		{
			//agregacna funkcia
			arit->expression[start + arit->var[i].len] = c;//vrat spat znak
			i++;//prejdi dalej za premennu

			start = arit->var[i].start;
			name = &arit->expression[start];
			c = arit->expression[start + arit->var[i].len];
			arit->expression[start + arit->var[i].len] = 0;//len potialto je string
		}

		DWORD n = rule->varNames->GetCount();
		DWORD varId = rule->varNames->Insert(name);//pridaj premennu do zoznamu premennych
		if (varId == n)//novo pridana?
		{
			//novo pridana
			char c=1; var_used->AddData(&c,1);//oznac premennu za pouzitu - len v aritmetickom atome
		}
		else
		{
			//uz existuje
			//mozno bola pouzita v relacnom podcieli (var_used = 2), prepis len ak nebola
			BYTE* b = &(((BYTE*)(var_used->GetPointer()))[varId]);
			if (!*b)
				*b = 1;//oznac za pouzitu len v aritmetickom atome
		}

		varId = (varId<<8) + type;//urob z nej identifikator premennej v stringu
		mem->AddData(&varId, 4);//zapis id premennej
		arit->expression[start + arit->var[i].len] = c;//vrat spat
		start += arit->var[i].len;

		if (type != DATALOG_EXP_VAR)
			start++;//preskoc koncovu zatvorku
	}
	//este tam daj zvysok
	mem->AddData(&arit->expression[start], strlen(arit->expression)-start+1);

	len = mem->GetSize()+1;
	char *ret = _NEW(char[len]);
	::CopyMemory(ret, mem->GetPointer(), len);
	_DELETE(mem);
	return ret;
}

DWORD CDatalog::ExpressionLength(char *exp)
{
	DWORD len = 0;
	while (exp[len])
	{
		if (BYTE(exp[len]) < DATALOG_EXP_SPECIAL)
			len+=4;
		else
			len++;
	}
	return len;
}

char *CDatalog::ExpressionToString(SYNTACTIC_RULE* rule,char *exp, bool sql)
{
	_agrInExp = false;
	CMemory *in = _NEW(CMemory()); in->AddData(exp, CDatalog::ExpressionLength(exp)+1);
	CMemory *out = _NEW(CMemory());
	//nahrad premenne vo vyraze exp ich hodnotou
	bool change = true;
	while (change)
	{
		change = false;
		DWORD start = 0, i = 0;
		out->DeleteData();
		char *s = (char*)in->GetPointer();
		while (s[i])
		{
			if (s[i] == DATALOG_EXP_VAR) //premenna?
			{
				change = true;
				if (i-start)
					//zapis data
					out->AddData(&s[start], i-start);

				DWORD varId = (*((DWORD*)(&s[i]))) >> 8;
				//zapis premennu - ozatvorkuj ju!!
				const char *var = rule->varNames->Get(varId);
				if (sql)
					out->AddData("(",1);
				out->AddData((void*)var, ExpressionLength((char*)var));
				if (sql)
					out->AddData(")",1);
				i+=4;
				start = i;
			}
			else
			if (BYTE(s[i]) < DATALOG_EXP_SPECIAL)
			{
				//vloz agregacnu funkciu

				_agrInExp = true;
				change = true;

				if (i-start)
					//zapis data
					out->AddData(&s[start], i-start);

				DWORD varId = (*((DWORD*)(&s[i]))) >> 8;//agregacna premenna
				//zapis funkciu + premennu
				char *fun = _agrFunctionsName[s[i]];
				out->AddData(fun, strlen(fun));
				if (sql)
					out->AddData("(DISTINCT ", 10);//chceme pocet roznych!
				else
					out->AddData("(", 1);
				const char *var = rule->varNames->Get(varId);
				out->AddData((void*)var, ExpressionLength((char*)var));
				out->AddData(")",1);
				i+=4;
				start = i;
			}
			else
				i++;
		}
		if (i-start)
			out->AddData(&s[start], i-start);
		char c=0; out->AddData(&c, 1);//ukonci string
		//skopiruj data z out do in
		in->DeleteData(); in->AddData(out->GetPointer(), out->GetSize());
	}

	DWORD len = out->GetSize();
	char *ret = _NEW(char[len]);
	::CopyMemory(ret, out->GetPointer(), len);
	_DELETE(out);_DELETE(in);
	return ret;
}

bool CDatalog::Evaluate()
{
	infoWin = _NEW(CInfoWindow());

	CSQLresult *res = NULL;
	CMemory *ord_list = NULL;

	try
	{
		sa->ReadRules();
		table = sa->GetTable();
		if (!table)
			throw "Chyba počas syntaktickej analýzy! Program nebude vyhodnotený.";

		DEBUGPRINT("DEBUG: *** EVALUATION ***");

		//najskor zisti, ktore IDB predikaty sa pouziju pri vypocte
		//teda zisti vsetky dosiahnutelne z answer predikatu
		if (graph) {_DELETE(graph);}
		graph = _NEW(CGraph(table));
		ord_list = graph->TopologicalSort(table->answerID, false);
		//vsetky predikaty, ktore nie su dosiahnutelne (nie su v ord_list) "vymaz"
		DWORD *list = (DWORD*)ord_list->GetPointer();
		DWORD listSize = ord_list->GetSize()/4;
		for (DWORD i=0; i<table->nAtoms; i++)
		{
			if ((table->atoms[i]->idb.type == SYNTACTIC_ATOM_IDBATOM) ||
				(table->atoms[i]->idb.type == SYNTACTIC_ATOM_ANSWER))
			{
				//ak sa tento idb predikat nenachadza v ord_liste, oznac ho ako dropped
				bool inlist = false;
				for (DWORD j=0; j<listSize; j++)
				{
					DWORD atomId = graph->GetVertex(list[j])->id;
					if (atomId == i)
					{
						inlist = true;
						break;
					}
				}
				//ak nie je v ord_liste, tak ho "dropni"
				if (!inlist)
					table->atoms[i]->idb.type = SYNTACTIC_ATOM_DROPPEDIDBATOM;
			}
		}
		_DELETE(ord_list); ord_list = NULL;

		//over semantiku pravidiel
		DWORD ret = CheckSemantic();
		if (ret == DATALOG_ERROR)
			throw "Chybná sémantika programu! Program nebude vyhodnotený.";

		//znova vypocitaj ord_list, ale tentokrat chceme dat pozor na cykly!
		ord_list = graph->TopologicalSort(table->answerID, true);

		if (ord_list)
		{
			//existuje topologicke utriedenie -> graf neobsahuje cyklus
			res = EvaluateWithoutLoop(ord_list);
			_DELETE(ord_list);
		}
		else
		{
			res = EvaluateWithLoop();
		}

		//------------------------------------------------------------------------------------------

	} catch (int err)
	{
		err;
		ViewResult(res);
		if (graph) {_DELETE(graph); graph = NULL;}
		if (ord_list) {_DELETE(ord_list);}
		if (res) {_DELETE(res);}
		_DELETE(infoWin);
		return false;
	} catch (char *err)
	{
		app.mainWindow->GetOutputWindow()->AddLine(err);
		ViewResult(res);
		if (graph) {_DELETE(graph); graph = NULL;}
		if (ord_list) {_DELETE(ord_list);}
		if (res) {_DELETE(res);}
		_DELETE(infoWin);
		return false;
	}

	if (graph) {_DELETE(graph); graph = NULL;}
	ViewResult(res); if (res) {_DELETE(res);}
	_DELETE(infoWin);
	return true;
}

CDatalog::~CDatalog()
{
//	if (graph) {_DELETE(graph);}

	_DELETE(argsAnswer);
	_DELETE(sa);
}

BYTE CDatalog::GetTypeInExpression(char *exp, DWORD &i, SYNTACTIC_RULE *rule)
{
	DWORD var;
	BYTE type;

	while (exp[i]=='(')
		i++;

	if (exp[i] == DATALOG_EXP_VAR)
	{
		//premenna vo vyraze
		var = *((DWORD*)&exp[i]);
		type = rule->varTypes[var>>8].type;
		i+=4;
	}
	else
	if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
	{
		//agregacna funkcia
		DWORD ret = 0;
		var = *((DWORD*)&exp[i]);
		type = CheckAgregFunType(var, rule, &ret);
		if (ret == DATALOG_ERROR)
			return -1;
		i+=4;
	}
	else
	if (exp[i] == '\'')
	{
		DWORD j=i;
		//najdi koniec retazca
		i++;while (true){while (exp[i]!='\'')i++;if (exp[++i]!='\'')break;i++;}

		char c = exp[i]; exp[i]=0;

		//je to datum?
		DWORD date;
		try {date=CanBeDate((char*)&exp[j]);}
		catch (int err){err;return -1;}

		exp[i]=c;

		switch (date)
		{
		case DATALOG_DATE_YES:
			type = DATABASE_TYPE_DATE;
			break;
		case DATALOG_DATE_NO:
			type = DATABASE_TYPE_STRING;	//prava strana je string
			break;
		case DATALOG_DATE_OVERFLOW:
			char *tmp = CMemory::printstr("Upozornenie: Konštanta %s považovaná za string (pretečenie dátumu?)",
				(char*)&exp[j]);
			app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
			type = DATABASE_TYPE_STRING;
			break;
		}
	}
	else
	{
		//nic ine nezostalo -> cislo
		type = DATABASE_TYPE_NUMBER;
		while ( (exp[i]>='0')&&(exp[i]<='9'))
			i++;
		if (exp[i]=='.')
		{
			i++;
			while ( (exp[i]>='0')&&(exp[i]<='9'))
				i++;
		}
	}

	while (exp[i]==')')
		i++;

	return type;
}

DWORD CDatalog::GetAllNotMarkedVars(SYNTACTIC_RULE *rule, DWORD varId, BYTE *marked, CMemory *tmp, DWORD *ret)
{
	//premenna varId je premenna substitucie
	//najdi aritm. podciel (substituciu) a do tmp pridaj vsetky premenne, ktore nie su oznacene (4 alebo 8)
	DWORD count=0;
	for (DWORD i=0; i<rule->nAtoms; i++)
	{
		DWORD id = rule->atom[i]->id;//id atomu na pravej strane
		if (table->atoms[id]->arit.type == SYNTACTIC_ATOM_ARITATOM)
			if (table->atoms[id]->arit.substitution)
			{
				char *exp = table->atoms[id]->arit.expression;
				DWORD var = (*((DWORD*)exp))>>8; //lava strana
				if (varId == var)
				{
					//vypis vsetky premenne z pravej strany substitucie, ktore nie su "marked"
					DWORD i=5;//zacni az za operaciu
					while (exp[i])
					{
						if (exp[i] == DATALOG_EXP_VAR)
						{
							DWORD varId = (*((DWORD*)&exp[i]))>>8;
							if (!((marked[varId]&4) || (marked[varId]&8) || (marked[varId]&16)))
							{
								if (count)//uz sme nieco vypisali?
									tmp->AddData(",", 1);
								char *str = (char*)rule->varNames->Get(varId);
								tmp->AddData(str, strlen(str));
								count++;
								rule->varTypes[varId].error |= DATALOG_ERR_MUSTGROUPBY;
								*ret = DATALOG_ERROR;
							}
							i+=4;
						}
						else
						if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
							i+=4;
						else
							i++;
					}
				}
			}
	}
	return count;
}

DWORD CDatalog::GetPredicateOfBinding(DWORD varId, SYNTACTIC_RULE *rule, DWORD *index)
{
	//vrati identifikator relacneho predikatu, v ktorom je premenna viazana
	//predpokladom je, ze je viazana v relacnom predikate

	for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
	{
		DWORD atomId = rule->atom[atomIndex]->id;
		switch (table->atoms[atomId]->idb.type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_EDBATOM:
			for (DWORD i=0; i<rule->atom[atomIndex]->nArgs; i++)
			{
				if (rule->atom[atomIndex]->argsId[i] == varId)
				{
					if (index)
						*index = i;
					return atomId;
				}
			}
			break;
		}

	}
	//nenasli sme relacny podciel, v ktorom je premenna viazana
	app.abort();
	return -1;//sem sa uz aj tak nedostaneme
}

DWORD CDatalog::GetAssignmentOfBinding(DWORD varId, SYNTACTIC_RULE *rule)
{
	//vrati identifikator aritmetickeho podcielu, v ktorom je premenna viazana
	//predpokladom je, ze je viazana v priradeni

	for (DWORD atomIndex=0; atomIndex<rule->nAtoms; atomIndex++)
	{
		DWORD atomId = rule->atom[atomIndex]->id;
		if (table->atoms[atomId]->idb.type == SYNTACTIC_ATOM_ARITATOM)
			if (table->atoms[atomId]->arit.typeOp == SYNTACTIC_ARIT_OPERAND_ASSIGN)
			{
				//na lavej strane musi byt varId
				const char *exp = table->atoms[atomId]->arit.expression;
				if (((*(DWORD*)exp)>>8) == varId)
					return atomId;
			}
	}
	//nenasli sme relacny podciel, v ktorom je premenna viazana
	app.abort();
	return -1;//sem sa uz aj tak nedostaneme
}

BYTE CDatalog::AllSubexpressionMustBeNumbers(DWORD atomId, char *exp, SYNTACTIC_RULE *rule, DWORD *ret)
{
	//vsetky premenne/agr. funkcia musia byt ciselneho typu

	bool ok = true;

	DWORD i=0, var;
	while (exp[i])
	{
		int type = -1;
		
		if (exp[i] == DATALOG_EXP_VAR)
		{
			//premenna
			var = *((DWORD*)&exp[i]);
			type = rule->varTypes[var>>8].type;
			i+=4;
		}
		else
		if (BYTE(exp[i]) < DATALOG_EXP_SPECIAL)
		{
			//agregacna funkcia
			DWORD ret = 0;
			var = *((DWORD*)&exp[i]);
			type = CheckAgregFunType(var, rule, &ret);
			if (ret == DATALOG_ERROR)
				return DATABASE_TYPE_UNKNOWN;//chyba
			i+=4;
		}
		else
		{
			if (exp[i] == '\'')
			{
				char *tmp;
				if (table->atoms[atomId]->arit.substitution)
				{
					char *exp2 = ExpressionToString(rule, (char*)&exp[5], false);
					tmp = CMemory::printstr("Numerický argument ’%s’ nesmie obsahovať reťazec!", exp2);
					_DELETE(exp2);
				}
				else
				{
					char *exp2 = ExpressionToString(rule, (char*)exp, false);
					tmp = CMemory::printstr("Numerický výraz ’%s’ nesmie obsahovať reťazec!", exp2);
					_DELETE(exp2);
				}
				app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
				ok = false;

				i++;while (true){while (exp[i]!='\'')i++;if (exp[++i]!='\'')break;i++;}
				if (!exp[i])
					break;
			}
			else
				i++;
		}


		//ak sme v prirovnani na premennej na lavej strane a nepozname typ, nic sa nedeje, lebo sa ho dozvieme az po navrate
		if ( (type == DATABASE_TYPE_NOTYPE) && (table->atoms[atomId]->arit.type = SYNTACTIC_ATOM_ARITATOM) && (i==4))
			continue;

		//ak teraz epozname typ, tak skonci
		if (type == DATABASE_TYPE_NOTYPE)
			return DATABASE_TYPE_NOTYPE;

		//vypis chybu, ak treba
		if ( (type != -1) && (type != DATABASE_TYPE_NUMBER))
		{
			if (!(rule->varTypes[var>>8].error&DATALOG_ERR_TYPE))
			{
				char *tmp;
				if ( (table->atoms[atomId]->arit.substitution) &&
					(((*((DWORD*)exp))>>8) == (var>>8)))
				{
					//spracovavame premennu na lavej strane substitucie
					//jej typ musi byt rovny typu argumentu, v ktorom sa viaze
					DWORD index;
					DWORD id = GetPredicateOfBinding(var>>8, rule, &index);
					char *name;
					BYTE type;
					if (table->atoms[id]->idb.type == SYNTACTIC_ATOM_IDBATOM)
					{
						name = table->atoms[id]->idb.name;
						type = table->atoms[id]->idb.argTypes[index];
					}
					else
					{
						const CDbStructure *db = app.GetDatabase()->GetStructure();
						name = db->tables[id].name;
						type = db->tables[id].attributes[index].type;
					}
					char *exp2 = ExpressionToString(rule, (char*)&exp[5], false);
					tmp = CMemory::printstr("Argument ’%s’ podcieľa ’%s’ musí byť typu ’%s’!",
						exp2, name, CDatabase::GetType(type));
					_DELETE(exp2);
					rule->varTypes[var>>8].error |= DATALOG_ERR_TYPE;
				}
				else
				{
					char tmp2[5]; tmp2[4] = 0; *(DWORD*)tmp2 = var;
					char *name = ExpressionToString(rule, tmp2, false);
					char *exp2;
					if (table->atoms[atomId]->arit.substitution)
					{
						exp2 = ExpressionToString(rule, (char*)&exp[5], false);
						tmp = CMemory::printstr("Numerický argument ’%s’ obsahuje výraz ’%s’ typu ’%s’!",
							exp2, name, CDatabase::GetType(type));
					}
					else
					{
						exp2 = ExpressionToString(rule, (char*)exp, false);
						tmp = CMemory::printstr("Numerický výraz ’%s’ obsahuje výraz ’%s’ typu ’%s’!",
							exp2, name, CDatabase::GetType(type));
					}
					_DELETE(exp2);
					_DELETE(name);
					rule->varTypes[var>>8].error |= DATALOG_ERR_TYPE;
				}
				app.mainWindow->GetOutputWindow()->AddNumberedLineToBuffer(tmp, rule->inputLine); _DELETE(tmp);
				ok = false;
			}
		}
	}
	if (ok)
		return DATABASE_TYPE_NUMBER;
	else
	{
		*ret = DATALOG_ERROR;
		return DATABASE_TYPE_UNKNOWN;//chyba
	}
}

char *CDatalog::_agrFunctionsName[] = {NULL, NULL, "sum", "avg", "count", "min", "max"};
bool CDatalog::_agrInExp = false;