Index: /cpp/f4/conv_f4.cpp
===================================================================
--- /cpp/f4/conv_f4.cpp	(revision 4)
+++ /cpp/f4/conv_f4.cpp	(revision 4)
@@ -0,0 +1,304 @@
+/**
+ *  conv_f4.cpp - f4 genotype functions.
+ *
+ *  f4genotype - f4 format genotype conversions for Framsticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *  Copyright (C) 2001-2003  Maciej Komosinski
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "conv_f4.h"
+#include "framsg.h"
+#include "geno_fx.h" //for GENOPER_OK constant
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+GenoConv_F40::GenoConv_F40()
+{
+  name = "f4 converter";
+  in_format  = '4';
+  out_format = '0';
+  mapsupport = 1;
+  info = "Developmental encoding";
+}
+
+
+SString GenoConv_F40::convert(SString &in, MultiMap * map)
+{
+  int res;
+  f4_Model * model = new f4_Model();
+  res = model->buildFromF4(in);
+  if (GENOPER_OK != res) return SString();  // oops
+  if (NULL != map)
+    // generate to-f0 conversion map
+    model->getCurrentToF0Map(*map);
+  SString out = model->getF0Geno().getGene();
+  delete model;
+  return out;
+}
+
+
+GenoConv_F41_TestOnly::GenoConv_F41_TestOnly()
+{
+  name = "test-only approximate f4 -> f0 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)
+{
+  int res;
+  f4_Model * model = new f4_Model();
+  res = model->buildFromF4(in);
+  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)
+{
+  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(); // begin model build
+
+  // process every cell
+  int res;
+  for(i=0; i<cells->nc; i++)
+  {
+    res = buildModelRec(cells->C[i]);
+    if (res)
+    {
+      FramMessage("f4_Model", "buildModelRec", "Error in building Model", 2);
+      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!
+  FramMessage("f4_Model", "getStick", "Not a single stick", 2);
+  return NULL;
+}
+
+
+/// updated by Macko to follow new GDK 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, "p:m=1,fr=%g,ing=%g,as=%g",
+        /*1.0/C->P.mass,*/ C->P.friction, C->P.ingest, C->P.assim
+        //C->firstend.x, C->firstend.y, C->firstend.z
+        );
+      partidx = singleStepBuild(tmpLine, &range );
+      if (partidx < 0) return -1;
+      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, "p:m=1,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.ingest, C->P.assim
+      );
+    partidx = singleStepBuild(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, "j: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.len,
+      // relative rotation
+      C->xrot, C->zrot,
+      //C->P.ruch,   // rotstif
+      C->P.odpor
+    );
+    partidx = singleStepBuild(tmpLine, &range );
+    if (partidx < 0) return -13;
+    C->joint_refno = partidx;
+  }
+
+  if (C->type == T_NEURON4) ///<this case was updated by MacKo
+  {
+    // new neuron object
+    // it is linked to a part (not a joint!)
+    int p_refno = C->dadlink->p2_refno;
+    if ((p_refno < 0) || (p_refno >= getPartCount())) return -21;
+    // joint_refno is currently not used
+    sprintf(tmpLine, "n:p=%ld,d=\"N:in=%g,fo=%g,si=%g\"",
+      p_refno,
+      C->inertia, C->force, C->sigmo);
+    partidx = singleStepBuild(tmpLine, &range );
+    if (partidx < 0) return -22;
+    C->neuro_refno = partidx;
+    int n_refno = C->neuro_refno;
+
+    if (C->ctrl)
+    {
+      if (1 == C->ctrl)
+        sprintf(tmpLine, "n:j=%d,d=\"@:p=%g\"",   C->dadlink->joint_refno, C->P.ruch);
+      else
+        sprintf(tmpLine, "n:j=%d,d=\"|:p=%g,r=%g\"",C->dadlink->joint_refno, C->P.ruch, C->mz);
+      partidx = singleStepBuild(tmpLine, &range );
+      if (partidx < 0) return -32;
+      sprintf(tmpLine, "c:%d,%d",partidx,n_refno);
+      if (singleStepBuild(tmpLine, &range )<0) return -33;
+    }
+
+    for (j=0; j<C->nolink; j++)
+    {
+      if (NULL != C->links[j]->from)
+        buildModelRec(C->links[j]->from);
+
+      tmpLine[0]=0;
+      if (1 == C->links[j]->t) sprintf(tmpLine, "n:p=%d,d=\"*\"",p_refno);
+      if (2 == C->links[j]->t) sprintf(tmpLine, "n:j=%d,d=\"G\"", C->dadlink->joint_refno);
+      if (3 == C->links[j]->t) sprintf(tmpLine, "n:p=%d,d=\"T\"",p_refno);
+      if (4 == C->links[j]->t) sprintf(tmpLine, "n:p=%d,d=\"S\"",p_refno);
+      int from=-1;
+      if (tmpLine[0]) //input from receptor
+      {
+        from=singleStepBuild(tmpLine, &range );
+        if (from<0) return -34;
+      } /*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, "c:%d,%d,%g", n_refno,from,C->links[j]->w);
+        if (singleStepBuild(tmpLine, &range ) < 0) return -35;
+      }
+    }
+  }
+  return 0;
+}
+
+
+void f4_Model::toF1Geno(SString &out)
+{
+  cells->toF1Geno(out);
+}
+
+
Index: /cpp/f4/conv_f4.h
===================================================================
--- /cpp/f4/conv_f4.h	(revision 4)
+++ /cpp/f4/conv_f4.h	(revision 4)
@@ -0,0 +1,73 @@
+/**
+ *  conv_f4.h - f4 conversion functions.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *  Copyright (C) 2001-2003  Maciej Komosinski
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _CONV_F4_H_
+#define _CONV_F4_H_
+
+#include "model.h"
+#include "modelparts.h"
+#include "genoconv.h"
+#include "f4_general.h"
+
+
+// The f4->f0 converter
+class GenoConv_F40: public GenoConverter
+{
+ public:
+  GenoConv_F40();
+  SString convert(SString &in, MultiMap * map);
+};
+
+
+// a test-only f4->f1 converter, approximates only
+class GenoConv_F41_TestOnly: public GenoConverter
+{
+ public:
+  GenoConv_F41_TestOnly();
+  SString convert(SString &in, MultiMap * map);
+};
+
+
+// A Model descendant, which support build from an f4 genotype.
+class f4_Model: public Model
+{
+ public:
+  f4_Model();
+  ~f4_Model();
+  int      buildFromF4(SString &geno);
+  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/f4/f4_general.cpp
===================================================================
--- /cpp/f4/f4_general.cpp	(revision 4)
+++ /cpp/f4/f4_general.cpp	(revision 4)
@@ -0,0 +1,1408 @@
+/*
+ *  f4_general.cpp - f4 genotype functions.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "f4_general.h"
+#include "nonstd.h"
+#include "framsg.h"
+#include "model.h" // for min and max attributes
+#include "geno_fx.h" //for GENOPER_ constants
+#include <stdio.h>
+#include <math.h>
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+f4_Props::f4_Props()
+{
+  len      = 1.0;
+  curv     = 0.0;
+  mass     = 1.0; 
+  friction = 0.4;
+  ruch     = 0.25; // bio
+  assim    = 0.25; // bio
+  odpor    = 0.25; // bio
+  ingest   = 0.25; // bio
+  twist    = 0.0;
+  energ    = 1.0;
+  normalizeBiol4();
+}
+
+void f4_Props::normalizeBiol4()
+{
+  // must sum to 1
+  double sum = ruch + assim + odpor + ingest;
+  if (0 == sum)
+  {
+    ruch = assim = odpor = ingest = 0.25;
+  } else {
+    ruch   /= sum;
+    assim  /= sum;
+    odpor  /= sum;
+    ingest /= sum;
+  }
+}
+
+void f4_Props::executeModifier(char modif)
+{
+  switch (modif)
+  {
+   case 'L': len += (2.5 - len) * 0.3;
+             len = min(len, Model::getMaxJoint().d.x); break;
+   case 'l': len += (0.3 - len) * 0.3;
+             len = max(len, Model::getMinJoint().d.x); break;
+   case 'C': curv += ( 2.0 - curv) * 0.25;  break;
+   case 'c': curv += (-2.0 - curv) * 0.25;  break;
+   case 'Q': twist += ( 1.58 - twist) * 0.3; break;
+   case 'q': twist += (-1.58 - twist) * 0.3; break;
+   case 'A': assim += (1 - assim) * 0.8; normalizeBiol4(); break;
+   case 'a': assim -= assim * 0.4;       normalizeBiol4(); break;
+   case 'I': ingest += (1 - ingest) * 0.8;   normalizeBiol4(); break;
+   case 'i': ingest -= ingest * 0.4;         normalizeBiol4(); break;
+   case 'S': odpor += (1 - odpor) * 0.8; normalizeBiol4(); break;
+   case 's': odpor -= odpor * 0.4;       normalizeBiol4(); break;
+   case 'M': ruch += (1 - ruch) * 0.8;   normalizeBiol4(); break;
+   case 'm': ruch -= ruch * 0.4;         normalizeBiol4(); break;
+   case 'F': friction += (4 - friction) * 0.2; break;
+   case 'f': friction -= friction * 0.2;       break;
+   case 'W': mass += (2.0 - mass) * 0.3;   break;
+   case 'w': mass += (0.5 - mass) * 0.3;   break;
+   case 'E': energ += (10.0 - energ) * 0.1; break;
+   case 'e': energ -= energ * 0.1;          break;
+  }
+}
+
+void f4_Props::adjust()
+{
+  len = 0.5*len + 0.5*stdProps.len;
+  curv = 0.66 * curv;
+  twist = 0.66 * twist;
+}
+
+f4_Props stdProps;
+
+
+void rolling_dec(double * v) {
+  *v -= 0.7853;  // 0.7853981  45 degrees
+}
+void rolling_inc(double * v) {
+  *v += 0.7853;  // 0.7853981  45 degrees
+}
+
+
+int scanrec(const char * s, unsigned int slen, char stopchar)
+{
+  unsigned int i = 0;
+  //DB( printf("    scan('%s', '%c')\n", s, stopchar); )
+  while (1)
+  {
+    if (i >= slen)  // ran out the string, should never happen with correct string
+      return 1;
+    if (stopchar == s[i])  // bumped into stopchar
+      return i;
+    if (i < slen-1)  
+    { // s[i] is not the last char
+      if (s[i] == '(') 
+      {
+        i += 2+scanrec(s+i+1, slen-i-1, ')');
+        continue;
+      }
+      if (s[i] == '<') 
+      {
+        i += 2+scanrec(s+i+1, slen-i-1, '>');
+        continue;
+      }
+      if (s[i] == '#') 
+      {
+        i += 2+scanrec(s+i+1, slen-i-1, '>');
+        continue;
+      }
+    }
+    // s[i] a non-special character
+    i++;
+  }
+  return i;
+}
+
+
+f4_Cell::f4_Cell(int nname,
+    f4_Cell * ndad, int nangle, f4_Props newP)
+{
+  name    = nname;
+  type    = T_UNDIFF4;
+  dadlink = ndad;
+  org     = NULL;
+  genot   = NULL;
+  gcur    = NULL;
+  active  = 1;
+  repeat.null();
+  //genoRange.clear(); -- implicit
+
+  anglepos   = nangle;
+  commacount = 0;
+  childcount = 0;
+  P          = newP;
+  rolling    = 0;
+  xrot       = 0;
+  zrot       = 0;
+  //OM = Orient_1;
+  ctrl    = 0;
+  state   = 0;
+  inertia = 0.8;
+  force   = 0.04;
+  sigmo   = 2;
+  nolink = 0;
+
+  // adjust firstend and OM if there is a stick dad
+  if (ndad != NULL)
+  {
+    // make sure it is a stick (and not a stick f4_Cell!)
+    if (T_STICK4 == ndad->type)
+    {
+      //firstend = ndad->lastend;
+      //OM = ndad->OM;
+      ndad->childcount++;
+    }
+    if (T_NEURON4 == ndad->type)
+    {
+      state   = ndad->state;
+      inertia = ndad->inertia;
+      force   = ndad->force;
+      sigmo   = ndad->sigmo;
+    }
+  }
+  // adjust lastend
+  //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
+  mz = 1;
+}
+
+
+f4_Cell::f4_Cell(f4_Cells * nO, int nname, f4_node * ngeno, f4_node * ngcur,
+    f4_Cell * ndad, int nangle, f4_Props newP)
+{
+  name    = nname;
+  type    = T_UNDIFF4;
+  dadlink = ndad;
+  org     = nO;
+  genot   = ngeno;
+  gcur    = ngcur;
+  active = 1;
+  repeat.null();
+  //genoRange.clear(); -- implicit
+  // preserve geno range of parent cell
+  if (NULL != ndad)
+    genoRange.add( ndad->genoRange );
+
+  anglepos   = nangle;
+  commacount = 0;
+  childcount = 0;
+  P          = newP;
+  rolling    = 0;
+  xrot       = 0;
+  zrot       = 0;
+  //OM = Orient_1;
+  ctrl    = 0;
+  state   = 0;
+  inertia = 0.8;
+  force   = 0.04;
+  sigmo   = 2;
+  nolink = 0;
+
+  // adjust firstend and OM if there is a stick dad
+  if (ndad != NULL)
+  {
+    // make sure it is a stick (and not a stick f4_Cell!)
+    if (T_STICK4 == ndad->type) {
+      //firstend = ndad->lastend;
+      //OM = ndad->OM;
+      ndad->childcount++;
+    }
+    if (T_NEURON4 == ndad->type) 
+    {
+      state   = ndad->state;
+      inertia = ndad->inertia;
+      force   = ndad->force;
+      sigmo   = ndad->sigmo;
+    }
+  }
+  // adjust lastend
+  //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
+  mz = 1;
+}
+
+
+f4_Cell::~f4_Cell()
+{
+  // remove links
+  if (nolink)
+  {
+    int i;
+    for (i=nolink-1; i>=0; i--)
+      delete links[i];
+    nolink=0;
+  }
+}
+
+
+/* return codes:
+    >1 error at pos
+     0  halt development for a cycle
+    -1  development finished OK
+*/
+int f4_Cell::onestep()
+{
+  int i, j, k, relfrom, t;
+  double w;
+  f4_Cell * tmp;
+  f4_Cell * tneu;
+  if (gcur == NULL)
+  {
+    active = 0;
+    return 0;  // stop
+  }
+  while ( NULL != gcur )
+  {
+    //DB( printf("  %d (%d) executing '%c' %d\n", name, type, gcur->name, gcur->pos); )
+    // currently this is the last one processed
+    // the current genotype code is processed
+    genoRange.add( gcur->pos );
+    switch (gcur->name)
+    {
+    case '<':
+      // cell division!
+      //DB( printf("  div! %d\n", name); )
+
+      // error: sticks cannot divide
+      if (T_STICK4 == type) {
+        // cannot fix
+        org->setError(gcur->pos);
+        return 1;  // stop
+      }
+
+      // undiff divides
+      if (T_UNDIFF4 == type) {
+        // commacount is set only when daughter turns into X
+        // daughter cell
+        // adjust new len
+        f4_Props newP = P;
+        newP.adjust();
+        tmp = new f4_Cell(org, org->nc, genot, gcur->child2,
+         this,  commacount, newP);
+        tmp->repeat = repeat;
+        repeat.null();
+        org->addCell( tmp );
+      }
+      // a neuron divides: create a new, duplicate links
+      if (T_NEURON4 == type) {
+        // daughter cell
+        tmp = new f4_Cell(org, org->nc, genot, gcur->child2,
+          // has the same dadlink
+          this->dadlink, commacount, P);
+        tmp->repeat = repeat;
+        repeat.null();
+        // it is a neuron from start
+        tmp->type = T_NEURON4;
+        // duplicate links
+        f4_CellLink * ll;
+        for (i=0; i<nolink; i++) {
+          ll = links[i];
+          tmp->addlink(ll->from, ll->w, ll->t);
+        }
+        org->addCell( tmp );
+      }
+      // adjustments for this cell
+      gcur = gcur->child;
+      // halt development
+      return 0;
+
+    case '>':
+      // finish
+      // see if there is a repet count
+      if (repeat.top > 0)
+      { // there is a repeat counter
+        if (!repeat.first()->isNull())
+        { // repeat counter is not null
+          repeat.first()->dec();
+          if (repeat.first()->count > 0)
+          {
+            // return to repeat
+            gcur = repeat.first()->node->child;
+          } else {
+            // continue
+            gcur = repeat.first()->node->child2;
+            repeat.pop();
+          }
+          break;
+        } else {
+          repeat.pop();
+        }
+      } else {
+        // error: still undiff
+        if (T_UNDIFF4 == type)
+        {
+          // fix it: insert an 'X'
+          f4_node * insertnode = new f4_node('X', NULL, gcur->pos);
+          if (org->setRepairInsert(gcur->pos, gcur, insertnode))
+            // not in repair mode, release
+            delete insertnode;
+          return 1;
+        }
+        repeat.null();
+        active = 0;  // stop
+        // eat up rest
+        gcur = NULL;
+        return 0;
+      }
+
+    case '#':
+      // repetition marker
+      if (repeat.top >= repeat_stack::stackSize)
+      {
+        // repepeat pointer stack is full, cannot remember this one.
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      repeat.push( repeat_ptr(gcur, gcur->i1) );
+      gcur = gcur->child;
+      break;
+
+    case ',':
+      commacount++;
+      gcur = gcur->child;
+      break;
+
+    case 'r':  case 'R':
+      // error: if neuron
+      if (T_NEURON4 == type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      switch (gcur->name) {
+       case 'r':   rolling_dec( &rolling ); break;
+       case 'R':   rolling_inc( &rolling ); break;
+      }
+      gcur = gcur->child;
+      break;
+
+    case 'l':  case 'L':
+    case 'c':  case 'C':
+    case 'q':  case 'Q':
+    case 'a':  case 'A':
+    case 'i':  case 'I':
+    case 's':  case 'S':
+    case 'm':  case 'M':
+    case 'f':  case 'F':
+    case 'w':  case 'W':
+    case 'e':  case 'E':
+      // error: if neuron
+      if (T_NEURON4 == type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      P.executeModifier(gcur->name);
+      gcur = gcur->child;
+      break;
+
+    case 'X':
+      // turn undiff. cell into a stick
+      // error: already differentiated
+      if (T_UNDIFF4 != type) {
+        // fix: delete this node
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      type = T_STICK4;
+      // fix dad commacount and own anglepos
+      if (NULL != dadlink) {
+        dadlink->commacount++;
+        anglepos = dadlink->commacount;
+      }
+      // change of type halts developments, see comment at 'N'
+      gcur = gcur->child;
+      return 0;
+
+    case 'N':
+      // turn undiff. cell into a neuron
+      // error: already differentiated
+      if (T_UNDIFF4 != type) {
+        // fix: delete this node
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      // error: if no previous
+      if (NULL == dadlink) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      type = T_NEURON4;
+      // change of type also halts development, to give other
+      // cells a chance for adjustment.  Namely, it is important
+      // to wait for other cells to turn N before adding links
+      gcur = gcur->child;
+      return 0;
+
+    case '@':
+    case '|':
+      // neuron rotating / bending
+      j = 1;
+      if ('@' == gcur->name) j = 1; // rot
+      if ('|' == gcur->name) j = 2; // bend
+      // error: not a neuron (undiff)
+      if (T_UNDIFF4 == type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      // error: not a neuron (stick)
+      if (T_NEURON4 != type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      // error: already has control
+      if (ctrl != 0) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      // make neuron ctrl = 1 or 2
+      ctrl = j;
+      gcur = gcur->child;
+      break;
+
+    case '[':
+      // link to neuron
+      // error: not a neuron
+      if (T_NEURON4 != type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      // input ('*', 'G', 'T', 'S', or %d)
+      t       = gcur->i1;
+      relfrom = gcur->l1;
+      w       = gcur->f1;
+      if (t>0) {
+        // * or G
+        tneu = NULL;
+      } else {
+        // input from other neuron
+        // find neuron at relative i
+        // find own index
+        j = 0; k = 0;
+        for(i=0; i<org->nc; i++) {
+          if (org->C[i]->type == T_NEURON4) k++;
+          if (org->C[i] == this) {j=k-1; break;}
+        }
+        // find index of incoming
+        j = j + relfrom;
+        if (j<0) goto wait_link;
+        if (j>=org->nc) goto wait_link;
+        // find that neuron
+        k = 0;
+        for(i=0; i<org->nc; i++) {
+          if (org->C[i]->type == T_NEURON4) k++;
+          if (j == (k-1)) break;
+        }
+        if (i>=org->nc) goto wait_link;
+        tneu = org->C[i];
+      }
+      // add link
+      // error: could not add link (too many?)
+      if (addlink(tneu, w, t)) {
+        // cannot fix
+        org->setError(gcur->pos);
+        return 1;  // stop
+      }
+      gcur = gcur->child;
+      break;
+    wait_link:
+      // wait for other neurons to develop
+      // if there are others still active
+      active = 0;
+      j = 0;
+      for(i=0; i<org->nc; i++) {
+        if (org->C[i]->active) j++;
+      }
+      if (j>0)
+        return 0;  // there is other active, halt, try again
+      // no more actives, cannot add link, ignore, but treat not as an error
+      gcur = gcur->child;
+      break;
+
+    case ':':
+      // neuron parameter
+      // error: not a neuron
+      if (T_NEURON4 != type) {
+        // fix: delete it
+        org->setRepairRemove(gcur->pos, gcur);
+        return 1;  // stop
+      }
+      j = (int)gcur->l1;
+      switch ((char)gcur->i1) {
+       case '!': 
+        if (j) force += (1.0 - force) * 0.2;
+        else   force -= force * 0.2; break;
+       case '=': 
+        if (j) inertia += (1.0 - inertia) * 0.2;
+        else   inertia -= inertia * 0.2; break;
+       case '/': 
+        if (j) sigmo *= 1.4;
+        else   sigmo /= 1.4; break;
+       default:
+         org->setRepairRemove(gcur->pos, gcur);
+         return 1;  // stop
+      }
+      gcur = gcur->child;
+      break;
+
+    case ' ':
+      // space has no effect, should not occur
+      // fix: delete it
+      org->setRepairRemove(gcur->pos, gcur);
+      gcur = gcur->child;
+      break;
+
+    default:
+      // error: unknown code
+      char buf[40];
+      sprintf(buf, "unknown code '%c'", gcur->name);
+      FramMessage("f4_Cell", "onestep", buf, 2);
+      // fix: delete it
+      org->setRepairRemove(gcur->pos, gcur);
+      return 1; // stop
+    }
+  }
+  active = 0;  // done
+  return 0;
+}
+
+
+int f4_Cell::addlink(f4_Cell * nfrom, double nw, long nt)
+{
+  if (nolink >= MAXINPUTS-1) return -1; // full!
+  links[nolink] = new f4_CellLink(nfrom, nw, nt);
+  nolink++;
+  return 0;
+}
+
+
+void f4_Cell::adjustRec()
+{
+  //f4_OrientMat rot;
+  int     i;
+
+  if (recProcessedFlag)
+    // already processed
+    return;
+
+  // mark it processed
+  recProcessedFlag = 1;
+
+  // make sure its parent is processed first
+  if (NULL != dadlink)
+    dadlink->adjustRec();
+
+  // count children
+  childcount = 0;
+  for(i=0; i<org->nc; i++)
+  {
+    if (org->C[i]->dadlink == this)
+      if (org->C[i]->type == T_STICK4)
+        childcount++;
+  }
+
+  if (type == T_STICK4)
+  {
+    if (NULL == dadlink)
+    {
+      //firstend = Pt3D_0;
+      // rotation due to rolling
+      xrot = rolling;
+      mz = 1;
+    } else {
+      //firstend = dadlink->lastend;
+      f4_Props Pdad = dadlink->P;
+      f4_Props Padj = Pdad;
+      Padj.adjust();
+
+      //rot = Orient_1;
+
+      // rotation due to rolling
+      xrot = rolling +
+        // rotation due to twist
+        Pdad.twist;
+      if (dadlink->commacount <= 1)
+      {
+        // rotation due to curvedness
+        zrot = Padj.curv;
+      } else {
+        zrot = Padj.curv +
+          // GDK uses 3.141 instead of PI!
+          (anglepos * 1.0/(dadlink->commacount+1) - 0.5) * 3.141 * 2.0;
+      }
+
+      //rot = rot * f4_OrientMat(yOz, xrot);
+      //rot = rot * f4_OrientMat(xOy, zrot);
+      // rotation relative to parent stick
+      //OM = rot * OM;
+
+      // rotation in world coordinates
+      //OM =  ((f4_OrientMat)dadlink->OM) * OM;
+      mz = dadlink->mz / dadlink->childcount;
+    }
+    //Pt3D lastoffset = (Orient)OM * (Pt3D(1,0,0)*P.len);
+    //lastend = firstend + lastoffset;
+  }
+}
+
+
+
+f4_CellLink::f4_CellLink(f4_Cell * nfrom, double nw, long nt)
+{
+  from = nfrom;
+  w    = nw;
+  t    = nt;
+}
+
+
+
+f4_Cells::f4_Cells(f4_node * genome, int nrepair)
+{
+  // create ancestor cell
+  repair = nrepair;
+  error  = 0;
+  errorpos = -1;
+  repair_remove = NULL;
+  repair_parent = NULL;
+  repair_insert = NULL;
+  tmpcel = NULL;
+  f4rootnode = NULL;
+
+  C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, stdProps);
+  nc   = 1;
+}
+
+
+f4_Cells::f4_Cells(SString & genome, int nrepair)
+{
+  int res;
+  repair = nrepair;
+  error  = 0;
+  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();
+  res = f4_processrec((const char*)genome, (unsigned)0, f4rootnode);
+  if ((res<0) || (1 != f4rootnode->childCount()))
+  {
+    error = GENOPER_OPFAIL;
+    errorpos = -1;
+  }
+
+  // create ancestor cell
+  C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child,
+    NULL, 0, stdProps);
+  nc   = 1;
+}
+
+f4_Cells::~f4_Cells()
+{
+  // release cells
+  int i;
+  if (nc)
+  {
+    for (i=nc-1; i>=0; i--)
+      delete C[i];
+    nc = 0;
+  }
+  if (f4rootnode)
+    delete f4rootnode;
+}
+
+
+int f4_Cells::onestep()
+{
+  int i, ret, oldnc, ret2;
+  oldnc = nc;
+  ret=0;
+  for (i=0; i<oldnc; i++) {
+    ret2 = C[i]->onestep();
+    if (ret2>0) {
+      // error
+      C[i]->active = 0;  // stop
+      return 0;
+    }
+    // if still active
+    if (C[i]->active)
+      ret=1;
+  }
+  return ret;
+}
+
+
+int f4_Cells::simulate()
+{
+  int i;
+  error = GENOPER_OK;
+
+  for (i=0; i<nc; i++)  C[i]->active = 1;
+
+  // execute onestep() in a cycle
+  while (onestep()) ;
+
+  if (GENOPER_OK != error) return error;
+
+  // fix neuron attachements
+  for(i=0; i<nc; i++)
+    if (C[i]->type == T_NEURON4) {
+      while (T_NEURON4 == C[i]->dadlink->type) {
+        C[i]->dadlink = C[i]->dadlink->dadlink;
+      }
+  }
+
+  // there should be no undiff. cells
+  // make undifferentiated cells sticks
+  for (i=0; i<nc ; i++)
+    if (C[i]->type == T_UNDIFF4) {
+      C[i]->type = T_STICK4;
+      //seterror();
+  }
+
+  // recursive adjust
+  // reset recursive traverse flags
+  for(i=0; i<nc; i++)
+    C[i]->recProcessedFlag = 0;
+  // process every cell
+  for(i=0; i<nc; i++)
+    C[i]->adjustRec();
+
+  //DB( printf("Cell simulation done, %d cells. \n", nc); )
+
+  return error;
+}
+
+
+void f4_Cells::addCell(f4_Cell * newcell)
+{
+  if (nc >= MAX4CELLS-1) {
+    delete newcell;
+    return;
+  }
+  C[nc] = newcell;
+  nc++;
+}
+
+
+void f4_Cells::setError(int nerrpos)
+{
+  error = GENOPER_OPFAIL;
+  errorpos = nerrpos;
+}
+
+void f4_Cells::setRepairRemove(int nerrpos, f4_node * rem)
+{
+  if (!repair) {
+    // not in repair mode, treat as repairable error
+    error = GENOPER_REPAIR;
+    errorpos = nerrpos;
+  } else {
+    error = GENOPER_REPAIR;
+    errorpos = nerrpos;
+    repair_remove = rem;
+  }
+}
+
+int f4_Cells::setRepairInsert(int nerrpos, f4_node * parent, f4_node * insert)
+{
+  if (!repair) {
+    // not in repair mode, treat as repairable error
+    error = GENOPER_REPAIR;
+    errorpos = nerrpos;
+    return -1;
+  } else {
+    error = GENOPER_REPAIR;
+    errorpos = nerrpos;
+    repair_parent = parent;
+    repair_insert = insert;
+    return 0;
+  }
+}
+
+void f4_Cells::repairGeno(f4_node * geno, int whichchild)
+{
+  // assemble repaired geno, if the case
+  if (!repair) return;
+  if ((NULL==repair_remove) && (NULL==repair_insert)) return;
+  // traverse genotype tree, remove / insert node
+  f4_node * g2;
+  if (1==whichchild) g2 = geno->child;
+    else             g2 = geno->child2;
+  if (NULL == g2)
+    return;
+  if (g2 == repair_remove) {
+    f4_node * oldgeno;
+    geno->removeChild(g2);
+    if (g2->child) {
+      // add g2->child as child to geno
+      if (1==whichchild) geno->child  = g2->child;
+        else             geno->child2 = g2->child;
+      g2->child->parent = geno;
+    }
+    oldgeno = g2;
+    oldgeno->child = NULL;
+    delete oldgeno;
+    if (NULL == geno->child) return;
+    // check this new
+    repairGeno(geno, whichchild);
+    return;
+  }
+  if (g2 == repair_parent) {
+    geno->removeChild(g2);
+    geno->addChild(repair_insert);
+    repair_insert->parent = geno;
+    repair_insert->child  = g2;
+    repair_insert->child2 = NULL;
+    g2->parent = repair_insert;
+  }
+  // recurse
+  if (g2->child)  repairGeno(g2, 1);
+  if (g2->child2) repairGeno(g2, 2);
+}
+
+
+void f4_Cells::toF1Geno(SString &out)
+{
+  if (tmpcel) delete tmpcel;
+  tmpcel = new f4_Cell(-1, NULL, 0, stdProps);
+  out = "";
+  toF1GenoRec(0, out);
+  delete tmpcel;
+}
+
+
+void f4_Cells::toF1GenoRec(int curc, SString &out)
+{
+  int i, j, ccount;
+  f4_Cell * thisti;
+  f4_Cell * thneu;
+  char buf[200];
+
+  if (curc >= nc) return;
+
+  if (T_STICK4 != C[curc]->type) return;
+
+  thisti = C[curc];
+  if (NULL != thisti->dadlink)  
+    *tmpcel = *(thisti->dadlink);
+
+  // adjust length, curvedness, etc.
+  tmpcel->P.adjust();
+  while (tmpcel->P.len > thisti->P.len)
+  {
+    tmpcel->P.executeModifier('l');
+    out += "l";
+  }
+  while (tmpcel->P.len < thisti->P.len) 
+  {
+    tmpcel->P.executeModifier('L');
+    out += "L";
+  }
+  while (tmpcel->P.curv > thisti->P.curv) 
+  {
+    tmpcel->P.executeModifier('c');
+    out += "c";
+  }
+  while (tmpcel->P.curv < thisti->P.curv) 
+  {
+    tmpcel->P.executeModifier('C');
+    out += "C";
+  }
+  while (thisti->rolling > 0.0f) {
+    rolling_dec( &(thisti->rolling) );
+    out += "R";
+  }
+  while (thisti->rolling < 0.0f) {
+    rolling_inc( &(thisti->rolling) );
+    out += "r";
+  }
+
+  // output X for this stick
+  out += "X";
+
+  // neurons attached to it
+  for (i=0; i<nc; i++)
+    if (C[i]->type == T_NEURON4) {
+      if (C[i]->dadlink == thisti) {
+        thneu = C[i];
+        out += "[";
+        // ctrl
+        if (1 == thneu->ctrl) out += "@";
+        if (2 == thneu->ctrl) out += "|";
+        // links
+        for (j=0; j<thneu->nolink; j++) 
+        {
+          if (j) out += ",";
+          if (NULL == thneu->links[j]->from) 
+          {
+            // sensory
+            if (1 == thneu->links[j]->t) out += "*";
+            if (2 == thneu->links[j]->t) out += "G";
+            if (3 == thneu->links[j]->t) out += "T";
+            if (4 == thneu->links[j]->t) out += "S";
+          } else {
+            sprintf(buf, "%d", thneu->links[j]->from->name - thneu->name);
+            out += buf;
+          }
+          out += ":";
+          // weight
+          sprintf(buf, "%g", thneu->links[j]->w );
+          out += buf;
+        }
+        out += "]";
+    }
+  }
+
+  // sticks connected to it
+  if (thisti->commacount>=2)
+    out += "(";
+
+  ccount=1;
+  for (i=0; i<nc; i++)
+   if (C[i]->type == T_STICK4)
+    if (C[i]->dadlink == thisti) {
+      while (ccount < (C[i])->anglepos) {
+        ccount++;
+        out += ",";
+      }
+      toF1GenoRec(i, out);
+  }
+  while (ccount < thisti->commacount) {
+    ccount++;
+    out += ",";
+  }
+
+  if (thisti->commacount >= 2)
+    out += ")";
+}
+
+
+
+// to organize a f4 genotype in a tree structure
+
+f4_node::f4_node()
+{
+  name   = '?';
+  parent = NULL;
+  child  = NULL;
+  child2 = NULL;
+  pos    = -1;
+}
+
+f4_node::f4_node(char nname, f4_node * nparent, int npos)
+{
+  name   = nname;
+  parent = nparent;
+  child  = NULL;
+  child2 = NULL;
+  pos    = npos;
+  if (parent) parent->addChild(this);
+}
+
+f4_node::~f4_node()
+{
+  // (destroy() copied here for efficiency)
+  // children are destroyed (recursively) through the destructor
+  if (NULL != child2)  delete child2;
+  if (NULL != child)   delete child;
+}
+
+int f4_node::addChild(f4_node * nchi)
+{
+  if (NULL==child) {
+    child = nchi;
+    return 0;
+  }
+  if (NULL==child2) {
+    child2 = nchi;
+    return 0;
+  }
+  return -1;
+}
+
+int f4_node::removeChild(f4_node * nchi)
+{
+  if (nchi==child2) {
+    child2 = NULL;
+    return 0;
+  }
+  if (nchi==child) {
+    child = NULL;
+    return 0;
+  }
+  return -1;
+}
+
+int f4_node::childCount()
+{
+  if (NULL!=child) {
+    if (NULL!=child2) return 2;
+                 else return 1;
+  } else {
+    if (NULL!=child2) return 1;
+                 else return 0;
+  }
+}
+
+int f4_node::count()
+{
+  int c=1;
+  if (NULL!=child)  c+=child->count();
+  if (NULL!=child2) c+=child2->count();
+  return c;
+}
+
+f4_node * f4_node::ordNode(int n)
+{
+  int n1;
+  if (0 == n) return this;
+  n--;
+  if (NULL != child) {
+    n1 = child->count();
+    if (n<n1) return child->ordNode(n);
+    n -= n1;
+  }
+  if (NULL != child2) {
+    n1 = child2->count();
+    if (n<n1) return child2->ordNode(n);
+    n -= n1;
+  }
+  return NULL;
+}
+
+f4_node * f4_node::randomNode()
+{
+  int n, i;
+  n = count();
+  // pick a random node, between 0 and n-1
+  i = (int)( ((float)n-0.0001) / RAND_MAX * rand());
+  return ordNode(i);
+}
+
+f4_node * f4_node::randomNodeWithSize(int min, int max)
+{
+  // try random nodes, and accept if size in range
+  // limit to maxlim tries
+  int i, n, maxlim;
+  f4_node * nod = NULL;
+  maxlim = count();
+  for (i=0; i<maxlim; i++) {
+    nod = randomNode();
+    n = nod->count();
+    if ((n>=min) && (n<=max)) return nod;
+  }
+  // failed, doesn't matter
+  return nod;
+}
+
+void f4_node::sprint(SString & out)
+{
+  char buf2[20];
+  // special case: repetition code
+  if ('#' == name)
+  {
+    out += "#";
+    if (i1 != 1) {
+      sprintf(buf2, "%d", i1);
+      out += buf2;
+    }
+  } else {
+  // special case: neuron link
+  if ('[' == name) {
+    out += "[";
+    if (i1>0) {
+      // sensor input
+      if (1==i1) out += "*";
+      if (2==i1) out += "G";
+      if (3==i1) out += "T";
+      if (4==i1) out += "S";
+    } else {
+      sprintf(buf2, "%ld", l1);
+      out += buf2;
+    }
+    sprintf(buf2, ":%g]", f1);
+    out += buf2;
+  } else if (':' == name) {
+    sprintf(buf2, ":%c%c:", l1 ? '+' : '-', (char)i1);
+    out += buf2;
+  } else {
+    buf2[0] = name;
+    buf2[1] = 0;
+    out += buf2;
+  }}
+  if (NULL != child)     child->sprint(out);
+  // if two children, make sure last char is a '>'
+  if (2 == childCount())
+    if (0 == out[0]) out += ">"; else
+      if ('>' != out[out.len()-1]) out += ">";
+  if (NULL != child2)    child2->sprint(out);
+  // make sure last char is a '>'
+  if (0 == out[0]) out += ">"; else
+    if ('>' != out[out.len()-1]) out += ">";
+}
+
+void f4_node::sprintAdj(char *& buf)
+{
+  unsigned int len;
+  // build in a SString, with initial size
+  SString out(strlen(buf)+2000);
+  out="";
+
+  sprint(out);
+
+  // very last '>' can be omitted
+  len = out.len();
+  if (len>1)
+    if ('>' == out[len-1]) { (out.directWrite())[len-1]=0; out.endWrite();};
+  // copy back to string
+  // if new is longer, reallocate buf
+  if (len+1 > strlen(buf)) {
+    buf = (char*) realloc(buf, len+1 );
+  }
+  strcpy(buf, (const char*) out);
+}
+
+f4_node * f4_node::duplicate()
+{
+  f4_node * copy;
+  copy = new f4_node(*this);
+  copy->parent = NULL;  // set later
+  copy->child  = NULL;
+  copy->child2 = NULL;
+  if (NULL != child ) {
+    copy->child  = child ->duplicate();
+    copy->child ->parent = copy;
+  }
+  if (NULL != child2 ) {
+    copy->child2 = child2->duplicate();
+    copy->child2->parent = copy;
+  }
+  return copy;
+}
+
+
+void f4_node::destroy()
+{
+  // children are destroyed (recursively) through the destructor
+  if (NULL != child2)  delete child2;
+  if (NULL != child)   delete child;
+}
+
+
+// scan genotype string and build tree
+// return >1 for error (errorpos)
+int f4_processrec(const char * genot, unsigned pos0, f4_node * parent)
+{
+  int i, j, t, res;
+  char tc1, tc2;
+  long relfrom;
+  double w;
+  unsigned gpos, oldpos;
+  f4_node * node1, * par;
+
+  gpos = pos0;
+  par = parent;
+  if (gpos >= strlen(genot) ) return 1;
+  while (gpos<strlen(genot)) {
+    //DB( printf(" processing '%c' %d %s\n", genot[gpos], gpos, genot); )
+    switch (genot[gpos]) {
+    case '<':
+      // cell division!
+      //DB( printf("  div! %d\n", name); )
+
+      // find out genotype start for child
+      j = scanrec(genot+gpos+1, strlen(genot+gpos+1), '>');
+
+      node1 = new f4_node('<', par, gpos);
+      par = node1;
+      res = f4_processrec(genot, gpos+1,   par);
+      if (res) return res;
+      if (gpos+j+2 < strlen(genot)) {
+        res = f4_processrec(genot, gpos+j+2, par);
+        if (res) return res;
+      } else {  // ran out
+        node1 = new f4_node('>', par, strlen(genot)-1 );
+        par = node1;
+      }
+      // adjustments
+      gpos++;
+      return 0;  // OK
+
+    case '>':
+      node1 = new f4_node('>', par, gpos );
+      par = node1;
+      gpos = strlen(genot);
+      return 0;  // OK
+
+    case '#':
+      // repetition marker, 1 by default
+      if (sscanf(genot+gpos, "#%d", &i) != 1) i=1;
+      // find out genotype start for continuation
+      j = scanrec(genot+gpos+1, strlen(genot+gpos+1), '>');
+      // skip number
+      oldpos = gpos;
+      gpos++;
+      while ((genot[gpos]>='0') && (genot[gpos]<='9')) gpos++;
+      node1 = new f4_node('#', par, oldpos );
+      node1->i1 = i;
+      par = node1;
+      res = f4_processrec(genot, gpos,   node1);
+      if (res) return res;
+      if (oldpos+j+2 < strlen(genot) ) {
+        res = f4_processrec(genot, oldpos+j+2, node1);
+        if (res) return res;
+      } else {  // ran out
+        node1 = new f4_node('>', par, strlen(genot)-1 );
+      }
+      return 0;  // OK
+
+    // 'simple' nodes:
+    case ',':
+    case 'l':  case 'L':
+    case 'c':  case 'C':
+    case 'q':  case 'Q':
+    case 'r':  case 'R':
+    case 'X':  case 'N':
+    case '@':  case '|':
+    case 'a':  case 'A':
+    case 's':  case 'S':
+    case 'm':  case 'M':
+    case 'i':  case 'I':
+    case 'f':  case 'F':
+    case 'w':  case 'W':
+    case 'e':  case 'E':
+      node1 = new f4_node(genot[gpos], par, gpos );
+      par = node1;
+      gpos++;
+      break;
+
+    case '[':
+      // link to neuron
+      // input (%d, '*', 'G', 'T', 'S')
+      t = -1;
+      if (sscanf(genot+gpos, "[%ld:%lf]", &relfrom, &w) == 2) t=0;
+      else if (sscanf(genot+gpos, "[*:%lf]", &w) == 1) t=1;
+      else if (sscanf(genot+gpos, "[G:%lf]", &w) == 1) t=2;
+      else if (sscanf(genot+gpos, "[T:%lf]", &w) == 1) t=3;
+      else if (sscanf(genot+gpos, "[S:%lf]", &w) == 1) t=4;
+      // error: no correct format
+      if (t<0) return gpos+1+1;
+      node1 = new f4_node('[', par, gpos );
+      node1->i1 = t;
+      node1->l1 = relfrom;
+      node1->f1 = w;
+      par = node1;
+      j = scanrec(genot+gpos+1, strlen(genot+gpos+1), ']');
+      gpos += j+2;
+      break;
+
+    case ':':
+      // neuron parameter  +! -! += -= +/ or -/
+      if (sscanf(genot+gpos, ":%c%c:", &tc1, &tc2) != 2)
+        // error: incorrect format
+        return gpos+1+1;
+      if ('+' == tc1) j=1;
+      else if ('-' == tc1) j=0;
+      else return gpos+1+1;
+      switch (tc2) {
+       case '!':  case '=':  case '/':  break;
+       default:
+        return gpos+1+1;
+      }
+      node1 = new f4_node(':', par, gpos );
+      node1->l1 = j;
+      node1->i1 = (int)tc2;
+      par = node1;
+      j = scanrec(genot+gpos+1, strlen(genot+gpos+1), ':');
+      gpos += j+2;
+      break;
+
+    case ' ':
+    case '\n':
+    case '\t':
+      // whitespace: ignore
+      //node1 = new f4_node(' ', par, gpos );
+      //par = node1;
+      gpos++;
+      break;
+
+    default:
+      //DB( printf("unknown character '%c' ! \n", genot[gpos]); )
+      //add it, build will give the error or repair
+      node1 = new f4_node(genot[gpos], par, gpos );
+      par = node1;
+      gpos++;
+      break;
+    }
+  }
+  // should end with a '>'
+  if (par)
+    if ('>' != par->name) {
+      node1 = new f4_node('>', par, strlen(genot)-1 );
+      par = node1;
+  }
+  return 0;  // OK
+}
+
+
+f4_node * f4_processtree(const char * geno)
+{
+  f4_node * root;
+  int res;
+  root = new f4_node();
+  res = f4_processrec(geno, 0, root);
+  if (res) return NULL;
+  //DB( printf("test f4  "); )
+  DB(
+    if (root->child) {
+      char * buf = (char*) malloc( 300 );
+      DB( printf("(%d) ", root->child->count() ); )
+      buf[0]=0;
+      root->child->sprintAdj(buf);
+      DB( printf("%s\n", buf); )
+      free(buf);
+    }
+  )
+  return root->child;
+}
+
Index: /cpp/f4/f4_general.h
===================================================================
--- /cpp/f4/f4_general.h	(revision 4)
+++ /cpp/f4/f4_general.h	(revision 4)
@@ -0,0 +1,249 @@
+/*
+ *  f4_general.h - f4 genotype functions.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _F4_GENERAL_H_
+#define _F4_GENERAL_H_
+
+#include "f4_orientmat.h"
+#include "3d.h"
+#include "sstring.h"
+#include "multirange.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+class f4_Props
+{
+ public:
+  // fill with default values
+  f4_Props();
+  // must sum to 1
+  void normalizeBiol4();
+  void executeModifier(char modif);
+  void adjust();
+
+  double len;      // length (dlug)
+  double curv;     // curvedness (skr)
+  double mass;
+  double friction;
+  double ruch;
+  double assim;
+  double odpor;
+  double ingest;  // ingestion (wchl)
+  double twist;
+  double energ;
+};
+
+extern f4_Props stdProps;
+
+
+// rolling (one-time)
+void rolling_dec(double * v);
+void rolling_inc(double * v);
+
+
+class f4_node;   // later
+class f4_Cell;   // later
+class f4_Cells;  // later
+
+
+// cell types
+#define T_UNDIFF4 40
+#define T_STICK4  41
+#define T_NEURON4 42
+
+int scanrec(const char * s, unsigned int slen, char stopchar);
+
+
+class f4_CellLink;
+#define MAXINPUTS 100
+
+// an abstract cell type, extension of part/stick -- for developmental encoding
+class f4_Cell
+{
+public:
+  class repeat_ptr
+  {
+   public:
+    repeat_ptr() : node(NULL), count(-1) { };
+    repeat_ptr(f4_node * a, int b) : node(a), count(b) { };
+    inline void null() { node = NULL; count = -1; };
+    inline bool isNull() const { return ((node == NULL) || (count <= 0)); };
+    inline void dec() { count--; };
+    f4_node *  node;  // ptr to repetition code
+    char       count; // repetition counter
+  };
+
+  class repeat_stack  // a stack of repet_ptr's
+  {
+   public:
+    repeat_stack() { top=0; };
+    inline void null() { top=0; };
+    inline void push(repeat_ptr A) { if (top>=stackSize) return; ptr[top]=A; top++; };
+    inline void pop() { if (top>0) top--; };
+    inline repeat_ptr * first() { return &(ptr[top-(top>0)]); };
+    static const int stackSize = 4;  // max 4 nested levels
+    repeat_ptr ptr[stackSize];
+    short int top;  // top of the stack
+  };
+
+  f4_Cell(int nname,
+    f4_Cell * ndad, int nangle, f4_Props newP);
+  f4_Cell(f4_Cells * nO, int nname, f4_node * ngeno, f4_node * ngcur,
+    f4_Cell * ndad, int nangle, f4_Props newP);
+  ~f4_Cell();
+
+  int onestep();	// execute one simulation step (till a division)
+
+  int   addlink(f4_Cell * nfrom, double nw, long nt);
+  void  adjustRec();
+
+  int        name;     // name (number)
+  int        type;     // type
+  f4_Cell *  dadlink;
+  f4_Cells * org;	// uplink to organism
+
+  f4_node *  genot;	  // genotype
+  f4_node *  gcur;        // current genotype execution pointer
+  int        active;      // whether development is still active
+  repeat_stack repeat;
+  int        recProcessedFlag;  // used during recursive traverse
+  // remember the genotype codes affecting this cell so far
+  MultiRange genoRange;
+
+  f4_Props     P;             // properties
+  int          anglepos;      // number of position within dad's children (,)
+  int          childcount;    // number of children
+  int          commacount;    // number of postitions at lastend (>=childcount)
+  double       rolling;       // rolling angle ('R') (around x)
+  double       xrot;
+  double       zrot;          // horizontal rotation angle due to
+                              // branching (around z)
+  //Pt3D         firstend;      // coord.s of first end (connects to parent)
+  //Pt3D         lastend;       // last end
+  //f4_OrientMat OM;
+  double       mz;            // freedom in z
+  long         p2_refno;   // number of last end part object, used in f0
+  long         joint_refno;   // number of the joint object, used in f0
+  long         neuro_refno;   // number of the neuro object, used in f0
+
+  long         ctrl;  // neuron type
+  double       state;
+  double       inertia;
+  double       force;
+  double       sigmo;
+  f4_CellLink* links[MAXINPUTS];
+  int          nolink;
+};
+
+
+// an input link to a neuron
+class f4_CellLink
+{
+public:
+               f4_CellLink(f4_Cell * nfrom, double nw, long nt);
+  f4_Cell *    from;
+  // type: 0: input, 1 '*', 2 'G', 3 'T', 4 'S'
+  long         t;
+  double       w;
+};
+
+
+// a collection of cells, like Organism, for developmental encoding
+#define MAX4CELLS 100
+class f4_Cells
+{
+ public:
+  f4_Cells(f4_node * genome, int nrepair);
+  f4_Cells(SString &genome, int nrepair);
+  ~f4_Cells();
+  void addCell(f4_Cell * newcell);
+  void toF1Geno(SString &out);       // output to f1 format, approximation
+  int  onestep();       // simulate all parts for one step
+  int  simulate();      // simulate development, return error (0 for ok)
+  // for error reporting / genotype fixing
+  int  geterror() { return error;};
+  int  geterrorpos() { return errorpos;};
+  void setError(int nerrpos);
+  void setRepairRemove(int nerrpos, f4_node * rem);
+  int  setRepairInsert(int nerrpos, f4_node * parent, f4_node * insert);
+  void repairGeno(f4_node * geno, int whichchild);
+
+  // the cells
+  f4_Cell * C[MAX4CELLS];
+  int       nc;
+
+private:
+  // for error reporting / genotype fixing
+  int repair;
+  int error;
+  int errorpos;
+  f4_node * repair_remove;
+  f4_node * repair_parent;
+  f4_node * repair_insert;
+  void toF1GenoRec(int curc, SString &out);
+  f4_Cell * tmpcel;		// needed by toF1Geno
+  f4_node * f4rootnode;          // used by constructor
+};
+
+
+/**
+ * Class to organize a f4 genotype in a tree structure.
+ */
+class f4_node
+{
+public:
+  char      name;	// one-letter 'name'
+  f4_node * parent;	// parent link, or NULL
+  f4_node * child;	// child, or NULL
+  f4_node * child2;	// second child, or NULL
+  int       pos;        // original position in string
+  int       i1;		// internal int  parameter1
+  long      l1;		// internal long parameter1
+  double    f1;		// internal double parameter1
+
+            f4_node();
+            f4_node(char nname, f4_node * nparent, int npos);
+            ~f4_node();
+  int       addChild(f4_node * nchi);
+  int       removeChild(f4_node * nchi);
+  int       childCount();	// return no of children, 0, 1, or 2
+  int       count();	// return no of nodes (recursive)
+  f4_node * ordNode(int n);	// returns the nth subnode (0-)
+  f4_node * randomNode();	// returns a random subnode
+  f4_node * randomNodeWithSize(int min, int max);	// returns a random subnode with given size
+  void      sprintAdj(char *& buf);	// print recursively
+  f4_node * duplicate();         // create duplicate copy. recursive.
+  void      destroy();	// release memory. recursive.
+private:
+  void     sprint(SString & out);	// print recursively
+};
+
+// convert f4 geno string to tree structure (internal)
+f4_node * f4_processtree(const char * geno);
+int f4_processrec(const char * genot, unsigned pos0, f4_node * parent);
+
+
+#endif
Index: /cpp/f4/f4_orientmat.cpp
===================================================================
--- /cpp/f4/f4_orientmat.cpp	(revision 4)
+++ /cpp/f4/f4_orientmat.cpp	(revision 4)
@@ -0,0 +1,65 @@
+/*
+ *  f4_orientmat.cpp - Extension of Orient with matrix multiplication.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "f4_orientmat.h"
+#include <math.h>
+
+
+f4_OrientMat::f4_OrientMat(XYZplanes plane, float angle) 
+{
+  switch(plane) 
+  {
+   case xOy:
+    x.x = cos(angle); x.y = -sin(angle); x.z = 0.0;
+    y.x = sin(angle); y.y =  cos(angle); y.z = 0.0;
+    z.x = 0.0;        z.y = 0.0;         z.z = 1.0;
+    break;
+   case xOz:
+    x.x = cos(angle); x.y = 0.0; x.z = -sin(angle);
+    y.x = 0.0;        y.y = 1.0; y.z = 0.0;
+    z.x = sin(angle); z.y = 0.0; z.z = cos(angle);
+    break;
+   case yOz:
+    x.x = 1.0;  x.y = 0.0;        x.z = 0.0;
+    y.x = 0.0;  y.y = cos(angle); y.z = -sin(angle);
+    z.x = 0.0;  z.y = sin(angle); z.z =  cos(angle);
+    break;
+  }
+} 
+
+
+// M3 = this * M2;
+f4_OrientMat f4_OrientMat::operator*(const Orient & M2)
+{
+  f4_OrientMat M3;
+  M3.x.x = x.x * M2.x.x + x.y * M2.y.x + x.z * M2.z.x;
+  M3.x.y = x.x * M2.x.y + x.y * M2.y.y + x.z * M2.z.y;
+  M3.x.z = x.x * M2.x.z + x.y * M2.y.z + x.z * M2.z.z;
+  M3.y.x = y.x * M2.x.x + y.y * M2.y.x + y.z * M2.z.x;
+  M3.y.y = y.x * M2.x.y + y.y * M2.y.y + y.z * M2.z.y;
+  M3.y.z = y.x * M2.x.z + y.y * M2.y.z + y.z * M2.z.z;
+  M3.z.x = z.x * M2.x.x + z.y * M2.y.x + z.z * M2.z.x;
+  M3.z.y = z.x * M2.x.y + z.y * M2.y.y + z.z * M2.z.y;
+  M3.z.z = z.x * M2.x.z + z.y * M2.y.z + z.z * M2.z.z;
+  return M3;
+}
Index: /cpp/f4/f4_orientmat.h
===================================================================
--- /cpp/f4/f4_orientmat.h	(revision 4)
+++ /cpp/f4/f4_orientmat.h	(revision 4)
@@ -0,0 +1,44 @@
+/*
+ *  f4_orientmat.h - Extension of Orient with matrix multiplication.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _F4_ORIENTMAT_H_
+#define _F4_ORIENTMAT_H_
+
+#include "3d.h"
+
+typedef enum {
+   xOy, xOz, yOz
+} XYZplanes;
+
+class f4_OrientMat: public Orient 
+{ 
+ public:
+  f4_OrientMat() : Orient() {};
+  f4_OrientMat(const Orient & o) : Orient(o) {};
+  /// matrix multiplication
+  f4_OrientMat operator*(const Orient & M2);
+  /// rotation matrix in a given cartesian plane
+  f4_OrientMat(XYZplanes plane, float angle);
+};
+
+#endif
Index: /cpp/f4/geno_f4.cpp
===================================================================
--- /cpp/f4/geno_f4.cpp	(revision 4)
+++ /cpp/f4/geno_f4.cpp	(revision 4)
@@ -0,0 +1,641 @@
+/*
+ *  geno_f4.cpp - f4 genetic operators.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *  Copyright (C) 2001-2004  Maciej Komosinski
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "geno_f4.h"
+#include "nonstd.h"
+#include "sstring.h"
+#include "framsg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+
+#define FIELDSTRUCT Geno_f4
+
+static ParamEntry GENO4param_tab[]=
+{
+ {"Genetics: f4",1,F4_COUNT+F4_ADD_COUNT,},
+ {"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", },
+ {0,},
+};
+
+#undef FIELDSTRUCT
+
+
+Geno_f4::Geno_f4()
+{
+   supported_format='4';
+   par.setParamTab(GENO4param_tab);
+   par.select(this);
+   par.setDefault();
+
+   mutation_method_names=new 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) FramMessage("Geno_f4","Constructor","Mutation names init error",3);
+}
+
+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)
+{
+  // 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)
+{
+  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) !
+
+  // codes that can be changed (apart being added/deleted)
+  #define MUT_CHAN_CODES "<[#"
+  #define ADD_SIMPLE_CODES ",XlLcCrRaAiIsSmMfFwWeEN@|"
+  #define REP_MAXCOUNT 19
+
+  f4_node * n1, * n2, * n3, * n4, * n5;
+  int i, j;
+
+  // do the mutation
+  // pick a random node
+  n1 = g->child->randomNode();
+  //DB( printf("%c\n", n1->name); )
+
+  switch(roulette(prob,F4_COUNT))
+  {
+    case F4_ADD:
+    {
+      // add a node
+      switch(method=roulette(probadd,F4_ADD_COUNT))
+      {
+        case F4_ADD_DIV:
+        {
+          // add division ('<')
+          n3 = n1->parent;
+          n3->removeChild(n1);
+          n2 = new f4_node('<', n3, n3->pos );
+          n2->addChild(n1);
+          // new cell is stick or neuron
+          // "X>" or "N>"
+          double pr = rnd01;
+          pr -= 0.5;
+          if (pr<0) n3 = new f4_node('X', n2, n2->pos);
+          else
+          {
+            pr -= 0.5;
+            if (pr<0)
+            {
+              // if neuron, make muscle and add a link
+              n3 = new f4_node('N', n2, n2->pos);
+              if (randomN(2) == 0)
+                n4 = new f4_node('|', n3, n2->pos);
+              else
+                n4 = new f4_node('@', n3, n2->pos);
+              n5 = new f4_node('[', n4, n2->pos);
+              linkNodeMakeRandom(n5);
+            }
+          }
+          new f4_node('>', n3, n3->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
+          n1->parent->removeChild(n1);
+          n2 = new f4_node('[', n1->parent, n1->parent->pos);
+          linkNodeMakeRandom(n2);
+          n2->addChild(n1);
+          n1->parent = n2;
+        }
+        break;
+        case F4_ADD_NEUPAR:
+        {
+          // add neuron modifier
+          n1->parent->removeChild(n1);
+          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
+          n3 = n1->parent;
+          n3->removeChild(n1);
+          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);
+          n2 = new f4_node(ADD_SIMPLE_CODES[randomN(strlen(ADD_SIMPLE_CODES))], 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 (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
+         {
+            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
+            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
+      i=0;
+      while (1)
+      {
+        if (strchr(MUT_CHAN_CODES, n1->name)) break;
+        // try a new one
+        n1 = g->child->randomNode();
+        i++;
+        if (i>=20) return GENOPER_OPFAIL;
+      }
+      switch (n1->name) {
+        case '<':
+          // swap children
+          n2 = n1->child; n1->child = n1->child2; n1->child2 = n2;
+          break;
+        case '[':
+          linkNodeChangeRandom(n1);
+          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) const
+{
+  int i;
+  float prob1;
+
+  i = 0;
+  // 35% chance one of *GTS
+  prob1 = 1.0f / RAND_MAX * rand();
+  prob1 -= 0.35f;
+  if (prob1 < 0)
+  {
+    // '*', 'G', 'T', or 'S', 1/4 chance each
+    i = 1 + (int)(3.999f / RAND_MAX * rand());
+  }
+  nn->i1 = i;
+  nn->l1 = 0;
+  if (0 == i) {
+     // relative input link
+     nn->l1 = (int)(4.0f * (1.0f /  RAND_MAX * rand() - 0.5f));
+  }
+  // weight
+  nn->f1 = 10.0f * (1.0f / RAND_MAX * rand() - 0.5f);
+}
+
+// change a [ node
+void Geno_f4::linkNodeChangeRandom(f4_node * nn) const      //rewritten by M.K. - should work as before (not tested)
+{
+  int i;
+  float prob2;
+
+  double probs[3]={0.1, 0.3, 0.6};
+  // 10% change type
+  // 30% change link
+  // 60% change weight
+
+  switch (roulette(probs,3))
+  {
+     case 0: // change type
+          i = 0;
+          // * G, 10% chance each
+          prob2 = rnd01 - 0.10f;
+          if (prob2 < 0) i=1; else {prob2 -= 0.10f; if (prob2 < 0) i=2;}
+          nn->i1 = i;
+          break;
+     case 1: // change link
+          if (0 == nn->i1) // relative input link
+             nn->l1 += (int)(2.0f * (rnd01 - 0.5f));
+          break;
+     case 2: // change weight
+          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 / RAND_MAX * rand() );
+  int param = (int)( 3.0f / RAND_MAX * rand() );
+  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 = 1.0f / RAND_MAX * rand();
+  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 + 1.0f/RAND_MAX * rand() * 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;
+}
+
+
+
+unsigned long Geno_f4::style(const char *g, int pos)
+{
+  char ch = g[pos];
+  // style categories
+  #define STYL4CAT_MODIFIC "LlRrCcQqAaIiSsMmFfWwEe,"
+  #define STYL4CAT_NEUMOD "[]|@*GTS:+-/!="
+  #define STYL4CAT_DIGIT "0123456789."
+  #define STYL4CAT_REST "XN<># "
+  if (!strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_DIGIT STYL4CAT_REST, ch))
+    return GENSTYLE_CS(0,GENSTYLE_INVALID);
+  unsigned long style=GENSTYLE_CS(0,GENSTYLE_STRIKEOUT); //default, should be changed below
+  if (strchr("X ", ch))              style=GENSTYLE_CS(0,GENSTYLE_NONE);
+  if (strchr("N", ch))               style=GENSTYLE_RGBS(0,200,0,GENSTYLE_NONE);  
+  if (strchr("<", ch))               style=GENSTYLE_RGBS(0,0,200,GENSTYLE_BOLD);  
+  if (strchr(">", ch))               style=GENSTYLE_RGBS(0,0,100,GENSTYLE_NONE);  
+  if (strchr(STYL4CAT_DIGIT, ch))     style=GENSTYLE_RGBS(100,100,100,GENSTYLE_NONE);
+  if (strchr(STYL4CAT_MODIFIC, ch))   style=GENSTYLE_RGBS(100,100,100,GENSTYLE_NONE);
+  if (strchr(STYL4CAT_NEUMOD, ch))    style=GENSTYLE_RGBS(0,150,0,GENSTYLE_NONE);
+  return style;
+}
+
+
Index: /cpp/f4/geno_f4.h
===================================================================
--- /cpp/f4/geno_f4.h	(revision 4)
+++ /cpp/f4/geno_f4.h	(revision 4)
@@ -0,0 +1,78 @@
+/*
+ *  geno_f4.h - f4 genetic operators.
+ *
+ *  f4genotype - f4 format genotype conversions for FramSticks
+ *
+ *  Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com)
+ *  Copyright (C) 2001-2004  Maciej Komosinski
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _GENO_F4_H_
+#define _GENO_F4_H_
+
+#include "f4_general.h"
+#include "nonstd.h"
+#include <stdio.h>
+#include "geno_fx.h"
+#include "param.h"
+
+
+#define F4_ADD           0
+#define F4_DEL           1
+#define F4_MOD           2
+#define F4_COUNT         3
+
+#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 Geno_fx
+{
+public:
+  Geno_f4();
+  int validate(char *&);
+  int checkValidity(const char *);
+  int mutate(char *& g, float & chg,int &method);
+  int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
+  char* getSimplest() {return "X";};
+  unsigned long style(const char *g, int pos);
+
+  // mutation probabilities
+  double prob[F4_COUNT],probadd[F4_ADD_COUNT];
+
+protected:
+  /* int MutateMany(char *& g, float & chg); // not used any more */
+  int  ValidateRec(f4_node * geno, int retrycount) const;
+  int  MutateOne(f4_node *& g,int &method) const;
+  void linkNodeMakeRandom(f4_node * nn) const;
+  void linkNodeChangeRandom(f4_node * nn) const;
+  void nparNodeMakeRandom(f4_node * nn) const;
+  void repeatNodeChangeRandom(f4_node * nn) const;
+  int  MutateOneValid(f4_node * &g,int &method) const;
+  int  CrossOverOne(f4_node *g1, f4_node *g2, float chg) const;
+    // returns GENOPER_OK or GENOPER_OPFAIL
+    // chg: fraction of parent1 genes in child (in g1) (parent2 has the rest)
+};
+
+
+#endif
+
