: $Id: bnet.mod,v 1.49 2012/08/09 16:26:55 samn Exp $ 

:* main COMMENT
COMMENT

boolean network 

ENDCOMMENT

:* main VERBATIM block
VERBATIM

#include "misc.h"

// BP gets a pointer to the boonet* that belongs to each BNET object
#define BP (*((boonet**) &(_p_sop)))

// node (can represent a molecule)
typedef struct BNODE {
  char* name;    // name of the node
  int id;        // id
  int state;  // state of the node
  int count;  // temporary counter
  int knockout; // is this node knocked-out? 
  int start; // starting state
  int sthresh; // special threshold for switching the node's state -- 0 means it's a normal node
  int scount; // special count (# of times its rule is activated)
  double x,y,z;  // location of the node
} bnode;

// represents a rule (contains edges or links between nodes)
typedef struct BRULE {
  struct BNODE** psrc; // source (usually 1 node but can be ANDed)
  struct BNODE*  ptarg;   // pointer to target
  int weight;             // weight of the rule ( < 0 == inhib, > 0 == activate )
  int* psrcstate;       // state of source nodes required for this rule
  int nsrc;           // # of sources in the rule
} brule;

// C-level structure/representation of the boolean network (stores nodes)
typedef struct BOONET {
  struct BNODE* pnodes;
  int numnodes;
  int id;
  struct BRULE* prules; // list of outgoing rules (pointers to targets and weights)
  int nrules;  // number of outgoing edges
  int rulebufsz; // prules buffer size (in units of brules)
} boonet;

// set the state of each node to its starting value
void startboonet (boonet* pnet) {
  int i;
  if(verbose) printf("startboonet\n");
  for(i=0;i<pnet->numnodes;i++) {
    pnet->pnodes[i].count = pnet->pnodes[i].scount = 0;
    pnet->pnodes[i].state = pnet->pnodes[i].start; // set to starting
  }
}

// create and return the C-level structure/representation of the boolean network
boonet* makeboonet (int numnodes) {
  int i;
  boonet* pnet;
  pnet = (boonet*) calloc(1, sizeof(boonet)); // allocate memory for the boonet
  pnet->pnodes = (bnode*) calloc(numnodes, sizeof(bnode)); // allocate memory for the nodes
  pnet->numnodes = numnodes; // store # of nodes
  startboonet(pnet);         // set states to starting values
  for(i=0;i<numnodes;i++) pnet->pnodes[i].id = i; // assign the IDs
  return pnet;
}

// free a single bnode
void freebnode(bnode* p) {
  if(p->name) {
    free(p->name);
    p->name = 0x0;
  }
}

// free a single brule's memory
void freebrule(brule* p) {
  if(p->psrc) free(p->psrc);
  if(p->psrcstate) free(p->psrcstate);
}

// free the entire boolean network
void freeboonet (boonet* pnet) {
  int i;
  if(pnet->pnodes) {
    for(i=0;i<pnet->numnodes;i++) freebnode(&pnet->pnodes[i]);
    free(pnet->pnodes);
  }  
  pnet->pnodes = 0x0;
  if(pnet->prules) {
    for(i=0;i<pnet->nrules;i++) freebrule(&pnet->prules[i]);
    free(pnet->prules);
    pnet->prules = 0x0;
  }
  free(pnet);
}

// add a rule from psrc to targid with specified weight and source state
// return pointer to the rule
brule* addrule (boonet* pnet, double* psrc, int targid, double weight, double* psrcstate, int nsrc) {
  int idx,i;
  if(pnet->prules == 0x0) { // take care of memory allocation
    pnet->rulebufsz = 16;
    pnet->prules = (struct BRULE*) calloc(pnet->rulebufsz, sizeof(brule) );
    pnet->nrules = 0;
  } else if(pnet->nrules >= pnet->rulebufsz) {
    pnet->rulebufsz *= 2;
    pnet->prules = (struct BRULE*) realloc(pnet->prules, pnet->rulebufsz * sizeof(brule) );
  }
  idx = pnet->nrules;
  pnet->prules[idx].nsrc = nsrc;           // set # of sources
  pnet->prules[idx].ptarg = &pnet->pnodes[targid]; // set target
  pnet->prules[idx].weight = weight;               // set weight
  // set the sources
  pnet->prules[idx].psrc = (struct BNODE **) calloc(nsrc, sizeof(bnode*));
  for(i=0;i<nsrc;i++) pnet->prules[idx].psrc[i] = &pnet->pnodes[(int)psrc[i]];
  // set source state for rule to be 'on'
  pnet->prules[idx].psrcstate = (int *) calloc(nsrc, sizeof(int));
  for(i=0;i<nsrc;i++) pnet->prules[idx].psrcstate[i] = (int) psrcstate[i]; 
  return &pnet->prules[ pnet->nrules++ ]; // return the new rule and inc # rules
}

// advance the full boolean network by a single iteration
void advanceboonet (boonet* pnet) {
  int i , j, nn = pnet->numnodes, nsrcact;
  bnode *pnodes = pnet->pnodes;
  brule *prule;
  for(i=0;i<nn;i++) pnodes[i].count = 0;      // initialize temporary counts to 0
  for(i=0;i<pnet->nrules;i++) {
    prule = &pnet->prules[i];
    nsrcact = 0; // number of sources that are activated
    for(j=0;j<prule->nsrc;j++) { // go thru the sources of the rule
      if(prule->psrc[j]->knockout) continue; // skip knockouts
      if(prule->psrc[j]->state == prule->psrcstate[j]){//check if source node in correct state
        nsrcact += 1; // count number of source nodes in correct state (for activation of the rule)
      }
    }
    if(prule->nsrc == nsrcact) prule->ptarg->count += prule->weight;//integrate if ALL sources in proper state
  }
  for(i=0;i<nn;i++) { // update the state of each node based on its inputs
    if(pnodes[i].knockout) {pnodes[i].scount=pnodes[i].count=pnodes[i].state=0; continue;}
    if(pnodes[i].count > 0) {       // turn on (more activating vs inhibiting input)     
      if(pnodes[i].sthresh > 0) { // special node?
        pnodes[i].scount += 1;    // increase the special counter
      } else {//normal node so gets turned on when activation count > inhibition count
        pnodes[i].state = 1;
      }      
    } else if(pnodes[i].count<0||(pnodes[i].sthresh>0 && pnodes[i].count==0)){//turn off (inhibitory > activating input) or special node
      pnodes[i].state = 0;
      pnodes[i].scount = 0; // special node count reset to 0
    }     
    if(pnodes[i].sthresh > 0){//set the state of any special nodes here
      if(pnodes[i].scount >= pnodes[i].sthresh) { // passed the threshold? turn on
        pnodes[i].state = 1;
      } else pnodes[i].state = 0;
    }
  }
}

ENDVERBATIM

:* NEURON, PARAMETER, ASSIGNED blocks
NEURON {
  ARTIFICIAL_CELL BNET
  : POINT_PROCESS BNET
  RANGE tstep
  RANGE xloc, yloc, zloc
  GLOBAL verbose, installed
  POINTER sop :::: Structure pointer for the boolean network
}

PARAMETER {
  tstep = 0
  verbose = 0
  sop = 0
}

ASSIGNED {
  installed
}

:* CONSTRUCTOR, DESTRUCTOR, INITIAL
:** CONSTRUCT: create a structure to save the identity of this unit and char integer flags
CONSTRUCTOR {
  VERBATIM 
  boonet* pnet;
  int sz; 
  if((sz = (int)*getarg(1)) < 1) {
    printf("BNET err0: must have a network with positive # of nodes!\n");
    hxe();
  }
  _p_sop = (double*) makeboonet(sz);
  pnet = BP;
  pnet->id = ifarg(2) ? (int) *getarg(2) : 0;
  ENDVERBATIM
}

: setrule(vsource, targid, weight, vsourcestate) - sets the rule from a set of 3 vectors
: vsource has node IDs of sources, targid is ID of target, weight is weight
: vsourcestate has states the source must be in for the given rule to be turned on.
FUNCTION setrule () {
  VERBATIM
  double *psrc, weight, *psrcstate;
  int targid, i, nsrc;
  if (! ifarg(4) ) {
    printf("BNET.setrule(vsource, targid, weight, vsourcestates) - sets a rule.\n");
    printf("vsource has node IDs of sources, targid is ID of target, weight is (-1,1) for inhib/activating\n");
    printf("vsourcestate has states the source must be in for the given rule to be turned on.\n");
    return 0.0;
  }
  if ( (nsrc = vector_arg_px(1,&psrc)) < 1) {
    printf("BNET.setrule WARN0: empty source Vector!\n");
    return 0.0;
  }
  targid = (int) *getarg(2);
  if(targid < 0 || targid >= BP->numnodes) {
    printf("BNET.setrule ERR0: invalid target id : %d\n",targid);
    return 0.0;
  }
  weight = (int) *getarg(3);
  if( nsrc != vector_arg_px(4,&psrcstate) ) {
    printf("BNET.setrule ERR1: vsource, vsourcestate must have same size!\n");
    return 0.0;
  }
  for(i=0;i<nsrc;i++) {
    if( psrc[i] < 0 || psrc[i] >= BP->numnodes) {
      printf("BNET.setrule ERR2: invalid source node id %d. netsize=%d\n",(int)psrc[i],BP->numnodes);
      return 0.0;
    }
    if(verbose>1) printf("adding rule from %d -> %d : w = %g\n",(int)psrc[i],targid,weight);
  }
  addrule(BP, psrc, targid, weight, psrcstate, nsrc);
  return 1.0;
  ENDVERBATIM
}

: get rid of all the edges (but keep the nodes)
PROCEDURE clearrules () {
  VERBATIM
  BP->nrules = 0;
  ENDVERBATIM
}

: print the network
PROCEDURE pr () {
  VERBATIM
  int i, j, k;
  bnode* pnodes = BP->pnodes;
  brule* prule;
  char srcstr[4096], stmp[4096];
  printf("net: numnodes=%d, numrules=%d\n",BP->numnodes,BP->nrules);
  for(i=0;i<BP->numnodes;i++) {
    if(pnodes[i].name) {
      printf("%s: state=%d, count=%d\n",pnodes[i].name,pnodes[i].state,pnodes[i].count);
    } else {
      printf("%d: state=%d, count=%d\n",i,pnodes[i].state,pnodes[i].count);
    }    
  }
  for(i=0;i<BP->nrules;i++) {
    prule = &BP->prules[i];
    srcstr[0]=0;
    for(j=0;j<prule->nsrc;j++) {
      if(prule->psrc[j]->name) {
        sprintf(stmp,"%s%s%s ", j>0?"AND ":"", prule->psrcstate[j]?"":"!", prule->psrc[j]->name);
      } else {
        sprintf(stmp,"%s%s%d ", j>0?"AND ":"", prule->psrcstate[j]?"":"!", prule->psrc[j]->id);
      }
      strcat(srcstr,stmp);
    }
    if(prule->ptarg->name) {
      printf("%s-> %s , w = %d\n",srcstr,prule->ptarg->name,prule->weight);
    } else {
      printf("%s-> %d , w = %d]\n",srcstr,prule->ptarg->id,prule->weight);
    }
  }
  ENDVERBATIM
}

: BNET.graphviz([dotname,imageename,format,L->R direction,width,height,fontsize]) - print the network as a graphviz string,
: and optionally save to a dot file (dotname) and to png/pdf. format should be png or pdf or other output formats
: supported by graphviz.
FUNCTION graphviz () {
  VERBATIM
  int i, j, k, LR, fsz, w, h;
  bnode* pnodes = BP->pnodes;
  brule* prule;
  char buf[4096], *dotname, *fname, fontsize[128];
  double penw; // penwidth
  FILE* fp = 0x0;
  dotname = ifarg(1) ? gargstr(1) : 0x0;
  fname = ifarg(2) ? gargstr(2) : 0x0;
  const char* ext =   ifarg(3) ? gargstr(3) : "gif";
  LR = ifarg(4) ? (int) *getarg(4) : 1;
  w = ifarg(5) ? (int) *getarg(5) : -1;
  h = ifarg(6) ? (int) *getarg(6) : -1;
  fsz = ifarg(7) ? (int) *getarg(7) : -1;
  if(fsz==-1) sprintf(fontsize,"%s"," "); else sprintf(fontsize,"fontsize=%d,",fsz);
  if(fname) if( !(fp = fopen(dotname,"w"))) {
    printf("BNET.graphviz ERR0: could not open %s\n",fname);
    return 0.0;
  }
  sprintf(buf, "%s", "digraph G {\n"); if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf); 
  if(LR){sprintf(buf, "%s", "\trankdir=LR;\n"); if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf); }
  if(w>0 && h>0) {sprintf(buf, "size=\"%d,%d\"\n",w,h); if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf);}
  for(i=0;i<BP->numnodes;i++) {
    const char* ncolor = BP->pnodes[i].knockout ? "white" : BP->pnodes[i].state > 0 ? "black" : "gray";
    const char* fcolor = BP->pnodes[i].knockout ? "black" : "white";
    const char* shape = pnodes[i].sthresh > 0 ? "invtriangle" : "doublecircle";
    if(BP->pnodes[i].name) {
      sprintf(buf,"\t%s [fontcolor=%s,%sstyle=filled,shape=%s,fillcolor=%s,color=%s]\n",
              BP->pnodes[i].name,fcolor,fontsize,shape,ncolor,ncolor);
    } else {
      sprintf(buf,"\t%d [fontcolor=%s,%sstyle=filled,shape=%s,fillcolor=%s,color=%s]\n",
              i,fcolor,fontsize,shape,ncolor,ncolor);
    }
    if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf); 
  }
  for(i=0;i<BP->nrules;i++) {
    prule = &BP->prules[i];
    for(j=0;j<prule->nsrc;j++) {
      penw = prule->psrcstate[j] == prule->psrc[j]->state ? 6.0 : 1.0;
      const char* arrowtype = prule->weight < 0 ? "tee" : "open";
      const char* lstyle = prule->psrcstate[j] == 0 ? ",style=dashed" : " ";
      if(prule->psrc[j]->name) {
        if(prule->ptarg->name) {
          sprintf(buf,"\t%s -> %s [arrowhead=%s,penwidth=%g,color=%s%s]\n",
                  prule->psrc[j]->name,prule->ptarg->name,arrowtype,penw,prule->weight>0?"red":"blue",lstyle);
        } else {
          sprintf(buf,"\t%s -> %d [arrowhead=%s,penwidth=%g,color=%s%s]\n",
                  prule->psrc[j]->name,prule->ptarg->id,arrowtype,penw,prule->weight>0?"red":"blue",lstyle);
        }
      } else if(prule->ptarg->name) {
          sprintf(buf,"\t%d -> %s [arrowhead=%s,penwidth=%g,color=%s%s]\n",
                  prule->psrc[j]->id,prule->ptarg->name,arrowtype,penw,prule->weight>0?"red":"blue",lstyle);
      } else {
          sprintf(buf,"\t%d -> %d [arrowhead=%s,penwidth=%g,color=%s%s]\n",
                  prule->psrc[j]->id,prule->ptarg->id,arrowtype,penw,prule->weight>0?"red":"blue",lstyle);
      }
      if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf); 
    }
  }
  sprintf(buf,"%s","}\n"); if(fp) fprintf(fp,"%s",buf); else fprintf(stdout,"%s",buf); 
  if(fp) fclose(fp);
  if(fname) {
    sprintf(buf,"dot %s -T%s > %s",dotname,ext,fname);
    if(0!=system(buf)) {printf("BNET.graphviz ERR1 : couldn't run %s\n",buf); return 0.0;}
  }
  return 1.0;
  ENDVERBATIM
}

DESTRUCTOR {
  VERBATIM
  freeboonet(BP);
  ENDVERBATIM
}

: all nodes in network set to starting states
PROCEDURE start () {
  tstep = 0
  VERBATIM
  startboonet(BP); 
  ENDVERBATIM
}

:** INITIAL
INITIAL {
  start()
}

: strvalid - return the index to be used for get/setnodevals
FUNCTION strvalid () {
  VERBATIM
  char *pname;
  static char *pnames[6] = {"state", "count", "knockout", "start", "sthresh", "scount"};
  int i;
  pname = gargstr(1);
  for(i=0;i<6;i++) {
    if(!strcmp(pname,pnames[i])) return i;
  }
  return -1;
  ENDVERBATIM
}

:** getscount(vec) - retrieves BNET scount into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getscount () {
  VERBATIM
  double *ps; int i; void *vs;
  if(!ifarg(1)) {
    printf("BNET.getscount(vec) - returns scount of each node in vec\n");
    return 0; 
  }
  vs = vector_arg(1);
  ps = vector_newsize(vs,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) ps[i] = (double) BP->pnodes[i].scount;
  ENDVERBATIM
}

:** setscount(vec) - sets vector vec into BNET node scount. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setscount () {
  VERBATIM
  double *ps; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.setscount(vec) - sets scount of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&ps)) != BP->numnodes ) {
    printf("BNET.setscount ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].scount = (int) ps[i];
  return 1.0;
  ENDVERBATIM
}

:** getsthresh(vec) - retrieves BNET sthresh into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getsthresh () {
  VERBATIM
  double *ps; int i; void *vs;
  if(!ifarg(1)) {
    printf("BNET.getsthresh(vec) - returns sthresh of each node in vec\n");
    return 0;
  }
  vs = vector_arg(1);
  ps = vector_newsize(vs,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) ps[i] = (double) BP->pnodes[i].sthresh;
  ENDVERBATIM
}

:** setsthresh(vec) - sets vector vec into BNET node sthresh. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setsthresh () {
  VERBATIM
  double *ps; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.setsthresh(vec) - sets sthresh of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&ps)) != BP->numnodes ) {
    printf("BNET.setsthresh ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].sthresh = (int) ps[i];
  return 1.0;
  ENDVERBATIM
}

:** getcount(vec) - retrieves BNET count into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getcount () {
  VERBATIM
  double *ps; int i; void *vs;
  if(!ifarg(1)) {
    printf("BNET.getcount(vec) - returns count of each node in vec\n");
    return 0;
  }
  vs = vector_arg(1);
  ps = vector_newsize(vs,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) ps[i] = (double) BP->pnodes[i].count;
  ENDVERBATIM
}

:** setcount(vec) - sets vector vec into BNET node counts. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setcount () {
  VERBATIM
  double *ps; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.setcount(vec) - sets count of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&ps)) != BP->numnodes ) {
    printf("BNET.setcount ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].count = (int) ps[i];
  return 1.0;
  ENDVERBATIM
}

:** getstate(vec) - retrieves BNET state into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getstate () {
  VERBATIM
  double *ps; int i; void *vs;
  if(!ifarg(1)) {
    printf("BNET.getstate(vec) - returns state of each node in vec\n");
    return 0;
  }
  vs = vector_arg(1);
  ps = vector_newsize(vs,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) ps[i] = (double) BP->pnodes[i].state;
  ENDVERBATIM
}

:** setstate(vec) - sets vector vec into BNET node states. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setstate () {
  VERBATIM
  double *ps; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.setstate(vec) - sets state of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&ps)) != BP->numnodes ) {
    printf("BNET.setstate ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].state = (int) ps[i];
  return 1.0;
  ENDVERBATIM
}

:** getstart(vec) - retrieves BNET start states into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getstart () {
  VERBATIM
  double *ps; int i; void *vs;
  if(!ifarg(1)) {
    printf("BNET.getstart(vec) - returns start state of each node in vec\n");
    return 0;
  }
  vs = vector_arg(1);
  ps = vector_newsize(vs,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) ps[i] = (double) BP->pnodes[i].start;
  ENDVERBATIM
}

:** setstart(vec) - sets vector vec into BNET node start states. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setstart () {
  VERBATIM
  double *ps; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.setstart(vec) - sets start state of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&ps)) != BP->numnodes ) {
    printf("BNET.setstart ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].start = (int) ps[i];
  return 1.0;
  ENDVERBATIM
}

:** setknockout(vec) - sets vector vec into BNET node knockout flag variables. each element indexed into the vector
: corresponds to the node with the given id.
FUNCTION setknockout () {
  VERBATIM
  double *pk; int i, sz;
  if(!ifarg(1)) {
    printf("BNET.knockout(vec) - sets knockout flag of each node in vec\n");
    return 0.0;
  }
  if( (sz = vector_arg_px(1,&pk)) != BP->numnodes ) {
    printf("BNET.knockout ERR0: vec.size(%d) != BNET.numnodes(%d)\n",sz,BP->numnodes);
    return 0.0;
  }
  for(i=0;i<BP->numnodes;i++) BP->pnodes[i].knockout = (int) pk[i];
  return 1.0;
  ENDVERBATIM
}

:** getknockout(vec) - retrieves BNET knockout flags into a vector. each element indexed into the vector
: corresponds to the node with the given id.
PROCEDURE getknockout () {
  VERBATIM
  double *pk; int i; void *vk;
  if(!ifarg(1)) {
    printf("BNET.getknockout(vec) - returns knockout flag of each node in vec\n");
    return 0;
  }
  vk = vector_arg(1);
  pk = vector_newsize(vk,BP->numnodes);
  for(i=0;i<BP->numnodes;i++) pk[i] = (double) BP->pnodes[i].knockout;
  ENDVERBATIM
}

: BNET.setnname(node id, string name) - set node name
FUNCTION setnname () {
  VERBATIM
  int id, sz; char *name;
  id = (int) *getarg(1);
  if(id < 0 || id >= BP->numnodes) {
    printf("BNET.setnname ERR0: invalid node index %d\n",id);
    return 0.0;    
  }
  name = gargstr(2);
  if(!(sz=strlen(name))) {
    printf("BNET.setnname ERR1: empty string\n");
    return 0.0;
  }
  if(BP->pnodes[id].name) free(BP->pnodes[id].name);
  if(!(BP->pnodes[id].name = (char*) malloc(sizeof(char) * (sz + 1)))) {
    printf("BNET.setnname ERR2: couldn't alloc mem for %s\n",name);
    return 0.0;
  }
  strcpy(BP->pnodes[id].name,name);
  return 1.0;
  ENDVERBATIM
}

: getnname(node id, string) - get node name
FUNCTION getnname () {
  VERBATIM
  int i, id, sz; char **pname, string[BUFSIZ];
  id = (int) *getarg(1);
  if(id < 0 || id >= BP->numnodes) {
    printf("BNET.getnname ERR0: invalid node index %d\n",id);
    return 0.0;    
  }
  if(!BP->pnodes[id].name || !(sz=strlen(BP->pnodes[id].name))) {
    printf("BNET.getnname ERR1: node %d has no name\n",id);
    return 0.0;
  }
  for(i=0;i<sz && i<BUFSIZ;i++) string[i] = BP->pnodes[id].name[i];
  if(i < BUFSIZ) string[i]=0;
  printf("Aname is %s, %s\n",BP->pnodes[id].name,string);
  pname = hoc_pgargstr(2);
  printf("Bname is %s, %s\n",BP->pnodes[id].name,string);
  hoc_assign_str(pname,string);
  return 1.0;
  ENDVERBATIM
}

: advance the BNET by a single iteration - called from a simulation loop
FUNCTION advancebn () {
  VERBATIM
  advanceboonet(BP);  
  tstep = tstep + 1;
  return tstep;
  ENDVERBATIM
}

FUNCTION advancebnfor () {
  VERBATIM
  int i, n;
  n = (int) *getarg(1);
  for(i=0;i<n;i++) advancebn();
  return tstep;
  ENDVERBATIM
}

FUNCTION numnodes () {
  VERBATIM
  return BP->numnodes;
  ENDVERBATIM
}

FUNCTION numrules () {
  VERBATIM
  return BP->nrules;
  ENDVERBATIM
}

: identifier for BNET
FUNCTION id () {
  VERBATIM
  return (double) BP->id;
  ENDVERBATIM
}