/* * 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 #include #ifdef DMALLOC #include #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; iaddlink(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; inc; 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; inc; 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; inc; 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; inc; 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; ionestep(); 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; iactive = 1; // execute onestep() in a cycle while (onestep()) ; if (GENOPER_OK != error) return error; // fix neuron attachements for(i=0; itype == 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; itype == T_UNDIFF4) { C[i]->type = T_STICK4; //seterror(); } // recursive adjust // reset recursive traverse flags for(i=0; irecProcessedFlag = 0; // process every cell for(i=0; iadjustRec(); //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; itype == 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; jnolink; 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; itype == 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 (nordNode(n); n -= n1; } if (NULL != child2) { n1 = child2->count(); if (nordNode(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; icount(); 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'); 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; }