Index: cpp/frams/Makefile-SDK-files
===================================================================
--- cpp/frams/Makefile-SDK-files	(revision 777)
+++ cpp/frams/Makefile-SDK-files	(revision 779)
@@ -4,15 +4,15 @@
 ALL_DIRS={common,frams,frams/canvas,frams/config,common/loggers,frams/genetics,frams/genetics/f0,frams/genetics/f1,frams/genetics/f2,frams/genetics/f3,frams/genetics/f4,frams/genetics/f5,frams/genetics/f6,frams/genetics/f7,frams/genetics/f8,frams/genetics/f9,frams/genetics/fn,frams/genetics/fF,frams/genetics/fT,frams/model,frams/neuro,frams/neuro/impl,frams/param,frams/test,frams/util,frams/vm/classes,common/virtfile,frams/_demos,frams/model/geometry,frams/_demos/geometry,frams/model/similarity,frams/model/similarity/SVD}
 
-GENMANF4=frams/genetics/f4/oper_f4.o
-GENMANF9=frams/genetics/f9/oper_f9.o
-GENMANFF=frams/genetics/fF/oper_fF.o
-GENMANFT=frams/genetics/fT/oper_fTest.o
-GENMANFN=frams/genetics/fn/oper_fn.o
+GENMANF4=frams/genetics/f4/f4_oper.o
+GENMANF9=frams/genetics/f9/f9_oper.o
+GENMANFF=frams/genetics/fF/fF_oper.o
+GENMANFT=frams/genetics/fT/fTest_oper.o
+GENMANFN=frams/genetics/fn/fn_oper.o
 
-CONVF1=frams/genetics/f1/conv_f1.o frams/genetics/geneprops.o
-CONVF4=frams/genetics/f4/conv_f4.o frams/genetics/f4/f4_general.o frams/genetics/geneprops.o
-CONVF9=frams/genetics/f9/conv_f9.o
-CONVFF=frams/genetics/fF/conv_fF.o frams/genetics/fF/fF_genotype.o frams/genetics/fF/fF_chamber3d.o
-CONVFN=frams/genetics/fn/conv_fn.o
+CONVF1=frams/genetics/f1/f1_conv.o frams/genetics/geneprops.o
+CONVF4=frams/genetics/f4/f4_conv.o frams/genetics/f4/f4_general.o frams/genetics/geneprops.o
+CONVF9=frams/genetics/f9/f9_conv.o
+CONVFF=frams/genetics/fF/fF_conv.o frams/genetics/fF/fF_genotype.o frams/genetics/fF/fF_chamber3d.o
+CONVFN=frams/genetics/fn/fn_conv.o
 
 # $(sort - remove duplicates
@@ -22,5 +22,5 @@
 GENMAN_COMMON_OBJS=frams/genetics/genman.o frams/param/mutableparam.o frams/param/mutparamlist.o frams/neuro/geneticneuroparam.o frams/neuro/neurolibparam.o
 
-SDK_OBJS=frams/util/list.o frams/util/advlist.o frams/param/param.o frams/util/sstring.o frams/util/sstringutils.o frams/util/3d.o frams/vm/classes/3dobject.o frams/model/model.o frams/model/modelparts.o frams/neuro/neurolibrary.o frams/genetics/geno.o frams/genetics/genoconv.o frams/util/extvalue.o frams/vm/classes/collectionobj.o frams/util/hashtable.o common/log.o common/stl-util.o common/nonstd_stdio.o frams/util/callbacks.o frams/param/syntparam.o frams/util/multirange.o frams/util/multimap.o frams/param/paramtabobj.o common/loggers/loggers.o frams/param/paramobj.o frams/genetics/oper_fx.o common/nonstd_math.o frams/util/validitychecks.o common/Convert.o frams/util/rndutil.o common/virtfile/stringfile.o
+SDK_OBJS=frams/util/list.o frams/util/advlist.o frams/param/param.o frams/util/sstring.o frams/util/sstringutils.o frams/util/3d.o frams/vm/classes/3dobject.o frams/model/model.o frams/model/modelparts.o frams/neuro/neurolibrary.o frams/genetics/geno.o frams/genetics/genoconv.o frams/util/extvalue.o frams/vm/classes/collectionobj.o frams/util/hashtable.o common/log.o common/stl-util.o common/nonstd_stdio.o frams/util/callbacks.o frams/param/syntparam.o frams/util/multirange.o frams/util/multimap.o frams/param/paramtabobj.o common/loggers/loggers.o frams/param/paramobj.o frams/genetics/genooperators.o common/nonstd_math.o frams/util/validitychecks.o common/Convert.o frams/util/rndutil.o common/virtfile/stringfile.o
 
 GEOMETRY_OBJS=frams/model/geometry/meshbuilder.o frams/model/geometry/modelgeometryinfo.o frams/model/geometry/geometryutils.o
Index: cpp/frams/_demos/genooper_test_fTest.cpp
===================================================================
--- cpp/frams/_demos/genooper_test_fTest.cpp	(revision 777)
+++ cpp/frams/_demos/genooper_test_fTest.cpp	(revision 779)
@@ -3,5 +3,5 @@
 // See LICENSE.txt for details.
 
-#include <frams/genetics/fT/oper_fTest.h>
+#include <frams/genetics/fT/fTest_oper.h>
 
 GenoOper_fTest gft;
Index: cpp/frams/genetics/defgenoconv.cpp
===================================================================
--- cpp/frams/genetics/defgenoconv.cpp	(revision 777)
+++ cpp/frams/genetics/defgenoconv.cpp	(revision 779)
@@ -8,38 +8,38 @@
 
 #ifdef USE_GENCONV_f10
-#include "f1/conv_f1.h"
+#include "f1/f1_conv.h"
 #endif
 #ifdef USE_GENCONV_f20
-#include "f2/conv_f2.h"
+#include "f2/f2_conv.h"
 #endif
 #ifdef USE_GENCONV_f32
-#include "f3/conv_f3.h"
+#include "f3/f3_conv.h"
 #endif
 #ifdef USE_GENCONV_f40
-#include "f4/conv_f4.h"
+#include "f4/f4_conv.h"
 #endif
 #ifdef USE_GENCONV_f41_TEST
-#include "f4/conv_f4.h"
+#include "f4/f4_conv.h"
 #endif
 #ifdef USE_GENCONV_f50
-#include "f5/conv_f5.h"
+#include "f5/f5_conv.h"
 #endif
 #ifdef USE_GENCONV_f60
-#include "f6/geno_f6.h"
+#include "f6/f6_geno.h"
 #endif
 #ifdef USE_GENCONV_f70
-#include "f7/conv_f7.h"
+#include "f7/f7_conv.h"
 #endif
 #ifdef USE_GENCONV_f81     
-#include "f8/conv_f8tof1.h"
+#include "f8/f8_convtof1.h"
 #endif
 #ifdef USE_GENCONV_f90
-#include "f9/conv_f9.h"
+#include "f9/f9_conv.h"
 #endif
 #ifdef USE_GENCONV_fF0
-#include "fF/conv_fF.h"
+#include "fF/fF_conv.h"
 #endif
 #ifdef USE_GENCONV_fn0
-#include "fn/conv_fn.h"
+#include "fn/fn_conv.h"
 #endif
 
Index: cpp/frams/genetics/f1/conv_f1.cpp
===================================================================
--- cpp/frams/genetics/f1/conv_f1.cpp	(revision 777)
+++ 	(revision )
@@ -1,455 +1,0 @@
-// 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 "conv_f1.h"
-#include <common/nonstd_stl.h>
-#include <common/log.h>
-#include <frams/util/multirange.h>
-#include <frams/util/multimap.h>
-#include <frams/genetics/geneprops.h>
-#include <ctype.h>
-#include <assert.h>
-
-//#define v1f1COMPATIBLE //as in ancient Framsticks 1.x
-
-class Builder
-{
-public:
-	Builder(const char*g, int mapping = 0) :invalid(0), genbegin(g), usemapping(mapping), first_part_mapping(NULL), own_first_part_mapping(true), model_energy(0), model_energy_count(0) {}
-	~Builder() { if (own_first_part_mapping) SAFEDELETE(first_part_mapping); }
-	char tmp[222];
-	bool invalid;
-	Model model;
-	const char *genbegin;
-	SList neuro_f1_to_f0; // neuro_f1_to_f0(f1_refno) = actual neuro pointer
-	Neuro *last_f1_neuro;
-	SyntParam *neuro_cls_param;
-
-	struct Connection
-	{
-		int n1, n2; double w;
-		Connection(int _n1, int _n2, double _w) :n1(_n1), n2(_n2), w(_w) {}
-	};
-
-	SListTempl<Connection> connections;
-	int usemapping;
-	MultiRange range;
-	MultiRange *first_part_mapping;
-	bool own_first_part_mapping;
-	double lastjoint_muscle_power;
-	double model_energy;
-	int model_energy_count;
-	void grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part);
-	void setPartMapping(int p, const char* g);
-	int growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g);
-	int growPart(GeneProps &c, const char *g);
-	const char *skipNeuro(const char *z);
-	const char* growNeuro(const char* t, GeneProps &c, int&);
-	void growConnection(const char* begin, const char* colon, const char* end, GeneProps& props);
-	int countBranches(const char*g, SList &out);
-	SyntParam* lastNeuroClassParam();
-	void addClassParam(const char* name, double value);
-	void addClassParam(const char* name, const char* value);
-
-	const MultiRange* makeRange(const char*g) { return makeRange(g, g); }
-	const MultiRange* makeRange(const char*g, const char*g2);
-	Part *getLastPart() { return getLastJoint()->part2; }
-	Neuro *getLastNeuro() { return model.getNeuro(model.getNeuroCount() - 1); }
-	Joint *getLastJoint() { return model.getJoint(model.getJointCount() - 1); }
-	void addOrRememberInput(int n1, int n2, double w)
-	{
-		//if (!addInput(n1,n2,w,false))
-		connections += Connection(n1, n2, w);
-	}
-	bool addInput(int n1, int n2, double w, bool final)
-	{
-		if ((n1 < 0) || (n2 < 0) || (n1 >= neuro_f1_to_f0.size()) || (n2 >= neuro_f1_to_f0.size()))
-		{
-			if (final) logPrintf("GenoConvF1", "addInput", LOG_WARN,
-				"illegal neuron connection %d <- %d (ignored)", n1, n2);
-			return 0;
-		}
-		Neuro *neuro = (Neuro*)neuro_f1_to_f0(n1);
-		Neuro *input = (Neuro*)neuro_f1_to_f0(n2);
-		neuro->addInput(input, w);
-		return 1;
-	}
-	void addPendingInputs()
-	{
-		for (int i = 0; i < connections.size(); i++)
-		{
-			Connection *c = &connections(i);
-			addInput(c->n1, c->n2, c->w, true);
-		}
-	}
-};
-
-const MultiRange* Builder::makeRange(const char*g, const char*g2)
-{
-	if (!usemapping) return 0;
-	range.clear();
-	range.add(g - genbegin, g2 - genbegin);
-	return &range;
-}
-
-/** main conversion function - with conversion map support */
-SString GenoConv_f1::convert(SString &i, MultiMap *map, bool using_checkpoints)
-{
-	const char* g = i.c_str();
-	Builder builder(g, map ? 1 : 0);
-	builder.model.open(using_checkpoints);
-	builder.grow(-1, g, Pt3D_0, GeneProps::standard_values, -1); // uses Model::addFromString() to create model elements
-	if (builder.invalid) return SString();
-	builder.addPendingInputs();
-	builder.model.startenergy = (builder.model_energy_count > 0) ? (builder.model_energy / builder.model_energy_count) : 1.0;
-	builder.model.close(); // model is ready to use now
-	if (map) builder.model.getCurrentToF0Map(*map); // generate f1-to-f0 conversion map
-	return builder.model.getF0Geno().getGenes();
-}
-
-void Builder::setPartMapping(int p, const char* g)
-{
-	if (!usemapping) return;
-	const MultiRange *r = makeRange(g);
-	if (p < 0)
-	{ //special case: mapping the part which is not yet created
-		if (first_part_mapping) first_part_mapping->add(*r);
-		else { first_part_mapping = new MultiRange(*r); own_first_part_mapping = true; }
-	}
-	else
-		model.getPart(p)->addMapping(*r);
-}
-
-void Builder::grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part)
-{
-	int hasmuscles = 0;
-	k += Pt3D(c.twist, 0, c.curvedness);
-	while (1)
-	{
-		if (c.executeModifier(*g) == 0)
-		{
-			setPartMapping(part1, g);
-		}
-		else
-		{
-			switch (*g)
-			{
-			case 0: return;
-			case ',': case ')': setPartMapping(branching_part, g); return;
-			case 'R': k.x += 0.7853; setPartMapping(part1, g); break;
-			case 'r': k.x -= 0.7853;	setPartMapping(part1, g); break;
-			case '[': //neuron
-				//		setdebug(g-(char*)geny,DEBUGNEURO | !l_neu);
-				if (model.getJointCount())
-					g = growNeuro(g + 1, c, hasmuscles);
-				else
-				{
-					logMessage("GenoConv_F1", "grow", 1, "Illegal neuron position (ignored)");
-					g = skipNeuro(g + 1);
-				}
-				break;
-			case 'X':
-			{
-				int freshpart = 0;
-				//setdebug(g-(char*)geny,DEBUGEST | !l_est);
-				if (part1 < 0) //initial grow
-				{
-					if (model.getPartCount() > 0)
-						part1 = 0;
-					else
-					{
-						part1 = growPart(c, g);
-						freshpart = 1;
-						if (first_part_mapping)
-						{
-							//mapping was defined before creating this initial Part -> put it into the Part
-							assert(own_first_part_mapping);
-							model.getPart(part1)->setMapping(*first_part_mapping);
-							delete first_part_mapping;
-							//first_part_mapping can be still used later but from now on it references the internal Part mapping
-							first_part_mapping = model.getPart(part1)->getMapping();
-							own_first_part_mapping = false;
-						}
-					}
-				}
-				if (!freshpart)
-				{
-					Part *part = model.getPart(part1);
-					part->density = ((part->mass*part->density) + 1.0 / c.weight) / (part->mass + 1.0); // v=m*d
-					//			part->volume+=1.0/c.weight;
-					part->mass += 1.0;
-				}
-				model_energy += 0.9*c.energy + 0.1;
-				model_energy_count++;
-
-				int part2 = growPart(c, g);
-				growJoint(part1, part2, k, c, g);
-				//		est* e = new est(*s,*s2,k,c,zz,this);
-
-				// attenuate properties as they are propagated along the structure
-				c.propagateAlong(true);
-
-				model.checkpoint();
-				grow(part2, g + 1, Pt3D_0, c, branching_part);
-				return;
-			}
-			case '(':
-			{
-				setPartMapping(part1, g);
-				SList ga;
-				int i, count;
-				count = countBranches(g + 1, ga);
-				c.muscle_reset_range = false;
-				c.muscle_bend_range = 1.0 / count;
-				for (i = 0; i < count; i++)
-					grow(part1, (char*)ga(i), k + Pt3D(0, 0, -M_PI + (i + 1)*(2 * M_PI / (count + 1))), c, part1);
-				return;
-			}
-			case ' ': case '\t': case '\n': case '\r': break;
-			default: invalid = 1; return;
-			}
-		}
-		g++;
-	}
-}
-
-SyntParam* Builder::lastNeuroClassParam()
-{
-	if (!neuro_cls_param)
-	{
-		NeuroClass *cls = last_f1_neuro->getClass();
-		if (cls)
-		{
-			neuro_cls_param = new SyntParam(last_f1_neuro->classProperties());
-			// this is equivalent to:
-			//		SyntParam tmp=last_f1_neuro->classProperties();
-			//		neuro_cls_param=new SyntParam(tmp);
-			// interestingly, some compilers eliminate the call to new SyntParam,
-			// realizing that a copy constructor is redundant when the original object is
-			// temporary. there are no side effect of such optimization, as long as the
-			// copy-constructed object is exact equivalent of the original.
-		}
-	}
-	return neuro_cls_param;
-}
-
-void Builder::addClassParam(const char* name, double value)
-{
-	lastNeuroClassParam();
-	if (neuro_cls_param)
-		neuro_cls_param->setDoubleById(name, value);
-}
-
-void Builder::addClassParam(const char* name, const char* value)
-{
-	lastNeuroClassParam();
-	if (neuro_cls_param)
-	{
-		ExtValue e(value);
-		const ExtValue &re(e);
-		neuro_cls_param->setById(name, re);
-	}
-}
-
-int Builder::countBranches(const char*g, SList &out)
-{
-	int gl = 0;
-	out += (void*)g;
-	while (gl >= 0)
-	{
-		switch (*g)
-		{
-		case 0: gl = -1; break;
-		case '(': case '[': ++gl; break;
-		case ')': case ']': --gl; break;
-		case ',': if (!gl) out += (void*)(g + 1);
-		}
-		g++;
-	}
-	return out.size();
-}
-
-int Builder::growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g)
-{
-	double len = min(2.0, c.length);
-	sprintf(tmp, "p1=%ld,p2=%ld,dx=%lg,rx=%lg,ry=%lg,rz=%lg,stam=%lg,vr=%g,vg=%g,vb=%g",
-		part1, part2, len, angle.x, angle.y, angle.z, c.stamina, c.cred, c.cgreen, c.cblue);
-	lastjoint_muscle_power = c.muscle_power;
-	return model.addFromString(Model::JointType, tmp, makeRange(g));
-}
-
-int Builder::growPart(GeneProps &c, const char *g)
-{
-	sprintf(tmp, "dn=%lg,fr=%lg,ing=%lg,as=%lg,vs=%g,vr=%g,vg=%g,vb=%g",
-		1.0 / c.weight, c.friction, c.ingestion, c.assimilation, c.visual_size, c.cred, c.cgreen, c.cblue);
-	return model.addFromString(Model::PartType, tmp, makeRange(g));
-}
-
-const char *Builder::skipNeuro(const char *z)
-{
-	for (; *z; z++) if ((*z == ']') || (*z == ')')) break;
-	return z - 1;
-}
-
-const char* Builder::growNeuro(const char* t, GeneProps& props, int &hasmuscles)
-{
-	const char*neuroend = skipNeuro(t);
-	last_f1_neuro = model.addNewNeuro();
-	neuro_cls_param = NULL;
-	last_f1_neuro->attachToPart(getLastPart());
-	const MultiRange *mr = makeRange(t - 1, neuroend + 1);
-	if (mr) last_f1_neuro->addMapping(*mr);
-	neuro_f1_to_f0 += last_f1_neuro;
-
-	SString clsname;
-	bool haveclass = 0;
-	while (*t && *t <= ' ') t++;
-	const char* next = (*t) ? (t + 1) : t;
-	while (*next && *next <= ' ') next++;
-	if (*t && *next != ',' && *next != ']') // old style muscles [|rest] or [@rest]
-		switch (*t)
-	{
-		case '@': if (t[1] == ':') break;
-			haveclass = 1;
-			//		if (!(hasmuscles&1))
-			{
-				hasmuscles |= 1;
-				Neuro *muscle = model.addNewNeuro();
-				sprintf(tmp, "@:p=%lg", lastjoint_muscle_power);
-				muscle->addInput(last_f1_neuro);
-				muscle->setDetails(tmp);
-				muscle->attachToJoint(getLastJoint());
-				if (usemapping) muscle->addMapping(*makeRange(t));
-			}
-			t++;
-			break;
-		case '|': if (t[1] == ':') break;
-			haveclass = 1;
-			//		if (!(hasmuscles&2))
-			{
-				hasmuscles |= 2;
-				Neuro *muscle = model.addNewNeuro();
-				sprintf(tmp, "|:p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
-				muscle->addInput(last_f1_neuro);
-				muscle->setDetails(tmp);
-				muscle->attachToJoint(getLastJoint());
-				if (usemapping) muscle->addMapping(*makeRange(t));
-			}
-			t++;
-			break;
-	}
-	while (*t && *t <= ' ') t++;
-	bool finished = 0;
-	const char *begin = t;
-	const char* colon = 0;
-	SString classparams;
-	while (!finished)
-	{
-		switch (*t)
-		{
-		case ':': colon = t; break;
-		case 0: case ']': case ')': finished = 1;
-			// NO break!
-		case ',':
-			if (!haveclass && !colon && t > begin)
-			{
-				haveclass = 1;
-				SString clsname(begin, t - begin);
-				clsname = trim(clsname);
-				last_f1_neuro->setClassName(clsname);
-				NeuroClass *cls = last_f1_neuro->getClass();
-				if (cls)
-				{
-					if (cls->getPreferredLocation() == 2)
-						last_f1_neuro->attachToJoint(getLastJoint());
-					else if (cls->getPreferredLocation() == 1)
-						last_f1_neuro->attachToPart(getLastPart());
-
-					lastNeuroClassParam();
-					//special handling: muscle properties (can be overwritten by subsequent property assignments)
-					if (!strcmp(cls->getName().c_str(), "|"))
-					{
-						neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
-						neuro_cls_param->setDoubleById("r", props.muscle_bend_range);
-					}
-					else if (!strcmp(cls->getName().c_str(), "@"))
-					{
-						neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
-					}
-				}
-			}
-			else if (colon && (colon > begin) && (t > colon))
-				growConnection(begin, colon, t, props);
-			if (t[0] != ',') t--;
-			begin = t + 1; colon = 0;
-			break;
-		}
-		t++;
-	}
-	SAFEDELETE(neuro_cls_param);
-	return t;
-}
-void Builder::growConnection(const char* begin, const char* colon, const char* end, GeneProps& props)
-{
-	while (*begin && *begin <= ' ') begin++;
-	int i;
-	if (isdigit(begin[0]) || (begin[0] == '-'))
-	{
-		double conn_weight = ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str());
-		paInt relative = ExtValue::getInt(trim(SString(begin, colon - begin)).c_str(), false);
-		int this_refno = neuro_f1_to_f0.size() - 1;
-		addOrRememberInput(this_refno, this_refno + relative, conn_weight);
-	}
-	else if ((i = last_f1_neuro->extraProperties().findIdn(begin, colon - begin)) >= 0)
-	{
-		last_f1_neuro->extraProperties().setFromString(i, colon + 1);
-	}
-	else if (isupper(begin[0]) || strchr("*|@", begin[0]))
-	{
-		SString clsname(begin, colon - begin);
-		trim(clsname);
-		Neuro *receptor = model.addNewNeuro();
-		receptor->setClassName(clsname);
-		NeuroClass *cls = receptor->getClass();
-		if (cls)
-		{
-			if (cls->getPreferredLocation() == 2) receptor->attachToJoint(getLastJoint());
-			else if (cls->getPreferredLocation() == 1) receptor->attachToPart(getLastPart());
-		}
-		last_f1_neuro->addInput(receptor, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
-		if (usemapping) receptor->addMapping(*makeRange(begin, end - 1));
-	}
-	else if ((begin[0] == '>') && (begin[1]))
-	{
-		Neuro *out = model.addNewNeuro();
-		out->addInput(last_f1_neuro, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
-		out->setClassName(SString(begin + 1, end - colon - 1));
-		if (begin[1] == '@')
-		{
-			sprintf(tmp, "p=%lg", lastjoint_muscle_power);
-			out->setClassParams(tmp);
-		}
-		else if (begin[1] == '|')
-		{
-			sprintf(tmp, "p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
-			out->setClassParams(tmp);
-		}
-		NeuroClass *cls = out->getClass();
-		if (cls)
-		{
-			if (cls->getPreferredLocation() == 2) out->attachToJoint(getLastJoint());
-			else if (cls->getPreferredLocation() == 1) out->attachToPart(getLastPart());
-		}
-		if (usemapping) out->addMapping(*makeRange(begin, end - 1));
-	}
-	else if (*begin == '!') addClassParam("fo", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
-	else if (*begin == '=') addClassParam("in", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
-	else if (*begin == '/') addClassParam("si", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
-	else if (islower(begin[0]))
-	{
-		SString name(begin, colon - begin);
-		SString value(colon + 1, end - (colon + 1));
-		addClassParam(name.c_str(), value.c_str());
-	}
-}
Index: cpp/frams/genetics/f1/conv_f1.h
===================================================================
--- cpp/frams/genetics/f1/conv_f1.h	(revision 777)
+++ 	(revision )
@@ -1,67 +1,0 @@
-// 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.
-
-#ifndef _CONV_F1_H
-#define _CONV_F1_H
-
-#include <frams/genetics/genoconv.h>
-#include <frams/model/model.h>
-
-/**
- Official Framsticks f1 -> f0 converter.
- Source code is published as educational example, but currently not
- well documented.
- This converter features building creature using Model class
- and some advanced tricks (changing model data during construction).
- Final f0 genotype is generated by model class.
-
- <b>SDK 1.0.5 NN extension: (for Framsticks 2.0)</B>
-
- Until now, only the "standard" Framsticks v1 neuron classes could be used.
- Starting with SDK 1.0.5 you can use all neuron types.
-
- \b Syntax: [classname , input_or_parameter:weight , input_or_parameter:weight , ...]
-
- The classname is optional, the default class will be used if it is omitted.
- Note the comma after \b classname. It must be present if you provide the classname.
- This is to distinguish between old muscle syntax and new classname specification:
- - "[|,0:1]" is the neuron of class "|" (bend muscle) with one self connection.
- \image html nn-ex1.gif
- - "[|0:1]" describes the default neuron class (N) connected with the bend muscle (|).
- \image html nn-ex2.gif
-
-
- Neuron parameters can be specified along with inputs, eg: "[myclass,param1:34,param2:56]"
- The names beginning with UPPER case character are assumed to be neuron classes.
- "[myclass,g:1.23,G:2.34]" - the first 'g' is the parameter, the second one is the receptor
- G ("gyroscope").
-
- Another example:
-
- "X[G][-1:2.3][-2:3.4]" - The first neuron is the standalone G receptor. Other neuron can use its output
- signal by specifying it as regular input ("-1:2.3" and "-2:3.4"). This NN contains 3 neurons.
- \image html nn-ex3.gif
-
- "X[G:2.3][G:3.4]" - with the old syntax it is impossible to connect 2 neurons with the same gyroscope,
- because everything inside brackets is private to the neuron, others can't refer to it.
- Adding another neuron with "G" input will add another gyroscope object. This NN contains 4 neurons
- (or 2 neurons if you try it in Framsticks v1).
- \image html nn-ex4.gif
-
- */
-class GenoConv_f1 : public GenoConverter
-{
-public:
-	GenoConv_f1()
-	{
-		name = "Recursive encoding";
-		in_format = '1';
-		mapsupport = 1;
-	}
-	SString convert(SString &i, MultiMap *map, bool using_checkpoints);
-	~GenoConv_f1() {}
-};
-
-
-#endif
Index: cpp/frams/genetics/f1/f1_conv.cpp
===================================================================
--- cpp/frams/genetics/f1/f1_conv.cpp	(revision 779)
+++ cpp/frams/genetics/f1/f1_conv.cpp	(revision 779)
@@ -0,0 +1,455 @@
+// 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 "f1_conv.h"
+#include <common/nonstd_stl.h>
+#include <common/log.h>
+#include <frams/util/multirange.h>
+#include <frams/util/multimap.h>
+#include <frams/genetics/geneprops.h>
+#include <ctype.h>
+#include <assert.h>
+
+//#define v1f1COMPATIBLE //as in ancient Framsticks 1.x
+
+class Builder
+{
+public:
+	Builder(const char*g, int mapping = 0) :invalid(0), genbegin(g), usemapping(mapping), first_part_mapping(NULL), own_first_part_mapping(true), model_energy(0), model_energy_count(0) {}
+	~Builder() { if (own_first_part_mapping) SAFEDELETE(first_part_mapping); }
+	char tmp[222];
+	bool invalid;
+	Model model;
+	const char *genbegin;
+	SList neuro_f1_to_f0; // neuro_f1_to_f0(f1_refno) = actual neuro pointer
+	Neuro *last_f1_neuro;
+	SyntParam *neuro_cls_param;
+
+	struct Connection
+	{
+		int n1, n2; double w;
+		Connection(int _n1, int _n2, double _w) :n1(_n1), n2(_n2), w(_w) {}
+	};
+
+	SListTempl<Connection> connections;
+	int usemapping;
+	MultiRange range;
+	MultiRange *first_part_mapping;
+	bool own_first_part_mapping;
+	double lastjoint_muscle_power;
+	double model_energy;
+	int model_energy_count;
+	void grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part);
+	void setPartMapping(int p, const char* g);
+	int growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g);
+	int growPart(GeneProps &c, const char *g);
+	const char *skipNeuro(const char *z);
+	const char* growNeuro(const char* t, GeneProps &c, int&);
+	void growConnection(const char* begin, const char* colon, const char* end, GeneProps& props);
+	int countBranches(const char*g, SList &out);
+	SyntParam* lastNeuroClassParam();
+	void addClassParam(const char* name, double value);
+	void addClassParam(const char* name, const char* value);
+
+	const MultiRange* makeRange(const char*g) { return makeRange(g, g); }
+	const MultiRange* makeRange(const char*g, const char*g2);
+	Part *getLastPart() { return getLastJoint()->part2; }
+	Neuro *getLastNeuro() { return model.getNeuro(model.getNeuroCount() - 1); }
+	Joint *getLastJoint() { return model.getJoint(model.getJointCount() - 1); }
+	void addOrRememberInput(int n1, int n2, double w)
+	{
+		//if (!addInput(n1,n2,w,false))
+		connections += Connection(n1, n2, w);
+	}
+	bool addInput(int n1, int n2, double w, bool final)
+	{
+		if ((n1 < 0) || (n2 < 0) || (n1 >= neuro_f1_to_f0.size()) || (n2 >= neuro_f1_to_f0.size()))
+		{
+			if (final) logPrintf("GenoConvF1", "addInput", LOG_WARN,
+				"illegal neuron connection %d <- %d (ignored)", n1, n2);
+			return 0;
+		}
+		Neuro *neuro = (Neuro*)neuro_f1_to_f0(n1);
+		Neuro *input = (Neuro*)neuro_f1_to_f0(n2);
+		neuro->addInput(input, w);
+		return 1;
+	}
+	void addPendingInputs()
+	{
+		for (int i = 0; i < connections.size(); i++)
+		{
+			Connection *c = &connections(i);
+			addInput(c->n1, c->n2, c->w, true);
+		}
+	}
+};
+
+const MultiRange* Builder::makeRange(const char*g, const char*g2)
+{
+	if (!usemapping) return 0;
+	range.clear();
+	range.add(g - genbegin, g2 - genbegin);
+	return &range;
+}
+
+/** main conversion function - with conversion map support */
+SString GenoConv_f1::convert(SString &i, MultiMap *map, bool using_checkpoints)
+{
+	const char* g = i.c_str();
+	Builder builder(g, map ? 1 : 0);
+	builder.model.open(using_checkpoints);
+	builder.grow(-1, g, Pt3D_0, GeneProps::standard_values, -1); // uses Model::addFromString() to create model elements
+	if (builder.invalid) return SString();
+	builder.addPendingInputs();
+	builder.model.startenergy = (builder.model_energy_count > 0) ? (builder.model_energy / builder.model_energy_count) : 1.0;
+	builder.model.close(); // model is ready to use now
+	if (map) builder.model.getCurrentToF0Map(*map); // generate f1-to-f0 conversion map
+	return builder.model.getF0Geno().getGenes();
+}
+
+void Builder::setPartMapping(int p, const char* g)
+{
+	if (!usemapping) return;
+	const MultiRange *r = makeRange(g);
+	if (p < 0)
+	{ //special case: mapping the part which is not yet created
+		if (first_part_mapping) first_part_mapping->add(*r);
+		else { first_part_mapping = new MultiRange(*r); own_first_part_mapping = true; }
+	}
+	else
+		model.getPart(p)->addMapping(*r);
+}
+
+void Builder::grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part)
+{
+	int hasmuscles = 0;
+	k += Pt3D(c.twist, 0, c.curvedness);
+	while (1)
+	{
+		if (c.executeModifier(*g) == 0)
+		{
+			setPartMapping(part1, g);
+		}
+		else
+		{
+			switch (*g)
+			{
+			case 0: return;
+			case ',': case ')': setPartMapping(branching_part, g); return;
+			case 'R': k.x += 0.7853; setPartMapping(part1, g); break;
+			case 'r': k.x -= 0.7853;	setPartMapping(part1, g); break;
+			case '[': //neuron
+				//		setdebug(g-(char*)geny,DEBUGNEURO | !l_neu);
+				if (model.getJointCount())
+					g = growNeuro(g + 1, c, hasmuscles);
+				else
+				{
+					logMessage("GenoConv_F1", "grow", 1, "Illegal neuron position (ignored)");
+					g = skipNeuro(g + 1);
+				}
+				break;
+			case 'X':
+			{
+				int freshpart = 0;
+				//setdebug(g-(char*)geny,DEBUGEST | !l_est);
+				if (part1 < 0) //initial grow
+				{
+					if (model.getPartCount() > 0)
+						part1 = 0;
+					else
+					{
+						part1 = growPart(c, g);
+						freshpart = 1;
+						if (first_part_mapping)
+						{
+							//mapping was defined before creating this initial Part -> put it into the Part
+							assert(own_first_part_mapping);
+							model.getPart(part1)->setMapping(*first_part_mapping);
+							delete first_part_mapping;
+							//first_part_mapping can be still used later but from now on it references the internal Part mapping
+							first_part_mapping = model.getPart(part1)->getMapping();
+							own_first_part_mapping = false;
+						}
+					}
+				}
+				if (!freshpart)
+				{
+					Part *part = model.getPart(part1);
+					part->density = ((part->mass*part->density) + 1.0 / c.weight) / (part->mass + 1.0); // v=m*d
+					//			part->volume+=1.0/c.weight;
+					part->mass += 1.0;
+				}
+				model_energy += 0.9*c.energy + 0.1;
+				model_energy_count++;
+
+				int part2 = growPart(c, g);
+				growJoint(part1, part2, k, c, g);
+				//		est* e = new est(*s,*s2,k,c,zz,this);
+
+				// attenuate properties as they are propagated along the structure
+				c.propagateAlong(true);
+
+				model.checkpoint();
+				grow(part2, g + 1, Pt3D_0, c, branching_part);
+				return;
+			}
+			case '(':
+			{
+				setPartMapping(part1, g);
+				SList ga;
+				int i, count;
+				count = countBranches(g + 1, ga);
+				c.muscle_reset_range = false;
+				c.muscle_bend_range = 1.0 / count;
+				for (i = 0; i < count; i++)
+					grow(part1, (char*)ga(i), k + Pt3D(0, 0, -M_PI + (i + 1)*(2 * M_PI / (count + 1))), c, part1);
+				return;
+			}
+			case ' ': case '\t': case '\n': case '\r': break;
+			default: invalid = 1; return;
+			}
+		}
+		g++;
+	}
+}
+
+SyntParam* Builder::lastNeuroClassParam()
+{
+	if (!neuro_cls_param)
+	{
+		NeuroClass *cls = last_f1_neuro->getClass();
+		if (cls)
+		{
+			neuro_cls_param = new SyntParam(last_f1_neuro->classProperties());
+			// this is equivalent to:
+			//		SyntParam tmp=last_f1_neuro->classProperties();
+			//		neuro_cls_param=new SyntParam(tmp);
+			// interestingly, some compilers eliminate the call to new SyntParam,
+			// realizing that a copy constructor is redundant when the original object is
+			// temporary. there are no side effect of such optimization, as long as the
+			// copy-constructed object is exact equivalent of the original.
+		}
+	}
+	return neuro_cls_param;
+}
+
+void Builder::addClassParam(const char* name, double value)
+{
+	lastNeuroClassParam();
+	if (neuro_cls_param)
+		neuro_cls_param->setDoubleById(name, value);
+}
+
+void Builder::addClassParam(const char* name, const char* value)
+{
+	lastNeuroClassParam();
+	if (neuro_cls_param)
+	{
+		ExtValue e(value);
+		const ExtValue &re(e);
+		neuro_cls_param->setById(name, re);
+	}
+}
+
+int Builder::countBranches(const char*g, SList &out)
+{
+	int gl = 0;
+	out += (void*)g;
+	while (gl >= 0)
+	{
+		switch (*g)
+		{
+		case 0: gl = -1; break;
+		case '(': case '[': ++gl; break;
+		case ')': case ']': --gl; break;
+		case ',': if (!gl) out += (void*)(g + 1);
+		}
+		g++;
+	}
+	return out.size();
+}
+
+int Builder::growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g)
+{
+	double len = min(2.0, c.length);
+	sprintf(tmp, "p1=%ld,p2=%ld,dx=%lg,rx=%lg,ry=%lg,rz=%lg,stam=%lg,vr=%g,vg=%g,vb=%g",
+		part1, part2, len, angle.x, angle.y, angle.z, c.stamina, c.cred, c.cgreen, c.cblue);
+	lastjoint_muscle_power = c.muscle_power;
+	return model.addFromString(Model::JointType, tmp, makeRange(g));
+}
+
+int Builder::growPart(GeneProps &c, const char *g)
+{
+	sprintf(tmp, "dn=%lg,fr=%lg,ing=%lg,as=%lg,vs=%g,vr=%g,vg=%g,vb=%g",
+		1.0 / c.weight, c.friction, c.ingestion, c.assimilation, c.visual_size, c.cred, c.cgreen, c.cblue);
+	return model.addFromString(Model::PartType, tmp, makeRange(g));
+}
+
+const char *Builder::skipNeuro(const char *z)
+{
+	for (; *z; z++) if ((*z == ']') || (*z == ')')) break;
+	return z - 1;
+}
+
+const char* Builder::growNeuro(const char* t, GeneProps& props, int &hasmuscles)
+{
+	const char*neuroend = skipNeuro(t);
+	last_f1_neuro = model.addNewNeuro();
+	neuro_cls_param = NULL;
+	last_f1_neuro->attachToPart(getLastPart());
+	const MultiRange *mr = makeRange(t - 1, neuroend + 1);
+	if (mr) last_f1_neuro->addMapping(*mr);
+	neuro_f1_to_f0 += last_f1_neuro;
+
+	SString clsname;
+	bool haveclass = 0;
+	while (*t && *t <= ' ') t++;
+	const char* next = (*t) ? (t + 1) : t;
+	while (*next && *next <= ' ') next++;
+	if (*t && *next != ',' && *next != ']') // old style muscles [|rest] or [@rest]
+		switch (*t)
+	{
+		case '@': if (t[1] == ':') break;
+			haveclass = 1;
+			//		if (!(hasmuscles&1))
+			{
+				hasmuscles |= 1;
+				Neuro *muscle = model.addNewNeuro();
+				sprintf(tmp, "@:p=%lg", lastjoint_muscle_power);
+				muscle->addInput(last_f1_neuro);
+				muscle->setDetails(tmp);
+				muscle->attachToJoint(getLastJoint());
+				if (usemapping) muscle->addMapping(*makeRange(t));
+			}
+			t++;
+			break;
+		case '|': if (t[1] == ':') break;
+			haveclass = 1;
+			//		if (!(hasmuscles&2))
+			{
+				hasmuscles |= 2;
+				Neuro *muscle = model.addNewNeuro();
+				sprintf(tmp, "|:p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
+				muscle->addInput(last_f1_neuro);
+				muscle->setDetails(tmp);
+				muscle->attachToJoint(getLastJoint());
+				if (usemapping) muscle->addMapping(*makeRange(t));
+			}
+			t++;
+			break;
+	}
+	while (*t && *t <= ' ') t++;
+	bool finished = 0;
+	const char *begin = t;
+	const char* colon = 0;
+	SString classparams;
+	while (!finished)
+	{
+		switch (*t)
+		{
+		case ':': colon = t; break;
+		case 0: case ']': case ')': finished = 1;
+			// NO break!
+		case ',':
+			if (!haveclass && !colon && t > begin)
+			{
+				haveclass = 1;
+				SString clsname(begin, t - begin);
+				clsname = trim(clsname);
+				last_f1_neuro->setClassName(clsname);
+				NeuroClass *cls = last_f1_neuro->getClass();
+				if (cls)
+				{
+					if (cls->getPreferredLocation() == 2)
+						last_f1_neuro->attachToJoint(getLastJoint());
+					else if (cls->getPreferredLocation() == 1)
+						last_f1_neuro->attachToPart(getLastPart());
+
+					lastNeuroClassParam();
+					//special handling: muscle properties (can be overwritten by subsequent property assignments)
+					if (!strcmp(cls->getName().c_str(), "|"))
+					{
+						neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
+						neuro_cls_param->setDoubleById("r", props.muscle_bend_range);
+					}
+					else if (!strcmp(cls->getName().c_str(), "@"))
+					{
+						neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
+					}
+				}
+			}
+			else if (colon && (colon > begin) && (t > colon))
+				growConnection(begin, colon, t, props);
+			if (t[0] != ',') t--;
+			begin = t + 1; colon = 0;
+			break;
+		}
+		t++;
+	}
+	SAFEDELETE(neuro_cls_param);
+	return t;
+}
+void Builder::growConnection(const char* begin, const char* colon, const char* end, GeneProps& props)
+{
+	while (*begin && *begin <= ' ') begin++;
+	int i;
+	if (isdigit(begin[0]) || (begin[0] == '-'))
+	{
+		double conn_weight = ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str());
+		paInt relative = ExtValue::getInt(trim(SString(begin, colon - begin)).c_str(), false);
+		int this_refno = neuro_f1_to_f0.size() - 1;
+		addOrRememberInput(this_refno, this_refno + relative, conn_weight);
+	}
+	else if ((i = last_f1_neuro->extraProperties().findIdn(begin, colon - begin)) >= 0)
+	{
+		last_f1_neuro->extraProperties().setFromString(i, colon + 1);
+	}
+	else if (isupper(begin[0]) || strchr("*|@", begin[0]))
+	{
+		SString clsname(begin, colon - begin);
+		trim(clsname);
+		Neuro *receptor = model.addNewNeuro();
+		receptor->setClassName(clsname);
+		NeuroClass *cls = receptor->getClass();
+		if (cls)
+		{
+			if (cls->getPreferredLocation() == 2) receptor->attachToJoint(getLastJoint());
+			else if (cls->getPreferredLocation() == 1) receptor->attachToPart(getLastPart());
+		}
+		last_f1_neuro->addInput(receptor, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+		if (usemapping) receptor->addMapping(*makeRange(begin, end - 1));
+	}
+	else if ((begin[0] == '>') && (begin[1]))
+	{
+		Neuro *out = model.addNewNeuro();
+		out->addInput(last_f1_neuro, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+		out->setClassName(SString(begin + 1, end - colon - 1));
+		if (begin[1] == '@')
+		{
+			sprintf(tmp, "p=%lg", lastjoint_muscle_power);
+			out->setClassParams(tmp);
+		}
+		else if (begin[1] == '|')
+		{
+			sprintf(tmp, "p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
+			out->setClassParams(tmp);
+		}
+		NeuroClass *cls = out->getClass();
+		if (cls)
+		{
+			if (cls->getPreferredLocation() == 2) out->attachToJoint(getLastJoint());
+			else if (cls->getPreferredLocation() == 1) out->attachToPart(getLastPart());
+		}
+		if (usemapping) out->addMapping(*makeRange(begin, end - 1));
+	}
+	else if (*begin == '!') addClassParam("fo", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (*begin == '=') addClassParam("in", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (*begin == '/') addClassParam("si", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (islower(begin[0]))
+	{
+		SString name(begin, colon - begin);
+		SString value(colon + 1, end - (colon + 1));
+		addClassParam(name.c_str(), value.c_str());
+	}
+}
Index: cpp/frams/genetics/f1/f1_conv.h
===================================================================
--- cpp/frams/genetics/f1/f1_conv.h	(revision 779)
+++ cpp/frams/genetics/f1/f1_conv.h	(revision 779)
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef _F1_CONV_H_
+#define _F1_CONV_H_
+
+#include <frams/genetics/genoconv.h>
+#include <frams/model/model.h>
+
+/**
+ Official Framsticks f1 -> f0 converter.
+ Source code is published as educational example, but currently not
+ well documented.
+ This converter features building creature using Model class
+ and some advanced tricks (changing model data during construction).
+ Final f0 genotype is generated by model class.
+
+ <b>SDK 1.0.5 NN extension: (for Framsticks 2.0)</B>
+
+ Until now, only the "standard" Framsticks v1 neuron classes could be used.
+ Starting with SDK 1.0.5 you can use all neuron types.
+
+ \b Syntax: [classname , input_or_parameter:weight , input_or_parameter:weight , ...]
+
+ The classname is optional, the default class will be used if it is omitted.
+ Note the comma after \b classname. It must be present if you provide the classname.
+ This is to distinguish between old muscle syntax and new classname specification:
+ - "[|,0:1]" is the neuron of class "|" (bend muscle) with one self connection.
+ \image html nn-ex1.gif
+ - "[|0:1]" describes the default neuron class (N) connected with the bend muscle (|).
+ \image html nn-ex2.gif
+
+
+ Neuron parameters can be specified along with inputs, eg: "[myclass,param1:34,param2:56]"
+ The names beginning with UPPER case character are assumed to be neuron classes.
+ "[myclass,g:1.23,G:2.34]" - the first 'g' is the parameter, the second one is the receptor
+ G ("gyroscope").
+
+ Another example:
+
+ "X[G][-1:2.3][-2:3.4]" - The first neuron is the standalone G receptor. Other neuron can use its output
+ signal by specifying it as regular input ("-1:2.3" and "-2:3.4"). This NN contains 3 neurons.
+ \image html nn-ex3.gif
+
+ "X[G:2.3][G:3.4]" - with the old syntax it is impossible to connect 2 neurons with the same gyroscope,
+ because everything inside brackets is private to the neuron, others can't refer to it.
+ Adding another neuron with "G" input will add another gyroscope object. This NN contains 4 neurons
+ (or 2 neurons if you try it in Framsticks v1).
+ \image html nn-ex4.gif
+
+ */
+class GenoConv_f1 : public GenoConverter
+{
+public:
+	GenoConv_f1()
+	{
+		name = "Recursive encoding";
+		in_format = '1';
+		mapsupport = 1;
+	}
+	SString convert(SString &i, MultiMap *map, bool using_checkpoints);
+	~GenoConv_f1() {}
+};
+
+
+#endif
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
-
Index: cpp/frams/genetics/f9/conv_f9.cpp
===================================================================
--- cpp/frams/genetics/f9/conv_f9.cpp	(revision 777)
+++ 	(revision )
@@ -1,174 +1,0 @@
-// 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 "conv_f9.h"
-#include <frams/model/model.h>
-#include <string.h>
-#include <common/nonstd_stl.h> //ARRAY_LENGTH
-
-#define APPLY_DETERMINISTIC_BODY_NOISE //this genetic representation easily produces perfectly vertical sticks that would stay upright forever in simulation. In most cases such infinite perfection is not desired, so we make the construct less perfect by perturbing its coordinates.
-
-GenoConv_f90::GenoConv_f90()
-{
-	name = "Turtle3D-ortho encoding";
-	in_format = '9';
-	out_format = '0';
-	mapsupport = 1;
-}
-
-
-const char* turtle_commands_f9 = "LRBFDU";
-
-//const char* turtle_commandsX_f9="-+0000";
-//const char* turtle_commandsY_f9="00-+00";
-//const char* turtle_commandsZ_f9="0000-+";
-
-SString GenoConv_f90::convert(SString &in, MultiMap *map, bool using_checkpoints)
-{
-	vector<XYZ_LOC> vertices;
-	XYZ_LOC current;
-	Model m;
-	m.open(using_checkpoints);
-	int recently_added = addSegment(m, 0, vertices, current, 0xDead);
-	for (int i = 0; i < in.len(); i++)
-	{
-		char command = in[i];
-		char *ptr = strchr((char*)turtle_commands_f9, command);
-		if (ptr)
-		{
-			int delta[] = { 0, 0, 0 };
-			int pos = ptr - turtle_commands_f9;
-			int axis = pos / 2;
-			int dir = pos % 2;
-			(*(delta + axis)) += dir * 2 - 1; //+1 or -1 in the given axis
-			current.add(delta);
-			recently_added = addSegment(m, i, vertices, current, recently_added);
-			m.checkpoint();
-		}
-	}
-#ifdef APPLY_DETERMINISTIC_BODY_NOISE
-	perturbPartLocations(m);
-#endif
-	setColors(m, recently_added);
-	m.close();
-	if (m.getPartCount() < 2) //only one part <=> there were no valid turtle commands in the input genotype
-		return ""; //so we return an invalid f0 genotype
-	if (map != NULL)
-		m.getCurrentToF0Map(*map);
-	return m.getF0Geno().getGenes();
-}
-
-int GenoConv_f90::addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added)
-{
-	if (vertices.size() < 1) //empty model?
-	{
-		return addNewVertex(m, vertices, new_vertex);
-	}
-	else
-	{
-		int vertex_here = findVertexAt(vertices, new_vertex);
-		if (vertex_here < 0) //need to create a new Part
-		{
-			vertex_here = addNewVertex(m, vertices, new_vertex);
-		} //else there already exists a Part in new_vertex; new Joint may or may not be needed
-		Part *p1 = m.getPart(recently_added);
-		Part *p2 = m.getPart(vertex_here);
-		p1->addMapping(MultiRange(genenr, genenr));
-		p2->addMapping(MultiRange(genenr, genenr));
-
-		int j12 = m.findJoint(p1, p2);
-		int j21 = m.findJoint(p2, p1);
-		if (j12 >= 0)
-			m.getJoint(j12)->addMapping(MultiRange(genenr, genenr));
-		else if (j21 >= 0)
-			m.getJoint(j21)->addMapping(MultiRange(genenr, genenr));
-		else //both j12<0 and j21<0. New Joint needed. Should always happen if we just created a new Part (vertex_here was <0)
-			m.addNewJoint(p1, p2)->addMapping(MultiRange(genenr, genenr));
-		return vertex_here;
-	}
-}
-
-int GenoConv_f90::findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &vertex)
-{
-	for (size_t i = 0; i < vertices.size(); i++)
-		if (vertices[i].same_coordinates(vertex)) return i;
-	return -1;
-}
-
-
-int GenoConv_f90::addNewVertex(Model &m, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex)
-{
-	Part *p = new Part;
-	p->p.x = new_vertex.x;
-	p->p.y = new_vertex.y;
-	p->p.z = new_vertex.z;
-	m.addPart(p);
-
-	vertices.push_back(new_vertex);
-	return vertices.size() - 1;
-}
-
-double mix(int *colortab, int maxind, double ind)
-{
-	int indpre = (int)ind;
-	int indpost = indpre + 1;
-	if (indpost > maxind) indpost = maxind;
-	int v1 = colortab[indpre];
-	int v2 = colortab[indpost];
-	double d1 = ind - indpre;
-	double d2 = indpost - ind;
-	double v = indpre == indpost ? v1 : d2*v1 + d1*v2; //d1+d2==1
-	return v;
-}
-
-void GenoConv_f90::setColors(Model &m, int last_added_part) //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
-{
-	//a rainbow on Joints: from the first one red, through middle green, to blue or violet - last
-	static int r[] = { 1, 1, 0, 0, 0, 1 };
-	static int g[] = { 0, 1, 1, 1, 0, 0 };
-	static int b[] = { 0, 0, 0, 1, 1, 1 };
-	int maxind = ARRAY_LENGTH(r) - 1;
-
-	int joints_count = m.getJointCount();
-	for (int i = 0; i < joints_count; i++)
-	{
-		Joint *j = m.getJoint(i);
-		double x = joints_count < 2 ? 0 : (double)i / (joints_count - 1); //0..1, postion in the rainbow
-		double ind = x*maxind;
-		j->vcolor.x = mix(r, maxind, ind);
-		j->vcolor.y = mix(g, maxind, ind);
-		j->vcolor.z = mix(b, maxind, ind);
-	}
-
-	int parts_count = m.getPartCount();
-	SList jlist;
-	for (int i = 0; i < parts_count; i++)
-	{
-		Part *p = m.getPart(i);
-		jlist.clear();
-		int count = m.findJoints(jlist, p);
-		Pt3D averagecolor(0, 0, 0); //Parts will get averaged colors from all attached Joints
-		FOREACH(Joint*, j, jlist)
-			averagecolor += j->vcolor;
-		p->vcolor = averagecolor / count;
-		if (count>5) count = 5; //avoid too fat...
-		p->vsize = 0.3 + count / 15.0; //the more Joints is attached to a Part, the fatter it is
-	}
-	//m.getPart(0)->vcolor = Pt3D(0, 0, 0); //mark first Part black - a visual aid for easier editing
-	m.getPart(last_added_part)->vcolor = Pt3D(1, 1, 1); //mark last Part white - a visual aid for easier editing
-}
-
-void GenoConv_f90::perturbPartLocations(Model &m) //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
-{
-	for (int i = 0; i < m.getPartCount(); i++)
-	{
-		Part *p = m.getPart(i);
-		Pt3D noise(
-			((i + 1) % 10) - 4.5,
-			((3 * i + 5) % 10) - 4.5,
-			((7 * i + 2) % 10) - 4.5
-			); //-4.5 .. 4.5 in each axis
-		p->p += noise / 1000;
-	}
-}
Index: cpp/frams/genetics/f9/conv_f9.h
===================================================================
--- cpp/frams/genetics/f9/conv_f9.h	(revision 777)
+++ 	(revision )
@@ -1,48 +1,0 @@
-// 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.
-
-#ifndef _CONV_F9_H_
-#define _CONV_F9_H_
-
-#include <ctype.h>
-#include <common/nonstd_math.h>
-#include <frams/model/modelparts.h>
-#include <frams/util/multimap.h>
-#include <frams/util/sstring.h>
-#include <frams/genetics/genoconv.h>
-#include <vector>
-using std::vector;
-
-
-extern const char* turtle_commands_f9;
-
-
-struct XYZ_LOC
-{
-	int x, y, z; //coordinates xyz of a vertex - represented as int's so that it is easy and safe to check identity. Could also be done using lists of Model's Parts, but that would involve comparing floats
-	XYZ_LOC() { x = y = z = 0; }
-	void add(int delta[3]) { x += delta[0]; y += delta[1]; z += delta[2]; }
-	bool same_coordinates(const XYZ_LOC &loc) { return x == loc.x && y == loc.y && z == loc.z; }
-};
-
-
-// The f9->f0 converter
-class GenoConv_f90 : public GenoConverter
-{
-public:
-	GenoConv_f90();
-
-	//implementation of the GenoConverter method
-	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
-
-protected:
-	//auxiliary methods
-	int addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added);
-	int findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex);
-	int addNewVertex(Model &m, vector<XYZ_LOC> &punkty, const XYZ_LOC &nowypunkt);
-	void setColors(Model &m, int last_added_part); //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
-	void perturbPartLocations(Model &m); //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
-};
-
-#endif
Index: cpp/frams/genetics/f9/f9_conv.cpp
===================================================================
--- cpp/frams/genetics/f9/f9_conv.cpp	(revision 779)
+++ cpp/frams/genetics/f9/f9_conv.cpp	(revision 779)
@@ -0,0 +1,174 @@
+// 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 "f9_conv.h"
+#include <frams/model/model.h>
+#include <string.h>
+#include <common/nonstd_stl.h> //ARRAY_LENGTH
+
+#define APPLY_DETERMINISTIC_BODY_NOISE //this genetic representation easily produces perfectly vertical sticks that would stay upright forever in simulation. In most cases such infinite perfection is not desired, so we make the construct less perfect by perturbing its coordinates.
+
+GenoConv_f90::GenoConv_f90()
+{
+	name = "Turtle3D-ortho encoding";
+	in_format = '9';
+	out_format = '0';
+	mapsupport = 1;
+}
+
+
+const char* turtle_commands_f9 = "LRBFDU";
+
+//const char* turtle_commandsX_f9="-+0000";
+//const char* turtle_commandsY_f9="00-+00";
+//const char* turtle_commandsZ_f9="0000-+";
+
+SString GenoConv_f90::convert(SString &in, MultiMap *map, bool using_checkpoints)
+{
+	vector<XYZ_LOC> vertices;
+	XYZ_LOC current;
+	Model m;
+	m.open(using_checkpoints);
+	int recently_added = addSegment(m, 0, vertices, current, 0xDead);
+	for (int i = 0; i < in.len(); i++)
+	{
+		char command = in[i];
+		char *ptr = strchr((char*)turtle_commands_f9, command);
+		if (ptr)
+		{
+			int delta[] = { 0, 0, 0 };
+			int pos = ptr - turtle_commands_f9;
+			int axis = pos / 2;
+			int dir = pos % 2;
+			(*(delta + axis)) += dir * 2 - 1; //+1 or -1 in the given axis
+			current.add(delta);
+			recently_added = addSegment(m, i, vertices, current, recently_added);
+			m.checkpoint();
+		}
+	}
+#ifdef APPLY_DETERMINISTIC_BODY_NOISE
+	perturbPartLocations(m);
+#endif
+	setColors(m, recently_added);
+	m.close();
+	if (m.getPartCount() < 2) //only one part <=> there were no valid turtle commands in the input genotype
+		return ""; //so we return an invalid f0 genotype
+	if (map != NULL)
+		m.getCurrentToF0Map(*map);
+	return m.getF0Geno().getGenes();
+}
+
+int GenoConv_f90::addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added)
+{
+	if (vertices.size() < 1) //empty model?
+	{
+		return addNewVertex(m, vertices, new_vertex);
+	}
+	else
+	{
+		int vertex_here = findVertexAt(vertices, new_vertex);
+		if (vertex_here < 0) //need to create a new Part
+		{
+			vertex_here = addNewVertex(m, vertices, new_vertex);
+		} //else there already exists a Part in new_vertex; new Joint may or may not be needed
+		Part *p1 = m.getPart(recently_added);
+		Part *p2 = m.getPart(vertex_here);
+		p1->addMapping(MultiRange(genenr, genenr));
+		p2->addMapping(MultiRange(genenr, genenr));
+
+		int j12 = m.findJoint(p1, p2);
+		int j21 = m.findJoint(p2, p1);
+		if (j12 >= 0)
+			m.getJoint(j12)->addMapping(MultiRange(genenr, genenr));
+		else if (j21 >= 0)
+			m.getJoint(j21)->addMapping(MultiRange(genenr, genenr));
+		else //both j12<0 and j21<0. New Joint needed. Should always happen if we just created a new Part (vertex_here was <0)
+			m.addNewJoint(p1, p2)->addMapping(MultiRange(genenr, genenr));
+		return vertex_here;
+	}
+}
+
+int GenoConv_f90::findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &vertex)
+{
+	for (size_t i = 0; i < vertices.size(); i++)
+		if (vertices[i].same_coordinates(vertex)) return i;
+	return -1;
+}
+
+
+int GenoConv_f90::addNewVertex(Model &m, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex)
+{
+	Part *p = new Part;
+	p->p.x = new_vertex.x;
+	p->p.y = new_vertex.y;
+	p->p.z = new_vertex.z;
+	m.addPart(p);
+
+	vertices.push_back(new_vertex);
+	return vertices.size() - 1;
+}
+
+double mix(int *colortab, int maxind, double ind)
+{
+	int indpre = (int)ind;
+	int indpost = indpre + 1;
+	if (indpost > maxind) indpost = maxind;
+	int v1 = colortab[indpre];
+	int v2 = colortab[indpost];
+	double d1 = ind - indpre;
+	double d2 = indpost - ind;
+	double v = indpre == indpost ? v1 : d2*v1 + d1*v2; //d1+d2==1
+	return v;
+}
+
+void GenoConv_f90::setColors(Model &m, int last_added_part) //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
+{
+	//a rainbow on Joints: from the first one red, through middle green, to blue or violet - last
+	static int r[] = { 1, 1, 0, 0, 0, 1 };
+	static int g[] = { 0, 1, 1, 1, 0, 0 };
+	static int b[] = { 0, 0, 0, 1, 1, 1 };
+	int maxind = ARRAY_LENGTH(r) - 1;
+
+	int joints_count = m.getJointCount();
+	for (int i = 0; i < joints_count; i++)
+	{
+		Joint *j = m.getJoint(i);
+		double x = joints_count < 2 ? 0 : (double)i / (joints_count - 1); //0..1, postion in the rainbow
+		double ind = x*maxind;
+		j->vcolor.x = mix(r, maxind, ind);
+		j->vcolor.y = mix(g, maxind, ind);
+		j->vcolor.z = mix(b, maxind, ind);
+	}
+
+	int parts_count = m.getPartCount();
+	SList jlist;
+	for (int i = 0; i < parts_count; i++)
+	{
+		Part *p = m.getPart(i);
+		jlist.clear();
+		int count = m.findJoints(jlist, p);
+		Pt3D averagecolor(0, 0, 0); //Parts will get averaged colors from all attached Joints
+		FOREACH(Joint*, j, jlist)
+			averagecolor += j->vcolor;
+		p->vcolor = averagecolor / count;
+		if (count>5) count = 5; //avoid too fat...
+		p->vsize = 0.3 + count / 15.0; //the more Joints is attached to a Part, the fatter it is
+	}
+	//m.getPart(0)->vcolor = Pt3D(0, 0, 0); //mark first Part black - a visual aid for easier editing
+	m.getPart(last_added_part)->vcolor = Pt3D(1, 1, 1); //mark last Part white - a visual aid for easier editing
+}
+
+void GenoConv_f90::perturbPartLocations(Model &m) //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
+{
+	for (int i = 0; i < m.getPartCount(); i++)
+	{
+		Part *p = m.getPart(i);
+		Pt3D noise(
+			((i + 1) % 10) - 4.5,
+			((3 * i + 5) % 10) - 4.5,
+			((7 * i + 2) % 10) - 4.5
+			); //-4.5 .. 4.5 in each axis
+		p->p += noise / 1000;
+	}
+}
Index: cpp/frams/genetics/f9/f9_conv.h
===================================================================
--- cpp/frams/genetics/f9/f9_conv.h	(revision 779)
+++ cpp/frams/genetics/f9/f9_conv.h	(revision 779)
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef _F9_CONV_H_
+#define _F9_CONV_H_
+
+#include <ctype.h>
+#include <common/nonstd_math.h>
+#include <frams/model/modelparts.h>
+#include <frams/util/multimap.h>
+#include <frams/util/sstring.h>
+#include <frams/genetics/genoconv.h>
+#include <vector>
+using std::vector;
+
+
+extern const char* turtle_commands_f9;
+
+
+struct XYZ_LOC
+{
+	int x, y, z; //coordinates xyz of a vertex - represented as int's so that it is easy and safe to check identity. Could also be done using lists of Model's Parts, but that would involve comparing floats
+	XYZ_LOC() { x = y = z = 0; }
+	void add(int delta[3]) { x += delta[0]; y += delta[1]; z += delta[2]; }
+	bool same_coordinates(const XYZ_LOC &loc) { return x == loc.x && y == loc.y && z == loc.z; }
+};
+
+
+// The f9->f0 converter
+class GenoConv_f90 : public GenoConverter
+{
+public:
+	GenoConv_f90();
+
+	//implementation of the GenoConverter method
+	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
+
+protected:
+	//auxiliary methods
+	int addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added);
+	int findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex);
+	int addNewVertex(Model &m, vector<XYZ_LOC> &punkty, const XYZ_LOC &nowypunkt);
+	void setColors(Model &m, int last_added_part); //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
+	void perturbPartLocations(Model &m); //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
+};
+
+#endif
Index: cpp/frams/genetics/f9/f9_oper.cpp
===================================================================
--- cpp/frams/genetics/f9/f9_oper.cpp	(revision 779)
+++ cpp/frams/genetics/f9/f9_oper.cpp	(revision 779)
@@ -0,0 +1,123 @@
+// 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.
+
+#include "f9_oper.h"
+#include "f9_conv.h"
+#include <common/nonstd.h> //randomN, rnd01
+
+
+#define FIELDSTRUCT GenoOper_f9
+static ParamEntry GENOf9param_tab[] =
+{
+	{ "Genetics: f9", 1, 1, },
+	{ "f9_mut", 0, 0, "Mutation probability", "f 0 1 0.1", FIELD(mut_prob), "How many genes should be mutated during single mutation (1=all genes, 0.1=ten percent)", },
+	{ 0, },
+};
+#undef FIELDSTRUCT
+
+
+GenoOper_f9::GenoOper_f9()
+{
+	par.setParamTab(GENOf9param_tab);
+	par.select(this);
+	par.setDefault();
+	supported_format = '9';
+}
+
+int GenoOper_f9::checkValidity(const char* gene, const char *genoname)
+{
+	if (!gene[0]) return 1; //empty is not valid
+	bool ok = true;
+	size_t i;
+	for (i = 0; i < strlen(gene); i++) if (!strchr(turtle_commands_f9, gene[i])) { ok = false; break; }
+	return ok ? GENOPER_OK : i + 1;
+}
+
+///Remove all invalid letters from the genotype
+int GenoOper_f9::validate(char *&gene, const char *genoname)
+{
+	SString validated; //new genotype (everything except turtle_commands_f9 is skipped)
+	for (size_t i = 0; i < strlen(gene); i++)
+		if (strchr(turtle_commands_f9, gene[i])) validated += gene[i];  //validated contains only turtle_commands_f9
+	free(gene);
+	gene = strdup(validated.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+///Very simple mutation
+int GenoOper_f9::mutate(char *&gene, float &chg, int &method)
+{
+	method = 0;
+	int changes = 0, len = strlen(gene);
+	int symbols = strlen(turtle_commands_f9);
+
+	for (int i = 0; i < len; i++)
+	{
+		if (rnd01 < mut_prob) //normalize prob with the length of the genotype
+		{
+			char oldgene = gene[i];
+			gene[i] = turtle_commands_f9[randomN(symbols)];
+			if (gene[i] != oldgene) changes++;
+		}
+	}
+
+	if (rnd01 < mut_prob) //add or delete a random char
+	{
+		SString newgeno(gene);
+		if (randomN(2) == 0) //add
+		{
+			int symbols = strlen(turtle_commands_f9);
+			int p = randomN(len + 1);  //random location
+			//printf("before add: %s\n",(const char*)newgeno);
+			newgeno = newgeno.substr(0, p) + SString(turtle_commands_f9 + randomN(symbols), 1) + newgeno.substr(p);
+			//printf("after add: %s\n",(const char*)newgeno);
+			changes++;
+		}
+		else if (len > 1) //delete
+		{
+			int p = randomN(len);  //random location
+			//printf("before delete: %s\n",(const char*)newgeno);
+			newgeno = newgeno.substr(0, p) + newgeno.substr(p + 1);
+			//printf("after delete: %s\n",(const char*)newgeno);
+			changes++;
+		}
+		free(gene);
+		gene = strdup(newgeno.c_str()); //reallocate
+	}
+
+	chg = (float)changes / len;
+	return changes > 0 ? GENOPER_OK : GENOPER_OPFAIL; //no changes => OPFAIL so that GenMan will call mutate again
+}
+
+///A simple one-point crossover
+int GenoOper_f9::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
+{
+	int len1 = strlen(g1), len2 = strlen(g2);
+	int p1 = randomN(len1);  //random cut point for first genotype
+	int p2 = randomN(len2);  //random cut point for second genotype
+	char *child1 = (char*)malloc(p1 + len2 - p2 + 1);
+	char *child2 = (char*)malloc(p2 + len1 - p1 + 1);
+	strncpy(child1, g1, p1);   strcpy(child1 + p1, g2 + p2);
+	strncpy(child2, g2, p2);   strcpy(child2 + p2, g1 + p1);
+	free(g1); g1 = child1;
+	free(g2); g2 = child2;
+	chg1 = (float)p1 / strlen(child1);
+	chg2 = (float)p2 / strlen(child2);
+	return GENOPER_OK;
+}
+
+///Applying some colors and font styles...
+uint32_t GenoOper_f9::style(const char *g, int pos)
+{
+	char ch = g[pos];
+	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
+	char *ptr = strchr((char*)turtle_commands_f9, ch);
+	if (ptr)
+	{
+		int pos = ptr - turtle_commands_f9;
+		int axis = pos / 2;
+		style = GENSTYLE_RGBS(axis == 0 ? 200 : 0, axis == 1 ? 200 : 0, axis == 2 ? 200 : 0, GENSTYLE_NONE);
+	}
+	return style;
+}
Index: cpp/frams/genetics/f9/f9_oper.h
===================================================================
--- cpp/frams/genetics/f9/f9_oper.h	(revision 779)
+++ cpp/frams/genetics/f9/f9_oper.h	(revision 779)
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef _F9_OPER_H_
+#define _F9_OPER_H_
+
+#include "../genooperators.h"
+
+
+class GenoOper_f9 : public GenoOperators
+{
+public:
+	GenoOper_f9();
+	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);
+	uint32_t style(const char *g, int pos);
+	const char* getSimplest() { return "R"; }
+
+	double mut_prob; //mutation probability
+};
+
+#endif
Index: cpp/frams/genetics/f9/oper_f9.cpp
===================================================================
--- cpp/frams/genetics/f9/oper_f9.cpp	(revision 777)
+++ 	(revision )
@@ -1,123 +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.
-
-#include "oper_f9.h"
-#include "conv_f9.h"
-#include <common/nonstd.h> //randomN, rnd01
-
-
-#define FIELDSTRUCT GenoOper_f9
-static ParamEntry GENOf9param_tab[] =
-{
-	{ "Genetics: f9", 1, 1, },
-	{ "f9_mut", 0, 0, "Mutation probability", "f 0 1 0.1", FIELD(mut_prob), "How many genes should be mutated during single mutation (1=all genes, 0.1=ten percent)", },
-	{ 0, },
-};
-#undef FIELDSTRUCT
-
-
-GenoOper_f9::GenoOper_f9()
-{
-	par.setParamTab(GENOf9param_tab);
-	par.select(this);
-	par.setDefault();
-	supported_format = '9';
-}
-
-int GenoOper_f9::checkValidity(const char* gene, const char *genoname)
-{
-	if (!gene[0]) return 1; //empty is not valid
-	bool ok = true;
-	size_t i;
-	for (i = 0; i < strlen(gene); i++) if (!strchr(turtle_commands_f9, gene[i])) { ok = false; break; }
-	return ok ? GENOPER_OK : i + 1;
-}
-
-///Remove all invalid letters from the genotype
-int GenoOper_f9::validate(char *&gene, const char *genoname)
-{
-	SString validated; //new genotype (everything except turtle_commands_f9 is skipped)
-	for (size_t i = 0; i < strlen(gene); i++)
-		if (strchr(turtle_commands_f9, gene[i])) validated += gene[i];  //validated contains only turtle_commands_f9
-	free(gene);
-	gene = strdup(validated.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-///Very simple mutation
-int GenoOper_f9::mutate(char *&gene, float &chg, int &method)
-{
-	method = 0;
-	int changes = 0, len = strlen(gene);
-	int symbols = strlen(turtle_commands_f9);
-
-	for (int i = 0; i < len; i++)
-	{
-		if (rnd01 < mut_prob) //normalize prob with the length of the genotype
-		{
-			char oldgene = gene[i];
-			gene[i] = turtle_commands_f9[randomN(symbols)];
-			if (gene[i] != oldgene) changes++;
-		}
-	}
-
-	if (rnd01 < mut_prob) //add or delete a random char
-	{
-		SString newgeno(gene);
-		if (randomN(2) == 0) //add
-		{
-			int symbols = strlen(turtle_commands_f9);
-			int p = randomN(len + 1);  //random location
-			//printf("before add: %s\n",(const char*)newgeno);
-			newgeno = newgeno.substr(0, p) + SString(turtle_commands_f9 + randomN(symbols), 1) + newgeno.substr(p);
-			//printf("after add: %s\n",(const char*)newgeno);
-			changes++;
-		}
-		else if (len > 1) //delete
-		{
-			int p = randomN(len);  //random location
-			//printf("before delete: %s\n",(const char*)newgeno);
-			newgeno = newgeno.substr(0, p) + newgeno.substr(p + 1);
-			//printf("after delete: %s\n",(const char*)newgeno);
-			changes++;
-		}
-		free(gene);
-		gene = strdup(newgeno.c_str()); //reallocate
-	}
-
-	chg = (float)changes / len;
-	return changes > 0 ? GENOPER_OK : GENOPER_OPFAIL; //no changes => OPFAIL so that GenMan will call mutate again
-}
-
-///A simple one-point crossover
-int GenoOper_f9::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
-{
-	int len1 = strlen(g1), len2 = strlen(g2);
-	int p1 = randomN(len1);  //random cut point for first genotype
-	int p2 = randomN(len2);  //random cut point for second genotype
-	char *child1 = (char*)malloc(p1 + len2 - p2 + 1);
-	char *child2 = (char*)malloc(p2 + len1 - p1 + 1);
-	strncpy(child1, g1, p1);   strcpy(child1 + p1, g2 + p2);
-	strncpy(child2, g2, p2);   strcpy(child2 + p2, g1 + p1);
-	free(g1); g1 = child1;
-	free(g2); g2 = child2;
-	chg1 = (float)p1 / strlen(child1);
-	chg2 = (float)p2 / strlen(child2);
-	return GENOPER_OK;
-}
-
-///Applying some colors and font styles...
-uint32_t GenoOper_f9::style(const char *g, int pos)
-{
-	char ch = g[pos];
-	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
-	char *ptr = strchr((char*)turtle_commands_f9, ch);
-	if (ptr)
-	{
-		int pos = ptr - turtle_commands_f9;
-		int axis = pos / 2;
-		style = GENSTYLE_RGBS(axis == 0 ? 200 : 0, axis == 1 ? 200 : 0, axis == 2 ? 200 : 0, GENSTYLE_NONE);
-	}
-	return style;
-}
Index: cpp/frams/genetics/f9/oper_f9.h
===================================================================
--- cpp/frams/genetics/f9/oper_f9.h	(revision 777)
+++ 	(revision )
@@ -1,25 +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.
-
-#ifndef _GENOOPER_F9_H_
-#define _GENOOPER_F9_H_
-
-#include "../oper_fx.h"
-
-
-class GenoOper_f9 : public GenoOperators
-{
-public:
-	GenoOper_f9();
-	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);
-	uint32_t style(const char *g, int pos);
-	const char* getSimplest() { return "R"; }
-
-	double mut_prob; //mutation probability
-};
-
-#endif
Index: cpp/frams/genetics/fF/conv_fF.cpp
===================================================================
--- cpp/frams/genetics/fF/conv_fF.cpp	(revision 777)
+++ 	(revision )
@@ -1,309 +1,0 @@
-// 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 "conv_fF.h"
-#include "fF_genotype.h"
-#include <common/nonstd_stl.h>
-#include <common/Convert.h>
-
-GenoConv_fF0::GenoConv_fF0()
-{
-	name = "10-parameter Foraminifera encoding";
-	in_format = 'F';
-	out_format = '0';
-	mapsupport = 0;
-	cosines = new double[fF_LATITUDE_NUM];
-	sines = new double[fF_LATITUDE_NUM];
-	precompute_cos_and_sin();
-}
-
-GenoConv_fF0::~GenoConv_fF0()
-{
-	delete[] cosines;
-	delete[] sines;
-}
-
-Part *GenoConv_fF0::addNewPart(Model *m, const fF_chamber3d* c)
-{
-	Part *part = m->addNewPart(Part::SHAPE_ELLIPSOID);
-	part->p = Pt3D(c->centerX, c->centerY, c->centerZ);
-	Pt3D hole = Pt3D(c->holeX, c->holeY, c->holeZ);
-	Orient o;
-	o.lookAt(part->p - hole);
-	part->setOrient(o);
-	return part;
-}
-
-SString GenoConv_fF0::convert(SString &in, MultiMap *map, bool using_checkpoints)
-{
-	fF_growth_params gp;
-	if (!gp.load(in.c_str())) //invalid input genotype?
-		return ""; //so we return an invalid f0 genotype
-
-	Model m;
-	m.open(using_checkpoints);
-
-	m.vis_style = "foram"; //dedicated visual look for Foraminifera
-
-	fF_chamber3d **chambers = new fF_chamber3d*[gp.number_of_chambers];
-	for (int i = 0; i < gp.number_of_chambers; i++)
-		createSphere(i, chambers, gp.radius0x, gp.radius0y, gp.radius0z, gp.translation, gp.angle1, gp.angle2, gp.scalex, gp.scaley, gp.scalez);
-
-	Part *p1 = addNewPart(&m, chambers[0]);
-	p1->scale = Pt3D(gp.radius0x, gp.radius0y, gp.radius0z); //size of the initial chamber
-	m.checkpoint();
-	for (int i = 1; i < gp.number_of_chambers; i++)
-	{
-		Part *p2 = addNewPart(&m, chambers[i]);
-		p2->scale = p1->scale.entrywiseProduct(Pt3D(gp.scalex, gp.scaley, gp.scalez)); //each part's scale is its predecessor's scale * scaling
-		m.addNewJoint(p1, p2, Joint::SHAPE_FIXED); //all parts must be connected
-		m.checkpoint();
-		p1 = p2;
-	}
-
-	for (int i = 0; i < gp.number_of_chambers; i++)
-		delete chambers[i];
-	delete[] chambers;
-
-	m.close();
-	return m.getF0Geno().getGenes();
-}
-
-void GenoConv_fF0::createSphere(int which, fF_chamber3d **chambers, double radius0x, double radius0y, double radius0z, double translation, double alpha_, double gamma_, double kx_, double ky_, double kz_)
-{
-	chambers[which] = new fF_chamber3d(0.0, 0.0, 0.0,
-		radius0x, radius0y, radius0z, radius0x * kx_, 0.0, 0.0,
-		radius0x * translation, 0.0, 0.0, 0.0, 0.0);
-	if (which == 0)
-		chambers[which]->points = generate_points(chambers[which]);
-	if (which > 0)
-	{
-		chambers[which]->radius_x = get_radius(chambers[which - 1]->radius_x, kx_, chambers[0]->radius_x);
-		chambers[which]->radius_y = get_radius(chambers[which - 1]->radius_y, ky_, chambers[0]->radius_y);
-		chambers[which]->radius_z = get_radius(chambers[which - 1]->radius_z, kz_, chambers[0]->radius_z);
-
-		/* new growth vector length */
-		double len = chambers[which]->radius_y * translation;
-		double max_radius = fF_TOO_MUCH * chambers[which]->radius_y;
-		if (fabs(len) > (max_radius))
-			len = ((len < 0) ? (-1) : 1) * max_radius;
-		if (len == 0)
-			len = -0.0000001;
-
-		/* aperture of the previous chamber */
-		double pzx = chambers[which - 1]->holeX;
-		double pzy = chambers[which - 1]->holeY;
-		double pzz = chambers[which - 1]->holeZ;
-
-		//center of the previous chamber
-		double pcx = chambers[which - 1]->centerX;
-		double pcy = chambers[which - 1]->centerY;
-		//double pcz = chambers[which - 1]->centerZ; //not used
-
-		/* aperture of the next to last chamber */
-		double ppx;
-		double ppy;
-		//double ppz; //not used
-
-		if (which == 1)
-		{
-			ppx = pcx;
-			ppy = pcy;
-			//ppz = pcz;
-		}
-		else
-		{
-			ppx = chambers[which - 2]->holeX;
-			ppy = chambers[which - 2]->holeY;
-			//ppz = chambers[which - 2]->holeZ;
-		}
-
-		double pzxprim = pzx - ppx;
-		double pzyprim = pzy - ppy;
-		double angle;
-
-		angle = Convert::atan_2(pzyprim, pzxprim);
-		double alpha = angle - alpha_;
-
-		double gamma = chambers[which - 1]->phi + gamma_;
-
-		double wx = len * cos(alpha);
-		double wy = len * sin(alpha);
-		double wz = len * sin(alpha) * sin(gamma);
-
-		/*center of the new sphere*/
-		double x = pzx + wx;
-		double y = pzy + wy;
-		double z = pzz + wz;
-
-		chambers[which]->centerX = x;
-		chambers[which]->centerY = y;
-		chambers[which]->centerZ = z;
-		chambers[which]->vectorTfX = wx;
-		chambers[which]->vectorTfY = wy;
-		chambers[which]->vectorTfZ = wz;
-		chambers[which]->beta = alpha;
-		chambers[which]->phi = gamma;
-
-		chambers[which]->points = generate_points(chambers[which]);
-		search_hid(which, chambers);
-		int pun;
-		pun = find_hole(which, pzx, pzy, pzz, chambers);
-		if (pun < 0) //should never happen
-		{
-			logPrintf("GenoConv_fF0", "createSphere", LOG_ERROR, "find_hole(%d) returned %d", which, pun);
-			pun = 0;
-		}
-		chambers[which]->holeX = chambers[which]->points[pun].x;
-		chambers[which]->holeY = chambers[which]->points[pun].y;
-		chambers[which]->holeZ = chambers[which]->points[pun].z;
-	}
-}
-
-double GenoConv_fF0::get_radius(double prev_radius, double scale, double start_radius)
-{
-	double radius = prev_radius * scale;
-	double min_radius = fF_TOO_LITTLE*start_radius;
-	if (radius < min_radius) {
-		radius = min_radius;
-	}
-
-	return radius;
-}
-
-void GenoConv_fF0::precompute_cos_and_sin()
-{
-	double angle = M_PI * 2 / fF_LATITUDE_NUM;
-	for (int i = 0; i < fF_LATITUDE_NUM; i++)
-	{
-		cosines[i] = cos(i * angle);
-		sines[i] = sin(i * angle);
-	}
-}
-
-fF_point* GenoConv_fF0::generate_points(fF_chamber3d *chamber)
-{
-	double cenx = chamber->centerX;
-	double ceny = chamber->centerY;
-	double cenz = chamber->centerZ;
-
-	double rx = chamber->radius_x;
-	double ry = chamber->radius_y;
-	double rz = chamber->radius_z;
-
-	fF_point *points = new fF_point[fF_SIZE];
-
-	for (int i = 0; i < fF_LONGITUDE_NUM; i++)
-	{
-		double y = ceny + ry * cosines[i];
-
-		for (int j = 0; j < fF_LATITUDE_NUM; j++)
-		{
-			double x = cenx + rx * cosines[j] * sines[i];
-			double z = cenz + rz * sines[j] * sines[i];
-			fF_point &p = points[(i * fF_LATITUDE_NUM) + j];
-			p.x = x;
-			p.y = y;
-			p.z = z;
-			p.inside = false;
-		}
-	}
-	return points;
-
-}
-
-template<typename T> T Square(T x) { return x * x; }
-
-double GenoConv_fF0::dist(double x1, double y1, double z1, double x2, double y2, double z2)
-{
-	return sqrt(Square(x2 - x1) + Square(y2 - y1) + Square(z2 - z1));
-}
-
-void GenoConv_fF0::search_hid(int nr, fF_chamber3d **chambers)
-{
-	for (int i = 0; i < nr; i++)
-	{
-		fF_chamber3d *chamber = chambers[i];
-
-		double rx_sq = Square(chamber->radius_x);
-		double ry_sq = Square(chamber->radius_y);
-		double rz_sq = Square(chamber->radius_z);
-
-		for (int j = 0; j < fF_AMOUNT; j++)
-		{
-			fF_point &p = chambers[nr]->points[j];
-
-			double upx = Square(p.x - chamber->centerX);
-			double upy = Square(p.y - chamber->centerY);
-			double upz = Square(p.z - chamber->centerZ);
-
-			double expx = upx / rx_sq;
-			double expy = upy / ry_sq;
-			double expz = upz / rz_sq;
-
-			double result = expx + expy + expz;
-
-			if (result < fF_THICK_RATIO)
-			{
-				p.inside = true;
-			}
-		}
-	}
-}
-
-int GenoConv_fF0::find_hole(int which, double x, double y, double z, fF_chamber3d **chambers)
-{
-	int found = -1;
-	double distsq_found;
-
-	for (int i = 0; i < fF_AMOUNT; i++)
-	{
-		fF_point &p = chambers[which]->points[i];
-		if (!p.inside) //it is not inside another chamber
-		{
-			double distancesq = Square(p.x - x) + Square(p.y - y) + Square(p.z - z);
-			if (found < 0)
-			{
-				found = i;
-				distsq_found = distancesq;
-			}
-			if (distancesq < distsq_found)
-			{
-				if (which != 0)
-				{
-					bool good = true;
-					for (int j = 0; j < which && good; j++)
-					{
-						fF_chamber3d *chamber = chambers[j];
-
-						double rx_sq = Square(chamber->radius_x);
-						double ry_sq = Square(chamber->radius_y);
-						double rz_sq = Square(chamber->radius_z);
-
-						double upx = Square(p.x - chamber->centerX);
-						double upy = Square(p.y - chamber->centerY);
-						double upz = Square(p.z - chamber->centerZ);
-
-						double expx = upx / rx_sq;
-						double expy = upy / ry_sq;
-						double expz = upz / rz_sq;
-
-						double result = expx + expy + expz;
-						if (result < 1.0)
-						{
-							good = false;
-						}
-					}
-					if (good)
-					{
-						found = i;
-						distsq_found = distancesq;
-					}
-				}
-			}
-		}
-	}
-
-	return found;
-}
Index: cpp/frams/genetics/fF/conv_fF.h
===================================================================
--- cpp/frams/genetics/fF/conv_fF.h	(revision 777)
+++ 	(revision )
@@ -1,58 +1,0 @@
-// 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.
-
-#ifndef _CONV_FF_H_
-#define _CONV_FF_H_
-
-#define fF_TOO_MUCH 0.75
-#define fF_TOO_LITTLE 0.10
-
-#define fF_HOLE_RADIUS 0.05
-#define fF_LONGITUDE_NUM 69
-
-#define fF_LATITUDE_NUM ((fF_LONGITUDE_NUM - 1)*2)
-#define fF_AMOUNT ((fF_LATITUDE_NUM)*(fF_LONGITUDE_NUM))
-
-#define fF_THICK_RATIO 0.95
-
-#define fF_SIZE fF_LONGITUDE_NUM * fF_LATITUDE_NUM + fF_LATITUDE_NUM
-
-#include <frams/util/multimap.h>
-#include <frams/util/sstring.h>
-#include <frams/genetics/genoconv.h>
-#include <frams/model/model.h>
-#include "fF_chamber3d.h"
-
-
-//A point on the surface of a chamber
-struct fF_point
-{
-	double x, y, z;
-	bool inside; //helper field used when computing whether this point is inside some chamber
-};
-
-
-// The f9->f0 converter
-class GenoConv_fF0 : public GenoConverter {
-public:
-	GenoConv_fF0();
-	~GenoConv_fF0();
-	//implementation of the GenoConverter method
-	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
-
-protected:
-	void createSphere(int ktora, fF_chamber3d **chambers, double radius0x, double radius0y, double radius0z, double translation, double alpha, double gamma, double kx, double ky, double kz);
-	fF_point* generate_points(fF_chamber3d *chamber);
-	double dist(double x1, double y1, double z1, double x2, double y2, double z2);
-	void search_hid(int nr, fF_chamber3d **spheres);
-	int find_hole(int which, double x, double y, double z, fF_chamber3d **chambers);
-	double get_radius(double prev_radius, double scale, double radius0);
-private:
-	double* cosines;
-	double* sines;
-	void precompute_cos_and_sin();
-	Part *addNewPart(Model *m, const fF_chamber3d* c);
-};
-
-#endif
Index: cpp/frams/genetics/fF/fF_chamber3d.cpp
===================================================================
--- cpp/frams/genetics/fF/fF_chamber3d.cpp	(revision 777)
+++ cpp/frams/genetics/fF/fF_chamber3d.cpp	(revision 779)
@@ -4,5 +4,5 @@
 
 #include "fF_chamber3d.h"
-#include "conv_fF.h"
+#include "fF_conv.h"
 #include <iostream>
 
Index: cpp/frams/genetics/fF/fF_chamber3d.h
===================================================================
--- cpp/frams/genetics/fF/fF_chamber3d.h	(revision 777)
+++ cpp/frams/genetics/fF/fF_chamber3d.h	(revision 779)
@@ -3,6 +3,6 @@
 // See LICENSE.txt for details.
 
-#ifndef CHAMBER3D_H
-#define	CHAMBER3D_H
+#ifndef _FF_CHAMBER3D_H_
+#define	_FF_CHAMBER3D_H_
 
 struct fF_point;
Index: cpp/frams/genetics/fF/fF_conv.cpp
===================================================================
--- cpp/frams/genetics/fF/fF_conv.cpp	(revision 779)
+++ cpp/frams/genetics/fF/fF_conv.cpp	(revision 779)
@@ -0,0 +1,309 @@
+// 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 "fF_conv.h"
+#include "fF_genotype.h"
+#include <common/nonstd_stl.h>
+#include <common/Convert.h>
+
+GenoConv_fF0::GenoConv_fF0()
+{
+	name = "10-parameter Foraminifera encoding";
+	in_format = 'F';
+	out_format = '0';
+	mapsupport = 0;
+	cosines = new double[fF_LATITUDE_NUM];
+	sines = new double[fF_LATITUDE_NUM];
+	precompute_cos_and_sin();
+}
+
+GenoConv_fF0::~GenoConv_fF0()
+{
+	delete[] cosines;
+	delete[] sines;
+}
+
+Part *GenoConv_fF0::addNewPart(Model *m, const fF_chamber3d* c)
+{
+	Part *part = m->addNewPart(Part::SHAPE_ELLIPSOID);
+	part->p = Pt3D(c->centerX, c->centerY, c->centerZ);
+	Pt3D hole = Pt3D(c->holeX, c->holeY, c->holeZ);
+	Orient o;
+	o.lookAt(part->p - hole);
+	part->setOrient(o);
+	return part;
+}
+
+SString GenoConv_fF0::convert(SString &in, MultiMap *map, bool using_checkpoints)
+{
+	fF_growth_params gp;
+	if (!gp.load(in.c_str())) //invalid input genotype?
+		return ""; //so we return an invalid f0 genotype
+
+	Model m;
+	m.open(using_checkpoints);
+
+	m.vis_style = "foram"; //dedicated visual look for Foraminifera
+
+	fF_chamber3d **chambers = new fF_chamber3d*[gp.number_of_chambers];
+	for (int i = 0; i < gp.number_of_chambers; i++)
+		createSphere(i, chambers, gp.radius0x, gp.radius0y, gp.radius0z, gp.translation, gp.angle1, gp.angle2, gp.scalex, gp.scaley, gp.scalez);
+
+	Part *p1 = addNewPart(&m, chambers[0]);
+	p1->scale = Pt3D(gp.radius0x, gp.radius0y, gp.radius0z); //size of the initial chamber
+	m.checkpoint();
+	for (int i = 1; i < gp.number_of_chambers; i++)
+	{
+		Part *p2 = addNewPart(&m, chambers[i]);
+		p2->scale = p1->scale.entrywiseProduct(Pt3D(gp.scalex, gp.scaley, gp.scalez)); //each part's scale is its predecessor's scale * scaling
+		m.addNewJoint(p1, p2, Joint::SHAPE_FIXED); //all parts must be connected
+		m.checkpoint();
+		p1 = p2;
+	}
+
+	for (int i = 0; i < gp.number_of_chambers; i++)
+		delete chambers[i];
+	delete[] chambers;
+
+	m.close();
+	return m.getF0Geno().getGenes();
+}
+
+void GenoConv_fF0::createSphere(int which, fF_chamber3d **chambers, double radius0x, double radius0y, double radius0z, double translation, double alpha_, double gamma_, double kx_, double ky_, double kz_)
+{
+	chambers[which] = new fF_chamber3d(0.0, 0.0, 0.0,
+		radius0x, radius0y, radius0z, radius0x * kx_, 0.0, 0.0,
+		radius0x * translation, 0.0, 0.0, 0.0, 0.0);
+	if (which == 0)
+		chambers[which]->points = generate_points(chambers[which]);
+	if (which > 0)
+	{
+		chambers[which]->radius_x = get_radius(chambers[which - 1]->radius_x, kx_, chambers[0]->radius_x);
+		chambers[which]->radius_y = get_radius(chambers[which - 1]->radius_y, ky_, chambers[0]->radius_y);
+		chambers[which]->radius_z = get_radius(chambers[which - 1]->radius_z, kz_, chambers[0]->radius_z);
+
+		/* new growth vector length */
+		double len = chambers[which]->radius_y * translation;
+		double max_radius = fF_TOO_MUCH * chambers[which]->radius_y;
+		if (fabs(len) > (max_radius))
+			len = ((len < 0) ? (-1) : 1) * max_radius;
+		if (len == 0)
+			len = -0.0000001;
+
+		/* aperture of the previous chamber */
+		double pzx = chambers[which - 1]->holeX;
+		double pzy = chambers[which - 1]->holeY;
+		double pzz = chambers[which - 1]->holeZ;
+
+		//center of the previous chamber
+		double pcx = chambers[which - 1]->centerX;
+		double pcy = chambers[which - 1]->centerY;
+		//double pcz = chambers[which - 1]->centerZ; //not used
+
+		/* aperture of the next to last chamber */
+		double ppx;
+		double ppy;
+		//double ppz; //not used
+
+		if (which == 1)
+		{
+			ppx = pcx;
+			ppy = pcy;
+			//ppz = pcz;
+		}
+		else
+		{
+			ppx = chambers[which - 2]->holeX;
+			ppy = chambers[which - 2]->holeY;
+			//ppz = chambers[which - 2]->holeZ;
+		}
+
+		double pzxprim = pzx - ppx;
+		double pzyprim = pzy - ppy;
+		double angle;
+
+		angle = Convert::atan_2(pzyprim, pzxprim);
+		double alpha = angle - alpha_;
+
+		double gamma = chambers[which - 1]->phi + gamma_;
+
+		double wx = len * cos(alpha);
+		double wy = len * sin(alpha);
+		double wz = len * sin(alpha) * sin(gamma);
+
+		/*center of the new sphere*/
+		double x = pzx + wx;
+		double y = pzy + wy;
+		double z = pzz + wz;
+
+		chambers[which]->centerX = x;
+		chambers[which]->centerY = y;
+		chambers[which]->centerZ = z;
+		chambers[which]->vectorTfX = wx;
+		chambers[which]->vectorTfY = wy;
+		chambers[which]->vectorTfZ = wz;
+		chambers[which]->beta = alpha;
+		chambers[which]->phi = gamma;
+
+		chambers[which]->points = generate_points(chambers[which]);
+		search_hid(which, chambers);
+		int pun;
+		pun = find_hole(which, pzx, pzy, pzz, chambers);
+		if (pun < 0) //should never happen
+		{
+			logPrintf("GenoConv_fF0", "createSphere", LOG_ERROR, "find_hole(%d) returned %d", which, pun);
+			pun = 0;
+		}
+		chambers[which]->holeX = chambers[which]->points[pun].x;
+		chambers[which]->holeY = chambers[which]->points[pun].y;
+		chambers[which]->holeZ = chambers[which]->points[pun].z;
+	}
+}
+
+double GenoConv_fF0::get_radius(double prev_radius, double scale, double start_radius)
+{
+	double radius = prev_radius * scale;
+	double min_radius = fF_TOO_LITTLE*start_radius;
+	if (radius < min_radius) {
+		radius = min_radius;
+	}
+
+	return radius;
+}
+
+void GenoConv_fF0::precompute_cos_and_sin()
+{
+	double angle = M_PI * 2 / fF_LATITUDE_NUM;
+	for (int i = 0; i < fF_LATITUDE_NUM; i++)
+	{
+		cosines[i] = cos(i * angle);
+		sines[i] = sin(i * angle);
+	}
+}
+
+fF_point* GenoConv_fF0::generate_points(fF_chamber3d *chamber)
+{
+	double cenx = chamber->centerX;
+	double ceny = chamber->centerY;
+	double cenz = chamber->centerZ;
+
+	double rx = chamber->radius_x;
+	double ry = chamber->radius_y;
+	double rz = chamber->radius_z;
+
+	fF_point *points = new fF_point[fF_SIZE];
+
+	for (int i = 0; i < fF_LONGITUDE_NUM; i++)
+	{
+		double y = ceny + ry * cosines[i];
+
+		for (int j = 0; j < fF_LATITUDE_NUM; j++)
+		{
+			double x = cenx + rx * cosines[j] * sines[i];
+			double z = cenz + rz * sines[j] * sines[i];
+			fF_point &p = points[(i * fF_LATITUDE_NUM) + j];
+			p.x = x;
+			p.y = y;
+			p.z = z;
+			p.inside = false;
+		}
+	}
+	return points;
+
+}
+
+template<typename T> T Square(T x) { return x * x; }
+
+double GenoConv_fF0::dist(double x1, double y1, double z1, double x2, double y2, double z2)
+{
+	return sqrt(Square(x2 - x1) + Square(y2 - y1) + Square(z2 - z1));
+}
+
+void GenoConv_fF0::search_hid(int nr, fF_chamber3d **chambers)
+{
+	for (int i = 0; i < nr; i++)
+	{
+		fF_chamber3d *chamber = chambers[i];
+
+		double rx_sq = Square(chamber->radius_x);
+		double ry_sq = Square(chamber->radius_y);
+		double rz_sq = Square(chamber->radius_z);
+
+		for (int j = 0; j < fF_AMOUNT; j++)
+		{
+			fF_point &p = chambers[nr]->points[j];
+
+			double upx = Square(p.x - chamber->centerX);
+			double upy = Square(p.y - chamber->centerY);
+			double upz = Square(p.z - chamber->centerZ);
+
+			double expx = upx / rx_sq;
+			double expy = upy / ry_sq;
+			double expz = upz / rz_sq;
+
+			double result = expx + expy + expz;
+
+			if (result < fF_THICK_RATIO)
+			{
+				p.inside = true;
+			}
+		}
+	}
+}
+
+int GenoConv_fF0::find_hole(int which, double x, double y, double z, fF_chamber3d **chambers)
+{
+	int found = -1;
+	double distsq_found;
+
+	for (int i = 0; i < fF_AMOUNT; i++)
+	{
+		fF_point &p = chambers[which]->points[i];
+		if (!p.inside) //it is not inside another chamber
+		{
+			double distancesq = Square(p.x - x) + Square(p.y - y) + Square(p.z - z);
+			if (found < 0)
+			{
+				found = i;
+				distsq_found = distancesq;
+			}
+			if (distancesq < distsq_found)
+			{
+				if (which != 0)
+				{
+					bool good = true;
+					for (int j = 0; j < which && good; j++)
+					{
+						fF_chamber3d *chamber = chambers[j];
+
+						double rx_sq = Square(chamber->radius_x);
+						double ry_sq = Square(chamber->radius_y);
+						double rz_sq = Square(chamber->radius_z);
+
+						double upx = Square(p.x - chamber->centerX);
+						double upy = Square(p.y - chamber->centerY);
+						double upz = Square(p.z - chamber->centerZ);
+
+						double expx = upx / rx_sq;
+						double expy = upy / ry_sq;
+						double expz = upz / rz_sq;
+
+						double result = expx + expy + expz;
+						if (result < 1.0)
+						{
+							good = false;
+						}
+					}
+					if (good)
+					{
+						found = i;
+						distsq_found = distancesq;
+					}
+				}
+			}
+		}
+	}
+
+	return found;
+}
Index: cpp/frams/genetics/fF/fF_conv.h
===================================================================
--- cpp/frams/genetics/fF/fF_conv.h	(revision 779)
+++ cpp/frams/genetics/fF/fF_conv.h	(revision 779)
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef _FF_CONV_H_
+#define _FF_CONV_H_
+
+
+
+#define fF_TOO_MUCH 0.75
+#define fF_TOO_LITTLE 0.10
+
+#define fF_HOLE_RADIUS 0.05
+#define fF_LONGITUDE_NUM 69
+
+#define fF_LATITUDE_NUM ((fF_LONGITUDE_NUM - 1)*2)
+#define fF_AMOUNT ((fF_LATITUDE_NUM)*(fF_LONGITUDE_NUM))
+
+#define fF_THICK_RATIO 0.95
+
+#define fF_SIZE fF_LONGITUDE_NUM * fF_LATITUDE_NUM + fF_LATITUDE_NUM
+
+#include <frams/util/multimap.h>
+#include <frams/util/sstring.h>
+#include <frams/genetics/genoconv.h>
+#include <frams/model/model.h>
+#include "fF_chamber3d.h"
+
+
+//A point on the surface of a chamber
+struct fF_point
+{
+	double x, y, z;
+	bool inside; //helper field used when computing whether this point is inside some chamber
+};
+
+
+// The f9->f0 converter
+class GenoConv_fF0 : public GenoConverter {
+public:
+	GenoConv_fF0();
+	~GenoConv_fF0();
+	//implementation of the GenoConverter method
+	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
+
+protected:
+	void createSphere(int ktora, fF_chamber3d **chambers, double radius0x, double radius0y, double radius0z, double translation, double alpha, double gamma, double kx, double ky, double kz);
+	fF_point* generate_points(fF_chamber3d *chamber);
+	double dist(double x1, double y1, double z1, double x2, double y2, double z2);
+	void search_hid(int nr, fF_chamber3d **spheres);
+	int find_hole(int which, double x, double y, double z, fF_chamber3d **chambers);
+	double get_radius(double prev_radius, double scale, double radius0);
+private:
+	double* cosines;
+	double* sines;
+	void precompute_cos_and_sin();
+	Part *addNewPart(Model *m, const fF_chamber3d* c);
+};
+
+
+#endif
Index: cpp/frams/genetics/fF/fF_oper.cpp
===================================================================
--- cpp/frams/genetics/fF/fF_oper.cpp	(revision 779)
+++ cpp/frams/genetics/fF/fF_oper.cpp	(revision 779)
@@ -0,0 +1,101 @@
+// 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 "fF_oper.h"
+#include "fF_genotype.h"
+#include <common/nonstd.h> //randomN, rnd01
+
+
+#define FIELDSTRUCT GenoOper_fF
+static ParamEntry GENOfFparam_tab[] =
+{
+	{ "Genetics: fF", 1, 1, },
+	{ "fF_xover", 0, 0, "Inherited in linear mix crossover", "f 0.5 1.0 0.5", FIELD(xover_proportion), "0.5 => children are averaged parents.\n0.8 => children are only 20% different from parents.\n1.0 => each child is identical to one parent (no crossover).", },
+	{ 0, },
+};
+#undef FIELDSTRUCT
+
+
+GenoOper_fF::GenoOper_fF()
+{
+	par.setParamTab(GENOfFparam_tab);
+	par.select(this);
+	par.setDefault();
+	supported_format = 'F';
+}
+
+int GenoOper_fF::checkValidity(const char* gene, const char *genoname)
+{
+	fF_growth_params par;
+	return par.load(gene) ? GENOPER_OK : 1;
+}
+
+int GenoOper_fF::validate(char *&gene, const char *genoname)
+{
+	fF_growth_params par; //is initialized with default values
+	par.load(gene); //loads as much as possible, other fields remain with default values
+	string validated = par.save();
+	free(gene);
+	gene = strdup(validated.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+//Creep-mutate one property
+int GenoOper_fF::mutate(char *&gene, float &chg, int &method)
+{
+	method = 0;
+	fF_growth_params par;
+	par.load(gene);
+	static const int propsToMutate[] = fF_PROPS_TO_MUTATE;
+	int which = randomN(ARRAY_LENGTH(propsToMutate));
+	bool mutated_ok = GenoOperators::mutatePropertyNaive(par.param, propsToMutate[which]);
+	if (mutated_ok)
+	{
+		string saved = par.save();
+		free(gene);
+		gene = strdup(saved.c_str()); //reallocate
+		chg = 1.0f / par.param.getPropCount();
+		return GENOPER_OK;
+	}
+	else
+	{
+		chg = 0;
+		return GENOPER_OPFAIL;
+	}
+}
+
+///Averaging crossover
+int GenoOper_fF::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
+{
+	//g1 = strdup("1,0.5,0.5,0.5,0.5,1,1"); //testing...
+	//g2 = strdup("4,1,  1,  1,  1,  2,2"); //testing...
+	//xover_proportion = 0.1; //testing...
+	fF_growth_params par1;
+	par1.load(g1);
+	fF_growth_params par2;
+	par2.load(g2);
+	chg1 = xover_proportion;
+	chg2 = 1 - xover_proportion;
+	for (int i = 0; i < par1.param.getPropCount(); i++)
+		GenoOperators::linearMix(par1.param, i, par2.param, i, xover_proportion);
+	string saved = par1.save();
+	free(g1);
+	g1 = strdup(saved.c_str()); //reallocate
+	saved = par2.save();
+	free(g2);
+	g2 = strdup(saved.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+///Applying some colors and font styles...
+uint32_t GenoOper_fF::style(const char *g, int pos)
+{
+	char ch = g[pos];
+	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
+	if (strchr("-.e 0123456789", ch) != NULL)
+		style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
+	else if (ch == ',')
+		style = GENSTYLE_RGBS(0, 0, 0, GENSTYLE_BOLD);
+	return style;
+}
Index: cpp/frams/genetics/fF/fF_oper.h
===================================================================
--- cpp/frams/genetics/fF/fF_oper.h	(revision 779)
+++ cpp/frams/genetics/fF/fF_oper.h	(revision 779)
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef _FF_OPER_H_
+#define _FF_OPER_H_
+
+#include "../genooperators.h"
+
+
+class GenoOper_fF : public GenoOperators
+{
+public:
+	GenoOper_fF();
+	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);
+	uint32_t style(const char *g, int pos);
+	const char* getSimplest() { return "6, 0.1, 0.1, 0.1, 1.05, 1.05, 1.05, 0, 0, 0"; }
+
+	double xover_proportion;
+};
+
+#endif
+
Index: cpp/frams/genetics/fF/oper_fF.cpp
===================================================================
--- cpp/frams/genetics/fF/oper_fF.cpp	(revision 777)
+++ 	(revision )
@@ -1,101 +1,0 @@
-// 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 "oper_fF.h"
-#include "fF_genotype.h"
-#include <common/nonstd.h> //randomN, rnd01
-
-
-#define FIELDSTRUCT GenoOper_fF
-static ParamEntry GENOfFparam_tab[] =
-{
-	{ "Genetics: fF", 1, 1, },
-	{ "fF_xover", 0, 0, "Inherited in linear mix crossover", "f 0.5 1.0 0.5", FIELD(xover_proportion), "0.5 => children are averaged parents.\n0.8 => children are only 20% different from parents.\n1.0 => each child is identical to one parent (no crossover).", },
-	{ 0, },
-};
-#undef FIELDSTRUCT
-
-
-GenoOper_fF::GenoOper_fF()
-{
-	par.setParamTab(GENOfFparam_tab);
-	par.select(this);
-	par.setDefault();
-	supported_format = 'F';
-}
-
-int GenoOper_fF::checkValidity(const char* gene, const char *genoname)
-{
-	fF_growth_params par;
-	return par.load(gene) ? GENOPER_OK : 1;
-}
-
-int GenoOper_fF::validate(char *&gene, const char *genoname)
-{
-	fF_growth_params par; //is initialized with default values
-	par.load(gene); //loads as much as possible, other fields remain with default values
-	string validated = par.save();
-	free(gene);
-	gene = strdup(validated.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-//Creep-mutate one property
-int GenoOper_fF::mutate(char *&gene, float &chg, int &method)
-{
-	method = 0;
-	fF_growth_params par;
-	par.load(gene);
-	static const int propsToMutate[] = fF_PROPS_TO_MUTATE;
-	int which = randomN(ARRAY_LENGTH(propsToMutate));
-	bool mutated_ok = GenoOperators::mutatePropertyNaive(par.param, propsToMutate[which]);
-	if (mutated_ok)
-	{
-		string saved = par.save();
-		free(gene);
-		gene = strdup(saved.c_str()); //reallocate
-		chg = 1.0f / par.param.getPropCount();
-		return GENOPER_OK;
-	}
-	else
-	{
-		chg = 0;
-		return GENOPER_OPFAIL;
-	}
-}
-
-///Averaging crossover
-int GenoOper_fF::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
-{
-	//g1 = strdup("1,0.5,0.5,0.5,0.5,1,1"); //testing...
-	//g2 = strdup("4,1,  1,  1,  1,  2,2"); //testing...
-	//xover_proportion = 0.1; //testing...
-	fF_growth_params par1;
-	par1.load(g1);
-	fF_growth_params par2;
-	par2.load(g2);
-	chg1 = xover_proportion;
-	chg2 = 1 - xover_proportion;
-	for (int i = 0; i < par1.param.getPropCount(); i++)
-		GenoOperators::linearMix(par1.param, i, par2.param, i, xover_proportion);
-	string saved = par1.save();
-	free(g1);
-	g1 = strdup(saved.c_str()); //reallocate
-	saved = par2.save();
-	free(g2);
-	g2 = strdup(saved.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-///Applying some colors and font styles...
-uint32_t GenoOper_fF::style(const char *g, int pos)
-{
-	char ch = g[pos];
-	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
-	if (strchr("-.e 0123456789", ch) != NULL)
-		style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
-	else if (ch == ',')
-		style = GENSTYLE_RGBS(0, 0, 0, GENSTYLE_BOLD);
-	return style;
-}
Index: cpp/frams/genetics/fF/oper_fF.h
===================================================================
--- cpp/frams/genetics/fF/oper_fF.h	(revision 777)
+++ 	(revision )
@@ -1,26 +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.
-
-#ifndef _GENOOPER_FF_H_
-#define _GENOOPER_FF_H_
-
-#include "../oper_fx.h"
-
-
-class GenoOper_fF : public GenoOperators
-{
-public:
-	GenoOper_fF();
-	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);
-	uint32_t style(const char *g, int pos);
-	const char* getSimplest() { return "6, 0.1, 0.1, 0.1, 1.05, 1.05, 1.05, 0, 0, 0"; }
-
-	double xover_proportion;
-};
-
-#endif
-
Index: cpp/frams/genetics/fT/fTest_oper.cpp
===================================================================
--- cpp/frams/genetics/fT/fTest_oper.cpp	(revision 779)
+++ cpp/frams/genetics/fT/fTest_oper.cpp	(revision 779)
@@ -0,0 +1,126 @@
+// 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.
+
+#include "fTest_oper.h"
+#include <common/nonstd.h> //randomN, rnd01
+
+/**
+Sample output (simple examples of various genetic operations) produced by genooper_test_fTest.cpp:
+$ ./genooper_test_fTest.exe
+GATCGATTACA   [mutated 0.0%]
+GATCCATTACA   [mutated 9.1%]
+GATCCTGTACA   [mutated 27.3%]
+GATCCTGTACA   [mutated 0.0%]
+GATCCTGTACA   [mutated 0.0%]
+GATCCTGTATA   [mutated 9.1%]
+GATCCTGTATA   [mutated 0.0%]
+GATACTGTATA   [mutated 9.1%]
+GATACTGTATA   [mutated 9.1%]
+GATACTGTATA   [mutated 0.0%]
+
+Crossing over the last mutant,
+GATACTGTATA
+and the simplest genotype
+GATCGATTACA
+:
+Offspring 1:
+GATACTCGATTACA  (35.7% genes from parent1)
+Offspring 2:
+GATGTATA  (25.0% genes from parent2)
+
+Checking genotype:
+ATGsomethingCG... error at position 4.
+After validation:
+ATGCG
+...and how about YOUR genotype?
+*/
+
+
+// To test this genetic format, you can also use the general genooper_test app that supports all genetic formats:
+// $ ./genooper_test.exe /*T*/AAAAAAAAAAA
+
+
+
+#define FIELDSTRUCT GenoOper_fTest
+static ParamEntry GENOtestparam_tab[] =   //external access to ftest genetic parameters
+{
+	{ "Genetics: fTest", 1, 1, },
+	{ "fTest_mut", 0, 0, "Mutation probability", "f 0 1", FIELD(prob), "How many genes should be mutated during single mutation (1=all genes, 0.1=ten percent)", },
+	{ 0, },
+};
+#undef FIELDSTRUCT
+
+GenoOper_fTest::GenoOper_fTest()
+{
+	par.setParamTab(GENOtestparam_tab);
+	par.select(this);
+	supported_format = 'T'; //'0' for f0, '1' for f1, 'F' for fF, etc.
+	prob = 0.1;
+}
+
+///The only letters allowed are A,T,G,C
+int GenoOper_fTest::checkValidity(const char* gene)
+{
+	if (!gene[0]) return 1; //empty is not valid
+	bool ok = true;
+	size_t i;
+	for (i = 0; i < strlen(gene); i++) if (!strchr("ATGC", gene[i])) { ok = false; break; }
+	return ok ? GENOPER_OK : i + 1;
+}
+
+///Remove all invalid letters from the genotype
+int GenoOper_fTest::validate(char *&gene)
+{
+	SString validated; //new genotype (everything except ATGC is skipped)
+	for (size_t i = 0; i < strlen(gene); i++)
+		if (strchr("ATGC", gene[i])) validated += gene[i];  //validated contains only ATGC
+	free(gene);
+	gene = strdup(validated.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+///Very simple mutation; should be improved to guarantee at least one gene changed
+int GenoOper_fTest::mutate(char *&geno, float &chg, int &method)
+{
+	static char a[] = "ATGC";
+	method = 0;
+	int changes = 0, len = strlen(geno);
+	for (int i = 0; i < len; i++)
+		if (rnd01 < prob) //normalize prob with length of genotype
+		{
+			geno[i] = a[randomN(4)];
+			changes++;
+		}
+	chg = (float)changes / len;
+	return GENOPER_OK;
+}
+
+///A simple one-point crossover
+int GenoOper_fTest::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
+{
+	int len1 = strlen(g1), len2 = strlen(g2);
+	int p1 = randomN(len1);  //random cut point for first genotype
+	int p2 = randomN(len2);  //random cut point for second genotype
+	char *child1 = (char*)malloc(p1 + len2 - p2 + 1);
+	char *child2 = (char*)malloc(p2 + len1 - p1 + 1);
+	strncpy(child1, g1, p1);   strcpy(child1 + p1, g2 + p2);
+	strncpy(child2, g2, p2);   strcpy(child2 + p2, g1 + p1);
+	free(g1); g1 = child1;
+	free(g2); g2 = child2;
+	chg1 = (float)p1 / strlen(child1);
+	chg2 = (float)p2 / strlen(child2);
+	return GENOPER_OK;
+}
+
+///Applying some colors and font styles...
+uint32_t GenoOper_fTest::style(const char *g, int pos)
+{
+	char ch = g[pos];
+	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
+	if (ch == 'A') style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
+	if (ch == 'T') style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
+	if (ch == 'G') style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_NONE);
+	if (ch == 'C') style = GENSTYLE_RGBS(200, 200, 0, GENSTYLE_NONE);
+	return style;
+}
Index: cpp/frams/genetics/fT/fTest_oper.h
===================================================================
--- cpp/frams/genetics/fT/fTest_oper.h	(revision 779)
+++ cpp/frams/genetics/fT/fTest_oper.h	(revision 779)
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef _FTEST_OPER_H_
+#define _FTEST_OPER_H_
+
+#include "../genooperators.h"
+
+/** \file */
+
+///A sample class for genetic operations on DNA-like genotypes
+/**
+\author Maciej Komosinski
+
+This is a very simple class that illustrates basic genetic operations performed on ATGC sequences.
+For a more sophisticated and realistic examples of genetic formats and operators derived from GenoOperators,
+refer to the available source for genetic formats f9, fF, and f4.
+
+\sa \ref geno_ftest_example
+*/
+
+class GenoOper_fTest : public GenoOperators
+{
+public:
+	GenoOper_fTest();
+	int checkValidity(const char *);
+	int validate(char *&);
+	int mutate(char *&geno, float& chg, int &method);
+	int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
+	uint32_t style(const char *g, int pos);
+	const char* getSimplest() { return "GATCGATTACA"; }
+
+	double prob;
+};
+
+#endif
Index: cpp/frams/genetics/fT/oper_fTest.cpp
===================================================================
--- cpp/frams/genetics/fT/oper_fTest.cpp	(revision 777)
+++ 	(revision )
@@ -1,126 +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.
-
-#include "oper_fTest.h"
-#include <common/nonstd.h> //randomN, rnd01
-
-/**
-Sample output (simple examples of various genetic operations) produced by genooper_test_fTest.cpp:
-$ ./genooper_test_fTest.exe
-GATCGATTACA   [mutated 0.0%]
-GATCCATTACA   [mutated 9.1%]
-GATCCTGTACA   [mutated 27.3%]
-GATCCTGTACA   [mutated 0.0%]
-GATCCTGTACA   [mutated 0.0%]
-GATCCTGTATA   [mutated 9.1%]
-GATCCTGTATA   [mutated 0.0%]
-GATACTGTATA   [mutated 9.1%]
-GATACTGTATA   [mutated 9.1%]
-GATACTGTATA   [mutated 0.0%]
-
-Crossing over the last mutant,
-GATACTGTATA
-and the simplest genotype
-GATCGATTACA
-:
-Offspring 1:
-GATACTCGATTACA  (35.7% genes from parent1)
-Offspring 2:
-GATGTATA  (25.0% genes from parent2)
-
-Checking genotype:
-ATGsomethingCG... error at position 4.
-After validation:
-ATGCG
-...and how about YOUR genotype?
-*/
-
-
-// To test this genetic format, you can also use the general genooper_test app that supports all genetic formats:
-// $ ./genooper_test.exe /*T*/AAAAAAAAAAA
-
-
-
-#define FIELDSTRUCT GenoOper_fTest
-static ParamEntry GENOtestparam_tab[] =   //external access to ftest genetic parameters
-{
-	{ "Genetics: fTest", 1, 1, },
-	{ "fTest_mut", 0, 0, "Mutation probability", "f 0 1", FIELD(prob), "How many genes should be mutated during single mutation (1=all genes, 0.1=ten percent)", },
-	{ 0, },
-};
-#undef FIELDSTRUCT
-
-GenoOper_fTest::GenoOper_fTest()
-{
-	par.setParamTab(GENOtestparam_tab);
-	par.select(this);
-	supported_format = 'T'; //'0' for f0, '1' for f1, 'F' for fF, etc.
-	prob = 0.1;
-}
-
-///The only letters allowed are A,T,G,C
-int GenoOper_fTest::checkValidity(const char* gene)
-{
-	if (!gene[0]) return 1; //empty is not valid
-	bool ok = true;
-	size_t i;
-	for (i = 0; i < strlen(gene); i++) if (!strchr("ATGC", gene[i])) { ok = false; break; }
-	return ok ? GENOPER_OK : i + 1;
-}
-
-///Remove all invalid letters from the genotype
-int GenoOper_fTest::validate(char *&gene)
-{
-	SString validated; //new genotype (everything except ATGC is skipped)
-	for (size_t i = 0; i < strlen(gene); i++)
-		if (strchr("ATGC", gene[i])) validated += gene[i];  //validated contains only ATGC
-	free(gene);
-	gene = strdup(validated.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-///Very simple mutation; should be improved to guarantee at least one gene changed
-int GenoOper_fTest::mutate(char *&geno, float &chg, int &method)
-{
-	static char a[] = "ATGC";
-	method = 0;
-	int changes = 0, len = strlen(geno);
-	for (int i = 0; i < len; i++)
-		if (rnd01 < prob) //normalize prob with length of genotype
-		{
-			geno[i] = a[randomN(4)];
-			changes++;
-		}
-	chg = (float)changes / len;
-	return GENOPER_OK;
-}
-
-///A simple one-point crossover
-int GenoOper_fTest::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
-{
-	int len1 = strlen(g1), len2 = strlen(g2);
-	int p1 = randomN(len1);  //random cut point for first genotype
-	int p2 = randomN(len2);  //random cut point for second genotype
-	char *child1 = (char*)malloc(p1 + len2 - p2 + 1);
-	char *child2 = (char*)malloc(p2 + len1 - p1 + 1);
-	strncpy(child1, g1, p1);   strcpy(child1 + p1, g2 + p2);
-	strncpy(child2, g2, p2);   strcpy(child2 + p2, g1 + p1);
-	free(g1); g1 = child1;
-	free(g2); g2 = child2;
-	chg1 = (float)p1 / strlen(child1);
-	chg2 = (float)p2 / strlen(child2);
-	return GENOPER_OK;
-}
-
-///Applying some colors and font styles...
-uint32_t GenoOper_fTest::style(const char *g, int pos)
-{
-	char ch = g[pos];
-	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
-	if (ch == 'A') style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
-	if (ch == 'T') style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
-	if (ch == 'G') style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_NONE);
-	if (ch == 'C') style = GENSTYLE_RGBS(200, 200, 0, GENSTYLE_NONE);
-	return style;
-}
Index: cpp/frams/genetics/fT/oper_fTest.h
===================================================================
--- cpp/frams/genetics/fT/oper_fTest.h	(revision 777)
+++ 	(revision )
@@ -1,37 +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.
-
-#ifndef _GENOOPER_FTEST_H_
-#define _GENOOPER_FTEST_H_
-
-#include "../oper_fx.h"
-
-/** \file */
-
-///A sample class for genetic operations on DNA-like genotypes
-/**
-\author Maciej Komosinski
-
-This is a very simple class that illustrates basic genetic operations performed on ATGC sequences.
-For a more sophisticated and realistic examples of genetic formats and operators derived from GenoOperators,
-refer to the available source for genetic formats f9, fF, and f4.
-
-\sa \ref geno_ftest_example
-*/
-
-class GenoOper_fTest : public GenoOperators
-{
-public:
-	GenoOper_fTest();
-	int checkValidity(const char *);
-	int validate(char *&);
-	int mutate(char *&geno, float& chg, int &method);
-	int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
-	uint32_t style(const char *g, int pos);
-	const char* getSimplest() { return "GATCGATTACA"; }
-
-	double prob;
-};
-
-#endif
Index: cpp/frams/genetics/fn/conv_fn.cpp
===================================================================
--- cpp/frams/genetics/fn/conv_fn.cpp	(revision 777)
+++ 	(revision )
@@ -1,82 +1,0 @@
-// 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 "conv_fn.h"
-#include <frams/vm/classes/collectionobj.h>
-
-GenoConv_fn0::GenoConv_fn0()
-{
-	name = "Vector of real values, no phenotype"; //for numerical optimization; custom fitness function must be provided in script. See oper_fn.cpp for more details.
-	in_format = 'n';
-	out_format = '0';
-	mapsupport = 0;
-}
-
-
-
-SString GenoConv_fn0::convert(SString &in, MultiMap *map, bool using_checkpoints)
-{
-	vector<double> values = stringToVector(in.c_str());
-	if (values.size() == 0) //invalid input genotype?
-		return ""; //so we return an invalid f0 genotype
-
-	return SString("p:\n"); //phenotype not relevant for this genetic encoding
-}
-
-
-
-vector<double> GenoConv_fn0::stringToVector(const char *input) //returns empty vector on error
-{
-	vector<double> empty;
-	ExtValue val;
-	const char* after_des = val.deserialize(input);
-	if (after_des == NULL) //deserialization failed
-	{
-		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Unable to deserialize - expecting a vector of real values, got '%s'", input);
-		return empty;
-	}
-	if (after_des[0] != '\0') //not everything was consumed
-	{
-		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Extra characters after deserialized '%s'", input);
-		return empty;
-	}
-
-	VectorObject *vec = VectorObject::fromObject(val.getObject(), false);
-	if (vec)
-	{
-		vector<double> output;
-		for (int i = 0; i < vec->data.size(); i++)
-		{
-			ExtValue* val = (ExtValue*)vec->data(i);
-			if (val == NULL)
-			{
-				logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Expecting a real value in a vector, got NULL");
-				return empty;
-			}
-			else
-				output.push_back(val->getDouble());
-		}
-		return output;
-	}
-	else
-	{
-		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Expecting a vector of real values, got '%s'", input);
-		return empty;
-	}
-}
-
-
-string GenoConv_fn0::vectorToString(const vector<double> vec)
-{
-	char buffer[32];
-	string out = "[";
-	for (unsigned int i = 0; i < vec.size(); i++)
-	{
-		if (i > 0)
-			out += ", ";
-		snprintf(buffer, sizeof(buffer), "%.8g", vec[i]);
-		out += buffer; //+= std::to_string(1.23) still not available in embarcadero 10.2.3
-	}
-	return out + "]";
-}
Index: cpp/frams/genetics/fn/conv_fn.h
===================================================================
--- cpp/frams/genetics/fn/conv_fn.h	(revision 777)
+++ 	(revision )
@@ -1,23 +1,0 @@
-// 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.
-
-#ifndef _CONV_Fn_H_
-#define _CONV_Fn_H_
-
-#include <frams/genetics/genoconv.h>
-
-// The fn->f0 converter
-class GenoConv_fn0 : public GenoConverter
-{
-public:
-	GenoConv_fn0();
-	//~GenoConv_fn0();
-	//implementation of the GenoConverter method
-	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
-
-	static vector<double> stringToVector(const char *input); //returns empty vector on error
-	static string vectorToString(const vector<double> vec);
-};
-
-#endif
Index: cpp/frams/genetics/fn/fn_conv.cpp
===================================================================
--- cpp/frams/genetics/fn/fn_conv.cpp	(revision 779)
+++ cpp/frams/genetics/fn/fn_conv.cpp	(revision 779)
@@ -0,0 +1,82 @@
+// 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 "fn_conv.h"
+#include <frams/vm/classes/collectionobj.h>
+
+GenoConv_fn0::GenoConv_fn0()
+{
+	name = "Vector of real values, no phenotype"; //for numerical optimization; custom fitness function must be provided in script. See oper_fn.cpp for more details.
+	in_format = 'n';
+	out_format = '0';
+	mapsupport = 0;
+}
+
+
+
+SString GenoConv_fn0::convert(SString &in, MultiMap *map, bool using_checkpoints)
+{
+	vector<double> values = stringToVector(in.c_str());
+	if (values.size() == 0) //invalid input genotype?
+		return ""; //so we return an invalid f0 genotype
+
+	return SString("p:\n"); //phenotype not relevant for this genetic encoding
+}
+
+
+
+vector<double> GenoConv_fn0::stringToVector(const char *input) //returns empty vector on error
+{
+	vector<double> empty;
+	ExtValue val;
+	const char* after_des = val.deserialize(input);
+	if (after_des == NULL) //deserialization failed
+	{
+		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Unable to deserialize - expecting a vector of real values, got '%s'", input);
+		return empty;
+	}
+	if (after_des[0] != '\0') //not everything was consumed
+	{
+		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Extra characters after deserialized '%s'", input);
+		return empty;
+	}
+
+	VectorObject *vec = VectorObject::fromObject(val.getObject(), false);
+	if (vec)
+	{
+		vector<double> output;
+		for (int i = 0; i < vec->data.size(); i++)
+		{
+			ExtValue* val = (ExtValue*)vec->data(i);
+			if (val == NULL)
+			{
+				logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Expecting a real value in a vector, got NULL");
+				return empty;
+			}
+			else
+				output.push_back(val->getDouble());
+		}
+		return output;
+	}
+	else
+	{
+		logPrintf("GenoConv_fn0", "stringToVector", LOG_ERROR, "Expecting a vector of real values, got '%s'", input);
+		return empty;
+	}
+}
+
+
+string GenoConv_fn0::vectorToString(const vector<double> vec)
+{
+	char buffer[32];
+	string out = "[";
+	for (unsigned int i = 0; i < vec.size(); i++)
+	{
+		if (i > 0)
+			out += ", ";
+		snprintf(buffer, sizeof(buffer), "%.8g", vec[i]);
+		out += buffer; //+= std::to_string(1.23) still not available in embarcadero 10.2.3
+	}
+	return out + "]";
+}
Index: cpp/frams/genetics/fn/fn_conv.h
===================================================================
--- cpp/frams/genetics/fn/fn_conv.h	(revision 779)
+++ cpp/frams/genetics/fn/fn_conv.h	(revision 779)
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef _Fn_CONV_H_
+#define _Fn_CONV_H_
+
+#include <frams/genetics/genoconv.h>
+
+// The fn->f0 converter
+class GenoConv_fn0 : public GenoConverter
+{
+public:
+	GenoConv_fn0();
+	//~GenoConv_fn0();
+	//implementation of the GenoConverter method
+	SString convert(SString &in, MultiMap *map, bool using_checkpoints);
+
+	static vector<double> stringToVector(const char *input); //returns empty vector on error
+	static string vectorToString(const vector<double> vec);
+};
+
+#endif
Index: cpp/frams/genetics/fn/fn_oper.cpp
===================================================================
--- cpp/frams/genetics/fn/fn_oper.cpp	(revision 779)
+++ cpp/frams/genetics/fn/fn_oper.cpp	(revision 779)
@@ -0,0 +1,127 @@
+// 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 "fn_oper.h"
+#include "fn_conv.h"
+#include <common/nonstd.h> //randomN, rnd01
+
+
+/**
+\class GenoOper_fn
+
+This genetic representation only stores a vector of real numbers. A fitness function must be provided
+for the gene pool, for example the "Booth function" would be:
+
+var X = String.deserialize(this.geno.rawgenotype); //a vector of real values
+var result = Math.pow(X[0]+2*X[1]-7,2) + Math.pow(2*X[0]+X[1]-5,2);
+return -result; //negation because Framsticks assumes maximization, and the original function needs to be minimized
+*/
+
+
+
+#define FIELDSTRUCT GenoOper_fn
+static ParamEntry GENOfnparam_tab[] =
+{
+	{ "Genetics: fn", 1, 4, },
+	{ "fn_xover", 0, 0, "Inherited in linear mix crossover", "f 0.5 1.0 0.9", FIELD(xover_proportion), "0.5 => children are averaged parents.\n0.8 => children are only 20% different from parents.\n1.0 => each child is identical to one parent (no crossover).", },
+	{ "fn_mut_bound_low", 1, 0, "Lower bounds for mutation", "s 0 0 [-10.0, -10.0]", FIELD(mut_bound_low), "A vector of lower bounds (one real value for each variable)", },
+	{ "fn_mut_bound_high", 1, 0, "Higher bounds for mutation", "s 0 0 [10.0, 10.0]", FIELD(mut_bound_high), "A vector of higher bounds (one real value for each variable)", },
+	{ "fn_mut_stddev", 1, 0, "Standard deviations for mutation", "s 0 0 [0.1, 0.1]", FIELD(mut_stddev), "A vector of standard deviations (one real value for each variable)", },
+	{ 0, },
+};
+#undef FIELDSTRUCT
+
+
+
+GenoOper_fn::GenoOper_fn()
+{
+	par.setParamTab(GENOfnparam_tab);
+	par.select(this);
+	par.setDefault();
+	supported_format = 'n';
+}
+
+int GenoOper_fn::checkValidity(const char* gene, const char *genoname)
+{
+	vector<double> values = GenoConv_fn0::stringToVector(gene);
+	return values.size() > 0 ? GENOPER_OK : 1;
+}
+
+int GenoOper_fn::validate(char *&gene, const char *genoname)
+{
+	vector<double> values = GenoConv_fn0::stringToVector(gene);
+	if (values.size() == 0)
+		values.push_back(0.0);
+	string validated = GenoConv_fn0::vectorToString(values);
+	free(gene);
+	gene = strdup(validated.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+//Creep-mutate one property
+int GenoOper_fn::mutate(char *&gene, float &chg, int &method)
+{
+	method = 0;
+	vector<double> values = GenoConv_fn0::stringToVector(gene);
+	if (values.size() == 0)
+		return GENOPER_OPFAIL;
+	vector<double> bound_low = GenoConv_fn0::stringToVector(mut_bound_low.c_str());
+	vector<double> bound_high = GenoConv_fn0::stringToVector(mut_bound_high.c_str());
+	vector<double> stddev = GenoConv_fn0::stringToVector(mut_stddev.c_str());
+	if (bound_low.size() != bound_high.size() || bound_high.size() != stddev.size() || stddev.size() != values.size())
+	{
+		logPrintf("GenoOper_fn", "mutate", LOG_ERROR, "The solution vector, bound vectors, and standard deviation vectors must all have the same number of values");
+		return GENOPER_OPFAIL;
+	}
+
+	int which = randomN(values.size());
+	values[which] = GenoOperators::mutateCreep('f', values[which], bound_low[which], bound_high[which], stddev[which], false);
+	string saved = GenoConv_fn0::vectorToString(values);
+	free(gene);
+	gene = strdup(saved.c_str()); //reallocate
+	chg = 1.0f / values.size();
+	return GENOPER_OK;
+}
+
+///Averaging crossover
+int GenoOper_fn::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
+{
+	//g1 = strdup("[1,0.5,0.5,0.5,0.5,1,1]"); //testing...
+	//g2 = strdup("[4,1,  1,  1,  1,  2,2]"); //testing...
+	//xover_proportion = 0.1; //testing...
+
+	chg1 = xover_proportion;
+	chg2 = 1 - xover_proportion;
+
+	vector<double> v1 = GenoConv_fn0::stringToVector(g1);
+	vector<double> v2 = GenoConv_fn0::stringToVector(g2);
+
+	if (v1.size() != v2.size())
+	{
+		logPrintf("GenoOper_fn", "crossOver", LOG_ERROR, "Tried to cross over solutions with a differing number of variables (%d and %d)", v1.size(), v2.size());
+		return GENOPER_OPFAIL;
+	}
+
+	GenoOperators::linearMix(v1, v2, xover_proportion);
+
+	string saved = GenoConv_fn0::vectorToString(v1);
+	free(g1);
+	g1 = strdup(saved.c_str()); //reallocate
+	saved = GenoConv_fn0::vectorToString(v2);
+	free(g2);
+	g2 = strdup(saved.c_str()); //reallocate
+	return GENOPER_OK;
+}
+
+///Applying some colors and font styles...
+uint32_t GenoOper_fn::style(const char *g, int pos)
+{
+	char ch = g[pos];
+	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
+	if (strchr("-.e 0123456789", ch) != NULL)
+		style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
+	else if (strchr("[,]", ch) != NULL)
+		style = GENSTYLE_RGBS(0, 0, 0, GENSTYLE_BOLD);
+	return style;
+}
Index: cpp/frams/genetics/fn/fn_oper.h
===================================================================
--- cpp/frams/genetics/fn/fn_oper.h	(revision 779)
+++ cpp/frams/genetics/fn/fn_oper.h	(revision 779)
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef _Fn_OPER_H_
+#define _Fn_OPER_H_
+
+#include "../genooperators.h"
+
+
+class GenoOper_fn : public GenoOperators
+{
+public:
+	GenoOper_fn();
+	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);
+	uint32_t style(const char *g, int pos);
+	const char* getSimplest() { return "[0.0, 0.0]"; } //should actually correspond in length to vectors in mut_bound_low, mut_bound_high, mut_stddev
+
+	double xover_proportion;
+	SString mut_bound_low, mut_bound_high, mut_stddev;
+};
+
+#endif
Index: cpp/frams/genetics/fn/oper_fn.cpp
===================================================================
--- cpp/frams/genetics/fn/oper_fn.cpp	(revision 777)
+++ 	(revision )
@@ -1,127 +1,0 @@
-// 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 "oper_fn.h"
-#include "conv_fn.h"
-#include <common/nonstd.h> //randomN, rnd01
-
-
-/**
-\class GenoOper_fn
-
-This genetic representation only stores a vector of real numbers. A fitness function must be provided
-for the gene pool, for example the "Booth function" would be:
-
-var X = String.deserialize(this.geno.rawgenotype); //a vector of real values
-var result = Math.pow(X[0]+2*X[1]-7,2) + Math.pow(2*X[0]+X[1]-5,2);
-return -result; //negation because Framsticks assumes maximization, and the original function needs to be minimized
-*/
-
-
-
-#define FIELDSTRUCT GenoOper_fn
-static ParamEntry GENOfnparam_tab[] =
-{
-	{ "Genetics: fn", 1, 4, },
-	{ "fn_xover", 0, 0, "Inherited in linear mix crossover", "f 0.5 1.0 0.9", FIELD(xover_proportion), "0.5 => children are averaged parents.\n0.8 => children are only 20% different from parents.\n1.0 => each child is identical to one parent (no crossover).", },
-	{ "fn_mut_bound_low", 1, 0, "Lower bounds for mutation", "s 0 0 [-10.0, -10.0]", FIELD(mut_bound_low), "A vector of lower bounds (one real value for each variable)", },
-	{ "fn_mut_bound_high", 1, 0, "Higher bounds for mutation", "s 0 0 [10.0, 10.0]", FIELD(mut_bound_high), "A vector of higher bounds (one real value for each variable)", },
-	{ "fn_mut_stddev", 1, 0, "Standard deviations for mutation", "s 0 0 [0.1, 0.1]", FIELD(mut_stddev), "A vector of standard deviations (one real value for each variable)", },
-	{ 0, },
-};
-#undef FIELDSTRUCT
-
-
-
-GenoOper_fn::GenoOper_fn()
-{
-	par.setParamTab(GENOfnparam_tab);
-	par.select(this);
-	par.setDefault();
-	supported_format = 'n';
-}
-
-int GenoOper_fn::checkValidity(const char* gene, const char *genoname)
-{
-	vector<double> values = GenoConv_fn0::stringToVector(gene);
-	return values.size() > 0 ? GENOPER_OK : 1;
-}
-
-int GenoOper_fn::validate(char *&gene, const char *genoname)
-{
-	vector<double> values = GenoConv_fn0::stringToVector(gene);
-	if (values.size() == 0)
-		values.push_back(0.0);
-	string validated = GenoConv_fn0::vectorToString(values);
-	free(gene);
-	gene = strdup(validated.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-//Creep-mutate one property
-int GenoOper_fn::mutate(char *&gene, float &chg, int &method)
-{
-	method = 0;
-	vector<double> values = GenoConv_fn0::stringToVector(gene);
-	if (values.size() == 0)
-		return GENOPER_OPFAIL;
-	vector<double> bound_low = GenoConv_fn0::stringToVector(mut_bound_low.c_str());
-	vector<double> bound_high = GenoConv_fn0::stringToVector(mut_bound_high.c_str());
-	vector<double> stddev = GenoConv_fn0::stringToVector(mut_stddev.c_str());
-	if (bound_low.size() != bound_high.size() || bound_high.size() != stddev.size() || stddev.size() != values.size())
-	{
-		logPrintf("GenoOper_fn", "mutate", LOG_ERROR, "The solution vector, bound vectors, and standard deviation vectors must all have the same number of values");
-		return GENOPER_OPFAIL;
-	}
-
-	int which = randomN(values.size());
-	values[which] = GenoOperators::mutateCreep('f', values[which], bound_low[which], bound_high[which], stddev[which], false);
-	string saved = GenoConv_fn0::vectorToString(values);
-	free(gene);
-	gene = strdup(saved.c_str()); //reallocate
-	chg = 1.0f / values.size();
-	return GENOPER_OK;
-}
-
-///Averaging crossover
-int GenoOper_fn::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
-{
-	//g1 = strdup("[1,0.5,0.5,0.5,0.5,1,1]"); //testing...
-	//g2 = strdup("[4,1,  1,  1,  1,  2,2]"); //testing...
-	//xover_proportion = 0.1; //testing...
-
-	chg1 = xover_proportion;
-	chg2 = 1 - xover_proportion;
-
-	vector<double> v1 = GenoConv_fn0::stringToVector(g1);
-	vector<double> v2 = GenoConv_fn0::stringToVector(g2);
-
-	if (v1.size() != v2.size())
-	{
-		logPrintf("GenoOper_fn", "crossOver", LOG_ERROR, "Tried to cross over solutions with a differing number of variables (%d and %d)", v1.size(), v2.size());
-		return GENOPER_OPFAIL;
-	}
-
-	GenoOperators::linearMix(v1, v2, xover_proportion);
-
-	string saved = GenoConv_fn0::vectorToString(v1);
-	free(g1);
-	g1 = strdup(saved.c_str()); //reallocate
-	saved = GenoConv_fn0::vectorToString(v2);
-	free(g2);
-	g2 = strdup(saved.c_str()); //reallocate
-	return GENOPER_OK;
-}
-
-///Applying some colors and font styles...
-uint32_t GenoOper_fn::style(const char *g, int pos)
-{
-	char ch = g[pos];
-	uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
-	if (strchr("-.e 0123456789", ch) != NULL)
-		style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
-	else if (strchr("[,]", ch) != NULL)
-		style = GENSTYLE_RGBS(0, 0, 0, GENSTYLE_BOLD);
-	return style;
-}
Index: cpp/frams/genetics/fn/oper_fn.h
===================================================================
--- cpp/frams/genetics/fn/oper_fn.h	(revision 777)
+++ 	(revision )
@@ -1,26 +1,0 @@
-// 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.
-
-#ifndef _GENOOPER_Fn_H_
-#define _GENOOPER_Fn_H_
-
-#include "../oper_fx.h"
-
-
-class GenoOper_fn : public GenoOperators
-{
-public:
-	GenoOper_fn();
-	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);
-	uint32_t style(const char *g, int pos);
-	const char* getSimplest() { return "[0.0, 0.0]"; } //should actually correspond in length to vectors in mut_bound_low, mut_bound_high, mut_stddev
-
-	double xover_proportion;
-	SString mut_bound_low, mut_bound_high, mut_stddev;
-};
-
-#endif
Index: cpp/frams/genetics/genman.cpp
===================================================================
--- cpp/frams/genetics/genman.cpp	(revision 777)
+++ cpp/frams/genetics/genman.cpp	(revision 779)
@@ -19,44 +19,44 @@
 
 #ifdef USE_GENMAN_f0
-#include "f0/oper_f0.h"
+#include "f0/f0_oper.h"
 #endif
 #ifdef USE_GENMAN_f0FUZZY
-#include "f0/oper_f0Fuzzy.h"
+#include "f0/f0Fuzzy_oper.h"
 #endif
 #ifdef USE_GENMAN_f1
-#include "f1/oper_f1.h"
+#include "f1/f1_oper.h"
 #endif
 #ifdef USE_GENMAN_f2
-#include "f2/oper_f2.h"
+#include "f2/f2_oper.h"
 #endif
 #ifdef USE_GENMAN_f2
-#include "f3/oper_f3.h"
+#include "f3/f3_oper.h"
 #endif
 #ifdef USE_GENMAN_f4
-#include "f4/oper_f4.h"
+#include "f4/f4_oper.h"
 #endif
 #ifdef USE_GENMAN_f5
-#include "f5/oper_f5.h"
+#include "f5/f5_oper.h"
 #endif
 #ifdef USE_GENMAN_f6
-#include "f6/oper_f6.h"
+#include "f6/f6_oper.h"
 #endif
 #ifdef USE_GENMAN_f7
-#include "f7/oper_f7.h"
+#include "f7/f7_oper.h"
 #endif
 #ifdef USE_GENMAN_f8
-#include "f8/oper_f8.h"
+#include "f8/f8_oper.h"
 #endif
 #ifdef USE_GENMAN_f9
-#include "f9/oper_f9.h"
+#include "f9/f9_oper.h"
 #endif
 #ifdef USE_GENMAN_fF
-#include "fF/oper_fF.h"
+#include "fF/fF_oper.h"
 #endif
 #ifdef USE_GENMAN_fn
-#include "fn/oper_fn.h"
+#include "fn/fn_oper.h"
 #endif
 #ifdef USE_GENMAN_fT
-#include "fT/oper_fTest.h"
+#include "fT/fTest_oper.h"
 #endif
 
Index: cpp/frams/genetics/genman.h
===================================================================
--- cpp/frams/genetics/genman.h	(revision 777)
+++ cpp/frams/genetics/genman.h	(revision 779)
@@ -12,5 +12,5 @@
 #include <frams/neuro/geneticneuroparam.h>
 #include "geno.h"
-#include "oper_fx.h"
+#include "genooperators.h"
 
 #define GENSTYLE_COMMENT GENSTYLE_RGBS(0,150,0,GENSTYLE_BOLD)
Index: cpp/frams/genetics/genooperators.cpp
===================================================================
--- cpp/frams/genetics/genooperators.cpp	(revision 779)
+++ cpp/frams/genetics/genooperators.cpp	(revision 779)
@@ -0,0 +1,391 @@
+// 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 <ctype.h>  //isupper()
+#include "genooperators.h"
+#include <common/log.h>
+#include <common/nonstd_math.h>
+#include <frams/util/rndutil.h>
+
+static double distrib_force[] =   // for '!'
+{
+	3,             // distribution 0 -__/ +1
+	0.001, 0.2,    // "slow" neurons
+	0.001, 1,
+	1, 1,          // "fast" neurons
+};
+static double distrib_inertia[] =  // for '='
+{
+	2,             // distribution 0 |..- +1
+	0, 0,          // "fast" neurons
+	0.7, 0.98,
+};
+static double distrib_sigmo[] =  // for '/'
+{
+	5,             // distribution -999 -..-^-..- +999
+	-999, -999,    //"perceptron"
+	999, 999,
+	-5, -1,        // nonlinear
+	1, 5,
+	-1, 1,         // ~linear
+};
+
+
+int GenoOperators::roulette(const double *probtab, const int count)
+{
+	double sum = 0;
+	int i;
+	for (i = 0; i < count; i++) sum += probtab[i];
+	double sel = rnd01*sum;
+	for (sum = 0, i = 0; i < count; i++) { sum += probtab[i]; if (sel < sum) return i; }
+	return -1;
+}
+
+bool GenoOperators::getMinMaxDef(ParamInterface *p, int i, double &mn, double &mx, double &def)
+{
+	mn = mx = def = 0;
+	int defined = 0;
+	if (p->type(i)[0] == 'f')
+	{
+		double _mn = 0, _mx = 1, _def = 0.5;
+		defined = p->getMinMaxDouble(i, _mn, _mx, _def);
+		if (defined == 1) _mx = _mn + 1000.0; //only min was defined, so let's set some arbitrary range, just to have some freedom. Assumes _mn is not close to maxdouble...
+		if (_mx < _mn && defined == 3) //only default was defined, so let's assume some arbitrary range. Again, no check for min/maxdouble...
+		{
+			_mn = _def - 500.0;
+			_mx = _def + 500.0;
+		}
+		if (defined < 3) _def = (_mn + _mx) / 2.0;
+		mn = _mn; mx = _mx; def = _def;
+	}
+	if (p->type(i)[0] == 'd')
+	{
+		paInt _mn = 0, _mx = 1, _def = 0;
+		defined = p->getMinMaxInt(i, _mn, _mx, _def);
+		if (defined == 1) _mx = _mn + 1000; //only min was defined, so let's set some arbitrary range, just to have some freedom. Assumes _mn is not close to maxint...
+		if (_mx < _mn && defined == 3) //only default was defined, so let's assume some arbitrary range. Again, no check for min/maxint...
+		{
+			_mn = _def - 500;
+			_mx = _def + 500;
+		}
+		if (defined < 3) _def = (_mn + _mx) / 2;
+		mn = _mn; mx = _mx; def = _def;
+	}
+	return defined == 3;
+}
+
+int GenoOperators::selectRandomProperty(Neuro* n)
+{
+	int neuext = n->extraProperties().getPropCount(),
+		neucls = n->getClass() == NULL ? 0 : n->getClass()->getProperties().getPropCount();
+	if (neuext + neucls == 0) return -1; //no properties in this neuron
+	int index = randomN(neuext + neucls);
+	if (index >= neuext) index = index - neuext + 100;
+	return index;
+}
+
+double GenoOperators::mutateNeuProperty(double current, Neuro *n, int i)
+{
+	if (i == -1) return mutateCreepNoLimit('f', current, 2, true); //i==-1: mutating weight of neural connection
+	Param p;
+	if (i >= 100) { i -= 100; p = n->getClass()->getProperties(); }
+	else p = n->extraProperties();
+	double newval = current;
+	/*bool ok=*/getMutatedProperty(p, i, current, newval);
+	return newval;
+}
+
+bool GenoOperators::mutatePropertyNaive(ParamInterface &p, int i)
+{
+	double mn, mx, df;
+	if (p.type(i)[0] != 'f' && p.type(i)[0] != 'd') return false; //don't know how to mutate
+	getMinMaxDef(&p, i, mn, mx, df);
+
+	ExtValue ev;
+	p.get(i, ev);
+	ev.setDouble(mutateCreep(p.type(i)[0], ev.getDouble(), mn, mx, true));
+	p.set(i, ev);
+	return true;
+}
+
+bool GenoOperators::mutateProperty(ParamInterface &p, int i)
+{
+	double newval;
+	ExtValue ev;
+	p.get(i, ev);
+	bool ok = getMutatedProperty(p, i, ev.getDouble(), newval);
+	if (ok) { ev.setDouble(newval); p.set(i, ev); }
+	return ok;
+}
+
+bool GenoOperators::getMutatedProperty(ParamInterface &p, int i, double oldval, double &newval)
+{
+	newval = 0;
+	if (p.type(i)[0] != 'f' && p.type(i)[0] != 'd') return false; //don't know how to mutate
+	const char *n = p.id(i), *na = p.name(i);
+	if (strcmp(n, "si") == 0 && strcmp(na, "Sigmoid") == 0) newval = CustomRnd(distrib_sigmo); else
+		if (strcmp(n, "in") == 0 && strcmp(na, "Inertia") == 0) newval = CustomRnd(distrib_inertia); else
+			if (strcmp(n, "fo") == 0 && strcmp(na, "Force") == 0) newval = CustomRnd(distrib_force); else
+			{
+		double mn, mx, df;
+		getMinMaxDef(&p, i, mn, mx, df);
+		newval = mutateCreep(p.type(i)[0], oldval, mn, mx, true);
+			}
+	return true;
+}
+
+double GenoOperators::mutateCreepNoLimit(char type, double current, double stddev, bool limit_precision_3digits)
+{
+	double result = RndGen.Gauss(current, stddev);
+	if (type == 'd')
+	{
+		result = int(result + 0.5);
+		if (result == current) result += randomN(2) * 2 - 1; //force some change
+	}
+	else
+	{
+		if (limit_precision_3digits)
+			result = floor(result * 1000 + 0.5) / 1000.0; //round
+	}
+	return result;
+}
+
+double GenoOperators::mutateCreep(char type, double current, double mn, double mx, double stddev, bool limit_precision_3digits)
+{
+	double result = mutateCreepNoLimit(type, current, stddev, limit_precision_3digits);
+	if (result<mn || result>mx) //exceeds boundary, so bring to the allowed range
+	{
+		//reflect:
+		if (result > mx) result = mx - (result - mx); else
+			if (result < mn) result = mn + (mn - result);
+		//wrap (just in case 'result' exceeded the allowed range so much that after reflection above it exceeded the other boundary):
+		if (result > mx) result = mn + fmod(result - mx, mx - mn); else
+			if (result < mn) result = mn + fmod(mn - result, mx - mn);
+		if (limit_precision_3digits)
+		{
+			//reflect and wrap above may have changed the (limited) precision, so try to round again (maybe unnecessarily, because we don't know if reflect+wrap above were triggered)
+			double result_try = floor(result * 1000 + 0.5) / 1000.0; //round
+			if (mn <= result_try && result_try <= mx) result = result_try; //after rounding still witin allowed range, so keep rounded value
+		}
+	}
+	return result;
+}
+
+double GenoOperators::mutateCreep(char type, double current, double mn, double mx, bool limit_precision_3digits)
+{
+	double stddev = (mx - mn) / 2 / 5; // magic arbitrary formula for stddev, which becomes /halfinterval, 5 times narrower
+	return mutateCreep(type, current, mn, mx, stddev, limit_precision_3digits);
+}
+
+void GenoOperators::setIntFromDoubleWithProbabilisticDithering(ParamInterface &p, int index, double value) //TODO
+{
+	p.setInt(index, (paInt)(value + 0.5)); //TODO value=2.499 will result in 2 and 2.5 will result in 3, but we want these cases to be 2 or 3 with almost equal probability. value=2.1 should be mostly 2, rarely 3. Careful with negative values (test it!)
+}
+
+void GenoOperators::linearMix(vector<double> &p1, vector<double> &p2, double proportion)
+{
+	if (p1.size() != p2.size())
+	{
+		logPrintf("GenoOperators", "linearMix", LOG_ERROR, "Cannot mix vectors of different length (%d and %d)", p1.size(), p2.size());
+		return;
+	}
+	for (unsigned int i = 0; i < p1.size(); i++)
+	{
+		double v1 = p1[i];
+		double v2 = p2[i];
+		p1[i] = v1*proportion + v2*(1 - proportion);
+		p2[i] = v2*proportion + v1*(1 - proportion);
+	}
+}
+
+void GenoOperators::linearMix(ParamInterface &p1, int i1, ParamInterface &p2, int i2, double proportion)
+{
+	char type1 = p1.type(i1)[0];
+	char type2 = p2.type(i2)[0];
+	if (type1 == 'f' && type2 == 'f')
+	{
+		double v1 = p1.getDouble(i1);
+		double v2 = p2.getDouble(i2);
+		p1.setDouble(i1, v1*proportion + v2*(1 - proportion));
+		p2.setDouble(i2, v2*proportion + v1*(1 - proportion));
+	}
+	else
+		if (type1 == 'd' && type2 == 'd')
+		{
+		int v1 = p1.getInt(i1);
+		int v2 = p2.getInt(i2);
+		setIntFromDoubleWithProbabilisticDithering(p1, i1, v1*proportion + v2*(1 - proportion));
+		setIntFromDoubleWithProbabilisticDithering(p2, i2, v2*proportion + v1*(1 - proportion));
+		}
+		else
+			logPrintf("GenoOperators", "linearMix", LOG_WARN, "Cannot mix values of types '%c' and '%c'", type1, type2);
+}
+
+NeuroClass* GenoOperators::getRandomNeuroClass()
+{
+	vector<NeuroClass*> active;
+	for (int i = 0; i < Neuro::getClassCount(); i++)
+		if (Neuro::getClass(i)->genactive)
+			active.push_back(Neuro::getClass(i));
+	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
+}
+
+NeuroClass* GenoOperators::getRandomNeuroClassWithOutput()
+{
+	vector<NeuroClass*> active;
+	for (int i = 0; i < Neuro::getClassCount(); i++)
+		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredOutput() != 0)
+			active.push_back(Neuro::getClass(i));
+	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
+}
+
+NeuroClass* GenoOperators::getRandomNeuroClassWithInput()
+{
+	vector<NeuroClass*> active;
+	for (int i = 0; i < Neuro::getClassCount(); i++)
+		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredInputs() != 0)
+			active.push_back(Neuro::getClass(i));
+	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
+}
+
+NeuroClass* GenoOperators::getRandomNeuroClassWithOutputAndNoInputs()
+{
+	vector<NeuroClass*> active;
+	for (int i = 0; i < Neuro::getClassCount(); i++)
+		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredOutput() != 0 && Neuro::getClass(i)->getPreferredInputs() == 0)
+			active.push_back(Neuro::getClass(i));
+	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
+}
+
+int GenoOperators::getRandomNeuroClassWithOutput(const vector<NeuroClass*>& NClist)
+{
+	vector<int> allowed;
+	for (size_t i = 0; i < NClist.size(); i++)
+		if (NClist[i]->getPreferredOutput() != 0) //this NeuroClass provides output
+			allowed.push_back(i);
+	if (allowed.size() == 0) return -1; else return allowed[randomN(allowed.size())];
+}
+
+int GenoOperators::getRandomNeuroClassWithInput(const vector<NeuroClass*>& NClist)
+{
+	vector<int> allowed;
+	for (size_t i = 0; i < NClist.size(); i++)
+		if (NClist[i]->getPreferredInputs() != 0) //this NeuroClass wants one input connection or more			
+			allowed.push_back(i);
+	if (allowed.size() == 0) return -1; else return allowed[randomN(allowed.size())];
+}
+
+int GenoOperators::getRandomChar(const char *choices, const char *excluded)
+{
+	int allowed_count = 0;
+	for (size_t i = 0; i < strlen(choices); i++) if (!strchrn0(excluded, choices[i])) allowed_count++;
+	if (allowed_count == 0) return -1; //no char is allowed
+	int rnd_index = randomN(allowed_count) + 1;
+	allowed_count = 0;
+	for (size_t i = 0; i < strlen(choices); i++)
+	{
+		if (!strchrn0(excluded, choices[i])) allowed_count++;
+		if (allowed_count == rnd_index) return i;
+	}
+	return -1; //never happens
+}
+
+NeuroClass* GenoOperators::parseNeuroClass(char*& s)
+{
+	int maxlen = (int)strlen(s);
+	int NClen = 0;
+	NeuroClass *NC = NULL;
+	for (int i = 0; i<Neuro::getClassCount(); i++)
+	{
+		const char *ncname = Neuro::getClass(i)->name.c_str();
+		int ncnamelen = (int)strlen(ncname);
+		if (maxlen >= ncnamelen && ncnamelen>NClen && (strncmp(s, ncname, ncnamelen) == 0))
+		{
+			NC = Neuro::getClass(i);
+			NClen = ncnamelen;
+		}
+	}
+	s += NClen;
+	return NC;
+}
+
+Neuro* GenoOperators::findNeuro(const Model *m, const NeuroClass *nc)
+{
+	if (!m) return NULL;
+	for (int i = 0; i < m->getNeuroCount(); i++)
+		if (m->getNeuro(i)->getClass() == nc) return m->getNeuro(i);
+	return NULL; //neuron of class 'nc' was not found
+}
+
+int GenoOperators::neuroClassProp(char*& s, NeuroClass *nc, bool also_v1_N_props)
+{
+	int len = (int)strlen(s);
+	int Len = 0, I = -1;
+	if (nc)
+	{
+		Param p = nc->getProperties();
+		for (int i = 0; i<p.getPropCount(); i++)
+		{
+			const char *n = p.id(i);
+			int l = (int)strlen(n);
+			if (len >= l && l>Len && (strncmp(s, n, l) == 0)) { I = 100 + i; Len = l; }
+			if (also_v1_N_props) //recognize old properties symbols /=!
+			{
+				if (strcmp(n, "si") == 0) n = "/"; else
+					if (strcmp(n, "in") == 0) n = "="; else
+						if (strcmp(n, "fo") == 0) n = "!";
+				l = (int)strlen(n);
+				if (len >= l && l > Len && (strncmp(s, n, l) == 0)) { I = 100 + i; Len = l; }
+			}
+		}
+	}
+	Neuro n;
+	Param p = n.extraProperties();
+	for (int i = 0; i<p.getPropCount(); i++)
+	{
+		const char *n = p.id(i);
+		int l = (int)strlen(n);
+		if (len >= l && l>Len && (strncmp(s, n, l) == 0)) { I = i; Len = l; }
+	}
+	s += Len;
+	return I;
+}
+
+bool GenoOperators::isWS(const char c)
+{
+	return c == ' ' || c == '\n' || c == '\t' || c == '\r';
+}
+
+void GenoOperators::skipWS(char *&s)
+{
+	if (s == NULL)
+		logMessage("GenoOperators", "skipWS", LOG_WARN, "NULL reference!");
+	else
+		while (isWS(*s)) s++;
+}
+
+bool GenoOperators::areAlike(char *g1, char *g2)
+{
+	while (*g1 || *g2)
+	{
+		skipWS(g1);
+		skipWS(g2);
+		if (*g1 != *g2) return false; //when difference
+		if (!*g1 && !*g2) break; //both end
+		g1++;
+		g2++;
+	}
+	return true; //equal
+}
+
+char* GenoOperators::strchrn0(const char *str, char ch)
+{
+	return ch == 0 ? NULL : strchr((char*)str, ch);
+}
+
+bool GenoOperators::canStartNeuroClassName(const char firstchar)
+{
+	return isupper(firstchar) || firstchar == '|' || firstchar == '@' || firstchar == '*';
+}
+
Index: cpp/frams/genetics/genooperators.h
===================================================================
--- cpp/frams/genetics/genooperators.h	(revision 779)
+++ cpp/frams/genetics/genooperators.h	(revision 779)
@@ -0,0 +1,230 @@
+// 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.
+
+#ifndef _GENO_OPERATORS_H_
+#define _GENO_OPERATORS_H_
+
+#include <common/nonstd.h>
+#include <frams/model/model.h>
+
+/** @file */
+
+/** \name Return codes for genetic operators */
+//@{
+#define GENOPER_OK          0 ///<operation successful
+#define GENOPER_OPFAIL     -1 ///<operation failed or could not be completed
+#define GENOPER_REPAIR     -2 ///<do not use in Geno_fx. GenMan uses it in checkValidity()... but will not. only f4 uses it
+#define GENOPER_NOOPER     -3 ///<do not use in Geno_fx. GenMan uses it for "no suitable operator for this genotype format"
+//@}
+
+/** \name gene/character predefined styles (for style() method) */
+//@{
+#define GENSTYLE_NONE       0 ///<no style specified (=normal font)
+#define GENSTYLE_INVALID    1 ///<this char cannot be accepted
+#define GENSTYLE_BOLD       2 ///<bold
+#define GENSTYLE_ITALIC     4 ///<italic
+#define GENSTYLE_STRIKEOUT  8 ///<strikeout (not recommended)
+//UNDERLINE used to mark errors
+//@}
+
+/** \name other useful style/color macros */
+//@{
+#define GENRGB(r,g,b) ((uint32_t)(((uint8_t)(r)|((uint16_t)((uint8_t)(g))<<8))|(((uint32_t)(uint8_t)(b))<<16)))
+#define GENSTYLE_RGBS(r,g,b,s) ((uint32_t)((uint8_t)s)<<24 | GENRGB(r,g,b))
+#define GENSTYLE_CS(rgb,s) ((uint32_t)((uint8_t)s)<<24 | rgb)
+
+#define GENGETSTYLE(style) ((style)>>24)
+#define GENGETCOLOR(style) ((style)&0x00ffffff)
+#define GENGET_R(style) ((style)&0xff)
+#define GENGET_G(style) ((style>>8)&0xff)
+#define GENGET_B(style) ((style>>16)&0xff)
+
+#define GENCOLOR_TEXT    GENRGB(0,0,0) ///<recommended color to use for text genes
+#define GENCOLOR_NUMBER  GENRGB(200,0,0) ///<recommended color to use for number genes
+//@}
+
+///Base class for genetic operations on genotypes of some genetic format
+/**\author Maciej Komosinski
+
+When designing genetic operations on some representation, inherit your class
+(for example GenoOper_fMy) from GenoOperators. Define some methods,
+like mutate(), in your class, to allow for evolution.
+Ensure they have the same names and arguments as the corresponding
+virtual methods in Geno_fx. Set the 'supported_format' variable to the
+appropriate genetic representation ID.
+Whenever arguments are genotypes, they are without
+trailing characters which describe genetic format
+(for example, "p:", not "//0\np:").
+When allocating/reallocating char* parameters, use malloc, free, realloc, strdup, etc.
+Do not use new and delete.
+
+All the methods you might define are:
+- checkValidity()
+- validate()
+- mutate()
+- crossOver()
+- getSimplest()
+- style()
+
+Your code must not cause errors (like invalid memory access, memory
+leaks) on any arguments, even 'random' ones. GENOPER_OPFAIL should
+be returned when an operator cannot cope with its argument genotype.
+
+To compile your code, you may also need some SDK files.
+A simple example is Geno_ftest class (see \ref geno_ftest_example "C++ code" for details).
+A more realistic example is Geno_f4 derived from Geno_fx: refer to
+the available source on developmental encoding and f4 genotype format.*/
+
+class GenoOperators
+{
+public:
+	Param par;
+	char supported_format; ///<genotype format which is supported by this class ('6' for GenoOper_f6, 'F' for GenoOper_fF, etc.). Must be initialized in constructor
+	string name; ///<name of this set of genetic operators
+	const char **mutation_method_names; ///<array of names for mutation methods. If initialized (by new const char*[]), must have entries for each method index returned by mutate(geno,chg,METHOD).  If initialized, it is automatically freed by this destructor.
+	GenoOperators() : par(empty_paramtab) { supported_format = 'x'; name = "Default"; mutation_method_names = NULL; setDefaults(); }
+
+	/**Used to perform initializations of Param parameters that are not handled by the Param itself
+	(i.e. string parameters or fields that require some complex logic may be initialized here)*/
+	virtual void setDefaults() {}
+
+	/**Checks a genotype for minor mistakes and major errors.
+	\param geno genotype to be checked
+	\param genoname name of the genotype to be checked
+	\retval error_position 1-based (or 1 if no exact error position known)
+	\retval GENOPER_OK when the genotype is fully valid, and can be translated by the converter with \b no modifications nor tweaks*/
+	virtual int checkValidity(const char *geno, const char *genoname) { return GENOPER_NOOPER; }
+
+	/**Validates a genotype. The purpose of this function is to validate
+	obvious/minor errors (range overruns, invalid links, etc.). Do not try
+	to introduce entirely new genes in place of an error.
+	\param geno input/output: genotype to be validated
+	\param genoname name of the genotype to be validated
+	\retval GENOPER_OK must be returned in any case ("did my best to validate")*/
+	virtual int validate(char *&geno, const char *genoname) { return GENOPER_NOOPER; }
+
+	/**Mutates a genotype. Mutation should always change something.
+
+	Avoid unnecessary calls in your code. Every genotype argument passed to this
+	function is first checked, and validated if checkValidity() reported an error (or
+	if there is no checkValidity() implemented). Every resulting genotype is subject
+	to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
+	to call these functions on input and output genotypes, because they are validated
+	if needed.
+	\param geno input/output: genotype to be mutated
+	\param chg output: initialize with a value (in most cases 0..1) corresponding
+	to the amount of genotype mutated. For example, it could be the number of changed
+	genes divided by the total number of genes before mutation.
+	\param chg method: initialize with the ID (number) of mutation method used.
+	\retval GENOPER_OK
+	\retval GENOPER_OPFAIL
+	\sa
+	Mutation example to illustrate the exchange of pointers for \e geno.
+	The mutation adds random letter at the beginning or removes last letter from \e geno.
+	\code
+	{
+	int len=strlen(geno);
+	if (len==0 || random(2)==0) //add
+	{
+	method=0;
+	char* mutated=(char*)malloc(mutated,len+2); //allocate for mutated genotype
+	mutated[0]='A'+random(10); //first char random
+	strcpy(mutated+1,geno); //the rest is original
+	free(geno); //must take care of the original allocation
+	geno=mutated;
+	} else
+	{
+	method=1;
+	geno[len-1]=0; //simply shorten the string - remove last char
+	}
+	chg=1.0/max(len,1); //estimation of mutation strength, divby0-safe
+	} \endcode
+	*/
+	virtual int mutate(char *&geno, float& chg, int &method) { method = -1; chg = -1; return GENOPER_NOOPER; }
+
+	/**Crosses over two genotypes. It is sufficient to return only one child (in \e g1) and set \e chg1 only, then \e g2 must equal "".
+
+	Avoid unnecessary calls in your code. Every genotype argument passed to this
+	function is first checked, and validated if checkValidity() reported an error (or
+	if there is no checkValidity() implemented). Every resulting genotype is subject
+	to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
+	to call these functions on input and output genotypes, because they are validated
+	if needed.
+	\param g1 input/output: parent1 genotype, initialize with child1
+	\param g2 input/output: parent2 genotype, initialize with child2 if both children are available
+	\param chg1 output: initialize with the fraction of parent1 genes in child1 (parent2 has the rest)
+	\param chg2 output: initialize with the fraction of parent2 genes in child2 (parent1 has the rest)
+	\retval GENOPER_OK
+	\retval GENOPER_OPFAIL
+	\sa mutate() for an example*/
+	virtual int crossOver(char *&g1, char *&g2, float& chg1, float& chg2) { chg1 = chg2 = -1; return GENOPER_NOOPER; }
+
+	/**\return a pointer to the simplest genotype string*/
+	virtual const char* getSimplest() { return NULL; }
+
+	/**You may want to have your genotype colored. This method provides desired character styles for genes.
+	\param geno genotype
+	\param pos 0-based char offset
+	\retval number-encoded visual style (and validity) of the genotype char at \e geno[pos].
+	Assume white background.
+	\sa GENSTYLE_* macros, like GENSTYLE_BOLD*/
+	virtual uint32_t style(const char *geno, int pos) { return GENSTYLE_RGBS(0, 0, 0, GENSTYLE_NONE); }
+
+	///currently not used (similarity of two genotypes)
+	virtual float similarity(const char*, const char*) { return GENOPER_NOOPER; }
+	virtual ~GenoOperators() { if (mutation_method_names) { delete[]mutation_method_names; mutation_method_names = NULL; } }
+	//   virtual char getFormat() {return 255;} //returns supported genotype format, for ex. '1'
+	//   virtual int enabled() {return 1;} // should be enabled by default
+
+	/** \name Some helpful methods for you */
+	//@{
+	static int roulette(const double *probtab, const int count); 	///<returns random index according to probabilities in the \e probtab table or -1 if all probs are zero. \e count is the number of elements in \e probtab.
+	static bool getMinMaxDef(ParamInterface *p, int propindex, double &mn, double &mx, double &def); ///<perhaps a more useful (higher-level) way to obtain min/max/def info for integer and double properties. Returns true if min/max/def was really available (otherwise it is just invented).
+	static int selectRandomProperty(Neuro* n); ///<selects random property (either 0-based extraproperty of Neuro or 100-based property of its NeuroClass). -1 if Neuro has no properties.
+	static double mutateNeuProperty(double current, Neuro *n, int propindex); ///<returns value \e current mutated for the property \e propindex of NeuroClass \e nc or for extraproperty (\e propindex - 100) of Neuro. Neuro is used as read-only. Give \e propindex == -1 to mutate connection weight (\e nc is then ignored).
+	static bool mutatePropertyNaive(ParamInterface &p, int propindex); ///<creep-mutate selected property. Returns true when success. mutateProperty() should be used instead of this function.
+	static bool mutateProperty(ParamInterface &p, int propindex); ///<like mutatePropertyNaive(), but uses special probability distributions for some neuron properties.
+	static bool getMutatedProperty(ParamInterface &p, int i, double oldval, double &newval); ///<like mutateProperty(), but just returns \e newval, does not get nor set it using \e p.
+	static double mutateCreepNoLimit(char type, double current, double stddev, bool limit_precision_3digits); ///<returns \e current value creep-mutated with Gaussian distribution and \e stddev standard deviation. Precision limited to 3 digits after comma when \e limit_precision_3digits is true. \e type must be either 'd' (integer) or 'f' (float/double).
+	static double mutateCreep(char type, double current, double mn, double mx, double stddev, bool limit_precision_3digits); ///<just as mutateCreepNoLimit(), but forces mutated value into the [mn,mx] range using the 'reflect' approach.
+	static double mutateCreep(char type, double current, double mn, double mx, bool limit_precision_3digits); ///<just as mutateCreepNoLimit(), but forces mutated value into the [\e mn,\e mx] range using the 'reflect' approach and assumes standard deviation to be a fraction of the mx-mn interval width.
+	static void setIntFromDoubleWithProbabilisticDithering(ParamInterface &p, int index, double value); ///<sets a double value in an integer field; when a value is non-integer, applies random "dithering" so that both lower and higher integer value have some chance to be set.
+	static void linearMix(vector<double> &p1, vector<double> &p2, double proportion); ///<mixes two real-valued vectors; inherited proportion should be within [0,1]; 1.0 does not change values (all inherited), 0.5 causes both vectors to become their average, 0.0 swaps values (none inherited).
+	static void linearMix(ParamInterface &p1, int i1, ParamInterface &p2, int i2, double proportion); ///<mixes i1'th and i2'th properties of p1 and p2; inherited proportion should be within [0,1]; 1.0 does not change values (all inherited), 0.5 causes both properties to become their average, 0.0 swaps values (none inherited). For integer properties applies random "dithering" when necessary.
+	static NeuroClass* getRandomNeuroClass(); ///<returns random neuroclass or NULL when no active classes.
+	static NeuroClass* getRandomNeuroClassWithOutput(); ///<returns random neuroclass with output or NULL when no active classes.
+	static NeuroClass* getRandomNeuroClassWithInput(); ///<returns random neuroclass with input or NULL when no active classes.
+	static NeuroClass* getRandomNeuroClassWithOutputAndNoInputs(); ///<returns random sensor or NULL when no active classes.
+	static NeuroClass* getRandomSensorProb(float prob); ///<returns random sensor (only-output neuron class) with presented probability or NULL. \e prob probability of picking neuron class.
+	static int getRandomNeuroClassWithOutput(const vector<NeuroClass*>& NClist); ///<returns index of random NeuroClass from the NClist or -1 (no neurons on the list that provide output) \e NClist list of available neuron classes
+	static int getRandomNeuroClassWithInput(const vector<NeuroClass*>& NClist); ///<returns index of random NeuroClass from the NClist or -1 (no neurons on the list that want input(s)) \e NClist list of available neuron classes
+	static int getRandomChar(const char *choices, const char *excluded); ///<returns index of a random character from 'choices' excluding 'excluded', or -1 when everything is excluded or 'choices' is empty.
+	static NeuroClass* parseNeuroClass(char *&s); ///<returns longest matching neuroclass or NULL if the string does not begin with a valid neuroclass name. Advances \e s pointer.
+	static Neuro* findNeuro(const Model *m, const NeuroClass *nc); ///<returns pointer to first Neuro of class \e nc, or NULL if there is no such Neuro.
+	static int neuroClassProp(char *&s, NeuroClass *nc, bool also_v1_N_props = false); ///<returns 0-based property number for \e neuroclass, 100-based extraproperty number for Neuro, or -1 if the string does not begin with a valid property name. Advance \e s pointer if success.
+	static bool isWS(const char c); ///<is \e c a whitespace char?
+	static void skipWS(char *&s); ///<advances pointer \e s skipping whitespaces.
+	static bool areAlike(char*, char*); ///<compares two text strings skipping whitespaces. Returns 1 when equal, 0 when different.
+	static char* strchrn0(const char *str, char ch); ///<like strchr, but does not find zero char in \e str.
+	static bool canStartNeuroClassName(const char firstchar); ///<determines if \e firstchar may start NeuroClass name. If not, it may start NeuroClass' (or Neuro's) property name.
+	//@}
+};
+
+
+//
+// custom distributions for mutations of various parameters
+//
+/*
+static double distrib_weight[]=
+{
+5,                 // distribution -999 _-^_^-_ +999
+-999, 999,         // each weight value may be useful, especially...
+-5, -0.3,        // ...little non-zero values
+-3, -0.6,
+0.6, 3,
+0.3, 5,
+};
+*/
+
+#endif
Index: cpp/frams/genetics/oper_fx.cpp
===================================================================
--- cpp/frams/genetics/oper_fx.cpp	(revision 777)
+++ 	(revision )
@@ -1,391 +1,0 @@
-// 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 <ctype.h>  //isupper()
-#include "oper_fx.h"
-#include <common/log.h>
-#include <common/nonstd_math.h>
-#include <frams/util/rndutil.h>
-
-static double distrib_force[] =   // for '!'
-{
-	3,             // distribution 0 -__/ +1
-	0.001, 0.2,    // "slow" neurons
-	0.001, 1,
-	1, 1,          // "fast" neurons
-};
-static double distrib_inertia[] =  // for '='
-{
-	2,             // distribution 0 |..- +1
-	0, 0,          // "fast" neurons
-	0.7, 0.98,
-};
-static double distrib_sigmo[] =  // for '/'
-{
-	5,             // distribution -999 -..-^-..- +999
-	-999, -999,    //"perceptron"
-	999, 999,
-	-5, -1,        // nonlinear
-	1, 5,
-	-1, 1,         // ~linear
-};
-
-
-int GenoOperators::roulette(const double *probtab, const int count)
-{
-	double sum = 0;
-	int i;
-	for (i = 0; i < count; i++) sum += probtab[i];
-	double sel = rnd01*sum;
-	for (sum = 0, i = 0; i < count; i++) { sum += probtab[i]; if (sel < sum) return i; }
-	return -1;
-}
-
-bool GenoOperators::getMinMaxDef(ParamInterface *p, int i, double &mn, double &mx, double &def)
-{
-	mn = mx = def = 0;
-	int defined = 0;
-	if (p->type(i)[0] == 'f')
-	{
-		double _mn = 0, _mx = 1, _def = 0.5;
-		defined = p->getMinMaxDouble(i, _mn, _mx, _def);
-		if (defined == 1) _mx = _mn + 1000.0; //only min was defined, so let's set some arbitrary range, just to have some freedom. Assumes _mn is not close to maxdouble...
-		if (_mx < _mn && defined == 3) //only default was defined, so let's assume some arbitrary range. Again, no check for min/maxdouble...
-		{
-			_mn = _def - 500.0;
-			_mx = _def + 500.0;
-		}
-		if (defined < 3) _def = (_mn + _mx) / 2.0;
-		mn = _mn; mx = _mx; def = _def;
-	}
-	if (p->type(i)[0] == 'd')
-	{
-		paInt _mn = 0, _mx = 1, _def = 0;
-		defined = p->getMinMaxInt(i, _mn, _mx, _def);
-		if (defined == 1) _mx = _mn + 1000; //only min was defined, so let's set some arbitrary range, just to have some freedom. Assumes _mn is not close to maxint...
-		if (_mx < _mn && defined == 3) //only default was defined, so let's assume some arbitrary range. Again, no check for min/maxint...
-		{
-			_mn = _def - 500;
-			_mx = _def + 500;
-		}
-		if (defined < 3) _def = (_mn + _mx) / 2;
-		mn = _mn; mx = _mx; def = _def;
-	}
-	return defined == 3;
-}
-
-int GenoOperators::selectRandomProperty(Neuro* n)
-{
-	int neuext = n->extraProperties().getPropCount(),
-		neucls = n->getClass() == NULL ? 0 : n->getClass()->getProperties().getPropCount();
-	if (neuext + neucls == 0) return -1; //no properties in this neuron
-	int index = randomN(neuext + neucls);
-	if (index >= neuext) index = index - neuext + 100;
-	return index;
-}
-
-double GenoOperators::mutateNeuProperty(double current, Neuro *n, int i)
-{
-	if (i == -1) return mutateCreepNoLimit('f', current, 2, true); //i==-1: mutating weight of neural connection
-	Param p;
-	if (i >= 100) { i -= 100; p = n->getClass()->getProperties(); }
-	else p = n->extraProperties();
-	double newval = current;
-	/*bool ok=*/getMutatedProperty(p, i, current, newval);
-	return newval;
-}
-
-bool GenoOperators::mutatePropertyNaive(ParamInterface &p, int i)
-{
-	double mn, mx, df;
-	if (p.type(i)[0] != 'f' && p.type(i)[0] != 'd') return false; //don't know how to mutate
-	getMinMaxDef(&p, i, mn, mx, df);
-
-	ExtValue ev;
-	p.get(i, ev);
-	ev.setDouble(mutateCreep(p.type(i)[0], ev.getDouble(), mn, mx, true));
-	p.set(i, ev);
-	return true;
-}
-
-bool GenoOperators::mutateProperty(ParamInterface &p, int i)
-{
-	double newval;
-	ExtValue ev;
-	p.get(i, ev);
-	bool ok = getMutatedProperty(p, i, ev.getDouble(), newval);
-	if (ok) { ev.setDouble(newval); p.set(i, ev); }
-	return ok;
-}
-
-bool GenoOperators::getMutatedProperty(ParamInterface &p, int i, double oldval, double &newval)
-{
-	newval = 0;
-	if (p.type(i)[0] != 'f' && p.type(i)[0] != 'd') return false; //don't know how to mutate
-	const char *n = p.id(i), *na = p.name(i);
-	if (strcmp(n, "si") == 0 && strcmp(na, "Sigmoid") == 0) newval = CustomRnd(distrib_sigmo); else
-		if (strcmp(n, "in") == 0 && strcmp(na, "Inertia") == 0) newval = CustomRnd(distrib_inertia); else
-			if (strcmp(n, "fo") == 0 && strcmp(na, "Force") == 0) newval = CustomRnd(distrib_force); else
-			{
-		double mn, mx, df;
-		getMinMaxDef(&p, i, mn, mx, df);
-		newval = mutateCreep(p.type(i)[0], oldval, mn, mx, true);
-			}
-	return true;
-}
-
-double GenoOperators::mutateCreepNoLimit(char type, double current, double stddev, bool limit_precision_3digits)
-{
-	double result = RndGen.Gauss(current, stddev);
-	if (type == 'd')
-	{
-		result = int(result + 0.5);
-		if (result == current) result += randomN(2) * 2 - 1; //force some change
-	}
-	else
-	{
-		if (limit_precision_3digits)
-			result = floor(result * 1000 + 0.5) / 1000.0; //round
-	}
-	return result;
-}
-
-double GenoOperators::mutateCreep(char type, double current, double mn, double mx, double stddev, bool limit_precision_3digits)
-{
-	double result = mutateCreepNoLimit(type, current, stddev, limit_precision_3digits);
-	if (result<mn || result>mx) //exceeds boundary, so bring to the allowed range
-	{
-		//reflect:
-		if (result > mx) result = mx - (result - mx); else
-			if (result < mn) result = mn + (mn - result);
-		//wrap (just in case 'result' exceeded the allowed range so much that after reflection above it exceeded the other boundary):
-		if (result > mx) result = mn + fmod(result - mx, mx - mn); else
-			if (result < mn) result = mn + fmod(mn - result, mx - mn);
-		if (limit_precision_3digits)
-		{
-			//reflect and wrap above may have changed the (limited) precision, so try to round again (maybe unnecessarily, because we don't know if reflect+wrap above were triggered)
-			double result_try = floor(result * 1000 + 0.5) / 1000.0; //round
-			if (mn <= result_try && result_try <= mx) result = result_try; //after rounding still witin allowed range, so keep rounded value
-		}
-	}
-	return result;
-}
-
-double GenoOperators::mutateCreep(char type, double current, double mn, double mx, bool limit_precision_3digits)
-{
-	double stddev = (mx - mn) / 2 / 5; // magic arbitrary formula for stddev, which becomes /halfinterval, 5 times narrower
-	return mutateCreep(type, current, mn, mx, stddev, limit_precision_3digits);
-}
-
-void GenoOperators::setIntFromDoubleWithProbabilisticDithering(ParamInterface &p, int index, double value) //TODO
-{
-	p.setInt(index, (paInt)(value + 0.5)); //TODO value=2.499 will result in 2 and 2.5 will result in 3, but we want these cases to be 2 or 3 with almost equal probability. value=2.1 should be mostly 2, rarely 3. Careful with negative values (test it!)
-}
-
-void GenoOperators::linearMix(vector<double> &p1, vector<double> &p2, double proportion)
-{
-	if (p1.size() != p2.size())
-	{
-		logPrintf("GenoOperators", "linearMix", LOG_ERROR, "Cannot mix vectors of different length (%d and %d)", p1.size(), p2.size());
-		return;
-	}
-	for (unsigned int i = 0; i < p1.size(); i++)
-	{
-		double v1 = p1[i];
-		double v2 = p2[i];
-		p1[i] = v1*proportion + v2*(1 - proportion);
-		p2[i] = v2*proportion + v1*(1 - proportion);
-	}
-}
-
-void GenoOperators::linearMix(ParamInterface &p1, int i1, ParamInterface &p2, int i2, double proportion)
-{
-	char type1 = p1.type(i1)[0];
-	char type2 = p2.type(i2)[0];
-	if (type1 == 'f' && type2 == 'f')
-	{
-		double v1 = p1.getDouble(i1);
-		double v2 = p2.getDouble(i2);
-		p1.setDouble(i1, v1*proportion + v2*(1 - proportion));
-		p2.setDouble(i2, v2*proportion + v1*(1 - proportion));
-	}
-	else
-		if (type1 == 'd' && type2 == 'd')
-		{
-		int v1 = p1.getInt(i1);
-		int v2 = p2.getInt(i2);
-		setIntFromDoubleWithProbabilisticDithering(p1, i1, v1*proportion + v2*(1 - proportion));
-		setIntFromDoubleWithProbabilisticDithering(p2, i2, v2*proportion + v1*(1 - proportion));
-		}
-		else
-			logPrintf("GenoOperators", "linearMix", LOG_WARN, "Cannot mix values of types '%c' and '%c'", type1, type2);
-}
-
-NeuroClass* GenoOperators::getRandomNeuroClass()
-{
-	vector<NeuroClass*> active;
-	for (int i = 0; i < Neuro::getClassCount(); i++)
-		if (Neuro::getClass(i)->genactive)
-			active.push_back(Neuro::getClass(i));
-	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
-}
-
-NeuroClass* GenoOperators::getRandomNeuroClassWithOutput()
-{
-	vector<NeuroClass*> active;
-	for (int i = 0; i < Neuro::getClassCount(); i++)
-		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredOutput() != 0)
-			active.push_back(Neuro::getClass(i));
-	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
-}
-
-NeuroClass* GenoOperators::getRandomNeuroClassWithInput()
-{
-	vector<NeuroClass*> active;
-	for (int i = 0; i < Neuro::getClassCount(); i++)
-		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredInputs() != 0)
-			active.push_back(Neuro::getClass(i));
-	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
-}
-
-NeuroClass* GenoOperators::getRandomNeuroClassWithOutputAndNoInputs()
-{
-	vector<NeuroClass*> active;
-	for (int i = 0; i < Neuro::getClassCount(); i++)
-		if (Neuro::getClass(i)->genactive && Neuro::getClass(i)->getPreferredOutput() != 0 && Neuro::getClass(i)->getPreferredInputs() == 0)
-			active.push_back(Neuro::getClass(i));
-	if (active.size() == 0) return NULL; else return active[randomN(active.size())];
-}
-
-int GenoOperators::getRandomNeuroClassWithOutput(const vector<NeuroClass*>& NClist)
-{
-	vector<int> allowed;
-	for (size_t i = 0; i < NClist.size(); i++)
-		if (NClist[i]->getPreferredOutput() != 0) //this NeuroClass provides output
-			allowed.push_back(i);
-	if (allowed.size() == 0) return -1; else return allowed[randomN(allowed.size())];
-}
-
-int GenoOperators::getRandomNeuroClassWithInput(const vector<NeuroClass*>& NClist)
-{
-	vector<int> allowed;
-	for (size_t i = 0; i < NClist.size(); i++)
-		if (NClist[i]->getPreferredInputs() != 0) //this NeuroClass wants one input connection or more			
-			allowed.push_back(i);
-	if (allowed.size() == 0) return -1; else return allowed[randomN(allowed.size())];
-}
-
-int GenoOperators::getRandomChar(const char *choices, const char *excluded)
-{
-	int allowed_count = 0;
-	for (size_t i = 0; i < strlen(choices); i++) if (!strchrn0(excluded, choices[i])) allowed_count++;
-	if (allowed_count == 0) return -1; //no char is allowed
-	int rnd_index = randomN(allowed_count) + 1;
-	allowed_count = 0;
-	for (size_t i = 0; i < strlen(choices); i++)
-	{
-		if (!strchrn0(excluded, choices[i])) allowed_count++;
-		if (allowed_count == rnd_index) return i;
-	}
-	return -1; //never happens
-}
-
-NeuroClass* GenoOperators::parseNeuroClass(char*& s)
-{
-	int maxlen = (int)strlen(s);
-	int NClen = 0;
-	NeuroClass *NC = NULL;
-	for (int i = 0; i<Neuro::getClassCount(); i++)
-	{
-		const char *ncname = Neuro::getClass(i)->name.c_str();
-		int ncnamelen = (int)strlen(ncname);
-		if (maxlen >= ncnamelen && ncnamelen>NClen && (strncmp(s, ncname, ncnamelen) == 0))
-		{
-			NC = Neuro::getClass(i);
-			NClen = ncnamelen;
-		}
-	}
-	s += NClen;
-	return NC;
-}
-
-Neuro* GenoOperators::findNeuro(const Model *m, const NeuroClass *nc)
-{
-	if (!m) return NULL;
-	for (int i = 0; i < m->getNeuroCount(); i++)
-		if (m->getNeuro(i)->getClass() == nc) return m->getNeuro(i);
-	return NULL; //neuron of class 'nc' was not found
-}
-
-int GenoOperators::neuroClassProp(char*& s, NeuroClass *nc, bool also_v1_N_props)
-{
-	int len = (int)strlen(s);
-	int Len = 0, I = -1;
-	if (nc)
-	{
-		Param p = nc->getProperties();
-		for (int i = 0; i<p.getPropCount(); i++)
-		{
-			const char *n = p.id(i);
-			int l = (int)strlen(n);
-			if (len >= l && l>Len && (strncmp(s, n, l) == 0)) { I = 100 + i; Len = l; }
-			if (also_v1_N_props) //recognize old properties symbols /=!
-			{
-				if (strcmp(n, "si") == 0) n = "/"; else
-					if (strcmp(n, "in") == 0) n = "="; else
-						if (strcmp(n, "fo") == 0) n = "!";
-				l = (int)strlen(n);
-				if (len >= l && l > Len && (strncmp(s, n, l) == 0)) { I = 100 + i; Len = l; }
-			}
-		}
-	}
-	Neuro n;
-	Param p = n.extraProperties();
-	for (int i = 0; i<p.getPropCount(); i++)
-	{
-		const char *n = p.id(i);
-		int l = (int)strlen(n);
-		if (len >= l && l>Len && (strncmp(s, n, l) == 0)) { I = i; Len = l; }
-	}
-	s += Len;
-	return I;
-}
-
-bool GenoOperators::isWS(const char c)
-{
-	return c == ' ' || c == '\n' || c == '\t' || c == '\r';
-}
-
-void GenoOperators::skipWS(char *&s)
-{
-	if (s == NULL)
-		logMessage("GenoOperators", "skipWS", LOG_WARN, "NULL reference!");
-	else
-		while (isWS(*s)) s++;
-}
-
-bool GenoOperators::areAlike(char *g1, char *g2)
-{
-	while (*g1 || *g2)
-	{
-		skipWS(g1);
-		skipWS(g2);
-		if (*g1 != *g2) return false; //when difference
-		if (!*g1 && !*g2) break; //both end
-		g1++;
-		g2++;
-	}
-	return true; //equal
-}
-
-char* GenoOperators::strchrn0(const char *str, char ch)
-{
-	return ch == 0 ? NULL : strchr((char*)str, ch);
-}
-
-bool GenoOperators::canStartNeuroClassName(const char firstchar)
-{
-	return isupper(firstchar) || firstchar == '|' || firstchar == '@' || firstchar == '*';
-}
-
Index: cpp/frams/genetics/oper_fx.h
===================================================================
--- cpp/frams/genetics/oper_fx.h	(revision 777)
+++ 	(revision )
@@ -1,230 +1,0 @@
-// 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.
-
-#ifndef _GENO_OPERATORS_H_
-#define _GENO_OPERATORS_H_
-
-#include <common/nonstd.h>
-#include <frams/model/model.h>
-
-/** @file */
-
-/** \name Return codes for genetic operators */
-//@{
-#define GENOPER_OK          0 ///<operation successful
-#define GENOPER_OPFAIL     -1 ///<operation failed or could not be completed
-#define GENOPER_REPAIR     -2 ///<do not use in Geno_fx. GenMan uses it in checkValidity()... but will not. only f4 uses it
-#define GENOPER_NOOPER     -3 ///<do not use in Geno_fx. GenMan uses it for "no suitable operator for this genotype format"
-//@}
-
-/** \name gene/character predefined styles (for style() method) */
-//@{
-#define GENSTYLE_NONE       0 ///<no style specified (=normal font)
-#define GENSTYLE_INVALID    1 ///<this char cannot be accepted
-#define GENSTYLE_BOLD       2 ///<bold
-#define GENSTYLE_ITALIC     4 ///<italic
-#define GENSTYLE_STRIKEOUT  8 ///<strikeout (not recommended)
-//UNDERLINE used to mark errors
-//@}
-
-/** \name other useful style/color macros */
-//@{
-#define GENRGB(r,g,b) ((uint32_t)(((uint8_t)(r)|((uint16_t)((uint8_t)(g))<<8))|(((uint32_t)(uint8_t)(b))<<16)))
-#define GENSTYLE_RGBS(r,g,b,s) ((uint32_t)((uint8_t)s)<<24 | GENRGB(r,g,b))
-#define GENSTYLE_CS(rgb,s) ((uint32_t)((uint8_t)s)<<24 | rgb)
-
-#define GENGETSTYLE(style) ((style)>>24)
-#define GENGETCOLOR(style) ((style)&0x00ffffff)
-#define GENGET_R(style) ((style)&0xff)
-#define GENGET_G(style) ((style>>8)&0xff)
-#define GENGET_B(style) ((style>>16)&0xff)
-
-#define GENCOLOR_TEXT    GENRGB(0,0,0) ///<recommended color to use for text genes
-#define GENCOLOR_NUMBER  GENRGB(200,0,0) ///<recommended color to use for number genes
-//@}
-
-///Base class for genetic operations on genotypes of some genetic format
-/**\author Maciej Komosinski
-
-When designing genetic operations on some representation, inherit your class
-(for example GenoOper_fMy) from GenoOperators. Define some methods,
-like mutate(), in your class, to allow for evolution.
-Ensure they have the same names and arguments as the corresponding
-virtual methods in Geno_fx. Set the 'supported_format' variable to the
-appropriate genetic representation ID.
-Whenever arguments are genotypes, they are without
-trailing characters which describe genetic format
-(for example, "p:", not "//0\np:").
-When allocating/reallocating char* parameters, use malloc, free, realloc, strdup, etc.
-Do not use new and delete.
-
-All the methods you might define are:
-- checkValidity()
-- validate()
-- mutate()
-- crossOver()
-- getSimplest()
-- style()
-
-Your code must not cause errors (like invalid memory access, memory
-leaks) on any arguments, even 'random' ones. GENOPER_OPFAIL should
-be returned when an operator cannot cope with its argument genotype.
-
-To compile your code, you may also need some SDK files.
-A simple example is Geno_ftest class (see \ref geno_ftest_example "C++ code" for details).
-A more realistic example is Geno_f4 derived from Geno_fx: refer to
-the available source on developmental encoding and f4 genotype format.*/
-
-class GenoOperators
-{
-public:
-	Param par;
-	char supported_format; ///<genotype format which is supported by this class ('6' for GenoOper_f6, 'F' for GenoOper_fF, etc.). Must be initialized in constructor
-	string name; ///<name of this set of genetic operators
-	const char **mutation_method_names; ///<array of names for mutation methods. If initialized (by new const char*[]), must have entries for each method index returned by mutate(geno,chg,METHOD).  If initialized, it is automatically freed by this destructor.
-	GenoOperators() : par(empty_paramtab) { supported_format = 'x'; name = "Default"; mutation_method_names = NULL; setDefaults(); }
-
-	/**Used to perform initializations of Param parameters that are not handled by the Param itself
-	(i.e. string parameters or fields that require some complex logic may be initialized here)*/
-	virtual void setDefaults() {}
-
-	/**Checks a genotype for minor mistakes and major errors.
-	\param geno genotype to be checked
-	\param genoname name of the genotype to be checked
-	\retval error_position 1-based (or 1 if no exact error position known)
-	\retval GENOPER_OK when the genotype is fully valid, and can be translated by the converter with \b no modifications nor tweaks*/
-	virtual int checkValidity(const char *geno, const char *genoname) { return GENOPER_NOOPER; }
-
-	/**Validates a genotype. The purpose of this function is to validate
-	obvious/minor errors (range overruns, invalid links, etc.). Do not try
-	to introduce entirely new genes in place of an error.
-	\param geno input/output: genotype to be validated
-	\param genoname name of the genotype to be validated
-	\retval GENOPER_OK must be returned in any case ("did my best to validate")*/
-	virtual int validate(char *&geno, const char *genoname) { return GENOPER_NOOPER; }
-
-	/**Mutates a genotype. Mutation should always change something.
-
-	Avoid unnecessary calls in your code. Every genotype argument passed to this
-	function is first checked, and validated if checkValidity() reported an error (or
-	if there is no checkValidity() implemented). Every resulting genotype is subject
-	to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
-	to call these functions on input and output genotypes, because they are validated
-	if needed.
-	\param geno input/output: genotype to be mutated
-	\param chg output: initialize with a value (in most cases 0..1) corresponding
-	to the amount of genotype mutated. For example, it could be the number of changed
-	genes divided by the total number of genes before mutation.
-	\param chg method: initialize with the ID (number) of mutation method used.
-	\retval GENOPER_OK
-	\retval GENOPER_OPFAIL
-	\sa
-	Mutation example to illustrate the exchange of pointers for \e geno.
-	The mutation adds random letter at the beginning or removes last letter from \e geno.
-	\code
-	{
-	int len=strlen(geno);
-	if (len==0 || random(2)==0) //add
-	{
-	method=0;
-	char* mutated=(char*)malloc(mutated,len+2); //allocate for mutated genotype
-	mutated[0]='A'+random(10); //first char random
-	strcpy(mutated+1,geno); //the rest is original
-	free(geno); //must take care of the original allocation
-	geno=mutated;
-	} else
-	{
-	method=1;
-	geno[len-1]=0; //simply shorten the string - remove last char
-	}
-	chg=1.0/max(len,1); //estimation of mutation strength, divby0-safe
-	} \endcode
-	*/
-	virtual int mutate(char *&geno, float& chg, int &method) { method = -1; chg = -1; return GENOPER_NOOPER; }
-
-	/**Crosses over two genotypes. It is sufficient to return only one child (in \e g1) and set \e chg1 only, then \e g2 must equal "".
-
-	Avoid unnecessary calls in your code. Every genotype argument passed to this
-	function is first checked, and validated if checkValidity() reported an error (or
-	if there is no checkValidity() implemented). Every resulting genotype is subject
-	to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
-	to call these functions on input and output genotypes, because they are validated
-	if needed.
-	\param g1 input/output: parent1 genotype, initialize with child1
-	\param g2 input/output: parent2 genotype, initialize with child2 if both children are available
-	\param chg1 output: initialize with the fraction of parent1 genes in child1 (parent2 has the rest)
-	\param chg2 output: initialize with the fraction of parent2 genes in child2 (parent1 has the rest)
-	\retval GENOPER_OK
-	\retval GENOPER_OPFAIL
-	\sa mutate() for an example*/
-	virtual int crossOver(char *&g1, char *&g2, float& chg1, float& chg2) { chg1 = chg2 = -1; return GENOPER_NOOPER; }
-
-	/**\return a pointer to the simplest genotype string*/
-	virtual const char* getSimplest() { return NULL; }
-
-	/**You may want to have your genotype colored. This method provides desired character styles for genes.
-	\param geno genotype
-	\param pos 0-based char offset
-	\retval number-encoded visual style (and validity) of the genotype char at \e geno[pos].
-	Assume white background.
-	\sa GENSTYLE_* macros, like GENSTYLE_BOLD*/
-	virtual uint32_t style(const char *geno, int pos) { return GENSTYLE_RGBS(0, 0, 0, GENSTYLE_NONE); }
-
-	///currently not used (similarity of two genotypes)
-	virtual float similarity(const char*, const char*) { return GENOPER_NOOPER; }
-	virtual ~GenoOperators() { if (mutation_method_names) { delete[]mutation_method_names; mutation_method_names = NULL; } }
-	//   virtual char getFormat() {return 255;} //returns supported genotype format, for ex. '1'
-	//   virtual int enabled() {return 1;} // should be enabled by default
-
-	/** \name Some helpful methods for you */
-	//@{
-	static int roulette(const double *probtab, const int count); 	///<returns random index according to probabilities in the \e probtab table or -1 if all probs are zero. \e count is the number of elements in \e probtab.
-	static bool getMinMaxDef(ParamInterface *p, int propindex, double &mn, double &mx, double &def); ///<perhaps a more useful (higher-level) way to obtain min/max/def info for integer and double properties. Returns true if min/max/def was really available (otherwise it is just invented).
-	static int selectRandomProperty(Neuro* n); ///<selects random property (either 0-based extraproperty of Neuro or 100-based property of its NeuroClass). -1 if Neuro has no properties.
-	static double mutateNeuProperty(double current, Neuro *n, int propindex); ///<returns value \e current mutated for the property \e propindex of NeuroClass \e nc or for extraproperty (\e propindex - 100) of Neuro. Neuro is used as read-only. Give \e propindex == -1 to mutate connection weight (\e nc is then ignored).
-	static bool mutatePropertyNaive(ParamInterface &p, int propindex); ///<creep-mutate selected property. Returns true when success. mutateProperty() should be used instead of this function.
-	static bool mutateProperty(ParamInterface &p, int propindex); ///<like mutatePropertyNaive(), but uses special probability distributions for some neuron properties.
-	static bool getMutatedProperty(ParamInterface &p, int i, double oldval, double &newval); ///<like mutateProperty(), but just returns \e newval, does not get nor set it using \e p.
-	static double mutateCreepNoLimit(char type, double current, double stddev, bool limit_precision_3digits); ///<returns \e current value creep-mutated with Gaussian distribution and \e stddev standard deviation. Precision limited to 3 digits after comma when \e limit_precision_3digits is true. \e type must be either 'd' (integer) or 'f' (float/double).
-	static double mutateCreep(char type, double current, double mn, double mx, double stddev, bool limit_precision_3digits); ///<just as mutateCreepNoLimit(), but forces mutated value into the [mn,mx] range using the 'reflect' approach.
-	static double mutateCreep(char type, double current, double mn, double mx, bool limit_precision_3digits); ///<just as mutateCreepNoLimit(), but forces mutated value into the [\e mn,\e mx] range using the 'reflect' approach and assumes standard deviation to be a fraction of the mx-mn interval width.
-	static void setIntFromDoubleWithProbabilisticDithering(ParamInterface &p, int index, double value); ///<sets a double value in an integer field; when a value is non-integer, applies random "dithering" so that both lower and higher integer value have some chance to be set.
-	static void linearMix(vector<double> &p1, vector<double> &p2, double proportion); ///<mixes two real-valued vectors; inherited proportion should be within [0,1]; 1.0 does not change values (all inherited), 0.5 causes both vectors to become their average, 0.0 swaps values (none inherited).
-	static void linearMix(ParamInterface &p1, int i1, ParamInterface &p2, int i2, double proportion); ///<mixes i1'th and i2'th properties of p1 and p2; inherited proportion should be within [0,1]; 1.0 does not change values (all inherited), 0.5 causes both properties to become their average, 0.0 swaps values (none inherited). For integer properties applies random "dithering" when necessary.
-	static NeuroClass* getRandomNeuroClass(); ///<returns random neuroclass or NULL when no active classes.
-	static NeuroClass* getRandomNeuroClassWithOutput(); ///<returns random neuroclass with output or NULL when no active classes.
-	static NeuroClass* getRandomNeuroClassWithInput(); ///<returns random neuroclass with input or NULL when no active classes.
-	static NeuroClass* getRandomNeuroClassWithOutputAndNoInputs(); ///<returns random sensor or NULL when no active classes.
-	static NeuroClass* getRandomSensorProb(float prob); ///<returns random sensor (only-output neuron class) with presented probability or NULL. \e prob probability of picking neuron class.
-	static int getRandomNeuroClassWithOutput(const vector<NeuroClass*>& NClist); ///<returns index of random NeuroClass from the NClist or -1 (no neurons on the list that provide output) \e NClist list of available neuron classes
-	static int getRandomNeuroClassWithInput(const vector<NeuroClass*>& NClist); ///<returns index of random NeuroClass from the NClist or -1 (no neurons on the list that want input(s)) \e NClist list of available neuron classes
-	static int getRandomChar(const char *choices, const char *excluded); ///<returns index of a random character from 'choices' excluding 'excluded', or -1 when everything is excluded or 'choices' is empty.
-	static NeuroClass* parseNeuroClass(char *&s); ///<returns longest matching neuroclass or NULL if the string does not begin with a valid neuroclass name. Advances \e s pointer.
-	static Neuro* findNeuro(const Model *m, const NeuroClass *nc); ///<returns pointer to first Neuro of class \e nc, or NULL if there is no such Neuro.
-	static int neuroClassProp(char *&s, NeuroClass *nc, bool also_v1_N_props = false); ///<returns 0-based property number for \e neuroclass, 100-based extraproperty number for Neuro, or -1 if the string does not begin with a valid property name. Advance \e s pointer if success.
-	static bool isWS(const char c); ///<is \e c a whitespace char?
-	static void skipWS(char *&s); ///<advances pointer \e s skipping whitespaces.
-	static bool areAlike(char*, char*); ///<compares two text strings skipping whitespaces. Returns 1 when equal, 0 when different.
-	static char* strchrn0(const char *str, char ch); ///<like strchr, but does not find zero char in \e str.
-	static bool canStartNeuroClassName(const char firstchar); ///<determines if \e firstchar may start NeuroClass name. If not, it may start NeuroClass' (or Neuro's) property name.
-	//@}
-};
-
-
-//
-// custom distributions for mutations of various parameters
-//
-/*
-static double distrib_weight[]=
-{
-5,                 // distribution -999 _-^_^-_ +999
--999, 999,         // each weight value may be useful, especially...
--5, -0.3,        // ...little non-zero values
--3, -0.6,
-0.6, 3,
-0.3, 5,
-};
-*/
-
-#endif
