Index: cpp/frams/genetics/f4/conv_f4.cpp
===================================================================
--- cpp/frams/genetics/f4/conv_f4.cpp	(revision 777)
+++ 	(revision )
@@ -1,352 +1,0 @@
-// This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
-// See LICENSE.txt for details.
-
-// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
-// Copyright (C) since 2001 Maciej Komosinski
-// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
-
-#include "conv_f4.h"
-#include <common/log.h>
-#include "../oper_fx.h" //for GENOPER_OK constant
-
-#ifdef DMALLOC
-#include <dmalloc.h>
-#endif
-
-
-GenoConv_f40::GenoConv_f40()
-{
-	name = "Developmental encoding";
-	in_format = '4';
-	out_format = '0';
-	mapsupport = 1;
-}
-
-
-SString GenoConv_f40::convert(SString &in, MultiMap *map, bool using_checkpoints)
-{
-	int res;
-	f4_Model *model = new f4_Model();
-	res = model->buildFromF4(in, using_checkpoints);
-	if (GENOPER_OK != res) return SString();  // oops
-	if (NULL != map)
-		// generate to-f0 conversion map
-		model->getCurrentToF0Map(*map);
-	SString out = model->getF0Geno().getGenes();
-	delete model;
-	return out;
-}
-
-
-GenoConv_F41_TestOnly::GenoConv_F41_TestOnly()
-{
-	name = "test-only approximate f4 -> f1 converter";
-	in_format = '4';
-	out_format = '1';
-	mapsupport = 0;
-	info = "This is for testing.  Do not use in production! (adam)";
-}
-
-
-SString GenoConv_F41_TestOnly::convert(SString &in, MultiMap *map, bool using_checkpoints)
-{
-	int res;
-	f4_Model *model = new f4_Model();
-	res = model->buildFromF4(in, using_checkpoints);
-	if (GENOPER_OK != res) return SString();  // oops
-	SString out;
-	model->toF1Geno(out);
-	delete model;
-	return out;
-}
-
-
-f4_Model::f4_Model() : Model()
-{
-	cells = NULL;
-}
-
-f4_Model::~f4_Model()
-{
-	if (cells) delete cells;
-}
-
-int f4_Model::buildFromF4(SString &geno, bool using_checkpoints)
-{
-	int i;
-
-	error = GENOPER_OK;
-	errorpos = -1;
-
-	// build cells, and simulate
-	if (cells) delete cells;
-	cells = new f4_Cells(geno, 0);
-	if (GENOPER_OK != cells->geterror())
-	{
-		error = cells->geterror();
-		errorpos = cells->geterrorpos();
-		//delete cells;
-		return error;
-	}
-
-	cells->simulate();
-	if (GENOPER_OK != cells->geterror())
-	{
-		error = cells->geterror();
-		errorpos = cells->geterrorpos();
-		return error;
-	}
-
-	// reset recursive traverse flags
-	for (i = 0; i < cells->nc; i++)
-		cells->C[i]->recProcessedFlag = 0;
-
-	open(using_checkpoints); // begin model build
-
-	// process every cell
-	int res;
-	for (i = 0; i < cells->nc; i++)
-	{
-		res = buildModelRec(cells->C[i]);
-		if (res)
-		{
-			logMessage("f4_Model", "buildModelRec", 2, "Error in building Model");
-			error = res;
-			break;
-		}
-	}
-
-	res = close();
-	if (0 == res) // invalid
-		error = -10;
-
-	return error;
-}
-
-
-f4_Cell* f4_Model::getStick(f4_Cell *C)
-{
-	if (T_STICK4 == C->type) return C;
-	if (NULL != C->dadlink)
-		return getStick(C->dadlink);
-	// we have no more dadlinks, find any stick
-	for (int i = 0; i < cells->nc; i++)
-		if (cells->C[i]->type == T_STICK4)
-			return cells->C[i];
-	// none!
-	logMessage("f4_Model", "getStick", 2, "Not a single stick");
-	return NULL;
-}
-
-
-/// updated by Macko to follow new SDK standards (no more neuroitems)
-int f4_Model::buildModelRec(f4_Cell *C)
-{
-	int partidx;
-	int j, res;
-	MultiRange range;
-
-	if (C->recProcessedFlag)
-		// already processed
-		return 0;
-
-	// mark it processed
-	C->recProcessedFlag = 1;
-
-	// make sure parent is a stick
-	if (NULL != C->dadlink)
-		if (C->dadlink->type != T_STICK4)
-		{
-		C->dadlink = getStick(C->dadlink);
-		}
-
-	// make sure its parent is processed first
-	if (NULL != C->dadlink)
-	{
-		res = buildModelRec(C->dadlink);
-		if (res) return res;
-	}
-
-	char tmpLine[100];
-
-	range = C->genoRange;
-	if (C->type == T_STICK4)
-	{
-		int jj_p1_refno;  // save for later
-		// first end is connected to dad, or new
-		if (C->dadlink == NULL)
-		{
-			// new part object for firstend
-			// coordinates are left to be computed by Model
-			sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
-				/*1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
-				//C->firstend.x, C->firstend.y, C->firstend.z
-				);
-			partidx = addFromString(PartType, tmpLine, &range);
-			if (partidx < 0) return -1;
-			this->checkpoint();
-			jj_p1_refno = partidx;
-		}
-		else {
-			// adjust mass/vol of first endpoint
-			jj_p1_refno = C->dadlink->p2_refno;
-			Part *p1 = getPart(jj_p1_refno);
-			p1->mass += 1.0;
-			//      p1->volume += 1.0/C->P.mass;
-		}
-		// new part object for lastend
-		sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
-			//C->lastend.x, C->lastend.y, C->lastend.z
-			/*"vol=" 1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
-			);
-		partidx = addFromString(PartType, tmpLine, &range);
-		if (partidx < 0) return -2;
-		C->p2_refno = partidx;
-
-		// new joint object
-		// check that the part references are valid
-		int jj_p2_refno = C->p2_refno;
-		if ((jj_p1_refno < 0) || (jj_p1_refno >= getPartCount())) return -11;
-		if ((jj_p2_refno < 0) || (jj_p2_refno >= getPartCount())) return -12;
-		sprintf(tmpLine, "p1=%ld,p2=%ld,dx=%g,dy=0,dz=0,rx=%g,ry=0,rz=%g"\
-			",stam=%g",
-			jj_p1_refno, jj_p2_refno,
-			// relative position -- always (len, 0, 0), along the stick
-			// this is optional!
-			C->P.length,
-			// relative rotation
-			C->xrot, C->zrot,
-			//C->P.ruch,   // rotstif
-			C->P.stamina
-			);
-		partidx = addFromString(JointType, tmpLine, &range);
-		if (partidx < 0) return -13;
-		this->checkpoint();
-		C->joint_refno = partidx;
-	}
-
-	if (C->type == T_NEURON4) ///<this case was updated by MacKo
-	{
-		const char* nclass = C->neuclass->name.c_str();
-		int partno, jointno;
-		if (C->neuclass->getPreferredLocation() == 0)
-		{
-			if (strcmp(nclass, "N") == 0)
-			{
-				partno = C->dadlink->p2_refno;
-				if ((partno < 0) || (partno >= getPartCount())) return -21;
-				else sprintf(tmpLine, "p=%ld,d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo);
-			}
-			else
-			{
-				sprintf(tmpLine, "d=\"%s\"", nclass);
-			}
-			partidx = addFromString(NeuronType, tmpLine, &range);
-			if (partidx < 0) return -22;
-			this->checkpoint();
-			C->neuro_refno = partidx;
-		}
-		else if (C->neuclass->getPreferredLocation() == 1) // attached to Part or have no required attachment - also part
-		{
-			partno = C->dadlink->p2_refno;
-			if ((partno < 0) || (partno >= getPartCount())) return -21;
-			if (strcmp(nclass, "N") == 0)
-			{
-				sprintf(tmpLine, "d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo);
-			}
-			else
-			{
-				sprintf(tmpLine, "p=%ld,d=\"%s\"", partno, nclass);
-			}
-			partidx = addFromString(NeuronType, tmpLine, &range);
-			if (partidx < 0) return -22;
-			this->checkpoint();
-			C->neuro_refno = partidx;
-		}
-		else // attached to Joint
-		{
-			jointno = C->dadlink->joint_refno;
-			sprintf(tmpLine, "n:j=%d,d=\"%s\"", jointno, nclass);
-			partidx = addFromString(NeuronType, tmpLine, &range);
-			if (partidx < 0) return -32;
-			this->checkpoint();
-		}
-		C->neuro_refno = partidx;
-		int n_refno = C->neuro_refno;
-
-		if ((strcmp(nclass,"N") == 0) && C->ctrl)
-		{
-			if (1 == C->ctrl)
-				sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", C->dadlink->joint_refno, C->P.muscle_power);
-			else
-				sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", C->dadlink->joint_refno, C->P.muscle_power, C->mz);
-			partidx = addFromString(NeuronType, tmpLine, &range);
-			if (partidx < 0) return -32;
-			sprintf(tmpLine, "%d,%d", partidx, n_refno);
-			if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -33;
-			this->checkpoint();
-		}
-
-		for (j = 0; j < C->nolink; j++)
-		{
-			if (NULL != C->links[j]->from)
-				buildModelRec(C->links[j]->from);
-
-			tmpLine[0] = 0;
-			if (C->links[j]->from == NULL)
-			{
-				const char* nclass = C->links[j]->t.c_str();
-				char* temp = (char*)C->links[j]->t.c_str();
-				NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp);
-				//backward compatibility for G*TS
-				if (C->links[j]->t == "*" || C->links[j]->t == "S" || C->links[j]->t == "T")
-				{
-					sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
-				}
-				else if (C->links[j]->t == "G")
-				{
-					jointno = C->dadlink->joint_refno;
-					sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
-				}				
-				else if (sensortest->getPreferredLocation() == 0)
-				{
-					sprintf(tmpLine, "d=\"%s\"",nclass);
-				}
-				else if (sensortest->getPreferredLocation() == 1)
-				{
-					sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
-				}
-				else
-				{
-					jointno = C->dadlink->joint_refno;
-					sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
-				}
-
-			}
-			int from = -1;
-			if (tmpLine[0]) //input from receptor
-			{
-				from = addFromString(NeuronType, tmpLine, &range);
-				if (from < 0) return -34;
-				this->checkpoint();
-			} /*could be 'else'...*/
-
-			if (NULL != C->links[j]->from) // input from another neuron
-				from = C->links[j]->from->neuro_refno;
-			if (from >= 0)
-			{
-				sprintf(tmpLine, "%d,%d,%g", n_refno, from, C->links[j]->w);
-				if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -35;
-				this->checkpoint();
-			}
-		}
-	}
-	return 0;
-}
-
-
-void f4_Model::toF1Geno(SString &out)
-{
-	cells->toF1Geno(out);
-}
Index: cpp/frams/genetics/f4/conv_f4.h
===================================================================
--- cpp/frams/genetics/f4/conv_f4.h	(revision 777)
+++ 	(revision )
@@ -1,95 +1,0 @@
-// This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
-// See LICENSE.txt for details.
-
-// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
-// Copyright (C) since 2001 Maciej Komosinski
-// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
-
-#ifndef _CONV_F4_H_
-#define _CONV_F4_H_
-
-#include <frams/model/model.h>
-#include <frams/model/modelparts.h>
-#include <frams/genetics/genoconv.h>
-#include "f4_general.h"
-
-
-/**
- * Genotype converter from f4 to f0.
- */
-class GenoConv_f40 : public GenoConverter
-{
-public:
-	GenoConv_f40();
-
-	/**
-	 * Performs conversion from f4 to f0. Creates f4_Model from f4 genotype
-	 * and converts the Model to the f0 genotype string.
-	 * @param in f4 genotype
-	 * @param map mapping from f4 to Model
-	 * @param using_checkpoints determines if checkpoints will be available
-	 * @return generated f0 genotype
-	 */
-	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
-};
-
-
-/**
- * Genotype converter from f4 to f1. This is only experimental conversion and
- * returns an approximate f1 genotype.
- */
-class GenoConv_F41_TestOnly : public GenoConverter
-{
-public:
-	/**
-	 * Initializes converter.
-	 */
-	GenoConv_F41_TestOnly();
-
-	/**
-	 * Performs conversion from f4 to f1. Creates f4_Model from f4 genotype
-	 * and converts the Model to the f1 genotype string. The final f1 genotype is
-	 * an approximation.
-	 * @param in f4 genotype
-	 * @param map mapping from f4 to Model
-	 * @param using_checkpoints determines if checkpoints will be available
-	 * @return generated approximate f1 genotype
-	 */
-	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
-};
-
-
-/**
- * A Model descendant which supports building from an f4 genotype.
- */
-class f4_Model : public Model
-{
-public:
-	f4_Model();
-	~f4_Model();
-
-	/**
-	 * Builds a Model from the f4 genotype string.
-	 * @param geno genotype string
-	 * @return GENOPER_OK if a Model could be created, error code otherwise
-	 */
-	int      buildFromF4(SString &geno, bool using_checkpoints);
-	/**
-	 * Outputs a Model in f1 format. It is an approximation of the input f4 genotype.
-	 * @param out the reference that stores the conversion result
-	 */
-	void     toF1Geno(SString &out);       // output to f1 format, approximation
-private:
-	f4_Cells *cells;
-	int        buildModelRec(f4_Cell *ndad);
-	/**
-	 * Get a cell which is a stick, by traversing dadlinks.
-	 */
-	f4_Cell* getStick(f4_Cell *C);
-	int        error;
-	int        errorpos;
-};
-
-
-#endif
Index: cpp/frams/genetics/f4/f4_conv.cpp
===================================================================
--- cpp/frams/genetics/f4/f4_conv.cpp	(revision 779)
+++ cpp/frams/genetics/f4/f4_conv.cpp	(revision 779)
@@ -0,0 +1,352 @@
+// This file is a part of Framsticks SDK.  http://www.framsticks.com/
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
+// See LICENSE.txt for details.
+
+// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
+// Copyright (C) since 2001 Maciej Komosinski
+// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
+
+#include "f4_conv.h"
+#include <common/log.h>
+#include "../genooperators.h" //for GENOPER_OK constant
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+GenoConv_f40::GenoConv_f40()
+{
+	name = "Developmental encoding";
+	in_format = '4';
+	out_format = '0';
+	mapsupport = 1;
+}
+
+
+SString GenoConv_f40::convert(SString &in, MultiMap *map, bool using_checkpoints)
+{
+	int res;
+	f4_Model *model = new f4_Model();
+	res = model->buildFromF4(in, using_checkpoints);
+	if (GENOPER_OK != res) return SString();  // oops
+	if (NULL != map)
+		// generate to-f0 conversion map
+		model->getCurrentToF0Map(*map);
+	SString out = model->getF0Geno().getGenes();
+	delete model;
+	return out;
+}
+
+
+GenoConv_F41_TestOnly::GenoConv_F41_TestOnly()
+{
+	name = "test-only approximate f4 -> f1 converter";
+	in_format = '4';
+	out_format = '1';
+	mapsupport = 0;
+	info = "This is for testing.  Do not use in production! (adam)";
+}
+
+
+SString GenoConv_F41_TestOnly::convert(SString &in, MultiMap *map, bool using_checkpoints)
+{
+	int res;
+	f4_Model *model = new f4_Model();
+	res = model->buildFromF4(in, using_checkpoints);
+	if (GENOPER_OK != res) return SString();  // oops
+	SString out;
+	model->toF1Geno(out);
+	delete model;
+	return out;
+}
+
+
+f4_Model::f4_Model() : Model()
+{
+	cells = NULL;
+}
+
+f4_Model::~f4_Model()
+{
+	if (cells) delete cells;
+}
+
+int f4_Model::buildFromF4(SString &geno, bool using_checkpoints)
+{
+	int i;
+
+	error = GENOPER_OK;
+	errorpos = -1;
+
+	// build cells, and simulate
+	if (cells) delete cells;
+	cells = new f4_Cells(geno, 0);
+	if (GENOPER_OK != cells->geterror())
+	{
+		error = cells->geterror();
+		errorpos = cells->geterrorpos();
+		//delete cells;
+		return error;
+	}
+
+	cells->simulate();
+	if (GENOPER_OK != cells->geterror())
+	{
+		error = cells->geterror();
+		errorpos = cells->geterrorpos();
+		return error;
+	}
+
+	// reset recursive traverse flags
+	for (i = 0; i < cells->nc; i++)
+		cells->C[i]->recProcessedFlag = 0;
+
+	open(using_checkpoints); // begin model build
+
+	// process every cell
+	int res;
+	for (i = 0; i < cells->nc; i++)
+	{
+		res = buildModelRec(cells->C[i]);
+		if (res)
+		{
+			logMessage("f4_Model", "buildModelRec", 2, "Error in building Model");
+			error = res;
+			break;
+		}
+	}
+
+	res = close();
+	if (0 == res) // invalid
+		error = -10;
+
+	return error;
+}
+
+
+f4_Cell* f4_Model::getStick(f4_Cell *C)
+{
+	if (T_STICK4 == C->type) return C;
+	if (NULL != C->dadlink)
+		return getStick(C->dadlink);
+	// we have no more dadlinks, find any stick
+	for (int i = 0; i < cells->nc; i++)
+		if (cells->C[i]->type == T_STICK4)
+			return cells->C[i];
+	// none!
+	logMessage("f4_Model", "getStick", 2, "Not a single stick");
+	return NULL;
+}
+
+
+/// updated by Macko to follow new SDK standards (no more neuroitems)
+int f4_Model::buildModelRec(f4_Cell *C)
+{
+	int partidx;
+	int j, res;
+	MultiRange range;
+
+	if (C->recProcessedFlag)
+		// already processed
+		return 0;
+
+	// mark it processed
+	C->recProcessedFlag = 1;
+
+	// make sure parent is a stick
+	if (NULL != C->dadlink)
+		if (C->dadlink->type != T_STICK4)
+		{
+		C->dadlink = getStick(C->dadlink);
+		}
+
+	// make sure its parent is processed first
+	if (NULL != C->dadlink)
+	{
+		res = buildModelRec(C->dadlink);
+		if (res) return res;
+	}
+
+	char tmpLine[100];
+
+	range = C->genoRange;
+	if (C->type == T_STICK4)
+	{
+		int jj_p1_refno;  // save for later
+		// first end is connected to dad, or new
+		if (C->dadlink == NULL)
+		{
+			// new part object for firstend
+			// coordinates are left to be computed by Model
+			sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
+				/*1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
+				//C->firstend.x, C->firstend.y, C->firstend.z
+				);
+			partidx = addFromString(PartType, tmpLine, &range);
+			if (partidx < 0) return -1;
+			this->checkpoint();
+			jj_p1_refno = partidx;
+		}
+		else {
+			// adjust mass/vol of first endpoint
+			jj_p1_refno = C->dadlink->p2_refno;
+			Part *p1 = getPart(jj_p1_refno);
+			p1->mass += 1.0;
+			//      p1->volume += 1.0/C->P.mass;
+		}
+		// new part object for lastend
+		sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
+			//C->lastend.x, C->lastend.y, C->lastend.z
+			/*"vol=" 1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
+			);
+		partidx = addFromString(PartType, tmpLine, &range);
+		if (partidx < 0) return -2;
+		C->p2_refno = partidx;
+
+		// new joint object
+		// check that the part references are valid
+		int jj_p2_refno = C->p2_refno;
+		if ((jj_p1_refno < 0) || (jj_p1_refno >= getPartCount())) return -11;
+		if ((jj_p2_refno < 0) || (jj_p2_refno >= getPartCount())) return -12;
+		sprintf(tmpLine, "p1=%ld,p2=%ld,dx=%g,dy=0,dz=0,rx=%g,ry=0,rz=%g"\
+			",stam=%g",
+			jj_p1_refno, jj_p2_refno,
+			// relative position -- always (len, 0, 0), along the stick
+			// this is optional!
+			C->P.length,
+			// relative rotation
+			C->xrot, C->zrot,
+			//C->P.ruch,   // rotstif
+			C->P.stamina
+			);
+		partidx = addFromString(JointType, tmpLine, &range);
+		if (partidx < 0) return -13;
+		this->checkpoint();
+		C->joint_refno = partidx;
+	}
+
+	if (C->type == T_NEURON4) ///<this case was updated by MacKo
+	{
+		const char* nclass = C->neuclass->name.c_str();
+		int partno, jointno;
+		if (C->neuclass->getPreferredLocation() == 0)
+		{
+			if (strcmp(nclass, "N") == 0)
+			{
+				partno = C->dadlink->p2_refno;
+				if ((partno < 0) || (partno >= getPartCount())) return -21;
+				else sprintf(tmpLine, "p=%ld,d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo);
+			}
+			else
+			{
+				sprintf(tmpLine, "d=\"%s\"", nclass);
+			}
+			partidx = addFromString(NeuronType, tmpLine, &range);
+			if (partidx < 0) return -22;
+			this->checkpoint();
+			C->neuro_refno = partidx;
+		}
+		else if (C->neuclass->getPreferredLocation() == 1) // attached to Part or have no required attachment - also part
+		{
+			partno = C->dadlink->p2_refno;
+			if ((partno < 0) || (partno >= getPartCount())) return -21;
+			if (strcmp(nclass, "N") == 0)
+			{
+				sprintf(tmpLine, "d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo);
+			}
+			else
+			{
+				sprintf(tmpLine, "p=%ld,d=\"%s\"", partno, nclass);
+			}
+			partidx = addFromString(NeuronType, tmpLine, &range);
+			if (partidx < 0) return -22;
+			this->checkpoint();
+			C->neuro_refno = partidx;
+		}
+		else // attached to Joint
+		{
+			jointno = C->dadlink->joint_refno;
+			sprintf(tmpLine, "n:j=%d,d=\"%s\"", jointno, nclass);
+			partidx = addFromString(NeuronType, tmpLine, &range);
+			if (partidx < 0) return -32;
+			this->checkpoint();
+		}
+		C->neuro_refno = partidx;
+		int n_refno = C->neuro_refno;
+
+		if ((strcmp(nclass,"N") == 0) && C->ctrl)
+		{
+			if (1 == C->ctrl)
+				sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", C->dadlink->joint_refno, C->P.muscle_power);
+			else
+				sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", C->dadlink->joint_refno, C->P.muscle_power, C->mz);
+			partidx = addFromString(NeuronType, tmpLine, &range);
+			if (partidx < 0) return -32;
+			sprintf(tmpLine, "%d,%d", partidx, n_refno);
+			if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -33;
+			this->checkpoint();
+		}
+
+		for (j = 0; j < C->nolink; j++)
+		{
+			if (NULL != C->links[j]->from)
+				buildModelRec(C->links[j]->from);
+
+			tmpLine[0] = 0;
+			if (C->links[j]->from == NULL)
+			{
+				const char* nclass = C->links[j]->t.c_str();
+				char* temp = (char*)C->links[j]->t.c_str();
+				NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp);
+				//backward compatibility for G*TS
+				if (C->links[j]->t == "*" || C->links[j]->t == "S" || C->links[j]->t == "T")
+				{
+					sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
+				}
+				else if (C->links[j]->t == "G")
+				{
+					jointno = C->dadlink->joint_refno;
+					sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
+				}				
+				else if (sensortest->getPreferredLocation() == 0)
+				{
+					sprintf(tmpLine, "d=\"%s\"",nclass);
+				}
+				else if (sensortest->getPreferredLocation() == 1)
+				{
+					sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
+				}
+				else
+				{
+					jointno = C->dadlink->joint_refno;
+					sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
+				}
+
+			}
+			int from = -1;
+			if (tmpLine[0]) //input from receptor
+			{
+				from = addFromString(NeuronType, tmpLine, &range);
+				if (from < 0) return -34;
+				this->checkpoint();
+			} /*could be 'else'...*/
+
+			if (NULL != C->links[j]->from) // input from another neuron
+				from = C->links[j]->from->neuro_refno;
+			if (from >= 0)
+			{
+				sprintf(tmpLine, "%d,%d,%g", n_refno, from, C->links[j]->w);
+				if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -35;
+				this->checkpoint();
+			}
+		}
+	}
+	return 0;
+}
+
+
+void f4_Model::toF1Geno(SString &out)
+{
+	cells->toF1Geno(out);
+}
Index: cpp/frams/genetics/f4/f4_conv.h
===================================================================
--- cpp/frams/genetics/f4/f4_conv.h	(revision 779)
+++ cpp/frams/genetics/f4/f4_conv.h	(revision 779)
@@ -0,0 +1,95 @@
+// This file is a part of Framsticks SDK.  http://www.framsticks.com/
+// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// See LICENSE.txt for details.
+
+// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
+// Copyright (C) since 2001 Maciej Komosinski
+// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
+
+#ifndef _F4_CONV_H_
+#define _F4_CONV_H_
+
+#include <frams/model/model.h>
+#include <frams/model/modelparts.h>
+#include <frams/genetics/genoconv.h>
+#include "f4_general.h"
+
+
+/**
+ * Genotype converter from f4 to f0.
+ */
+class GenoConv_f40 : public GenoConverter
+{
+public:
+	GenoConv_f40();
+
+	/**
+	 * Performs conversion from f4 to f0. Creates f4_Model from f4 genotype
+	 * and converts the Model to the f0 genotype string.
+	 * @param in f4 genotype
+	 * @param map mapping from f4 to Model
+	 * @param using_checkpoints determines if checkpoints will be available
+	 * @return generated f0 genotype
+	 */
+	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
+};
+
+
+/**
+ * Genotype converter from f4 to f1. This is only experimental conversion and
+ * returns an approximate f1 genotype.
+ */
+class GenoConv_F41_TestOnly : public GenoConverter
+{
+public:
+	/**
+	 * Initializes converter.
+	 */
+	GenoConv_F41_TestOnly();
+
+	/**
+	 * Performs conversion from f4 to f1. Creates f4_Model from f4 genotype
+	 * and converts the Model to the f1 genotype string. The final f1 genotype is
+	 * an approximation.
+	 * @param in f4 genotype
+	 * @param map mapping from f4 to Model
+	 * @param using_checkpoints determines if checkpoints will be available
+	 * @return generated approximate f1 genotype
+	 */
+	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
+};
+
+
+/**
+ * A Model descendant which supports building from an f4 genotype.
+ */
+class f4_Model : public Model
+{
+public:
+	f4_Model();
+	~f4_Model();
+
+	/**
+	 * Builds a Model from the f4 genotype string.
+	 * @param geno genotype string
+	 * @return GENOPER_OK if a Model could be created, error code otherwise
+	 */
+	int      buildFromF4(SString &geno, bool using_checkpoints);
+	/**
+	 * Outputs a Model in f1 format. It is an approximation of the input f4 genotype.
+	 * @param out the reference that stores the conversion result
+	 */
+	void     toF1Geno(SString &out);       // output to f1 format, approximation
+private:
+	f4_Cells *cells;
+	int        buildModelRec(f4_Cell *ndad);
+	/**
+	 * Get a cell which is a stick, by traversing dadlinks.
+	 */
+	f4_Cell* getStick(f4_Cell *C);
+	int        error;
+	int        errorpos;
+};
+
+
+#endif
Index: cpp/frams/genetics/f4/f4_general.cpp
===================================================================
--- cpp/frams/genetics/f4/f4_general.cpp	(revision 777)
+++ cpp/frams/genetics/f4/f4_general.cpp	(revision 779)
@@ -7,5 +7,5 @@
 
 #include "f4_general.h"
-#include "../oper_fx.h" //for GENOPER_ constants
+#include "../genooperators.h" //for GENOPER_ constants
 #include <common/nonstd_stl.h>
 #include <common/log.h>
Index: cpp/frams/genetics/f4/f4_oper.cpp
===================================================================
--- cpp/frams/genetics/f4/f4_oper.cpp	(revision 779)
+++ cpp/frams/genetics/f4/f4_oper.cpp	(revision 779)
@@ -0,0 +1,754 @@
+// This file is a part of Framsticks SDK.  http://www.framsticks.com/
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
+// See LICENSE.txt for details.
+
+// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
+// Copyright (C) since 2001 Maciej Komosinski
+// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
+
+#include "f4_oper.h"
+#include <frams/util/sstring.h>
+#include <common/log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "common/nonstd_math.h"
+#include <string.h>
+
+
+const char *Geno_f4::all_modifiers = F14_MODIFIERS ","; //comma in f4 is handled the same way (simple node, F4_ADD_SIMP) as modifiers
+
+// codes that can be changed (apart from being added/deleted)
+#define MUT_CHAN_CODES "<[#"
+#define REP_MAXCOUNT 19
+
+#define FIELDSTRUCT Geno_f4
+
+static ParamEntry GENO4param_tab[] =
+{
+	{ "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + 1, },
+	{ "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "mutation: probability of adding a node", },
+	{ "f4_mut_add_div", 0, 0, "- add division", "f 0 100 20", FIELD(probadd[F4_ADD_DIV]), "add node mutation: probability of adding a division", },
+	{ "f4_mut_add_conn", 0, 0, "- add connection", "f 0 100 15", FIELD(probadd[F4_ADD_CONN]), "add node mutation: probability of adding a neural connection", },
+	{ "f4_mut_add_neupar", 0, 0, "- add neuron property", "f 0 100 5", FIELD(probadd[F4_ADD_NEUPAR]), "add node mutation: probability of adding a neuron property/modifier", },
+	{ "f4_mut_add_rep", 0, 0, "- add repetition", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "add node mutation: probability of adding a repetition", },
+	{ "f4_mut_add_simp", 0, 0, "- add simple node", "f 0 100 50", FIELD(probadd[F4_ADD_SIMP]), "add node mutation: probability of adding a random, simple gene", },
+	{ "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "mutation: probability of deleting a node", },
+	{ "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "mutation: probability of changing a node", },
+	{ "f4_mut_exmod", 1, 0, "Excluded modifiers", "s 0 30", FIELD(excluded_modifiers), "Modifiers that will not be added nor deleted during mutation\n(all: " F14_MODIFIERS ")", },
+	{ 0, },
+};
+
+#undef FIELDSTRUCT
+
+
+Geno_f4::Geno_f4()
+{
+	supported_format = '4';
+	par.setParamTab(GENO4param_tab);
+	par.select(this);
+	par.setDefault();
+
+	mutation_method_names = new const char*[F4_COUNT + F4_ADD_COUNT - 1];
+	int index = 0;
+	mutation_method_names[index++] = "added division";
+	mutation_method_names[index++] = "added neural connection";
+	mutation_method_names[index++] = "added neuron property";
+	mutation_method_names[index++] = "added repetition gene";
+	mutation_method_names[index++] = "added a simple node";
+	mutation_method_names[index++] = "deleted a node";
+	mutation_method_names[index++] = "modified a node";
+	if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", 3, "Mutation names init error");
+}
+
+void Geno_f4::setDefaults()
+{
+	excluded_modifiers = F14_MODIFIERS_RARE F14_MODIFIERS_VISUAL;
+}
+
+int Geno_f4::ValidateRec(f4_node *geno, int retrycount) const
+{
+	// ! the genotype is geno->child (not geno) !
+	// build from it with repair on
+
+	f4_Cells cells(geno->child, 1);
+	cells.simulate();  //we should simulate?!
+
+	// errors not fixed:
+	if (GENOPER_OPFAIL == cells.geterror())
+	{
+		if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
+		return GENOPER_OPFAIL;
+	}
+	// errors can be fixed
+	if (GENOPER_REPAIR == cells.geterror())
+	{
+		cells.repairGeno(geno, 1);
+		// note: geno might have been fixed
+		// check again
+		int res2 = GENOPER_OK;
+		if (retrycount > 0)
+			res2 = ValidateRec(geno, retrycount - 1);
+
+		if (res2 == GENOPER_OK) return GENOPER_REPAIR;
+		return res2;
+	}
+	// no errors:
+	return GENOPER_OK;
+}
+
+
+int Geno_f4::validate(char *& geno, const char *genoname)
+{
+	// convert geno to tree, then try to validate 20 times
+	f4_node root;
+	if (f4_processrec(geno, 0, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair
+	if (ValidateRec(&root, 20) == GENOPER_REPAIR) // if repaired, make it back to string
+	{
+		geno[0] = 0;
+		root.child->sprintAdj(geno);
+	}
+	return GENOPER_OK;
+}
+
+
+int Geno_f4::checkValidity(const char* geno, const char *genoname)
+{
+	f4_node root;
+	int res = f4_processrec(geno, 0, &root);
+	if (res) return res;  // errorpos, >0
+	if (root.childCount() != 1) return 1; //earlier: GENOPER_OPFAIL
+	f4_Cells cells(root.child, 0);
+	cells.simulate();
+	if (cells.geterror() == GENOPER_OPFAIL || cells.geterror() == GENOPER_REPAIR)
+	{
+		if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
+		else return 1; //earlier: GENOPER_OPFAIL;
+	}
+	else return GENOPER_OK;
+}
+
+
+int Geno_f4::MutateOne(f4_node *& g, int &method) const
+{
+	// ! the genotype is g->child (not g) !
+
+	// do the mutation
+	// pick a random node
+	f4_node *n1 = g->child->randomNode();
+	vector<NeuroClass*> neulist;
+	//DB( printf("%c\n", n1->name); )
+	int neuronid = -1;
+
+	switch (roulette(prob, F4_COUNT))
+	{
+	case F4_ADD:
+	{
+		// add a node
+		switch (method = roulette(probadd, F4_ADD_COUNT))
+		{
+		case F4_ADD_DIV:
+		{
+			// add division ('<')
+			f4_node *n3 = n1->parent;
+			n3->removeChild(n1);
+			f4_node *n2 = new f4_node('<', n3, n3->pos);
+			n2->addChild(n1);
+			// new cell is stick or neuron
+			// "X>" or "N>"
+			f4_node *n5 = NULL;
+			double pr = rnd01;
+			pr -= 0.5;
+			if (pr < 0) n5 = new f4_node('X', n2, n2->pos);
+			else
+			{
+				// make neuron
+				NeuroClass *rndclass = GenoOperators::getRandomNeuroClass();
+				if (rndclass == NULL)
+				{
+					n5 = new f4_node('X', n2, n2->pos);
+				}
+				else
+				{
+					f4_node *n4 = new f4_node(rndclass->getName().c_str(), n2, n2->pos);
+					if (rndclass->getPreferredInputs() != 0)
+					{
+						neuronid = -1;
+						for (int i = 0; i < g->count(); i++)
+						{
+							f4_node *gcur = g->ordNode(i);
+							char* temp = (char*)gcur->name.c_str();
+							NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
+							if (neuclass != NULL)
+							{
+								neulist.push_back(neuclass);
+							}
+							if (g->ordNode(i) == n3)
+							{
+								neuronid = neulist.size() - 1;
+							}
+						}
+						if (neuronid == -1)
+						{
+							return GENOPER_OPFAIL;
+						}
+						n5 = new f4_node('[', n4, n2->pos);
+						linkNodeMakeRandom(n5, neuronid, neulist);
+					}
+					else {
+						n5 = n4;
+					}
+				}
+			}
+			new f4_node('>', n5, n5->pos);
+			n1->parent = n2;
+			// now with 50% chance swap children
+			if (randomN(2) == 0)
+			{
+				n3 = n2->child;
+				n2->child = n2->child2;
+				n2->child2 = n3;
+			}
+		}
+			break;
+		case F4_ADD_CONN:
+		{
+			// add link
+			f4_node *par = n1->parent;
+			char* temp = (char*)par->name.c_str();
+			NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
+			if (neuclass != NULL)
+			{
+				n1->parent->removeChild(n1);
+				f4_node *n2 = new f4_node('[', n1->parent, n1->parent->pos);
+				n2->addChild(n1);
+				n1->parent = n2;
+				neuronid = -1;
+				for (int i = 0; i < g->count(); i++)
+				{
+					f4_node *gcur = g->ordNode(i);
+					temp = (char*)gcur->name.c_str();
+					NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
+					if (neuclass != NULL)
+					{
+						neulist.push_back(neuclass);
+					}
+					if (gcur == par)
+					{
+						neuronid = neulist.size() - 1;
+					}
+				}
+				if (neuronid == -1)
+				{
+					return GENOPER_OPFAIL;
+				}
+				linkNodeMakeRandom(n2, neuronid, neulist);
+			}
+		}
+			break;
+		case F4_ADD_NEUPAR:
+		{
+			// add neuron modifier
+			n1->parent->removeChild(n1);
+			f4_node *n2 = new f4_node(':', n1->parent, n1->parent->pos);
+			nparNodeMakeRandom(n2);
+			n2->addChild(n1);
+			n1->parent = n2;
+		}
+			break;
+		case F4_ADD_REP:
+		{
+			// add repetition ('#')
+			// repeated code (left child) is the original, right child is empty, count is 2
+			f4_node *n3 = n1->parent;
+			n3->removeChild(n1);
+			f4_node *n2 = new f4_node('#', n3, n3->pos);
+			n2->i1 = 2;
+			n2->addChild(n1);
+			new f4_node('>', n2, n2->pos);
+			n1->parent = n2;
+		}
+			break;
+		case F4_ADD_SIMP:
+		{
+			// add simple node
+			// choose a simple node from ADD_SIMPLE_CODES
+			n1->parent->removeChild(n1);
+			//f4_node *n2 = new f4_node(ADD_SIMPLE_CODES[randomN(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
+			int modifierid = GenoOperators::getRandomChar(all_modifiers, excluded_modifiers.c_str());
+			f4_node *n2 = new f4_node(all_modifiers[modifierid], n1->parent, n1->parent->pos);
+			n2->addChild(n1);
+			n1->parent = n2;
+		}
+			break;
+		}
+	}
+		break;
+
+	case F4_DEL:
+	{
+		method = F4_ADD_COUNT - 1 + F4_DEL;
+		// delete a node
+		// must pick a node with parent, and at least one child
+		// already picked a node, but repeat may be needed
+		for (int i = 0; i < 10; i++)
+		{
+			if ((NULL != n1->parent) && (g != n1->parent))
+				if (NULL != n1->child)
+					break;
+			// try a new one
+			n1 = g->child->randomNode();
+		}
+		if ((NULL != n1->parent) && (g != n1->parent))
+		{
+			switch (n1->childCount())
+			{
+			case 0: break;
+			case 1:  // one child
+			{
+				f4_node *n2 = n1->parent;
+				n2->removeChild(n1);
+				if (NULL != n1->child)
+				{
+					n1->child->parent = n2;
+					n2->addChild(n1->child);
+					n1->child = NULL;
+				}
+				if (NULL != n1->child2)
+				{
+					n1->child2->parent = n2;
+					n2->addChild(n1->child2);
+					n1->child2 = NULL;
+				}
+				// destroy n1
+				n1->parent = NULL;
+				delete n1;
+			}
+				break;
+
+			case 2:  // two children
+			{
+				// two children
+				f4_node *n2 = n1->parent;
+				n2->removeChild(n1);
+				// n1 has two children. pick one randomly 50-50, destroy other
+				if (randomN(2) == 0)
+				{
+					n1->child->parent = n2;
+					n2->addChild(n1->child);
+					n1->child = NULL;
+					n1->child2->parent = NULL;
+				}
+				else
+				{
+					n1->child2->parent = n2;
+					n2->addChild(n1->child2);
+					n1->child2 = NULL;
+					n1->child->parent = NULL;
+				}
+				// destroy n1
+				n1->parent = NULL;
+				delete n1;
+			}
+				break;
+			}
+		}
+		else return GENOPER_OPFAIL;
+	}
+		break;
+	case F4_MOD:
+	{
+		method = F4_ADD_COUNT - 1 + F4_MOD;
+		// change a node
+		// the only nodes that are modifiable are MUT_CHAN_CODES
+		// try to get a modifiable node
+		// already picked a node, but repeat may be needed
+		int i = 0;
+		while (1)
+		{
+			if (strchr(MUT_CHAN_CODES, n1->name[0])) break;
+			// try a new one
+			n1 = g->child->randomNode();
+			i++;
+			if (i >= 20) return GENOPER_OPFAIL;
+		}
+		switch (n1->name[0])
+		{
+		case '<':
+		{
+			// swap children
+			f4_node *n2 = n1->child; n1->child = n1->child2; n1->child2 = n2;
+		}
+			break;
+		case '[':
+		{
+			neuronid = -1;
+			for (int i = 0; i < g->count(); i++)
+			{
+				f4_node *gcur = g->ordNode(i);
+				char *temp = (char*)gcur->name.c_str();
+				NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
+				if (neuclass != NULL)
+				{
+					neulist.push_back(neuclass);
+				}
+				if (gcur == n1)
+				{
+					neuronid = neulist.size() - 1;
+				}
+			}
+			if (neuronid == -1)
+			{
+				return GENOPER_OPFAIL;
+			}
+			linkNodeChangeRandom(n1, neuronid, neulist);
+		}
+			break;
+
+		case '#':
+		{
+			repeatNodeChangeRandom(n1);
+		}
+			break;
+		}
+	}
+		break;
+
+	default: //no mutations allowed?
+		return GENOPER_OPFAIL;
+	}
+
+	return GENOPER_OK;
+}
+
+// make a random [ node
+void Geno_f4::linkNodeMakeRandom(f4_node *nn, int neuid, vector<NeuroClass*> neulist) const
+{
+	float prob1;
+	NeuroClass *nc = NULL;
+
+	// 35% chance one of *GTS
+	prob1 = rnd01;
+	prob1 -= 0.35f;
+	if (prob1 < 0)
+	{
+		// '*', 'G', 'T', or 'S', 1/4 chance each
+		nc = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs();
+	}
+	if (nc != NULL)
+	{
+		nn->i1 = 1;
+		nn->s1 = nc->getName().c_str();
+		nn->l1 = 0;
+	}
+	else
+	{
+		// relative input link
+		int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
+		int relid = neuid - id;
+		nn->l1 = relid;
+		//nn->l1 = (int)(4.0f * (rnd01 - 0.5f));
+	}
+	// weight
+	nn->f1 = GenoOperators::mutateNeuProperty(nn->f1, NULL, -1);
+	//nn->f1 = 10.0f * (rnd01 - 0.5f);
+}
+
+// change a [ node
+void Geno_f4::linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const      //rewritten by M.K. - should work as before (not tested)
+{
+	double probs[3] = { 0.1, 0.3, 0.6 };
+	NeuroClass *cl;
+	// 10% change type
+	// 30% change link
+	// 60% change weight
+
+	switch (roulette(probs, 3))
+	{
+	case 0: // change type
+		// 80% for link, 20% for random sensor
+		if (rnd01 < 0.2f)
+		{
+			cl = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs();
+			if (cl != NULL)
+			{
+				nn->i1 = 1;
+				nn->s1 = cl->name.c_str();
+				nn->l1 = 0;
+			}
+		}
+		break;
+	case 1: // change link
+		if (0 == nn->i1) // relative input link
+		{
+			int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
+			nn->l1 = neuid - id;
+		}
+		//nn->l1 += (int)(2.0f * (rnd01 - 0.5f));
+		break;
+	case 2: // change weight
+		nn->f1 = GenoOperators::mutateNeuProperty(nn->f1, NULL, -1);
+		//nn->f1 += 1.0f * (rnd01 - 0.5f);
+		break;
+	}
+}
+
+// make a random : node
+void Geno_f4::nparNodeMakeRandom(f4_node *nn) const
+{
+	int sign = (int)(2.0f * rnd01);
+	int param = (int)(3.0f * rnd01);
+	if (param > 2) param = 2;
+	nn->l1 = sign;
+	nn->i1 = "!=/"[param];
+}
+
+// change a repeat # node
+void Geno_f4::repeatNodeChangeRandom(f4_node *nn) const
+{
+	int count;
+	float prob1;
+
+	// change count
+	count = nn->i1;
+	prob1 = rnd01;
+	if (prob1 < 0.5f) count++;
+	else count--;
+	if (count < 1) count = 1;
+	if (count > REP_MAXCOUNT) count = REP_MAXCOUNT;
+	nn->i1 = count;
+}
+
+
+int Geno_f4::MutateOneValid(f4_node *& g, int &method) const
+// mutate one, until a valid genotype is obtained
+{
+	// ! the genotype is g->child (not g) !
+	int i, res;
+	f4_node *gcopy = NULL;
+	// try this max 20 times:
+	//   copy, mutate, then validate
+
+	for (i = 0; i < 20; i++)
+	{
+		gcopy = g->duplicate();
+
+		res = MutateOne(gcopy, method);
+
+		if (GENOPER_OK != res)
+		{
+			// mutation failed, try again
+			delete gcopy;
+			continue;  // for
+		}
+		// try to validate it
+		res = ValidateRec(gcopy, 10);
+		// accept if it is OK, or was repaired
+		if (GENOPER_OK == res)
+			//(GENOPER_REPAIR == res)
+		{
+			// destroy the original one
+			g->destroy();
+			// make it the new one
+			*g = *gcopy;
+			gcopy->child = NULL;
+			gcopy->child2 = NULL;
+			delete gcopy;
+			res = GENOPER_OK;
+			goto retm1v;
+		}
+		delete gcopy;
+	}
+	// attempts failed
+	res = GENOPER_OPFAIL;
+retm1v:
+	return res;
+}
+
+
+int Geno_f4::mutate(char *& g, float & chg, int &method)
+{
+	f4_node *root = new f4_node;
+	if (f4_processrec(g, 0, root) || root->childCount() != 1)
+	{
+		delete root;
+		return GENOPER_OPFAIL;
+	} // could not convert or bad: fail
+	// mutate one node, set chg as this percent
+	chg = 1.0 / float(root->child->count());
+	if (MutateOneValid(root, method) != GENOPER_OK)
+	{
+		delete root;
+		return GENOPER_OPFAIL;
+	}
+	// OK, convert back to string
+	g[0] = 0;
+	root->child->sprintAdj(g);
+	delete root;
+	return GENOPER_OK;
+}
+
+
+/*
+int Geno_f4::MutateMany(char *& g, float & chg)
+// check if original is valid, then
+// make a number of mutations
+{
+int res, n, i;
+int totNodes = 0;
+int maxToMut = 0;
+
+// convert to tree
+f4_node *root;
+root = new f4_node();
+res = f4_processrec(g, 0, root);
+if (res) {
+// could not convert, fail
+goto retm;
+}
+if (1 != root->childCount()) {
+res = GENOPER_OPFAIL;
+goto retm;
+}
+
+// check if original is valid
+res = ValidateRec( root, 20 );
+// might have been repaired!
+if (GENOPER_REPAIR==res) {
+res = GENOPER_OK;
+}
+if (GENOPER_OK != res) {
+goto retm;
+}
+
+// decide number of nodes to mutate
+// decide maximum number of nodes to mutate: 0.25*nodes, min 2
+totNodes = root->child->count();
+maxToMut = (int)( 0.25f * totNodes);
+if (maxToMut<2) maxToMut=2;
+if (maxToMut>totNodes) maxToMut=totNodes;
+
+// decide number of nodes to mutate
+n = (int)( 0.5f + rnd01 * maxToMut );
+if (n<1) n=1;
+if (n>totNodes) n=totNodes;
+// set chg as this percent
+chg = ((float)n) / ((float)totNodes);
+for (i=0; i<n; i++)
+{
+res = MutateOneValid(root);
+if (GENOPER_OK != res)
+{
+res = GENOPER_OPFAIL;
+goto retm;
+}
+}
+// OK, convert back to string
+g[0]=0;
+root->child->sprintAdj(g);
+retm:
+delete root;
+return res;
+}
+*/
+
+
+int Geno_f4::CrossOverOne(f4_node *g1, f4_node *g2, float chg) const
+{
+	// ! the genotypes are g1->child and g2->child (not g1 g2) !
+	// single offspring in g1
+	int smin, smax;
+	float size;
+	f4_node *n1, *n2, *n1p, *n2p;
+
+	// determine desired size
+	size = (1 - chg) * (float)g1->count();
+	smin = (int)(size*0.9f - 1);
+	smax = (int)(size*1.1f + 1);
+	// get a random node with desired size
+	n1 = g1->child->randomNodeWithSize(smin, smax);
+
+	// determine desired size
+	size = (1 - chg) * (float)g2->count();
+	smin = (int)(size*0.9f - 1);
+	smax = (int)(size*1.1f + 1);
+	// get a random node with desired size
+	n2 = g2->child->randomNodeWithSize(smin, smax);
+
+	// exchange the two nodes:
+	n1p = n1->parent;
+	n2p = n2->parent;
+	n1p->removeChild(n1);
+	n1p->addChild(n2);
+	n2p->removeChild(n2);
+	n2p->addChild(n1);
+	n1->parent = n2p;
+	n2->parent = n1p;
+
+	return GENOPER_OK;
+}
+
+int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2)
+{
+	f4_node root1, root2, *copy1, *copy2;
+
+	// convert genotype strings into tree structures
+	if (f4_processrec(g1, 0, &root1) || (root1.childCount() != 1)) return GENOPER_OPFAIL;
+	if (f4_processrec(g2, 0, &root2) || (root2.childCount() != 1)) return GENOPER_OPFAIL;
+
+	// decide amounts of crossover, 0.25-0.75
+	// adam: seems 0.1-0.9 -- MacKo
+	chg1 = 0.1f + 0.8f*rnd01;
+	chg2 = 0.1f + 0.8f*rnd01;
+
+	copy1 = root1.duplicate();
+	if (CrossOverOne(copy1, &root2, chg1) != GENOPER_OK) { delete copy1; copy1 = NULL; }
+	copy2 = root2.duplicate();
+	if (CrossOverOne(copy2, &root1, chg2) != GENOPER_OK) { delete copy2; copy2 = NULL; }
+
+	g1[0] = 0;
+	g2[0] = 0;
+	if (copy1) { copy1->child->sprintAdj(g1); delete copy1; }
+	if (copy2) { copy2->child->sprintAdj(g2); delete copy2; }
+	if (g1[0] || g2[0]) return GENOPER_OK; else return GENOPER_OPFAIL;
+}
+
+uint32_t Geno_f4::style(const char *g, int pos)
+{
+	char ch = g[pos];
+
+	// style categories
+#define STYL4CAT_MODIFIC F14_MODIFIERS ","
+#define STYL4CAT_NEUMOD "[]:+-/!="
+#define STYL4CAT_NEUSPECIAL "|@*"
+#define STYL4CAT_DIGIT "0123456789."
+#define STYL4CAT_REST "XN<># "
+
+	if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch))
+	{
+		return GENSTYLE_CS(0, GENSTYLE_INVALID);
+	}
+	uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT); //default, should be changed below
+	if (strchr("X ", ch))                    style = GENSTYLE_CS(0, GENSTYLE_NONE);
+	else if (strchr("N", ch))                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
+	else if (strchr("<", ch))                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
+	else if (strchr(">", ch))                style = GENSTYLE_RGBS(0, 0, 100, GENSTYLE_NONE);
+	else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
+	else if (strchr(STYL4CAT_MODIFIC, ch))   style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
+	else if (strchr(STYL4CAT_NEUMOD, ch))    style = GENSTYLE_RGBS(0, 150, 0, GENSTYLE_NONE);
+	if (isalpha(ch) || strchr(STYL4CAT_NEUSPECIAL, ch))
+	{
+		while (pos > 0)
+		{
+			pos--;
+			if (!(isalpha(g[pos]) || strchr(STYL4CAT_NEUSPECIAL, ch)))
+			{
+				if (isupper(g[pos + 1]) && (g[pos] == ':' || g[pos] == '[')) // name of neuron class
+					style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_ITALIC);
+				else // property
+					style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
+			}
+		}
+	}
+	return style;
+}
Index: cpp/frams/genetics/f4/f4_oper.h
===================================================================
--- cpp/frams/genetics/f4/f4_oper.h	(revision 779)
+++ cpp/frams/genetics/f4/f4_oper.h	(revision 779)
@@ -0,0 +1,141 @@
+// This file is a part of Framsticks SDK.  http://www.framsticks.com/
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
+// See LICENSE.txt for details.
+
+// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
+// Copyright (C) since 2001 Maciej Komosinski
+
+#ifndef _F4_OPER_H_
+#define _F4_OPER_H_
+
+#include <stdio.h>
+#include "f4_general.h"
+#include "common/nonstd.h"
+#include "../genooperators.h"
+#include <frams/param/param.h>
+
+/** @name Codes for general mutation types */
+//@{
+#define F4_ADD           0 ///<Adding new node
+#define F4_DEL           1 ///<Deletion of node
+#define F4_MOD           2 ///<Modification of node
+#define F4_COUNT         3 ///<Count of mutation types
+//@}
+
+/** @name Codes for specific F4_ADD mutation subtypes */
+//@{
+#define F4_ADD_DIV       0
+#define F4_ADD_CONN      1
+#define F4_ADD_NEUPAR    2
+#define F4_ADD_REP       3
+#define F4_ADD_SIMP      4
+#define F4_ADD_COUNT     5
+//@}
+
+class Geno_f4 : public GenoOperators
+{
+public:
+	Geno_f4();
+	void setDefaults();
+
+	int checkValidity(const char *, const char *genoname);
+	int validate(char *&, const char *genoname);
+	int mutate(char *& g, float & chg, int &method);
+	int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
+	const char* getSimplest() { return "X"; }
+	uint32_t style(const char *g, int pos);
+
+	// mutation probabilities
+	double prob[F4_COUNT];            ///<relative probabilities of selecting mutation types in f4 genotype
+	double probadd[F4_ADD_COUNT];     ///<relative probabilities of selecting mutation addition subtypes
+
+	SString excluded_modifiers;       ///<Modifiers that are excluded in mutation process
+	static const char *all_modifiers;
+
+protected:
+
+	/**
+	 * Validates a f4 genotype. If the genotype is invalid, the genotype is repaired
+	 * and the validation is repeated. Validation is performed as long as repairing
+	 * is not effective, or the number of retries exceeded the given limit.
+	 * @param geno genotype tree
+	 * @param retrycount maximum amount of repair retries
+	 * @return GENOOPER_OK if genotype is valid, GENOPER_REPAIR if genotype can be repaired, GENOPER_OPFAIL if genotype can't be repaired
+	 */
+	int  ValidateRec(f4_node *geno, int retrycount) const;
+
+	/**
+	 * Performs mutation of an f4 genotype. The mutation is performed on a random node
+	 * from a given tree. The method of mutation is chosen via the roulette selection,
+	 * where probabilities of choosing each mutation type are given in the 'prob'
+	 * array. Possible mutation types are:
+	 *  - F4_ADD - adds new element to the genotype by:
+	 *   - F4_ADD_DIV - replacing randomly selected node with division node '<', setting this node as a child and creating new stick or neuron sibling of the selected cell (the neuron-type sibling will be connected to a random existing neuron),
+	 *   - F4_ADD_CONN - adding connection for an existing neuron,
+	 *   - F4_ADD_NEUPAR - adding neuron property to the selected node, if it is a neuron node,
+	 *   - F4_ADD_REP - adding a repetition node before a randomly selected node (the repetition node has 2 repetitions),
+	 *   - F4_ADD_SIMP - adding a simple node before a selected node,
+	 *  - F4_DEL - removes a randomly selected node (the node needs to have a parent and at least one child, otherwise returns GENOPER_OPFAIL),
+	 *  - F4_MOD - modifies one of simple nodes by:
+	 *   - '<' - swapping children in division
+	 *   - '[' - modifying connection of a neuron
+	 *   - '#' - incrementing or decrementing repetition count
+	 *
+	 * @param g input genotype; the result of mutation will be stored in this parameter
+	 * @param method reference to the variable that will get the selected method of mutation
+	 * @return GENOPER_OK if mutation was performed successfully, GENOPER_FAIL otherwise
+	 */
+	int  MutateOne(f4_node *& g, int &method) const;
+
+	/**
+	 * Creates a random connection to an existing neuron or creates an additional
+	 * sensor for a neuron.
+	 * @param nn neuron class node
+	 * @param neuid id of a neuron
+	 * @param neulist list of genotype neuron classes
+	 */
+	void linkNodeMakeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
+
+	/**
+	 * Changes connection to an existing neuron or creates an additional
+	 * sensor for neuron.
+	 * @param nn neuron connection node
+	 * @param neuid id of a neuron
+	 * @param neulist list of genotype neuron classes
+	 */
+	void linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
+
+	/**
+	 * Introduces a random modification to the neuron node.
+	 * @param nn neuron node
+	 */
+	void nparNodeMakeRandom(f4_node *nn) const;
+
+	/**
+	 * Increases or decreases the amount of repetitions in the repetition node.
+	 * @param nn repetition node
+	 */
+	void repeatNodeChangeRandom(f4_node *nn) const;
+
+	/**
+	 * Tries to perform a mutation until success. There is a maximum of 20 tries. Returns GENOPER_OK or GENOPER_OPFAIL.
+	 * @param g genotype tree
+	 * @param method reference to the variable that will get the selected method of mutation
+	 * @return GENOPER_OK if performed successful mutation, GENOPER_FAIL otherwise
+	 */
+	int  MutateOneValid(f4_node *&g, int &method) const;
+
+	/**
+	 * Performs crossover of two creatures. The 'chg' parameter determines approximately what
+	 * percentage of the first creature should form the offspring. '1-chg' is the percentage
+	 * of the second creature in the offspring.
+	 * @param g1 first parent
+	 * @param g2 second parent
+	 * @param chg percentage of the first parent in offspring (the second parent has the rest)
+	 */
+	int  CrossOverOne(f4_node *g1, f4_node *g2, float chg) const;
+};
+
+
+#endif
+
Index: cpp/frams/genetics/f4/oper_f4.cpp
===================================================================
--- cpp/frams/genetics/f4/oper_f4.cpp	(revision 777)
+++ 	(revision )
@@ -1,754 +1,0 @@
-// This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
-// See LICENSE.txt for details.
-
-// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
-// Copyright (C) since 2001 Maciej Komosinski
-// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
-
-#include "oper_f4.h"
-#include <frams/util/sstring.h>
-#include <common/log.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include "common/nonstd_math.h"
-#include <string.h>
-
-
-const char *Geno_f4::all_modifiers = F14_MODIFIERS ","; //comma in f4 is handled the same way (simple node, F4_ADD_SIMP) as modifiers
-
-// codes that can be changed (apart from being added/deleted)
-#define MUT_CHAN_CODES "<[#"
-#define REP_MAXCOUNT 19
-
-#define FIELDSTRUCT Geno_f4
-
-static ParamEntry GENO4param_tab[] =
-{
-	{ "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + 1, },
-	{ "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "mutation: probability of adding a node", },
-	{ "f4_mut_add_div", 0, 0, "- add division", "f 0 100 20", FIELD(probadd[F4_ADD_DIV]), "add node mutation: probability of adding a division", },
-	{ "f4_mut_add_conn", 0, 0, "- add connection", "f 0 100 15", FIELD(probadd[F4_ADD_CONN]), "add node mutation: probability of adding a neural connection", },
-	{ "f4_mut_add_neupar", 0, 0, "- add neuron property", "f 0 100 5", FIELD(probadd[F4_ADD_NEUPAR]), "add node mutation: probability of adding a neuron property/modifier", },
-	{ "f4_mut_add_rep", 0, 0, "- add repetition", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "add node mutation: probability of adding a repetition", },
-	{ "f4_mut_add_simp", 0, 0, "- add simple node", "f 0 100 50", FIELD(probadd[F4_ADD_SIMP]), "add node mutation: probability of adding a random, simple gene", },
-	{ "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "mutation: probability of deleting a node", },
-	{ "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "mutation: probability of changing a node", },
-	{ "f4_mut_exmod", 1, 0, "Excluded modifiers", "s 0 30", FIELD(excluded_modifiers), "Modifiers that will not be added nor deleted during mutation\n(all: " F14_MODIFIERS ")", },
-	{ 0, },
-};
-
-#undef FIELDSTRUCT
-
-
-Geno_f4::Geno_f4()
-{
-	supported_format = '4';
-	par.setParamTab(GENO4param_tab);
-	par.select(this);
-	par.setDefault();
-
-	mutation_method_names = new const char*[F4_COUNT + F4_ADD_COUNT - 1];
-	int index = 0;
-	mutation_method_names[index++] = "added division";
-	mutation_method_names[index++] = "added neural connection";
-	mutation_method_names[index++] = "added neuron property";
-	mutation_method_names[index++] = "added repetition gene";
-	mutation_method_names[index++] = "added a simple node";
-	mutation_method_names[index++] = "deleted a node";
-	mutation_method_names[index++] = "modified a node";
-	if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", 3, "Mutation names init error");
-}
-
-void Geno_f4::setDefaults()
-{
-	excluded_modifiers = F14_MODIFIERS_RARE F14_MODIFIERS_VISUAL;
-}
-
-int Geno_f4::ValidateRec(f4_node *geno, int retrycount) const
-{
-	// ! the genotype is geno->child (not geno) !
-	// build from it with repair on
-
-	f4_Cells cells(geno->child, 1);
-	cells.simulate();  //we should simulate?!
-
-	// errors not fixed:
-	if (GENOPER_OPFAIL == cells.geterror())
-	{
-		if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
-		return GENOPER_OPFAIL;
-	}
-	// errors can be fixed
-	if (GENOPER_REPAIR == cells.geterror())
-	{
-		cells.repairGeno(geno, 1);
-		// note: geno might have been fixed
-		// check again
-		int res2 = GENOPER_OK;
-		if (retrycount > 0)
-			res2 = ValidateRec(geno, retrycount - 1);
-
-		if (res2 == GENOPER_OK) return GENOPER_REPAIR;
-		return res2;
-	}
-	// no errors:
-	return GENOPER_OK;
-}
-
-
-int Geno_f4::validate(char *& geno, const char *genoname)
-{
-	// convert geno to tree, then try to validate 20 times
-	f4_node root;
-	if (f4_processrec(geno, 0, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair
-	if (ValidateRec(&root, 20) == GENOPER_REPAIR) // if repaired, make it back to string
-	{
-		geno[0] = 0;
-		root.child->sprintAdj(geno);
-	}
-	return GENOPER_OK;
-}
-
-
-int Geno_f4::checkValidity(const char* geno, const char *genoname)
-{
-	f4_node root;
-	int res = f4_processrec(geno, 0, &root);
-	if (res) return res;  // errorpos, >0
-	if (root.childCount() != 1) return 1; //earlier: GENOPER_OPFAIL
-	f4_Cells cells(root.child, 0);
-	cells.simulate();
-	if (cells.geterror() == GENOPER_OPFAIL || cells.geterror() == GENOPER_REPAIR)
-	{
-		if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
-		else return 1; //earlier: GENOPER_OPFAIL;
-	}
-	else return GENOPER_OK;
-}
-
-
-int Geno_f4::MutateOne(f4_node *& g, int &method) const
-{
-	// ! the genotype is g->child (not g) !
-
-	// do the mutation
-	// pick a random node
-	f4_node *n1 = g->child->randomNode();
-	vector<NeuroClass*> neulist;
-	//DB( printf("%c\n", n1->name); )
-	int neuronid = -1;
-
-	switch (roulette(prob, F4_COUNT))
-	{
-	case F4_ADD:
-	{
-		// add a node
-		switch (method = roulette(probadd, F4_ADD_COUNT))
-		{
-		case F4_ADD_DIV:
-		{
-			// add division ('<')
-			f4_node *n3 = n1->parent;
-			n3->removeChild(n1);
-			f4_node *n2 = new f4_node('<', n3, n3->pos);
-			n2->addChild(n1);
-			// new cell is stick or neuron
-			// "X>" or "N>"
-			f4_node *n5 = NULL;
-			double pr = rnd01;
-			pr -= 0.5;
-			if (pr < 0) n5 = new f4_node('X', n2, n2->pos);
-			else
-			{
-				// make neuron
-				NeuroClass *rndclass = GenoOperators::getRandomNeuroClass();
-				if (rndclass == NULL)
-				{
-					n5 = new f4_node('X', n2, n2->pos);
-				}
-				else
-				{
-					f4_node *n4 = new f4_node(rndclass->getName().c_str(), n2, n2->pos);
-					if (rndclass->getPreferredInputs() != 0)
-					{
-						neuronid = -1;
-						for (int i = 0; i < g->count(); i++)
-						{
-							f4_node *gcur = g->ordNode(i);
-							char* temp = (char*)gcur->name.c_str();
-							NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
-							if (neuclass != NULL)
-							{
-								neulist.push_back(neuclass);
-							}
-							if (g->ordNode(i) == n3)
-							{
-								neuronid = neulist.size() - 1;
-							}
-						}
-						if (neuronid == -1)
-						{
-							return GENOPER_OPFAIL;
-						}
-						n5 = new f4_node('[', n4, n2->pos);
-						linkNodeMakeRandom(n5, neuronid, neulist);
-					}
-					else {
-						n5 = n4;
-					}
-				}
-			}
-			new f4_node('>', n5, n5->pos);
-			n1->parent = n2;
-			// now with 50% chance swap children
-			if (randomN(2) == 0)
-			{
-				n3 = n2->child;
-				n2->child = n2->child2;
-				n2->child2 = n3;
-			}
-		}
-			break;
-		case F4_ADD_CONN:
-		{
-			// add link
-			f4_node *par = n1->parent;
-			char* temp = (char*)par->name.c_str();
-			NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
-			if (neuclass != NULL)
-			{
-				n1->parent->removeChild(n1);
-				f4_node *n2 = new f4_node('[', n1->parent, n1->parent->pos);
-				n2->addChild(n1);
-				n1->parent = n2;
-				neuronid = -1;
-				for (int i = 0; i < g->count(); i++)
-				{
-					f4_node *gcur = g->ordNode(i);
-					temp = (char*)gcur->name.c_str();
-					NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
-					if (neuclass != NULL)
-					{
-						neulist.push_back(neuclass);
-					}
-					if (gcur == par)
-					{
-						neuronid = neulist.size() - 1;
-					}
-				}
-				if (neuronid == -1)
-				{
-					return GENOPER_OPFAIL;
-				}
-				linkNodeMakeRandom(n2, neuronid, neulist);
-			}
-		}
-			break;
-		case F4_ADD_NEUPAR:
-		{
-			// add neuron modifier
-			n1->parent->removeChild(n1);
-			f4_node *n2 = new f4_node(':', n1->parent, n1->parent->pos);
-			nparNodeMakeRandom(n2);
-			n2->addChild(n1);
-			n1->parent = n2;
-		}
-			break;
-		case F4_ADD_REP:
-		{
-			// add repetition ('#')
-			// repeated code (left child) is the original, right child is empty, count is 2
-			f4_node *n3 = n1->parent;
-			n3->removeChild(n1);
-			f4_node *n2 = new f4_node('#', n3, n3->pos);
-			n2->i1 = 2;
-			n2->addChild(n1);
-			new f4_node('>', n2, n2->pos);
-			n1->parent = n2;
-		}
-			break;
-		case F4_ADD_SIMP:
-		{
-			// add simple node
-			// choose a simple node from ADD_SIMPLE_CODES
-			n1->parent->removeChild(n1);
-			//f4_node *n2 = new f4_node(ADD_SIMPLE_CODES[randomN(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
-			int modifierid = GenoOperators::getRandomChar(all_modifiers, excluded_modifiers.c_str());
-			f4_node *n2 = new f4_node(all_modifiers[modifierid], n1->parent, n1->parent->pos);
-			n2->addChild(n1);
-			n1->parent = n2;
-		}
-			break;
-		}
-	}
-		break;
-
-	case F4_DEL:
-	{
-		method = F4_ADD_COUNT - 1 + F4_DEL;
-		// delete a node
-		// must pick a node with parent, and at least one child
-		// already picked a node, but repeat may be needed
-		for (int i = 0; i < 10; i++)
-		{
-			if ((NULL != n1->parent) && (g != n1->parent))
-				if (NULL != n1->child)
-					break;
-			// try a new one
-			n1 = g->child->randomNode();
-		}
-		if ((NULL != n1->parent) && (g != n1->parent))
-		{
-			switch (n1->childCount())
-			{
-			case 0: break;
-			case 1:  // one child
-			{
-				f4_node *n2 = n1->parent;
-				n2->removeChild(n1);
-				if (NULL != n1->child)
-				{
-					n1->child->parent = n2;
-					n2->addChild(n1->child);
-					n1->child = NULL;
-				}
-				if (NULL != n1->child2)
-				{
-					n1->child2->parent = n2;
-					n2->addChild(n1->child2);
-					n1->child2 = NULL;
-				}
-				// destroy n1
-				n1->parent = NULL;
-				delete n1;
-			}
-				break;
-
-			case 2:  // two children
-			{
-				// two children
-				f4_node *n2 = n1->parent;
-				n2->removeChild(n1);
-				// n1 has two children. pick one randomly 50-50, destroy other
-				if (randomN(2) == 0)
-				{
-					n1->child->parent = n2;
-					n2->addChild(n1->child);
-					n1->child = NULL;
-					n1->child2->parent = NULL;
-				}
-				else
-				{
-					n1->child2->parent = n2;
-					n2->addChild(n1->child2);
-					n1->child2 = NULL;
-					n1->child->parent = NULL;
-				}
-				// destroy n1
-				n1->parent = NULL;
-				delete n1;
-			}
-				break;
-			}
-		}
-		else return GENOPER_OPFAIL;
-	}
-		break;
-	case F4_MOD:
-	{
-		method = F4_ADD_COUNT - 1 + F4_MOD;
-		// change a node
-		// the only nodes that are modifiable are MUT_CHAN_CODES
-		// try to get a modifiable node
-		// already picked a node, but repeat may be needed
-		int i = 0;
-		while (1)
-		{
-			if (strchr(MUT_CHAN_CODES, n1->name[0])) break;
-			// try a new one
-			n1 = g->child->randomNode();
-			i++;
-			if (i >= 20) return GENOPER_OPFAIL;
-		}
-		switch (n1->name[0])
-		{
-		case '<':
-		{
-			// swap children
-			f4_node *n2 = n1->child; n1->child = n1->child2; n1->child2 = n2;
-		}
-			break;
-		case '[':
-		{
-			neuronid = -1;
-			for (int i = 0; i < g->count(); i++)
-			{
-				f4_node *gcur = g->ordNode(i);
-				char *temp = (char*)gcur->name.c_str();
-				NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
-				if (neuclass != NULL)
-				{
-					neulist.push_back(neuclass);
-				}
-				if (gcur == n1)
-				{
-					neuronid = neulist.size() - 1;
-				}
-			}
-			if (neuronid == -1)
-			{
-				return GENOPER_OPFAIL;
-			}
-			linkNodeChangeRandom(n1, neuronid, neulist);
-		}
-			break;
-
-		case '#':
-		{
-			repeatNodeChangeRandom(n1);
-		}
-			break;
-		}
-	}
-		break;
-
-	default: //no mutations allowed?
-		return GENOPER_OPFAIL;
-	}
-
-	return GENOPER_OK;
-}
-
-// make a random [ node
-void Geno_f4::linkNodeMakeRandom(f4_node *nn, int neuid, vector<NeuroClass*> neulist) const
-{
-	float prob1;
-	NeuroClass *nc = NULL;
-
-	// 35% chance one of *GTS
-	prob1 = rnd01;
-	prob1 -= 0.35f;
-	if (prob1 < 0)
-	{
-		// '*', 'G', 'T', or 'S', 1/4 chance each
-		nc = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs();
-	}
-	if (nc != NULL)
-	{
-		nn->i1 = 1;
-		nn->s1 = nc->getName().c_str();
-		nn->l1 = 0;
-	}
-	else
-	{
-		// relative input link
-		int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
-		int relid = neuid - id;
-		nn->l1 = relid;
-		//nn->l1 = (int)(4.0f * (rnd01 - 0.5f));
-	}
-	// weight
-	nn->f1 = GenoOperators::mutateNeuProperty(nn->f1, NULL, -1);
-	//nn->f1 = 10.0f * (rnd01 - 0.5f);
-}
-
-// change a [ node
-void Geno_f4::linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const      //rewritten by M.K. - should work as before (not tested)
-{
-	double probs[3] = { 0.1, 0.3, 0.6 };
-	NeuroClass *cl;
-	// 10% change type
-	// 30% change link
-	// 60% change weight
-
-	switch (roulette(probs, 3))
-	{
-	case 0: // change type
-		// 80% for link, 20% for random sensor
-		if (rnd01 < 0.2f)
-		{
-			cl = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs();
-			if (cl != NULL)
-			{
-				nn->i1 = 1;
-				nn->s1 = cl->name.c_str();
-				nn->l1 = 0;
-			}
-		}
-		break;
-	case 1: // change link
-		if (0 == nn->i1) // relative input link
-		{
-			int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
-			nn->l1 = neuid - id;
-		}
-		//nn->l1 += (int)(2.0f * (rnd01 - 0.5f));
-		break;
-	case 2: // change weight
-		nn->f1 = GenoOperators::mutateNeuProperty(nn->f1, NULL, -1);
-		//nn->f1 += 1.0f * (rnd01 - 0.5f);
-		break;
-	}
-}
-
-// make a random : node
-void Geno_f4::nparNodeMakeRandom(f4_node *nn) const
-{
-	int sign = (int)(2.0f * rnd01);
-	int param = (int)(3.0f * rnd01);
-	if (param > 2) param = 2;
-	nn->l1 = sign;
-	nn->i1 = "!=/"[param];
-}
-
-// change a repeat # node
-void Geno_f4::repeatNodeChangeRandom(f4_node *nn) const
-{
-	int count;
-	float prob1;
-
-	// change count
-	count = nn->i1;
-	prob1 = rnd01;
-	if (prob1 < 0.5f) count++;
-	else count--;
-	if (count < 1) count = 1;
-	if (count > REP_MAXCOUNT) count = REP_MAXCOUNT;
-	nn->i1 = count;
-}
-
-
-int Geno_f4::MutateOneValid(f4_node *& g, int &method) const
-// mutate one, until a valid genotype is obtained
-{
-	// ! the genotype is g->child (not g) !
-	int i, res;
-	f4_node *gcopy = NULL;
-	// try this max 20 times:
-	//   copy, mutate, then validate
-
-	for (i = 0; i < 20; i++)
-	{
-		gcopy = g->duplicate();
-
-		res = MutateOne(gcopy, method);
-
-		if (GENOPER_OK != res)
-		{
-			// mutation failed, try again
-			delete gcopy;
-			continue;  // for
-		}
-		// try to validate it
-		res = ValidateRec(gcopy, 10);
-		// accept if it is OK, or was repaired
-		if (GENOPER_OK == res)
-			//(GENOPER_REPAIR == res)
-		{
-			// destroy the original one
-			g->destroy();
-			// make it the new one
-			*g = *gcopy;
-			gcopy->child = NULL;
-			gcopy->child2 = NULL;
-			delete gcopy;
-			res = GENOPER_OK;
-			goto retm1v;
-		}
-		delete gcopy;
-	}
-	// attempts failed
-	res = GENOPER_OPFAIL;
-retm1v:
-	return res;
-}
-
-
-int Geno_f4::mutate(char *& g, float & chg, int &method)
-{
-	f4_node *root = new f4_node;
-	if (f4_processrec(g, 0, root) || root->childCount() != 1)
-	{
-		delete root;
-		return GENOPER_OPFAIL;
-	} // could not convert or bad: fail
-	// mutate one node, set chg as this percent
-	chg = 1.0 / float(root->child->count());
-	if (MutateOneValid(root, method) != GENOPER_OK)
-	{
-		delete root;
-		return GENOPER_OPFAIL;
-	}
-	// OK, convert back to string
-	g[0] = 0;
-	root->child->sprintAdj(g);
-	delete root;
-	return GENOPER_OK;
-}
-
-
-/*
-int Geno_f4::MutateMany(char *& g, float & chg)
-// check if original is valid, then
-// make a number of mutations
-{
-int res, n, i;
-int totNodes = 0;
-int maxToMut = 0;
-
-// convert to tree
-f4_node *root;
-root = new f4_node();
-res = f4_processrec(g, 0, root);
-if (res) {
-// could not convert, fail
-goto retm;
-}
-if (1 != root->childCount()) {
-res = GENOPER_OPFAIL;
-goto retm;
-}
-
-// check if original is valid
-res = ValidateRec( root, 20 );
-// might have been repaired!
-if (GENOPER_REPAIR==res) {
-res = GENOPER_OK;
-}
-if (GENOPER_OK != res) {
-goto retm;
-}
-
-// decide number of nodes to mutate
-// decide maximum number of nodes to mutate: 0.25*nodes, min 2
-totNodes = root->child->count();
-maxToMut = (int)( 0.25f * totNodes);
-if (maxToMut<2) maxToMut=2;
-if (maxToMut>totNodes) maxToMut=totNodes;
-
-// decide number of nodes to mutate
-n = (int)( 0.5f + rnd01 * maxToMut );
-if (n<1) n=1;
-if (n>totNodes) n=totNodes;
-// set chg as this percent
-chg = ((float)n) / ((float)totNodes);
-for (i=0; i<n; i++)
-{
-res = MutateOneValid(root);
-if (GENOPER_OK != res)
-{
-res = GENOPER_OPFAIL;
-goto retm;
-}
-}
-// OK, convert back to string
-g[0]=0;
-root->child->sprintAdj(g);
-retm:
-delete root;
-return res;
-}
-*/
-
-
-int Geno_f4::CrossOverOne(f4_node *g1, f4_node *g2, float chg) const
-{
-	// ! the genotypes are g1->child and g2->child (not g1 g2) !
-	// single offspring in g1
-	int smin, smax;
-	float size;
-	f4_node *n1, *n2, *n1p, *n2p;
-
-	// determine desired size
-	size = (1 - chg) * (float)g1->count();
-	smin = (int)(size*0.9f - 1);
-	smax = (int)(size*1.1f + 1);
-	// get a random node with desired size
-	n1 = g1->child->randomNodeWithSize(smin, smax);
-
-	// determine desired size
-	size = (1 - chg) * (float)g2->count();
-	smin = (int)(size*0.9f - 1);
-	smax = (int)(size*1.1f + 1);
-	// get a random node with desired size
-	n2 = g2->child->randomNodeWithSize(smin, smax);
-
-	// exchange the two nodes:
-	n1p = n1->parent;
-	n2p = n2->parent;
-	n1p->removeChild(n1);
-	n1p->addChild(n2);
-	n2p->removeChild(n2);
-	n2p->addChild(n1);
-	n1->parent = n2p;
-	n2->parent = n1p;
-
-	return GENOPER_OK;
-}
-
-int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2)
-{
-	f4_node root1, root2, *copy1, *copy2;
-
-	// convert genotype strings into tree structures
-	if (f4_processrec(g1, 0, &root1) || (root1.childCount() != 1)) return GENOPER_OPFAIL;
-	if (f4_processrec(g2, 0, &root2) || (root2.childCount() != 1)) return GENOPER_OPFAIL;
-
-	// decide amounts of crossover, 0.25-0.75
-	// adam: seems 0.1-0.9 -- MacKo
-	chg1 = 0.1f + 0.8f*rnd01;
-	chg2 = 0.1f + 0.8f*rnd01;
-
-	copy1 = root1.duplicate();
-	if (CrossOverOne(copy1, &root2, chg1) != GENOPER_OK) { delete copy1; copy1 = NULL; }
-	copy2 = root2.duplicate();
-	if (CrossOverOne(copy2, &root1, chg2) != GENOPER_OK) { delete copy2; copy2 = NULL; }
-
-	g1[0] = 0;
-	g2[0] = 0;
-	if (copy1) { copy1->child->sprintAdj(g1); delete copy1; }
-	if (copy2) { copy2->child->sprintAdj(g2); delete copy2; }
-	if (g1[0] || g2[0]) return GENOPER_OK; else return GENOPER_OPFAIL;
-}
-
-uint32_t Geno_f4::style(const char *g, int pos)
-{
-	char ch = g[pos];
-
-	// style categories
-#define STYL4CAT_MODIFIC F14_MODIFIERS ","
-#define STYL4CAT_NEUMOD "[]:+-/!="
-#define STYL4CAT_NEUSPECIAL "|@*"
-#define STYL4CAT_DIGIT "0123456789."
-#define STYL4CAT_REST "XN<># "
-
-	if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch))
-	{
-		return GENSTYLE_CS(0, GENSTYLE_INVALID);
-	}
-	uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT); //default, should be changed below
-	if (strchr("X ", ch))                    style = GENSTYLE_CS(0, GENSTYLE_NONE);
-	else if (strchr("N", ch))                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
-	else if (strchr("<", ch))                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
-	else if (strchr(">", ch))                style = GENSTYLE_RGBS(0, 0, 100, GENSTYLE_NONE);
-	else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
-	else if (strchr(STYL4CAT_MODIFIC, ch))   style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
-	else if (strchr(STYL4CAT_NEUMOD, ch))    style = GENSTYLE_RGBS(0, 150, 0, GENSTYLE_NONE);
-	if (isalpha(ch) || strchr(STYL4CAT_NEUSPECIAL, ch))
-	{
-		while (pos > 0)
-		{
-			pos--;
-			if (!(isalpha(g[pos]) || strchr(STYL4CAT_NEUSPECIAL, ch)))
-			{
-				if (isupper(g[pos + 1]) && (g[pos] == ':' || g[pos] == '[')) // name of neuron class
-					style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_ITALIC);
-				else // property
-					style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
-			}
-		}
-	}
-	return style;
-}
Index: cpp/frams/genetics/f4/oper_f4.h
===================================================================
--- cpp/frams/genetics/f4/oper_f4.h	(revision 777)
+++ 	(revision )
@@ -1,141 +1,0 @@
-// This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
-// See LICENSE.txt for details.
-
-// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
-// Copyright (C) since 2001 Maciej Komosinski
-
-#ifndef _GENO_F4_H_
-#define _GENO_F4_H_
-
-#include <stdio.h>
-#include "f4_general.h"
-#include "common/nonstd.h"
-#include "../oper_fx.h"
-#include <frams/param/param.h>
-
-/** @name Codes for general mutation types */
-//@{
-#define F4_ADD           0 ///<Adding new node
-#define F4_DEL           1 ///<Deletion of node
-#define F4_MOD           2 ///<Modification of node
-#define F4_COUNT         3 ///<Count of mutation types
-//@}
-
-/** @name Codes for specific F4_ADD mutation subtypes */
-//@{
-#define F4_ADD_DIV       0
-#define F4_ADD_CONN      1
-#define F4_ADD_NEUPAR    2
-#define F4_ADD_REP       3
-#define F4_ADD_SIMP      4
-#define F4_ADD_COUNT     5
-//@}
-
-class Geno_f4 : public GenoOperators
-{
-public:
-	Geno_f4();
-	void setDefaults();
-
-	int checkValidity(const char *, const char *genoname);
-	int validate(char *&, const char *genoname);
-	int mutate(char *& g, float & chg, int &method);
-	int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
-	const char* getSimplest() { return "X"; }
-	uint32_t style(const char *g, int pos);
-
-	// mutation probabilities
-	double prob[F4_COUNT];            ///<relative probabilities of selecting mutation types in f4 genotype
-	double probadd[F4_ADD_COUNT];     ///<relative probabilities of selecting mutation addition subtypes
-
-	SString excluded_modifiers;       ///<Modifiers that are excluded in mutation process
-	static const char *all_modifiers;
-
-protected:
-
-	/**
-	 * Validates a f4 genotype. If the genotype is invalid, the genotype is repaired
-	 * and the validation is repeated. Validation is performed as long as repairing
-	 * is not effective, or the number of retries exceeded the given limit.
-	 * @param geno genotype tree
-	 * @param retrycount maximum amount of repair retries
-	 * @return GENOOPER_OK if genotype is valid, GENOPER_REPAIR if genotype can be repaired, GENOPER_OPFAIL if genotype can't be repaired
-	 */
-	int  ValidateRec(f4_node *geno, int retrycount) const;
-
-	/**
-	 * Performs mutation of an f4 genotype. The mutation is performed on a random node
-	 * from a given tree. The method of mutation is chosen via the roulette selection,
-	 * where probabilities of choosing each mutation type are given in the 'prob'
-	 * array. Possible mutation types are:
-	 *  - F4_ADD - adds new element to the genotype by:
-	 *   - F4_ADD_DIV - replacing randomly selected node with division node '<', setting this node as a child and creating new stick or neuron sibling of the selected cell (the neuron-type sibling will be connected to a random existing neuron),
-	 *   - F4_ADD_CONN - adding connection for an existing neuron,
-	 *   - F4_ADD_NEUPAR - adding neuron property to the selected node, if it is a neuron node,
-	 *   - F4_ADD_REP - adding a repetition node before a randomly selected node (the repetition node has 2 repetitions),
-	 *   - F4_ADD_SIMP - adding a simple node before a selected node,
-	 *  - F4_DEL - removes a randomly selected node (the node needs to have a parent and at least one child, otherwise returns GENOPER_OPFAIL),
-	 *  - F4_MOD - modifies one of simple nodes by:
-	 *   - '<' - swapping children in division
-	 *   - '[' - modifying connection of a neuron
-	 *   - '#' - incrementing or decrementing repetition count
-	 *
-	 * @param g input genotype; the result of mutation will be stored in this parameter
-	 * @param method reference to the variable that will get the selected method of mutation
-	 * @return GENOPER_OK if mutation was performed successfully, GENOPER_FAIL otherwise
-	 */
-	int  MutateOne(f4_node *& g, int &method) const;
-
-	/**
-	 * Creates a random connection to an existing neuron or creates an additional
-	 * sensor for a neuron.
-	 * @param nn neuron class node
-	 * @param neuid id of a neuron
-	 * @param neulist list of genotype neuron classes
-	 */
-	void linkNodeMakeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
-
-	/**
-	 * Changes connection to an existing neuron or creates an additional
-	 * sensor for neuron.
-	 * @param nn neuron connection node
-	 * @param neuid id of a neuron
-	 * @param neulist list of genotype neuron classes
-	 */
-	void linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
-
-	/**
-	 * Introduces a random modification to the neuron node.
-	 * @param nn neuron node
-	 */
-	void nparNodeMakeRandom(f4_node *nn) const;
-
-	/**
-	 * Increases or decreases the amount of repetitions in the repetition node.
-	 * @param nn repetition node
-	 */
-	void repeatNodeChangeRandom(f4_node *nn) const;
-
-	/**
-	 * Tries to perform a mutation until success. There is a maximum of 20 tries. Returns GENOPER_OK or GENOPER_OPFAIL.
-	 * @param g genotype tree
-	 * @param method reference to the variable that will get the selected method of mutation
-	 * @return GENOPER_OK if performed successful mutation, GENOPER_FAIL otherwise
-	 */
-	int  MutateOneValid(f4_node *&g, int &method) const;
-
-	/**
-	 * Performs crossover of two creatures. The 'chg' parameter determines approximately what
-	 * percentage of the first creature should form the offspring. '1-chg' is the percentage
-	 * of the second creature in the offspring.
-	 * @param g1 first parent
-	 * @param g2 second parent
-	 * @param chg percentage of the first parent in offspring (the second parent has the rest)
-	 */
-	int  CrossOverOne(f4_node *g1, f4_node *g2, float chg) const;
-};
-
-
-#endif
-
