Index: /cpp/frams/genetics/f4/f4_conv.cpp
===================================================================
--- /cpp/frams/genetics/f4/f4_conv.cpp	(revision 1230)
+++ /cpp/frams/genetics/f4/f4_conv.cpp	(revision 1231)
@@ -27,8 +27,7 @@
 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)
+	int res = model->buildFromF4(in, using_checkpoints);
+	if (res != GENOPER_OK)
 	{
 		delete model;
@@ -55,8 +54,7 @@
 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)
+	int res = model->buildFromF4(in, using_checkpoints);
+	if (res != GENOPER_OK)
 	{
 		delete model;
@@ -82,13 +80,21 @@
 int f4_Model::buildFromF4(SString &geno, bool using_checkpoints)
 {
-	int i;
-
 	error = GENOPER_OK;
 	errorpos = -1;
 
+	// transform geno from string to nodes
+	f4_Node f4rootnode;
+	int res = f4_process(geno.c_str(), &f4rootnode);
+	if (res || (f4rootnode.childCount() != 1)) //consider any error fatal, preventing building a model
+	{
+		error = GENOPER_OPFAIL;
+		errorpos = res;
+		return error;
+	}
+
 	// build cells, and simulate
 	if (cells) delete cells;
-	cells = new f4_Cells(geno, 0);
-	if (GENOPER_OK != cells->getErrorCode())
+	cells = new f4_Cells(f4rootnode.child, false);
+	if (cells->getErrorCode() != GENOPER_OK)
 	{
 		error = cells->getErrorCode();
@@ -99,5 +105,5 @@
 
 	cells->simulate();
-	if (GENOPER_OK != cells->getErrorCode())
+	if (cells->getErrorCode() != GENOPER_OK)
 	{
 		error = cells->getErrorCode();
@@ -107,5 +113,5 @@
 
 	// reset recursive traverse flags
-	for (i = 0; i < cells->cell_count; i++)
+	for (int i = 0; i < cells->cell_count; i++)
 		cells->C[i]->recProcessedFlag = 0;
 
@@ -113,11 +119,10 @@
 
 	// process every cell
-	int res;
-	for (i = 0; i < cells->cell_count; i++)
-	{
-		res = buildModelRec(cells->C[i]);
+	for (int i = 0; i < cells->cell_count; i++)
+	{
+		int res = buildModelRec(cells->C[i]);
 		if (res)
 		{
-			logMessage("f4_Model", "buildFromF4", LOG_ERROR, "Error in building a Model");
+			logPrintf("f4_Model", "buildFromF4", LOG_ERROR, "Error %d when building a Model", res);
 			error = res;
 			break;
@@ -125,7 +130,10 @@
 	}
 
-	res = close();
-	if (0 == res) // invalid
+	int res_close = close();
+	if (res_close == 0) // invalid
+	{
+		logPrintf("f4_Model", "buildFromF4", LOG_ERROR, "Error %d when closing a Model", res_close);
 		error = -10;
+	}
 
 	return error;
Index: /cpp/frams/genetics/f4/f4_general.cpp
===================================================================
--- /cpp/frams/genetics/f4/f4_general.cpp	(revision 1230)
+++ /cpp/frams/genetics/f4/f4_general.cpp	(revision 1231)
@@ -640,7 +640,6 @@
 
 
-f4_Cells::f4_Cells(f4_Node *genome, int nrepair)
-{
-	// create ancestor cell
+f4_Cells::f4_Cells(f4_Node *genome, bool nrepair)
+{
 	repair = nrepair;
 	errorcode = GENOPER_OK;
@@ -650,5 +649,6 @@
 	repair_insert = NULL;
 	tmpcel = NULL;
-	f4rootnode = NULL;
+
+	// create ancestor cell
 	C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
 	cell_count = 1;
@@ -656,42 +656,14 @@
 
 
-f4_Cells::f4_Cells(SString & genome, int nrepair)
-{
-	repair = nrepair;
-	errorcode = GENOPER_OK;
-	errorpos = -1;
-	repair_remove = NULL;
-	repair_parent = NULL;
-	repair_insert = NULL;
-	tmpcel = NULL;
-	f4rootnode = NULL;
-
-	// transform geno from string to nodes
-	f4rootnode = new f4_Node();
-	int _ = 0;
-	int res = f4_processRecur(genome.c_str(), _, f4rootnode);
-	if (res || (f4rootnode->childCount() != 1))
-	{
-		errorcode = GENOPER_OPFAIL;
-		errorpos = -1;
-	}
-
-	// create ancestor cell
-	C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child, NULL, 0, GeneProps::standard_values);
-	cell_count = 1;
-}
 
 f4_Cells::~f4_Cells()
 {
 	// release cells
-	int i;
 	if (cell_count)
 	{
-		for (i = cell_count - 1; i >= 0; i--)
+		for (int i = cell_count - 1; i >= 0; i--)
 			delete C[i];
 		cell_count = 0;
 	}
-	if (f4rootnode)
-		delete f4rootnode;
 }
 
@@ -1130,14 +1102,5 @@
 int f4_Node::childCount()
 {
-	if (child != NULL)
-	{
-		if (child2 != NULL) return 2;
-		else return 1;
-	}
-	else
-	{
-		if (child2 != NULL) return 1;
-		else return 0;
-	}
+	return int(child != NULL) + int(child2 != NULL); //0, 1 or 2
 }
 
@@ -1256,5 +1219,5 @@
 	len = out.length();
 	if (len > 1)
-		if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); }; //Macko 2023-04 TODO "can be omitted", but should we remove it as a rule even in generated genotypes? see if I can somehow detect junk characters after top-level '>' ends properly: /*4*/<X>N:N>whatever
+		if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); }; //Macko 2023-04 "can be omitted", but it is removed as a rule even in generated genotypes :)
 	// copy back to string
 	// if new is longer, reallocate buf
@@ -1305,5 +1268,5 @@
 	while (pos_inout < (int)strlen(genot))
 	{
-		//#define PRINT_PARSING_LOCATION
+//#define PRINT_PARSING_LOCATION
 #ifdef PRINT_PARSING_LOCATION
 		printf("%s\n", genot);
@@ -1342,11 +1305,11 @@
 		case '#':
 		{
-			// repetition marker, 1 by default
-			ExtValue val;
-			const char* end = val.parseNumber(genot + pos_inout + 1, ExtPType::TInt);
-			//TODO end==NULL? -> error!
-			int reps = (end == NULL) ? 1 : val.getInt();
+			// repetition marker
+			ExtValue reps;
+			const char* end = reps.parseNumber(genot + pos_inout + 1, ExtPType::TInt);
+			if (end == NULL)
+				return pos_inout + 1; //error
 			f4_Node *node = new f4_Node("#", par, pos_inout);
-			node->reps = reps;
+			node->reps = reps.getInt();
 			// skip number
 			pos_inout += end - (genot + pos_inout);
@@ -1455,4 +1418,15 @@
 }
 
+int f4_process(const char *genot, f4_Node *root)
+{
+	int pos = 0;
+	int res = f4_processRecur(genot, pos, root);
+	if (res > 0)
+		return res; //error
+	else if (genot[pos] == 0) //parsed until the end - OK!
+		return 0;
+	else return pos + 1; //junk, unparsed genes after successful parsing, for example /*4*/<X>N:N>whatever or /*4*/<X>X>>>
+}
+
 const char* parseConnection(const char *fragm, int& relfrom, double &weight)
 {
Index: /cpp/frams/genetics/f4/f4_general.h
===================================================================
--- /cpp/frams/genetics/f4/f4_general.h	(revision 1230)
+++ /cpp/frams/genetics/f4/f4_general.h	(revision 1231)
@@ -242,14 +242,7 @@
 	 * Constructor taking genotype in a form of a tree.
 	 * @param genome genotype tree
-	 * @param nrepair 0 if nothing to repair
-	 */
-	f4_Cells(f4_Node *genome, int nrepair);
-
-	/**
-	 * Constructor taking genotype in a form of a string.
-	 * @param genome genotype string
-	 * @param nrepair 0 if nothing to repair
-	 */
-	f4_Cells(SString &genome, int nrepair);
+	 * @param nrepair false if nothing to repair
+	 */
+	f4_Cells(f4_Node *genome, bool nrepair);
 
 	/**
@@ -336,5 +329,5 @@
 private:
 	// for error reporting / genotype fixing
-	int repair;
+	bool repair;
 	int errorcode;
 	int errorpos;
@@ -343,6 +336,5 @@
 	f4_Node *repair_insert;
 	void toF1GenoRec(int curc, SString &out);
-	f4_Cell *tmpcel;		// needed by toF1Geno
-	f4_Node *f4rootnode;          // used by constructor
+	f4_Cell *tmpcel;  // needed by toF1Geno
 };
 
@@ -477,10 +469,20 @@
  * semantic aspect is neuron class name extraction, where the GenoOperators
  * class is used to parse the potential neuron class name.
- * @param genot the string holding all the genotype
- * @param pos0 the current position of processing in string
+ * This is an internal function; for regular cases, use f4_process().
+ * @param genot the string with the entire genotype
+ * @param pos_inout the current position of processing in string (advanced by the function)
  * @param parent current parent of the analysed branch of the genotype
  * @return 0 if processing was successful, otherwise returns the position of an error in the genotype
  */
 int f4_processRecur(const char *genot, int &pos_inout, f4_Node *parent);
+
+/**
+ * A wrapper for f4_processRecur(). Creates a tree of f4_Node objects corresponding to
+ * the provided genotype.
+ * @param genot the string with the entire genotype
+ * @param root root of the tree corresponding to the genotype
+ * @return 0 if processing was successful, otherwise returns the position of an error in the genotype
+ */
+int f4_process(const char *genot, f4_Node *root);
 
 /**
Index: /cpp/frams/genetics/f4/f4_oper.cpp
===================================================================
--- /cpp/frams/genetics/f4/f4_oper.cpp	(revision 1230)
+++ /cpp/frams/genetics/f4/f4_oper.cpp	(revision 1231)
@@ -11,7 +11,6 @@
 // may help, but it would be better to improve the source code to make genetic operators neutral in terms of genotype length. Adding such a penalty
 // removes "work in progress" changes in genotypes thus promoting immediate, straightforward improvements while hindering slower, multifaceted progress.
-// TODO getting rid of redundancy (having valid genotypes with a lot of "junk code") in this representation looks like a good idea.
+// TODO getting rid of redundancy (valid genotypes with a lot of "junk code") in this representation looks like a good idea; many improvements to this end have already been done in April & May 2023.
 // 
-// Note: symbols after the last > are ignored, for example /*4*/<X>N:N>blablaN:N[2:-0.5]XXXwhatever but since they are not parsed into the f4_Node tree, they will be lost after any mutation.
 //
 // TODO the behavior of neuron input indexes during mutation seems badly implemented (see also TREAT_BAD_CONNECTIONS_AS_INVALID_GENO). Are they kept properly maintained when nodes are added and removed? This could be done well because during mutation we operate on the tree structure with cross-references between nodes (so they should not be affected by local changes in the tree), and then convert the tree back to string. Yet, the f4_Node.conn_from is an integer and these fields in nodes do not seem to be maintained on tree node adding/removal... change these integer offsets to references to node objects? But actually, do the offsets that constitute relative connection references concern the f4_Node tree structure (and all these sophisticated calculations of offsets during mutation are useful) or rather they concern the f4_Cells development? verify all situations in f4_Cell::oneStep(), case '['.
@@ -87,10 +86,10 @@
 }
 
-int Geno_f4::ValidateRec(f4_Node *geno, int retrycount) const
+int Geno_f4::ValidateRecur(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);
+	f4_Cells cells(geno->child, true);
 	cells.simulate();  //we should simulate?!
 
@@ -109,5 +108,5 @@
 		int res2 = GENOPER_OK;
 		if (retrycount > 0)
-			res2 = ValidateRec(geno, retrycount - 1);
+			res2 = ValidateRecur(geno, retrycount - 1);
 
 		if (res2 == GENOPER_OK) return GENOPER_REPAIR;
@@ -123,12 +122,14 @@
 	// convert geno to a tree, then try to validate
 	f4_Node root;
-	int _ = 0;
-	if (f4_processRecur(geno, _, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair
-
+	int res = f4_process(geno, &root);
+	if (res == 0 || root.childCount() != 1) return GENOPER_OK; // either parsing says the genotype is OK or the resulting tree will not be repairable (fatal flaw; root must have exactly one child) - do not attempt repair
+
+	// here we have a genotype with res>0 (for sure has some error) and root.childCount()==1 (still something was parsed into a tree)
 	const int VALIDATE_TRIALS = 20;
-	if (ValidateRec(&root, VALIDATE_TRIALS) == GENOPER_REPAIR) // if repaired, make it back to string
+	res = ValidateRecur(&root, VALIDATE_TRIALS);
+	if (res != GENOPER_OPFAIL) // if repaired (GENOPER_REPAIR) or had no errors (GENOPER_OK, e.g. the genotype had some errors that were ignored during tree creation or had junk genes appended at the end, so the tree was OK but the genotype was not), 
 	{
 		geno[0] = 0;
-		root.child->sprintAdj(geno);
+		root.child->sprintAdj(geno); //make it back to string
 	}
 	return GENOPER_OK;
@@ -139,14 +140,13 @@
 {
 	f4_Node root;
-	int _ = 0;
-	int res = f4_processRecur(geno, _, &root);
+	int res = f4_process(geno, &root);
 	if (res) return res;  // errorpos, >0
-	if (root.childCount() != 1) return 1; //earlier: GENOPER_OPFAIL
-	f4_Cells cells(root.child, 0);
+	if (root.childCount() != 1) return 1; // fatal flaw; root must have exactly one child
+	f4_Cells cells(root.child, false);
 	cells.simulate();
 	if (cells.getErrorCode() == GENOPER_OPFAIL || cells.getErrorCode() == GENOPER_REPAIR)
 	{
 		if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
-		else return 1; //earlier: GENOPER_OPFAIL;
+		else return 1; //error, no known position
 	}
 	else return GENOPER_OK;
@@ -525,5 +525,5 @@
 		}
 		// try to validate it
-		res = ValidateRec(gcopy, 10);
+		res = ValidateRecur(gcopy, 10);
 		// accept if it is OK, or was repaired
 		if (GENOPER_OK == res)
@@ -552,6 +552,5 @@
 {
 	f4_Node *root = new f4_Node;
-	int _ = 0;
-	if (f4_processRecur(g, _, root) || root->childCount() != 1)
+	if (f4_process(g, root) || root->childCount() != 1)
 	{
 		delete root;
@@ -677,7 +676,6 @@
 
 	// convert genotype strings into tree structures
-	int _1 = 0, _2 = 0;
-	if (f4_processRecur(g1, _1, &root1) || (root1.childCount() != 1)) return GENOPER_OPFAIL;
-	if (f4_processRecur(g2, _2, &root2) || (root2.childCount() != 1)) return GENOPER_OPFAIL;
+	if (f4_process(g1, &root1) || (root1.childCount() != 1)) return GENOPER_OPFAIL;
+	if (f4_process(g2, &root2) || (root2.childCount() != 1)) return GENOPER_OPFAIL;
 
 	// decide amounts of crossover, 0.1-0.9
Index: /cpp/frams/genetics/f4/f4_oper.h
===================================================================
--- /cpp/frams/genetics/f4/f4_oper.h	(revision 1230)
+++ /cpp/frams/genetics/f4/f4_oper.h	(revision 1231)
@@ -72,5 +72,5 @@
 	 * @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;
+	int  ValidateRecur(f4_Node *geno, int retrycount) const;
 
 	/**
