/* bookeep.c   24-11-93  bookeeping functions for the Tierra Simulator */
/* Tierra Simulator V4.0: Copyright (c) 1991, 1992 Tom Ray & Virtual Life */

#ifndef lint
static char sccsid[] = "@(#)bookeep.c	1.5 7/21/92";
#endif /* lint */

#include "license.h"
#include "tierra.h"
#include "extern.h"

#ifdef MEM_CHK
#include <memcheck.h>
#endif /* MEM_CHK */

I32s  SizSoup, SizCells, SizFreeMem, SizSl, SizSli = 0;
I32s  SizGl = 0, SizGli = 0, SizGen = 0;

void DivideBookeep(ce, nc)
    Pcells   ce, nc; /* ce = mother cell, nc = daughter cell */
{   I8s      same = 0;

#ifdef CM5
    I32u     GBPN ;
    char     buf[5000] ;
    int      i ;
    int	     flag ;

    extern int   NumGBPNBits ;
    extern int   GBPNMask ;
    extern int   NumGBPN ;
#endif


    LastDiv = InstExe;
    if (!ce->d.fecundity && !ce->d.mut && !ce->d.flaw)
    {   ce->d.d1.flags = ce->d.flags;    /* record metabolic data 1st repl */
        ce->d.d1.inst = ce->d.inst + 1;
        ce->d.d1.mov_daught = ce->d.mov_daught;
    }
    ce->d.fecundity++;
    nc->d.gen.size = nc->mm.s;
    if (GeneBnker)
    {   nc->d.genome = soup + nc->mm.p;
        if (ce->mm.s == nc->mm.s &&    /* if cell breeds true */
            IsSameGen(nc->mm.s, soup + nc->mm.p, soup + ce->mm.p))
        {   if (ce->d.fecundity == 1)
                nc->d.d1.BreedTrue = ce->d.d1.BreedTrue = 1;
            nc->d.parent = ce->d.parent;
            nc->d.gen = ce->d.gen;
            nc->d.gi = ce->d.gi;
            nc->d.hash = ce->d.hash;
            same = 1;
        }
        else     /* if daughter is a new genotype (same = 0) */
        {   nc->d.parent = ce->d.gen; /* this will assign a gen.label */
	    nc->d.hash = Hash(nc->d.gen.size,nc->d.genome) ;
#ifndef CM5
            CheckGenotype(&(nc->d), 17); /* by checking .gen files */
#else
	    GBPN = hashGB(nc->d.hash) ;
	    /* copy both the cell structure 
	       and the genome into a single message */
	    bcopy(&(nc->d),buf,sizeof(nc->d)) ;
	    bcopy(nc->d.genome,&buf[ALLIGN(Dem)],nc->d.gen.size) ;

	    flag = CMMD_send_and_receive(GBPN,
					 CMMD_ANY_TAG,
					 &(nc->d.gi),
					 sizeof(nc->d.gi),
					 GBPN,
					 CMMD_CheckGenotype_TAG + 17 + (1 << 11),
					 buf,
					 ALLIGN(Dem) + nc->d.gen.size) ;
#endif

            strcpy(nc->d.gen.label, Int2Lbl(nc->d.gi));
        }


	/* replace SizGen = DivGenBook(ce, nc, InstExe, reaped, 1, same, 0); */

#ifndef CM5
	DivGenBook(nc, InstExe, reaped, 0, same, 0);
	DivGenBook(ce, InstExe, reaped, 1, same, 0);
#else
	GBPN = hashGB(nc->d.hash) ;
	bcopy(nc,buf,sizeof(struct cell)) ;
	bcopy(&InstExe,&buf[ALLIGN(struct cell)],sizeof(InstExe)) ;
	CMMD_send_block(GBPN,
			CMMD_DivGenBook_NC_TAG + reaped + (same << 10),
			buf,
			ALLIGN(struct cell) + sizeof(InstExe)) ;

	GBPN = hashGB(ce->d.hash) ;
	CMMD_send_block(GBPN,
			CMMD_DivGenBook_CE_TAG + same,
			ce,
			sizeof(struct cell)) ;
#endif
       
    }
    ce->d.mov_daught = ce->d.mut = 0;
    OutDisk((I32s)'b', nc);
#if FRONTEND != STDIO
    FEStats(); 
#endif /* FRONTEND != STDIO */
}

void ReapBookeep(ce)
    Pcells  ce;
{   

#ifdef CM5
    I32u    GBPN ;
    char    buf[5000] ;
    I32s    hash ;
    int     i ;
    int     flag ;

    extern int   NumGBPNBits ;
    extern int   GBPNMask ;
    extern int   NumGBPN ;
#endif

    OutDisk((I32s)'d', ce);
    if (GeneBnker)
    {   
      /* replace SizGen = ReapGenBook(ce); */
#ifndef CM5
      ReapGenBook(ce);
#else
      GBPN = hashGB(ce->d.hash) ;
      flag = CMMD_send_block(GBPN,
			     CMMD_ReapGenBook_TAG,
			     ce,
			     sizeof(struct cell)) ;
#endif
    }

#ifdef MICRO
    if (ce == MicroSlice)
    {   MicroSlice = NULL;
        MicroCpu = NULL;
    }
#endif /*MICRO */
    InitCell(ce->q.this.a,ce->q.this.i,ce);
    NumCells--;
    reaped = 1;
}

void MutBookeep(i)
    I32s i;
{
    I8s    md;
    Pcells ce;

#ifdef CM5
    I32u     GBPN ;      
    char     buf[5000] ;
    I32s     hash ;
    int	     flag ;

    extern int   NumGBPNBits ;
    extern int   GBPNMask ;
    extern int   NumGBPN ;
#endif

    if (!GeneBnker || IsFree(i)) return;
    WhichCell(i, &ce, &md);
    if (md == 'd')
	{
	return;
	}

    /* replace SizGen = ReapGenBook(ce); */
#ifndef CM5
    ReapGenBook(ce) ;
#else
    GBPN = hashGB(ce->d.hash) ;
    flag = CMMD_send_block(GBPN,
			   CMMD_ReapGenBook_TAG,
			   ce,
			   sizeof(struct cell)) ;
#endif

    OutDisk((I32s)'d', ce);
    ce->d.parent = ce->d.gen;    /* assign new genotype */
    ce->d.gi = -1;
    strcpy(ce->d.gen.label, "---");

    ce->d.hash = Hash(ce->d.gen.size,ce->d.genome);/* new hash <- mutation */

#ifndef CM5    
    CheckGenotype(&(ce->d), 17); /* by checking .gen files */
#else
    GBPN = hashGB(ce->d.hash) ;
    bcopy(&(ce->d),buf,sizeof(ce->d)) ;
    bcopy(ce->d.genome,&buf[ALLIGN(ce->d)],ce->d.gen.size) ;
    flag = CMMD_send_and_receive(GBPN,
				 CMMD_ANY_TAG,
				 &(ce->d.gi),
				 sizeof(ce->d.gi),
				 GBPN,
				 CMMD_CheckGenotype_TAG + 17 + (1 << 11),
				 buf,
				 ALLIGN(ce->d) + ce->d.gen.size) ;
#endif

    strcpy(ce->d.gen.label, Int2Lbl(ce->d.gi));

/* replace SizGen = DivGenBook(NULL, ce, InstExe, reaped, 0, 0, 0); */

#ifndef CM5
	DivGenBook(ce, InstExe, reaped, 0, 0, 0) ;
#else
	GBPN = hashGB(ce->d.hash) ;
	CMMD_send_block(GBPN,
			CMMD_DivGenBook_NC_TAG + (reaped & 1),
			ce,
			sizeof(struct cell)) ;
#endif

    OutDisk((I32s)'b', ce);
    ce->d.d1.flags = ce->d.d1.mov_daught = 0L;
    ce->d.fecundity = ce->d.flags = 0L;
    ce->d.d1.inst = ce->d.inst = 0L;
    ce->d.mut++;

}

void OutDisk(bd, nc)
    I32s bd;
    Pcells nc;
{   I32s ttime;
    I8s label[4];

    if (DiskOut)
    {   if (FirstOutDisk)
        {   FirstOutDisk = 0;
            BrkupCum = 0;
            BrkupCou = 1;
#ifdef IBM3090
            if (BrkupSiz)
                sprintf(Buff, "break.1.d");
            else sprintf(Buff, "tierra.run.d");
            oufr = fopen(Buff, "w");
#else /* IBM3090 */
            if (BrkupSiz)
                sprintf(Buff, "%sbreak.1", OutPath);
            else sprintf(Buff, "%stierra.run", OutPath);
            oufr = fopen(Buff, "w");
#endif /* IBM3090 */
            if (oufr == NULL)
            {   FEError(-108,EXIT,NOWRITE, 
                   "Tierra OutDisk() 1: file %s not opened, exiting\n", Buff);
            }
            sprintf(label, nc->d.gen.label);
#ifdef IBM3090
            Ascii2Ebcdic(label);
#endif /* IBM3090 */
            BrkupCum += fprintf(oufr, "%lx %c %ld", InstExe.i, (I8s) bd,
                nc->d.gen.size);
            if (GeneBnker)
                BrkupCum += 1 + fprintf(oufr, " %s\n", label);
            else BrkupCum += 1 + fprintf(oufr, "\n");
        }
        else
        {   ttime = InstExe.i - lo.time;
            if (ttime < 0)
                ttime += 1000000L;
            BrkupCum += fprintf(oufr, "%lx", ttime);
            if (lo.bd != bd)
                BrkupCum += fprintf(oufr, " %c", bd);
            if (lo.size != nc->d.gen.size)
                BrkupCum += fprintf(oufr, " %ld", nc->d.gen.size);
            if (GeneBnker && strcmp(lo.label, nc->d.gen.label))
            {   sprintf(label, nc->d.gen.label);
#ifdef IBM3090
                Ascii2Ebcdic(label);
#endif /* IBM3090 */
                BrkupCum += fprintf(oufr, " %s", label);
            }
            BrkupCum += 1 + fprintf(oufr, "\n");
            if (BrkupSiz && BrkupCum > BrkupSiz * 1024L)
            {   fclose(oufr);
                BrkupCum = 0;
                BrkupCou++;
#ifdef IBM3090
                sprintf(Buff, "break.%ld.d", BrkupCou);
                oufr = fopen(Buff, "w");
#else /* IBM3090 */
                sprintf(Buff, "%sbreak.%ld", OutPath, BrkupCou);
                oufr = fopen(Buff, "w");
#endif /* IBM3090 */
                if (oufr == NULL)
                {   FEError(-109,EXIT,WRITE,
                   "Tierra OutDisk() 2: file %s not opened, exiting\n", Buff);
                }
            }
        }
    }
    else
    {   if (FirstOutDisk) FirstOutDisk = 0;
        else
        {   ttime = InstExe.i - lo.time;
            if (ttime < 0) ttime += 1000000L;
        }
    }
    lo.bd = bd;
    lo.size = nc->d.gen.size;
    lo.time = InstExe.i;
    strcpy(lo.label, nc->d.gen.label);
    TimePop += (double) ttime *(double) NumCells;
    if ((I8s) bd == 'b')
        TimeBirth++;
    else TimeDeath++;
}

void plan()
{   I32s pop_gen_time;
    double prob_of_hit;

    if (reaped)
        CellsGarbageCollect(); /* call this function only on Tierra nodes */

    /* begin adjust control variables */

    if (GenPerMovMut)
        RateMovMut = (I32s) 2L * GenPerMovMut * AverageSize * PLOIDY;
    if (JmpSouTra)
        RateJmpSou = (I32s) (2 * JmpSouTra * AverageSize);
    if (InstExe.m)
        pop_gen_time = TimePop * TimeGenIndiv;
    else
    {   TimeGenIndiv = 10L * AverageSize;
        pop_gen_time = TimeGenIndiv * (SoupSize / (4L * AverageSize));
    }
    prob_of_hit = (double) AverageSize / (double) SoupSize;
    if (GenPerBkgMut)
        RateMut = (I32s) (pop_gen_time * 2L * GenPerBkgMut * prob_of_hit);
    if (GenPerFlaw)
        RateFlaw = (I32s) TimeGenIndiv * GenPerFlaw * 2L;
    if (DropDead) DropDead = 1L + AverageSize / 80L;  /* DAN */
    Search_limit = (I32s) (SearchLimit * AverageSize);
    Put_limit = (I32s) (PutLimit * AverageSize);
    MalLimit = MalTol * AverageSize;
    if (MalLimit >= SoupSize)
        MalLimit = SoupSize - 1;

    /* end adjust control variables */

#ifndef CM5
    FEPlan();
#ifdef MEM_PROF
    MemoryProfile();
    FEMemProf(SizSoup, SizCells, SizFreeMem, SizSl, SizSli,
        SizGl, SizGli, SizGen);
#endif /* MEM_PROF */
#endif /* CM5 */

    TimePop = 0.; /* these are used in CalcSoupStats() and FEPlan() */
    TimeBirth = TimeDeath = 0L;
    TimeStats = InstExe;

}

void stats()
{

#ifdef CM5
    CalcAverages() ;
#else /* CM5 */
    if (GeneBnker)
    {   if (reaped)
        {   GBGarbageCollect();
#ifdef ERROR
            VerifyGB();
#endif /* ERROR */
        }
        CalcGBStats(sl, siz_sl); 
    }
    else
        CalcSoupStats();
#endif /* CM5 */

    CalcTimeSoup(); /* call this function only on Tierra nodes */
}

void CalcTimeSoup()
{   Event  TimeSince;
    double  AvgBD, dt;

#ifdef CM5
    extern HostTNdata TNdata ;
#endif /* CM5 */

    if (InstExe.m)
    {   dt = (double) SubEvent(&InstExe, &TimeStats, &TimeSince);
        AvgBD =  (double) (TimeBirth + TimeDeath) / 2.;
        TimeGenIndiv = (I32s) (dt / AvgBD);
#ifdef CM5
	TNdata.TimePop = TimePop ;
	TNdata.dt = dt ;
	TNdata.TimeBirth = TimeBirth ;
	TNdata.TimeDeath = TimeDeath ;
#endif /* CM5 */
        TimePop /= (double) dt; /* average population over time-interval */
        Generations += AvgBD / TimePop;
    }
}

void CalcAverages() /* call this function only on Tierra nodes */
{   I32s    tNumCells = 0, ar, ci;
    Pcells  ce;

    AverageSize = 0;
    for (ar = 0; ar < NumCelAr; ar++) for (ci = 0; ci < CelArSiz; ci++)
    {   if (ar == 0 && ci < 2)
            continue;
        ce = &cells[ar][ci];
        if (ce->ld)
        {   tNumCells++;
            AverageSize += ce->d.gen.size;
        }
    }
#ifdef ERROR
    if (tNumCells != NumCells)
    {   FEError(-130,EXIT,NOWRITE,
            "Tierra CalcAverages() NumCells = %ld  count of cells = %ld\n",
            NumCells, tNumCells);
    }
#endif /* ERROR */
    AverageSize /= tNumCells;
}

void CalcSoupMaxes() /* call this function only on Tierra nodes */
/* This function builds a local soup genebank from a scan of the soup */
{   I32s  lsiz_sl = 1;
    SList  Fp Fp  lsl;

    lsl = MkGBFromSoup(&lsiz_sl);
    CalcGBMaxes(lsl, lsiz_sl);
    FreeGB(lsl, lsiz_sl);
}

void CalcSoupStats() /* call this function only on Tierra nodes */
/* This function builds a local soup genebank from a scan of the soup */
{   I32s  lsiz_sl = 1;
    SList  Fp Fp  lsl;

    lsl = MkGBFromSoup(&lsiz_sl);
    CalcGBStats(lsl, lsiz_sl);
    FreeGB(lsl, lsiz_sl);
}

void CalcGBStats(lsl, lsiz_sl)
SList Fp Fp  lsl;
I32s  lsiz_sl;
{   I32s  si, gi, pop, mem, tNumCells;
    GList Fp  pgl;

#ifdef CM5
    extern HostGBdata GBdata ;
#endif /* CM5 */

    tNumCells = AverageSize = MaxPop = MaxMem = 0;
    for (si = lsiz_sl - 1; si >= 0; si--) /* each size class */
    {   if (lsl[si] && lsl[si]->num_c)
        {   if (GeneBnker)
            for (gi = lsl[si]->a_num - 1; gi >= 0; gi--) /* each genotype */
            {   if (((I32u) (pgl = lsl[si]->g[gi]) > 4)
                    && (pop = pgl->pop))
                {   mem = pop * si;
                    if (pop > MaxPop)
                    {   MaxPop = pop;
                        MaxGenPop.size = si;
                        strcpy(MaxGenPop.label, Int2Lbl(gi));
                    }
                    if (mem > MaxMem)
                    {   MaxMem = mem;
                        MaxGenMem.size = si;
                        strcpy(MaxGenMem.label, Int2Lbl(gi));
                    }
                    AverageSize += mem;
                    tNumCells += pop;
                }
            }
            else
            {   tNumCells += lsl[si]->num_c;
                AverageSize += lsl[si]->num_c * si;
            }
        }
    }
#ifndef CM5
    if (tNumCells != NumCells)
    {   FEError(-130,EXIT,NOWRITE,
            "Tierra CalcGBStats() NumCells = %ld  count of cells = %ld\n",
            NumCells, tNumCells);
    }
#endif /* CM5 */

    AverageSize /= tNumCells;

#ifdef CM5
    GBdata.tNumCells = tNumCells ;
#endif /* CM5 */

}

void CalcGBMaxes(lsl, lsiz_sl)
SList Fp Fp  lsl;
I32s  lsiz_sl;
{   I32s  si, gi, pop, mem;
    GList Fp  pgl;

    MaxPop = MaxMem = 0;
    for (si = lsiz_sl - 1; si >= 0; si--) /* each size class */
    {   if (lsl[si] && lsl[si]->num_c)
        {   for (gi = lsl[si]->a_num - 1; gi >= 0; gi--) /* each genotype */
            {   if (((I32u) (pgl = lsl[si]->g[gi]) > 4)
                    && (pop = pgl->pop))
                {   mem = pop * si;
                    if (pop > MaxPop)
                    {   MaxPop = pop;
                        MaxGenPop.size = si;
                        strcpy(MaxGenPop.label, Int2Lbl(gi));
                    }
                    if (mem > MaxMem)
                    {   MaxMem = mem;
                        MaxGenMem.size = si;
                        strcpy(MaxGenMem.label, Int2Lbl(gi));
                    }
                }
            }
        }
    }
}

void FreeGB(lsl, lsiz_sl)
SList Fp Fp lsl;
I32s lsiz_sl;
{   I32s  si;
    I16s  gi;

    if (!GeneBnker)
        return;

    for (si = 0; si < lsiz_sl; si++) /* for each size */
    {   if (lsl[si] && lsl[si]->num_c && lsl[si]->g)
        {   for (gi = 0; gi < lsl[si]->a_num; gi++)
            {   if ((I32u) (lsl[si]->g[gi]) > 4)
                {   tfree(lsl[si]->g[gi]);
                    lsl[si]->g[gi] = NULL;
                }
            }
            tfree(lsl[si]->g);
            lsl[si]->g = NULL;
            tfree(lsl[si]);
            lsl[si] = NULL;
        }
    }
    tfree(lsl);
    lsl = NULL;
}

SList Fp Fp MkGBFromSoup(lsiz_sl)
I32s * lsiz_sl;
{   I32s  si, ar, ci;
    I16s  gi;
    Pcells ce;
    SList  Fp Fp lsl;

    *lsiz_sl = 1;
    lsl = (SList Fp Fp) tcalloc(1, sizeof(SList Fp));
    for (ar = 0; ar < NumCelAr; ar++) for (ci = 0; ci < CelArSiz; ci++)
    {   if (ar == 0 && ci < 2)
            continue;
        ce = &cells[ar][ci];
        if (ce->ld)
        {   si = ce->d.gen.size;
            gi = ce->d.gi;
            if (si >= *lsiz_sl)
            {   lsl = (SList Fp Fp) trecalloc(lsl,
                    (si + 1) * sizeof(SList Fp), *lsiz_sl * sizeof(SList Fp));
                *lsiz_sl = si + 1;
            }
            if (!lsl[si])
            {   lsl[si] = (SList Fp) tcalloc(1, sizeof(SList));
                if (GeneBnker)
                {   lsl[si]->g =
                        (GList Fp Fp) tcalloc(gi + 1, sizeof(GList Fp));
                    lsl[si]->a_num = gi + 1;
                }
            }
#ifdef ERROR
            if (!lsl[si]->num_c)
            {   if (lsl[si]->num_g)
                    FEError(-115,NOEXIT,NOWRITE,
                "Tierra MkGBFromSoup() !lsl[si]->num_c but lsl[si]->num_g\n");
            }
#endif /* ERROR */
            lsl[si]->num_c++;
            if (GeneBnker)
            {   if (gi >= lsl[si]->a_num)
                {   lsl[si]->g = (GList Fp Fp) trecalloc(lsl[si]->g,
                        (gi + 1) * sizeof(GList Fp),
                        lsl[si]->a_num * sizeof(GList Fp));
                    lsl[si]->a_num = gi + 1;
                }
                if ((I32u) lsl[si]->g[gi] < 4)
                {   lsl[si]->g[gi] = (GList Fp) tcalloc(1, sizeof(GList));
                    lsl[si]->num_g++;
                }
                lsl[si]->g[gi]->pop++;
            }
        }
    }
    return lsl;
}

void CellsGarbageCollect()
{   I32s  ar, ci;
    I8s   InUse;
    Pcells Fp  tcells;

    for(ar = NumCelAr - 1; ar > 0; ar--)
    {   InUse = 0;
        for (ci = 0; ci < CelArSiz; ci++)
        {   if (ar == 0 && ci < 2)
                continue;
            else if (cells[ar][ci].ld)
            {   InUse = 1;
                break;
            }
        }
        if (InUse)
            break;
        if (cells[ar])
        {   tfree(cells[ar]);
            cells[ar] = NULL;
        }
        NumCelAr--;
        tcells = (Pcells Fp) trecalloc((Pcells Fp) cells,
            (I32u) NumCelAr * sizeof(Pcells Fp),
            (I32u) (NumCelAr + 1) * sizeof(Pcells Fp));
        if (tcells)
            cells = tcells;
        else if (cells)
        {   tfree(cells);
            cells = NULL;
         FEError(-129,EXIT,WRITE,
             "Tierra CellsGarbageCollect() cells trecalloc error\n");
        }
        CellsSize = NumCelAr * CelArSiz;
    }
}

#ifdef MEM_PROF

void MemoryProfile()
{   I32s  i, j;

/* this is done on the Tierra nodes: */

    TotMemUse = SizSoup = SoupSize * sizeof(Instruction);
    TotMemUse += SizCells = CellsSize * sizeof(struct cell);
    TotMemUse += SizFreeMem = MaxFreeBlocks * sizeof(MemFr);

/* this is done on the genebank nodes: */

    if(GeneBnker)
    {   TotMemUse += SizSl = siz_sl * sizeof(SList Fp);
        for (i = 0; i < siz_sl; i++)
        {   if (sl[i])
            {   TotMemUse += sizeof(SList);
                SizSli += sizeof(SList);
                TotMemUse += sl[i]->a_num * sizeof(GList Fp);
                SizGl += sl[i]->a_num * sizeof(GList Fp);
                for (j = 0; j < sl[i]->a_num; j++)
                {   if ((I32s) sl[i]->g[j] > 4)
                    {   TotMemUse += sizeof(GList);
                        SizGli += sizeof(GList);
                        if (sl[i]->g[j]->genome)
                        {   TotMemUse += i * sizeof(Instruction);
                            SizGen += i * sizeof(Instruction);
                        }
                        if (sl[i]->g[j]->gbits)
                        {   TotMemUse += i * sizeof(GenBits);
                            SizGen += i * sizeof(GenBits);
                        }
                    }
                }
            }
        }
    }
}

#endif /* MEM_PROF */

I16s Lbl2Int(s)
    I8s *s;
{
#ifdef CM5
    I32s  index ;

    if (s[0] == '-')
    return -1;
    if (s[0] >= 'a')
      index = s[0] - 'a' ;
    else
      index = 26 + s[0] - 'A' ;
    if (s[1] >= 'a')
      index += (s[1] - 'a') * 52  ;
    else
      index += (26 + s[1] - 'A') * 52  ;
    if (s[2] >= 'a')
      index += (s[2] - 'a') * 52 * 52 ;
    else
      index += (26 + s[2] - 'A') * 52 * 52 ;
    return index ;

#else /* CM5 */
   
    if (s[0] == '-')
    return -1;
    return (s[2] - 'a') + (26 * (s[1] - 'a')) + (676 * (s[0] - 'a'));

#endif /* CM5 */
}

I8s *Int2Lbl(i)
    I32s i;
{
    static I8s s[4];

#ifdef CM5
    I32s index ;
    I32s gb_number = (CMMD_self_address() / (CMMD_partition_size() / NumGBPN));

    if (i < 0) {
    strcpy(s, "---");
    return s;
    }

    i = i * NumGBPN + gb_number ;

    index = i / (52 * 52) ;
    if (index >= 26)
      s[0] = 'A' + index - 26;
    else
      s[0] = 'a' + index ;
    i %= (52 * 52) ;
    index = i / 52 ;
    if (index >= 26)
      s[1] = 'A' + index - 26;
    else
      s[1] = 'a' + index ;
    i %= 52 ;
    index = i ;
    if (index >= 26)
      s[2] = 'A' + index - 26;
    else
      s[2] = 'a' + index ;
    s[3] = 0;
    return s;

#else /* CM5 */

    if (i < 0) {
    strcpy(s, "---");
    return s;
    }
    s[0] = 'a' + (I16s) i / 676;
    i %= 676;
    s[1] = 'a' + (I16s) i / 26;
    i %= 26;
    s[2] = 'a' + (I16s) i;
    s[3] = 0;
    return s;

#endif /* CM5 */
}

#ifdef JUNK /* old versions */

I16s Lbl2Int(s)
    I8s *s;
{
    if (s[0] == '-')
    return -1;
    return (s[2] - 'a') + (26 * (s[1] - 'a')) + (676 * (s[0] - 'a'));
}

I8s *Int2Lbl(i)
    I32s i;
{
    static I8s s[4];

    if (i < 0) {
    strcpy(s, "---");
    return s;
    }
    s[0] = 'a' + (I16s) i / 676;
    i %= 676;
    s[1] = 'a' + (I16s) i / 26;
    i %= 26;
    s[2] = 'a' + (I16s) i;
    s[3] = 0;
    return s;
}

#endif /* JUNK */

I32u WhoIs(ce, a)
    Pcells  Fp ce;
    I32s a;
{
    I8s md;

    if (a >= (*ce)->mm.p && a < (*ce)->mm.p + (*ce)->mm.s)
        return 0;          /* same cell */
    if (a >= (*ce)->md.p && a < (*ce)->md.p + (*ce)->md.s)
        return 1;          /* daughter cell */
    if (IsFree(a))
        return 3;          /* is free memory */
    WhichCell(a, ce, &md);
    if (md == 'm')
        return 2;          /* is other cell */
    return 4;              /* is the daughter of another cell */
}

I8s IsSameGen(size, g1, g2)/* compare two genomes */
    I32s size;
    FpInst g1, g2;
{
    I32s i, j;

    for (i = 0; i < size; i++) 
#if PLOIDY == 1
        if (*(g1 + i) != *(g2 + i))
#else /* PLOIDY > 1 */
        for (j = 0; j < PLOIDY; j++)
            if (g1[i][j] != g2[i][j])
#endif /* PLOIDY > 1 */
            return 0;
    return 1;
}
