Index: cpp/frams/genetics/fL/fL_general.cpp
===================================================================
--- cpp/frams/genetics/fL/fL_general.cpp	(revision 780)
+++ cpp/frams/genetics/fL/fL_general.cpp	(revision 797)
@@ -1,11 +1,17 @@
+// This file is a part of Framsticks SDK.  http://www.framsticks.com/
+// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// See LICENSE.txt for details.
+
 #include <algorithm>
 #include <stack>
 #include "fL_general.h"
-
-const char *fL_part_names[PART_PROPS_COUNT] = { "dn", "fr", "ing", "as" };
-const char *fL_part_fullnames[PART_PROPS_COUNT] = { "details", "friction", "ingestion", "assimilation" };
-
-const char *fL_joint_names[JOINT_PROPS_COUNT] = { "stif", "rotstif", "stam" };
-const char *fL_joint_fullnames[JOINT_PROPS_COUNT] = { "stiffness", "rotation stiffness", "stamina" };
+#include <frams/util/multirange.h>
+#include <iterator>
+
+const char *fL_part_names[FL_PART_PROPS_COUNT] = { "dn", "fr", "ing", "as" };
+const char *fL_part_fullnames[FL_PART_PROPS_COUNT] = { "details", "friction", "ingestion", "assimilation" };
+
+const char *fL_joint_names[FL_JOINT_PROPS_COUNT] = { "stif", "rotstif", "stam" };
+const char *fL_joint_fullnames[FL_JOINT_PROPS_COUNT] = { "stiffness", "rotation stiffness", "stamina" };
 
 #define FIELDSTRUCT fL_Word
@@ -14,5 +20,5 @@
 	{ "Word", 1, 2, "w" },
 	{ "name", 0, PARAM_CANOMITNAME, "word name", "s", FIELD(name), },
-	{ "npar", 0, PARAM_CANOMITNAME, "number of parameters", "d 0 3 0", FIELD(npar), },
+	{ "npar", 0, PARAM_CANOMITNAME, "number of parameters", "d 0 " FL_MAXPARAMS " 0", FIELD(npar), },
 	{ 0, 0, 0, }
 };
@@ -23,7 +29,7 @@
 {
 	{ "Rule", 1, 3, "r" },
-	{ "pred", 0, PARAM_CANOMITNAME, "predecessor", "s", FIELD(predecessor), },
-	{ "cond", 0, PARAM_CANOMITNAME, "parameter condition", "s", FIELD(condition), },
-	{ "succ", 0, PARAM_CANOMITNAME, "successor", "s", FIELD(successor), },
+	{ "pred", 0, 0, "predecessor", "s", FIELD(predecessor), },
+	{ "cond", 0, 0, "parameter condition", "s", FIELD(condition), },
+	{ "succ", 0, 0, "successor", "s", FIELD(successor), },
 	{ 0, 0, 0, }
 };
@@ -33,9 +39,10 @@
 ParamEntry fL_builder_paramtab[] =
 {
-	{ "LSystemInfo", 1, 3, "i" },
-	{ "axiom", 0, PARAM_CANOMITNAME, "starting sequence of L-System", "s", FIELD(axiom), },
-	{ "time", 0, PARAM_CANOMITNAME, "development time", "f 1.0 100.0 1.0", FIELD(time), },
-	{ "numckp", 0, PARAM_CANOMITNAME, "number of checkpoints", "d 1 50 1", FIELD(numckp), },
-	{ 0, 0, 0, }
+		{"LSystemInfo", 1, 4, "i"},
+		{"axiom", 0, 0, "starting sequence of L-System", "s", FIELD(axiom),},
+		{"time", 0, PARAM_CANOMITNAME, "development time", "f 0.0 " FL_MAXITER " 1.0", FIELD(time),},
+		{"numckp", 0, PARAM_CANOMITNAME, "number of checkpoints", "d 1 50 1", FIELD(numckp),},
+		{"maxwords", 0, PARAM_CANOMITNAME, "Maximum number of words within genotype sequence", "d -1 9999 -1", FIELD(maxwords),},
+		{0,0,0,}
 };
 #undef FIELDSTRUCT
@@ -134,5 +141,93 @@
 }
 
-int fL_Builder::tokenize(SString sequence, std::list<fL_Word *> &result, int numparams)
+int fL_Builder::createWord(SString token, fL_Word *&word, int numparams, int begin, int end)
+{
+	SString wordn;
+	int tokpos = 0;
+	// if word name cannot be extracted, then return error
+	if (!token.getNextToken(tokpos, wordn, '('))
+	{
+		return 1;
+	}
+	std::string wordname = fL_Builder::trimSpaces(wordn.c_str());
+	// if word cannot be found in available words, then return error
+	if (words.find(wordname) == words.end())
+	{
+		SString message = "Word '";
+		message += wordname.c_str();
+		message += "' in sequence does not exist";
+		logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
+		return 1;
+	}
+
+	if (word) delete word;
+	// create new word and assign parameters
+	word = new fL_Word(false, begin, end);
+
+	*word = *words[wordname];
+
+	SString temp;
+	temp = token.substr(tokpos);
+	temp = temp.substr(0, temp.len() - 1);
+
+	// if word has parameters
+	if (word->npar > 0)
+	{
+		// create ParamObject that will hold parameter data
+		word->data = ParamObject::makeObject(word->tab);
+		Param par(word->tab);
+		par.select(word->data);
+		par.setDefault();
+		ParamInterface::LoadOptions opts;
+
+		// load parameters from string
+		par.load(ParamInterface::FormatSingleLine, temp, &opts);
+		for (int i = 0; i < par.getPropCount(); i++)
+		{
+			SString t(par.id(i));
+			if (word->builtin && (t == SString("d") || t == SString(FL_PE_CONN_ATTR)))
+			{
+				word->parevals.push_back(NULL);
+			}
+			else
+			{
+				// create MathEvaluation object to check if string contained by
+				// parameter is valid
+				double tmp;
+				MathEvaluation *eval = NULL;
+				SString seq = par.getString(i);
+				// if string is empty, then evaluate this with 0
+				// if sequence could not be evaluated, then return error
+				if (seq.len() > 0)
+				{
+					eval = new MathEvaluation(numparams);
+					if (eval->evaluate(seq.c_str(), tmp) != 0)
+					{
+						SString message = "Word in sequence has invalid parameter:  ";
+						message += temp;
+						logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
+						delete eval;
+						delete word;
+						word = NULL;
+						return 1;
+					}
+				}
+				word->parevals.push_back(eval);
+			}
+		}
+	}
+	else if (word->npar == 0 && temp.len() > 0)
+	{
+		SString message = "Too many parameters for word:  ";
+		message += token;
+		logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
+		delete word;
+		word = NULL;
+		return 1;
+	}
+	return 0;
+}
+
+int fL_Builder::tokenize(SString sequence, std::list<fL_Word *> &result, int numparams, int begin, int end)
 {
 	int pos = 0;
@@ -153,5 +248,5 @@
 		if (token.indexOf("[", 0) != -1)
 		{
-			fL_Branch *word = new fL_Branch(fL_Branch::BranchType::OPEN);
+			fL_Branch *word = new fL_Branch(fL_Branch::BranchType::OPEN, begin, end);
 			result.push_back(word);
 			branchcount++;
@@ -168,13 +263,11 @@
 				return 1;
 			}
-			fL_Branch *word = new fL_Branch(fL_Branch::BranchType::CLOSE);
+			fL_Branch *word = new fL_Branch(fL_Branch::BranchType::CLOSE, begin, end);
 			result.push_back(word);
 			branchcount--;
 			continue;
 		}
-		SString wordn;
-		int tokpos = 0;
-		// if word name cannot be extracted, then return error
-		if (!token.getNextToken(tokpos, wordn, '('))
+		fL_Word *word = NULL;
+		if (createWord(token, word, numparams, begin, end) != 0)
 		{
 			SString message = "Error during parsing words sequence:  ";
@@ -183,58 +276,35 @@
 			return 1;
 		}
-		std::string wordname = fL_Builder::trimSpaces(wordn.c_str());
-		// if word cannot be found in available words, then return error
-		if (words.find(wordname) == words.end())
-		{
-			SString message = "Word '";
-			message += wordname.c_str();
-			message += "' in sequence does not exist";
-			logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
-			return 1;
-		}
-
-		// create new word and assign parameters
-		fL_Word *word = new fL_Word();
-
-		*word = *words[wordname];
-
-		// if word has parameters
-		if (word->npar > 0)
-		{
-			// create ParamObject that will hold parameter data
-			word->data = ParamObject::makeObject(word->tab);
-			Param par(word->tab);
-			par.select(word->data);
-			par.setDefault();
-			ParamInterface::LoadOptions opts;
-
-			SString temp;
-			temp = token.substr(tokpos);
-			temp = temp.substr(0, temp.len() - 1);
-			// load parameters from string
-			par.load(ParamInterface::FormatSingleLine, temp, &opts);
-			for (int i = 0; i < par.getPropCount(); i++)
-			{
-				// create MathEvaluation object to check if string contained by
-				// parameter is valid
-				double tmp;
-				MathEvaluation *eval = new MathEvaluation(numparams);
-				SString seq = par.getString(i);
-				// if string is empty, then evaluate this with 0
-				if (seq.len() == 0)
-				{
-					eval->evaluate("0", tmp);
-				}
-				// if sequence could not be evaluated, then return error
-				else if (eval->evaluate(seq.c_str(), tmp) != 0)
-				{
-					SString message = "Word in sequence has invalid parameter:  ";
-					message += temp;
+		if (word->name == "C")
+		{
+			Param par(word->tab, word->data);
+			SString attr = par.getStringById(FL_PE_CONN_ATTR);
+			if (attr.indexOf("$t", 0) != -1)
+			{
+				logMessage("fL_Builder", "tokenize", LOG_ERROR, "Attractor definition cannot contain time variable");
+				delete word;
+				return 1;
+
+			}
+			if (attr != "")
+			{
+				fL_Word *attrword = NULL;
+				if (createWord(attr, attrword, numparams, begin, end) != 0)
+				{
+					SString message = "Error during parsing attractor word:  ";
+					message += attr;
 					logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
-					delete eval;
 					delete word;
+					if (attrword) delete attrword;
 					return 1;
 				}
-				word->parevals.push_back(eval);
+				if (attrword->builtin)
+				{
+					logMessage("fL_Builder", "tokenize", LOG_ERROR, "Attractor words cannot be built-in");
+					delete word;
+					delete attrword;
+					return 1;
+				}
+				delete attrword;
 			}
 		}
@@ -272,4 +342,6 @@
 		parevals = src.parevals;
 
+		builtin = src.builtin;
+
 		data = NULL; // properties cannot be copied
 	}
@@ -303,4 +375,5 @@
 
 	builder->words[this->name.c_str()] = this;
+	builder->wordnames.push_back(this->name.c_str());
 	return 0;
 }
@@ -318,7 +391,18 @@
 	*objpred = *builder->words[predecessor.c_str()];
 
+	if (objpred->builtin)
+	{
+		logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Builtin words cannot be predecessors");
+		return 1;
+	}
+
 	// parse condition
 	if (condition != "")
 	{
+		if (objpred->builtin && (objpred->name == "N" || objpred->name == "C"))
+		{
+			logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Rules with neuron/connection word predecessors cannot contain conditions");
+			return 1;
+		}
 		std::string cond = condition.c_str();
 		condeval = new MathEvaluation(objpred->npar);
@@ -340,5 +424,5 @@
 	}
 
-	if (builder->tokenize(successor, objsucc, objpred->npar) != 0)
+	if (builder->tokenize(successor, objsucc, objpred->npar, begin, end) != 0)
 	{
 		logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Unable to process successor sequence");
@@ -353,5 +437,5 @@
 {
 	// tokenize axiom
-	if (tokenize(axiom, genotype, 0) != 0)
+	if (tokenize(axiom, genotype, 0, begin, end) != 0)
 	{
 		logMessage("fL_Builder", "processDefinition", LOG_ERROR, "Unable to process axiom sequence");
@@ -372,24 +456,24 @@
 	switch (type)
 	{
-	case fLElementType::TERM:
-	{
-		tab = fL_word_paramtab;
-		obj = new fL_Word();
-		break;
-	}
-	case fLElementType::INFO:
-	{
-		tab = fL_builder_paramtab;
-		obj = this;
-		break;
-	}
-	case fLElementType::RULE:
-	{
-		tab = fL_rule_paramtab;
-		obj = new fL_Rule(begin, end);
-		break;
-	}
-	default:
-		break;
+		case fLElementType::TERM:
+		{
+			tab = fL_word_paramtab;
+			obj = new fL_Word();
+			break;
+		}
+		case fLElementType::INFO:
+		{
+			tab = fL_builder_paramtab;
+			obj = this;
+			break;
+		}
+		case fLElementType::RULE:
+		{
+			tab = fL_rule_paramtab;
+			obj = new fL_Rule(begin, end);
+			break;
+		}
+		default:
+			break;
 	}
 	Param par(tab);
@@ -417,10 +501,10 @@
 	stick->name = "S";
 	stick->npar = 8;
-	for (int i = 0; i < PART_PROPS_COUNT; i++)
+	for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
 	{
 		stick->mut.addProperty(NULL, fL_part_names[i], "s", fL_part_fullnames[i], fL_part_fullnames[i], PARAM_CANOMITNAME, 0, -1);
 	}
 
-	for (int i = 0; i < JOINT_PROPS_COUNT; i++)
+	for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
 	{
 		stick->mut.addProperty(NULL, fL_joint_names[i], "s", fL_joint_fullnames[i], fL_joint_fullnames[i], PARAM_CANOMITNAME, 0, -1);
@@ -430,4 +514,5 @@
 	stick->tab = ParamObject::makeParamTab((ParamInterface *)&stick->mut, 0, 0, stick->mut.firstMutableIndex());
 	words["S"] = stick;
+	wordnames.push_back("S");
 
 	// neuron N
@@ -435,7 +520,8 @@
 	neuron->name = "N";
 	neuron->npar = 1;
-	neuron->mut.addProperty(NULL, "d", "s", "details", "details", PARAM_CANOMITNAME, 0, -1);
+	neuron->mut.addProperty(NULL, "d", "s", "details", "details", 0, 0, -1);
 	neuron->tab = ParamObject::makeParamTab((ParamInterface *)&neuron->mut, 0, 0, neuron->mut.firstMutableIndex());
 	words["N"] = neuron;
+	wordnames.push_back("N");
 
 	// connection C
@@ -443,8 +529,9 @@
 	connection->name = "C";
 	connection->npar = 2;
-	connection->mut.addProperty(NULL, "w", "s", "weight", "weight", PARAM_CANOMITNAME, 0, -1);
-	connection->mut.addProperty(NULL, "attr", "s", "attractor", "connection attractor", PARAM_CANOMITNAME, 0, -1);
+	connection->mut.addProperty(NULL, FL_PE_CONN_WEIGHT, "s", "weight", "weight", PARAM_CANOMITNAME, 0, -1);
+	connection->mut.addProperty(NULL, FL_PE_CONN_ATTR, "s", "attractor", "connection attractor", PARAM_CANOMITNAME, 0, -1);
 	connection->tab = ParamObject::makeParamTab((ParamInterface *)&connection->mut, 0, 0, connection->mut.firstMutableIndex());
 	words["C"] = connection;
+	wordnames.push_back("C");
 
 	// rotation objects
@@ -463,4 +550,9 @@
 	rotz->npar = 1;
 	rotz->processDefinition(this);
+
+	//fL_Branch *branch = new fL_Branch(fL_Branch::BranchType::OPEN, 0, 0);
+	//branch->processDefinition(this);
+
+	builtincount = words.size();
 }
 
@@ -521,4 +613,9 @@
 				return res;
 			}
+			if (obj == this)
+			{
+				begin = lastpos;
+				end = pos - 1;
+			}
 			res = obj->processDefinition(this);
 			if (res != 0)
@@ -546,4 +643,5 @@
 		for (int i = 0; i < npar; i++)
 		{
+			SString t(par.id(i));
 			if (parevals[i] != NULL)
 			{
@@ -667,4 +765,5 @@
 	}
 	getStringifiedProducts();
+	removeRedundantRules();
 	Param par(fL_builder_paramtab);
 	fL_Builder *obj = new fL_Builder();
@@ -681,5 +780,5 @@
 }
 
-int fL_Rule::deploy(fL_Word *in, std::list<fL_Word *>::iterator &it, std::list<fL_Word *> &genotype, double currtime)
+int fL_Rule::deploy(fL_Builder *builder, fL_Word *in, std::list<fL_Word *>::iterator &it, double currtime)
 {
 	// if predecessor and given word differ, then rule is not applicable
@@ -720,9 +819,9 @@
 
 	// remove predecessor word from genotype and replace it with successor
-	it = genotype.erase(it);
+	it = builder->genotype.erase(it);
 	for (std::list<fL_Word *>::iterator word = objsucc.begin(); word != objsucc.end(); word++)
 	{
 		// create new word and copy properties from word definition
-		fL_Word *nword = new fL_Word();
+		fL_Word *nword = new fL_Word(false, begin, end);
 		*nword = **word;
 		// store information about when word has been created
@@ -735,11 +834,46 @@
 		// calculate word parameters and store MathEvaluation objects for further
 		// time manipulations.
+		Param par((*word)->tab, (*word)->data);
+		Param npar(nword->tab, nword->data);
 		for (int q = 0; q < nword->npar; q++)
 		{
 			if ((*word)->parevals[q] == NULL)
 			{
-				MathEvaluation *ev = new MathEvaluation(0);
-				ev->convertString("0");
-				nword->parevals.push_back(ev);
+				if ((*word)->builtin && (strcmp(npar.id(q), "d") == 0))
+				{
+					SString t = par.getString(q);
+					npar.setString(q, t);
+					nword->parevals.push_back(NULL);
+				}
+				if ((*word)->builtin && (strcmp(npar.id(q), FL_PE_CONN_ATTR) == 0))
+				{
+					SString t = par.getString(q);
+					if (t.len() > 0)
+					{
+						fL_Word *attrword = NULL;
+						builder->createWord(t, attrword, in->npar, begin, end);
+						for (int j = 0; j < attrword->npar; j++)
+						{
+							if (attrword->parevals[j])
+							{
+								for (int i = 0; i < in->npar; i++)
+								{
+									attrword->parevals[j]->modifyVariable(i, inwordvalues[i]);
+								}
+							}
+						}
+						SString res = attrword->stringify(false);
+						npar.setString(q, res);
+						nword->parevals.push_back(NULL);
+						delete attrword;
+					}
+				}
+				else
+				{
+					//MathEvaluation *ev = new MathEvaluation(0);
+					//ev->convertString("0");
+					//nword->parevals.push_back(ev);
+					nword->parevals.push_back(NULL);
+				}
 			}
 			else
@@ -757,5 +891,5 @@
 			}
 		}
-		genotype.insert(it, nword);
+		builder->genotype.insert(it, nword);
 	}
 	delete[] inwordvalues;
@@ -773,5 +907,5 @@
 		for (fL_Rule * rule : rules)
 		{
-			if (rule->deploy((*word), word, genotype, currtime) == 0)
+			if (rule->deploy(this, (*word), word, currtime) == 0)
 			{
 				deployed = true;
@@ -806,16 +940,20 @@
 	Param par(stickword->tab, stickword->data);
 	Param ppar = part->properties();
-	for (int i = 0; i < PART_PROPS_COUNT; i++)
-	{
-		double partprop;
+	for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
+	{
+		double mn, mx, df;
+		ppar.getMinMaxDouble(ppar.findId(fL_part_names[i]), mn, mx, df);
 		double currval;
-		if (!ExtValue::parseDouble(par.getStringById(fL_part_names[i]).c_str(), currval, false))
-		{
-			logMessage("fL_Builder", "alterPartProperties", LOG_ERROR,
-				"Error parsing word parameter");
-			return 1;
-		}
-		partprop = (ppar.getDoubleById(fL_part_names[i]) * alterationcount +
-			currval) / (alterationcount + 1.0);
+		if (!stickword->parevals[i])
+		{
+			currval = df;
+		}
+		else
+		{
+			stickword->parevals[i]->evaluateRPN(currval);
+			currval = sigmoidTransform(currval, mn, mx);
+		}
+		double partprop = (ppar.getDoubleById(fL_part_names[i]) * alterationcount +
+				currval) / (alterationcount + 1.0);
 		ppar.setDoubleById(fL_part_names[i], partprop);
 	}
@@ -823,84 +961,88 @@
 }
 
-bool fL_Word::operator==(const fL_Word& other) const
-{
-	if (name == other.name) return false;
-	if (npar == other.npar) return false;
+double fL_Word::distance(fL_Word *right)
+{
+	if (name != right->name || npar != right->npar)
+	{
+		return -1;
+	}
+	double distance = 0;
 	for (int i = 0; i < npar; i++)
 	{
-		if (parevals[i] && other.parevals[i])
-		{
-			double first;
-			double second;
-			parevals[i]->evaluateRPN(first);
-			other.parevals[i]->evaluateRPN(second);
-			if (first != second)
-			{
-				return false;
-			}
-		}
-		else
-		{
-			return false;
-		}
-	}
-	return true;
-}
-
-void fL_Builder::findNext(fL_Word *word, std::list<fL_Word *> genotype, std::list<fL_Word *>::iterator &it)
-{
-	int rightdist = 0;
-	std::list<fL_Word *>::iterator iter(it);
-	for (; iter != genotype.end(); iter++)
-	{
-		if (*word == **iter)
-		{
-			break;
-		}
-		rightdist++;
-	}
-	int leftdist = 0;
-	std::list<fL_Word *>::reverse_iterator riter(it);
-	for (; riter != genotype.rend(); riter++)
-	{
-		if (*word == **riter)
-		{
-			break;
-		}
-		leftdist++;
-	}
-	if (iter == genotype.end())
-	{
-		it = riter.base();
-	}
-	else if (riter == genotype.rend())
-	{
-		it = iter;
-	}
-	else if (leftdist < rightdist)
-	{
-		it = riter.base();
+		double l = 0;
+		double r = 0;
+		if (parevals[i]) parevals[i]->evaluateRPN(l);
+		if (right->parevals[i]) right->parevals[i]->evaluateRPN(r);
+		distance += (l - r) * (l - r);
+	}
+	return sqrt(distance);
+}
+
+Neuro *fL_Builder::findInputNeuron(std::pair<std::list<fL_Word *>::iterator, Neuro *> currneu, fL_Word *attractor)
+{
+	if (!attractor)
+	{
+		std::list<fL_Word *>::reverse_iterator riter(currneu.first);
+		std::list<fL_Word *>::iterator iter(currneu.first);
+		iter++;
+		while (riter != genotype.rend() || iter != genotype.end())
+		{
+			if (iter != genotype.end())
+			{
+				if ((*iter)->name == "N" && (*iter)->bodyelementpointer != currneu.second)
+				{
+					return (Neuro *)(*iter)->bodyelementpointer;
+				}
+				iter++;
+			}
+			if (riter != genotype.rend())
+			{
+				if ((*riter)->name == "N" && (*riter)->bodyelementpointer != currneu.second)
+				{
+					return (Neuro *)(*riter)->bodyelementpointer;
+				}
+				riter++;
+			}
+		}
+		return NULL;
 	}
 	else
 	{
-		it = iter;
-	}
-}
-
-Neuro *fL_Builder::findInputNeuron(std::list<fL_Word *> genotype, std::list<fL_Word *>::iterator it, fL_Word *attractor)
-{
-	if (!attractor)
-	{
-		attractor = new fL_Word();
-		*attractor = *(*it);
-		attractor->data = NULL;
-		// TODO implement
+		double mindistance = -1;
+		std::list<fL_Word *>::iterator minit = genotype.end();
+		for (std::list<fL_Word *>::iterator it = genotype.begin(); it != genotype.end(); it++)
+		{
+			double currdist = attractor->distance((*it));
+			if (currdist != -1 && (currdist < mindistance || mindistance == -1))
+			{
+				mindistance = currdist;
+				minit = it;
+			}
+		}
+		if (minit != genotype.end())
+		{
+			for (; minit != genotype.end(); minit++)
+			{
+				if ((*minit)->name == "N" && (*minit)->bodyelementpointer)
+				{
+					Neuro *n = (Neuro *)(*minit)->bodyelementpointer;
+					if (n->getClass()->getPreferredOutput() != 0)
+					{
+						return n;
+					}
+				}
+			}
+		}
 	}
 	return NULL;
 }
 
-int fL_Builder::developModel(Model &model)
-{
-	// TODO implement
+double fL_Builder::sigmoidTransform(double input, double mn, double mx)
+{
+	return mn + (mx - mn) * (1.0 / (1.0 + exp(-input)));
+}
+
+int fL_Builder::buildModelFromSequence(Model *model)
+{
 	fL_State currstate;
 	std::unordered_map<Part *, double> counters;
@@ -923,5 +1065,6 @@
 						firstpart->p = Pt3D_0;
 						counters[firstpart] = 0;
-						model.addPart(firstpart);
+						model->addPart(firstpart);
+						if (using_mapping) firstpart->addMapping(IRange(word->begin, word->end));
 					}
 					currstate.currpart = firstpart;
@@ -941,32 +1084,52 @@
 				Param par(word->tab, word->data);
 				double length;
-				if (!ExtValue::parseDouble(par.getStringById("l").c_str(), length, false))
-				{
-					delete newpart;
-					logMessage("fL_Builder", "alterPartProperties", LOG_ERROR,
-						"Error parsing word parameter");
-					return 1;
+				if (!word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT])
+				{
+					length = FL_DEFAULT_LENGTH; // default length value
+				}
+				else
+				{
+					double parsedval = 0.0;
+					if (word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT]->evaluateRPN(parsedval) != 0)
+					{
+						delete newpart;
+						logMessage("fL_Builder", "developModel", LOG_ERROR,
+								"Error parsing word parameter");
+						return 1;
+					}
+					length = sigmoidTransform(parsedval, FL_MINIMAL_LENGTH, FL_MAXIMAL_LENGTH);
 				}
 				newpart->p = currstate.currpart->p + currstate.direction * length;
 				counters[newpart] += 1;
-				model.addPart(newpart);
-
+				model->addPart(newpart);
+				if (using_mapping) newpart->addMapping(IRange(word->begin, word->end));
 				Joint *newjoint = new Joint();
 				newjoint->attachToParts(currstate.currpart, newpart);
 
 				Param jpar = newjoint->properties();
-				for (int i = 0; i < JOINT_PROPS_COUNT; i++)
-				{
+				for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
+				{
+					double mn, mx, df;
+					jpar.getMinMaxDouble(jpar.findId(fL_joint_names[i]), mn, mx, df);
 					double jointprop;
-					if (!ExtValue::parseDouble(par.getStringById(fL_joint_names[i]).c_str(), jointprop, false))
+					if (!word->parevals[FL_PART_PROPS_COUNT + i])
 					{
-						logMessage("fL_Builder", "developModel", LOG_ERROR,
-							"Error parsing word parameter");
-						delete newjoint;
-						return 1;
+						jointprop = df; // assign default value
+					}
+					else
+					{
+						if (word->parevals[FL_PART_PROPS_COUNT + i]->evaluateRPN(jointprop) != 0)
+						{
+							logMessage("fL_Builder", "developModel", LOG_ERROR,
+									"Error parsing word parameter");
+							delete newjoint;
+							return 1;
+						}
+						jointprop = sigmoidTransform(jointprop, mn, mx);
 					}
 					jpar.setDoubleById(fL_joint_names[i], jointprop);
 				}
-				model.addJoint(newjoint);
+				model->addJoint(newjoint);
+				if (using_mapping) newjoint->addMapping(IRange(word->begin, word->end));
 				currstate.currpart = newpart;
 			}
@@ -975,5 +1138,10 @@
 				Param npar(word->tab, word->data);
 				Neuro *neu = new Neuro();
-				neu->setDetails(npar.getStringById("d"));
+				SString details = npar.getStringById("d");
+				if (details == "")
+				{
+					details = "N";
+				}
+				neu->setDetails(details);
 				if (!neu->getClass())
 				{
@@ -982,33 +1150,46 @@
 					return 1;
 				}
-				model.addNeuro(neu);
-				currstate.currneuron = neu;
+				model->addNeuro(neu);
+				if (using_mapping) neu->addMapping(IRange(word->begin, word->end));
+				if (neu->getClass()->getPreferredInputs() != 0)
+				{
+					currstate.currneuron = neu;
+				}
+				word->bodyelementpointer = neu;
 			}
 			else if (word->name == "C")
 			{
-				connsbuffer.push_back({ w, currstate.currneuron });
+				connsbuffer.push_back({w, currstate.currneuron});
 			}
 			else if (word->name.startsWith("rot"))
 			{
-				Orient rotmatrix;
+				Orient rotmatrix = Orient_1;
 				double rot;
-				if (word->parevals[0]->evaluateRPN(rot) != 0)
-				{
-					logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing neuron class");
+				if (!word->parevals[0])
+				{
+					rot = 0;
+				}
+				else if (word->parevals[0]->evaluateRPN(rot) != 0)
+				{
+					logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing rotation word");
 					return 1;
 				}
+
+				rot = sigmoidTransform(rot, -M_PI, M_PI);
+
 				if (word->name == "rotX")
 				{
-					rotmatrix.rotate(Pt3D(rot, 0.0, 0.0));
+					rotmatrix.rotate(Pt3D(rot,0,0));
 				}
 				else if (word->name == "rotY")
 				{
-					rotmatrix.rotate(Pt3D(0.0, rot, 0.0));
+					rotmatrix.rotate(Pt3D(0,rot,0));
 				}
 				else if (word->name == "rotZ")
 				{
-					rotmatrix.rotate(Pt3D(0.0, 0.0, rot));
+					rotmatrix.rotate(Pt3D(0,0,rot));
 				}
 				currstate.direction = rotmatrix.transform(currstate.direction);
+				currstate.direction.normalize();
 			}
 			else if (word->name == "[")
@@ -1025,18 +1206,83 @@
 
 	// connections need
-	//	for (std::pair<std::list<fL_Word *>::iterator, Neuro *> conndata : connsbuffer)
-	//	{
-	//
-	//	}
+	// std::pair<std::list<fL_Word *>::iterator, Neuro *> conndata : connsbuffer
+	for (unsigned int i = 0; i < connsbuffer.size(); i++)
+	{
+		if (connsbuffer[i].second == NULL ||
+				(connsbuffer[i].second->getClass()->getPreferredInputs() != -1 &&
+				connsbuffer[i].second->getInputCount() >=
+				connsbuffer[i].second->getClass()->getPreferredInputs()))
+		{
+			// since connections are separated entities from neurons, it may happen
+			// that there will be no neuron to connect to
+			// logMessage("fL_Builder", "developModel", LOG_DEBUG, "Connection could not be established");
+		}
+		else
+		{
+			Param par((*connsbuffer[i].first)->tab, (*connsbuffer[i].first)->data);
+			SString attr = par.getStringById(FL_PE_CONN_ATTR);
+			fL_Word *attractor = NULL;
+			if (attr.len() > 0)
+			{
+				createWord(attr, attractor, 0, (*connsbuffer[i].first)->begin, (*connsbuffer[i].first)->end);
+			}
+			Neuro *neu = findInputNeuron(connsbuffer[i], attractor);
+			double weight = 0.0;
+			if ((*connsbuffer[i].first)->parevals[0])
+			{
+				if ((*connsbuffer[i].first)->parevals[0]->evaluateRPN(weight) != 0)
+				{
+					logMessage("fL_Builder", "developModel", LOG_ERROR,
+							"Error parsing word parameter");
+					delete attractor;
+					return 1;
+				}
+			}
+			if (neu)
+			{
+				connsbuffer[i].second->addInput(neu, weight);
+				if (using_mapping) neu->addMapping(
+						IRange((*connsbuffer[i].first)->begin,
+								(*connsbuffer[i].first)->end));
+			}
+			else
+			{
+				connsbuffer[i].second->addInput(connsbuffer[i].second, weight);
+				if (using_mapping) neu->addMapping(
+						IRange((*connsbuffer[i].first)->begin,
+								(*connsbuffer[i].first)->end));
+			}
+			delete attractor;
+		}
+	}
 	return 0;
 }
 
-int fL_Builder::develop()
-{
-	Model m;
+void fL_Builder::clearModelElements(Model *m)
+{
+	for (int i = 0; i < m->getJointCount(); i++)
+	{
+		m->removeJoint(i, 0);
+	}
+	for (int i = 0; i < m->getNeuroCount(); i++)
+	{
+		m->removeNeuro(i, true);
+	}
+	for (int i = 0; i < m->getNeuroCount(); i++)
+	{
+		m->removePart(i, 0, 0);
+	}
+	m->clearMap();
+}
+
+Model* fL_Builder::developModel(double &neededtime)
+{
 	double curriter = 0;
 	double timestamp = 1.0 / numckp;
 	double t = 0;
-	for (; t <= time; t += timestamp)
+	Model *m = new Model();
+	m->open(using_checkpoints);
+	bool wordsexceeded = false;
+	for (; t <= time; t+= timestamp)
 	{
 		alterTimedProperties(t); // always alter timed properties in the beginning
@@ -1045,7 +1291,32 @@
 		{
 			iterate(t);
-			curriter += 1.0;
-		}
-	}
+			curriter+=1.0;
+		}
+		if (using_checkpoints)
+		{
+			clearModelElements(m);
+			if (buildModelFromSequence(m) != 0)
+			{
+				delete m;
+				return NULL;
+			}
+			m->checkpoint();
+		}
+		if (maxwords != -1 && genotype.size() > (unsigned int)maxwords)
+		{
+			wordsexceeded = true;
+			break;
+		}
+	}
+
+	if (wordsexceeded)
+	{
+		neededtime = t;
+	}
+	else
+	{
+		neededtime = time;
+	}
+
 	// if exact time of development was not reached due to floating point errors,
 	// then alter timed properties
@@ -1054,4 +1325,86 @@
 		alterTimedProperties(time);
 	}
-	return 0;
-}
+	clearModelElements(m);
+	if (buildModelFromSequence(m) != 0)
+	{
+		delete m;
+		return NULL;
+	}
+	if (using_checkpoints)
+	{
+		m->checkpoint();
+	}
+	m->close();
+	return m;
+}
+
+int fL_Builder::countSticksInSequence(std::list<fL_Word *> sequence)
+{
+	int count = 0;
+	for (std::list<fL_Word *>::iterator it = sequence.begin(); it != sequence.end(); it++)
+	{
+		if ((*it)->builtin && (*it)->name == "S")
+		{
+			count++;
+		}
+	}
+	return count;
+}
+
+int fL_Builder::countDefinedWords()
+{
+	return words.size() - builtincount;
+}
+
+int fL_Builder::countWordsInLSystem()
+{
+	int count = genotype.size();
+	for (fL_Rule *rul: rules)
+	{
+		count += rul->objsucc.size();
+	}
+	count += words.size();
+	return count;
+}
+
+void fL_Builder::removeRedundantRules()
+{
+	for (std::vector<fL_Rule *>::iterator it = rules.begin();
+			it != rules.end(); it++)
+	{
+		std::vector<fL_Rule *>::iterator it2 = it;
+		it2++;
+		while (it2 != rules.end())
+		{
+			bool todelete = false;
+			if ((*it)->objpred->name == (*it2)->objpred->name)
+			{
+				if ((*it)->condeval == NULL && (*it2)->condeval == NULL)
+				{
+					todelete = true;
+				}
+				else if ((*it)->condeval == NULL && (*it2)->condeval != NULL)
+				{
+					std::iter_swap(it, it2);
+				}
+				else if ((*it)->condeval != NULL && (*it2)->condeval != NULL)
+				{
+					if ((*it)->condeval->getStringifiedRPN() ==
+							(*it2)->condeval->getStringifiedRPN())
+					{
+						todelete = true;
+					}
+				}
+			}
+			if (todelete)
+			{
+				delete (*it2);
+				it2 = rules.erase(it2);
+			}
+			else
+			{
+				it2++;
+			}
+		}
+	}
+}
