/* rung45.c — attempt rung 4 -> 5: does DIVISION OF LABOR emerge? (expected to STALL)
*
* Rung 5 (a differentiated cell) vs rung 4 (a bag of identical cooperators): the parts
* SPECIALIZE into complementary roles the whole needs, and become mutually dependent.
* Honest test: a compartment needs ALL F functions present to be viable; each replicator
* has a heritable ROLE (0 = generalist, does all functions at reduced efficiency; 1..F =
* specialist for one function). Specialists replicate FASTER (free-rider pressure toward
* monoculture). Start every compartment as identical generalists (rung 4) and watch whether
* stable specialist DIVISION OF LABOR emerges (rung 5) or stalls.
*
* Predicted stall: the free-rider problem recurs HARDER one level up — a specialist that
* drops functions out-replicates, so compartments either collapse to monoculture (lose a
* function -> die) or retreat to safe generalists (no division of labor = still rung 4).
* Stable specialist complements (rung 5) are the hard middle. We report WHERE it lands.
*
* build: cc -O2 -Wall -std=c11 -o rung45 rung45.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define NCOMP 500
#define GEN 400
#define D 12 /* compartment size */
#define F 3 /* functions (roles 1..F); role 0 = generalist */
#define SSPEC 0.30 /* specialist replication advantage (the free-rider pressure) */
#define EGEN 0.2 /* generalist efficiency per function */
#define MUR 0.05 /* role mutation rate */
static unsigned long long RNG;
static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; }
static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); }
static int binom(int n,double p){ int k=0; for(int i=0;i<n;i++) if(urand()<p) k++; return k; }
static int comp[NCOMP][F+1]; /* role counts per compartment */
static int dau[2*NCOMP][F+1]; static double cum[2*NCOMP];
static double viability(const int *n){
double v=1e9;
for(int k=1;k<=F;k++){ double cov=n[k]+EGEN*n[0]; if(cov<v) v=cov; } /* weakest-link: need ALL functions */
return v;
}
int main(void){
printf("=== RUNG 4 -> 5 ATTEMPT: does division of labor emerge? (expected to stall) ===\n");
printf(" start: every compartment = %d identical generalists (rung 4, undifferentiated)\n",D);
printf(" specialists replicate %.0f%% faster; a compartment needs all %d functions to be viable\n\n",SSPEC*100,F);
for(int c=0;c<NCOMP;c++){ comp[c][0]=D; for(int k=1;k<=F;k++) comp[c][k]=0; }
printf(" %-5s %10s %14s %12s\n","gen","spec frac","all-3-roles","viable");
for(int g=0;g<=GEN;g++){
if(g%40==0||g==GEN){
long tot=0,spec=0; int dol=0,via=0;
for(int c=0;c<NCOMP;c++){ int s=0; for(int k=0;k<=F;k++){ tot+=comp[c][k]; if(k>0){spec+=comp[c][k]; if(comp[c][k]>0)s++;} }
if(s==F) dol++; if(viability(comp[c])>0.001) via++; }
printf(" %-5d %10.3f %13.2f%% %11.2f%%\n",g,(double)spec/tot,100.0*dol/NCOMP,100.0*via/NCOMP);
}
if(g==GEN) break;
/* one generation */
double w[F+1]; w[0]=1.0; for(int k=1;k<=F;k++) w[k]=1.0+SSPEC;
for(int c=0;c<NCOMP;c++){
double Z=0; for(int k=0;k<=F;k++) Z+=comp[c][k]*w[k]; if(Z<1e-12)Z=1e-12;
double pr[F+1],acc=0; for(int k=0;k<=F;k++){ acc+=comp[c][k]*w[k]/Z; pr[k]=acc; }
int m[F+1]={0}; for(int i=0;i<2*D;i++){ double u=urand(); int r=0; while(r<F && u>pr[r]) r++;
if(urand()<MUR) r=(int)(xs()%(F+1)); m[r]++; }
for(int k=0;k<=F;k++){ int d1=binom(m[k],0.5); dau[2*c][k]=d1; dau[2*c+1][k]=m[k]-d1; }
}
double tot=0; for(int i=0;i<2*NCOMP;i++){ tot+=viability(dau[i]); cum[i]=tot; }
if(tot<=0){ for(int c=0;c<NCOMP;c++) for(int k=0;k<=F;k++) comp[c][k]=0; }
else for(int c=0;c<NCOMP;c++){ double r=urand()*tot; int lo=0,hi=2*NCOMP-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
for(int k=0;k<=F;k++) comp[c][k]=dau[lo][k]; }
}
printf("\n Read the endpoint: high 'spec frac' + high 'all-3-roles' + high 'viable' = division of\n");
printf(" labor EMERGED (rung 5). Retreat to generalists (low spec frac) or low viability =\n");
printf(" STALLED at rung 4 — which is the expected, honest result.\n");
return 0;
}