/* rung45dup.c — 4->5 in a real population, WITH duplication (the rung-2 copying capacity).
*
* rung45 stalled: specialists arose only by CONVERTING generalists (displacement) at fixed
* compartment size -> viability drops -> purged. Here we give the population DUPLICATION — the
* copying capacity that already emerged at rung 2 — so a generalist can spawn a SPECIALIST
* offspring without losing itself (the compartment GROWS). Specialization becomes an ADDITION
* (neutral), not a displacement. A size cost in selection then pressures shedding of redundant
* generalists once specialists cover the functions. Does division of labor now EMERGE in a real
* evolving population (not the idealized Metropolis search of bridge.c)?
*
* compartment = role counts n[0..F] (0 generalist, 1..F specialists), variable size.
* viability = min_k(n[k] + EGEN*n[0]) (need ALL functions). selection ~ viability/(1+cost*size).
* grow (specialists replicate faster) -> duplication mutation adds specialists -> divide -> select.
*
* build: cc -O2 -Wall -std=c11 -o rung45dup rung45dup.c -lm
*/
#include <math.h>
#include <stdio.h>
#define NCOMP 400
#define GEN 500
#define F 3
#define EGEN 0.2
#define SSPEC 0.30 /* specialist replication advantage */
#define DMAX 24
#define MUDUP 0.06 /* generalist offspring specializes (duplication + divergence) */
#define MULOSS 0.02 /* specialist offspring reverts to generalist */
#define SIZECOST 0.06 /* per-member cost in selection (favors efficient, small compartments) */
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 n[NCOMP][F+1];
static int dau[2*NCOMP][F+1]; static double cum[2*NCOMP];
static int sizeof_(const int*a){ int s=0; for(int k=0;k<=F;k++) s+=a[k]; return s; }
static double viab(const int*a){ double m=1e9; for(int k=1;k<=F;k++){ double c=a[k]+EGEN*a[0]; if(c<m)m=c; } return m; }
int main(void){
printf("=== RUNG 4->5 WITH DUPLICATION: does division of labor emerge in a real population? ===\n");
printf(" (rung45 without duplication stalled: spec frac stayed 0. Here specialization ADDS, not displaces.)\n\n");
for(int c=0;c<NCOMP;c++){ n[c][0]=6; for(int k=1;k<=F;k++) n[c][k]=0; } /* all generalist, size 6 */
RNG=0x9e3779b97f4a7c15ULL;
double w[F+1]; w[0]=1.0; for(int k=1;k<=F;k++) w[k]=1.0+SSPEC;
printf(" %-5s %10s %13s %9s %9s\n","gen","spec frac","all-F present","mean sz","viab");
for(int g=0;g<=GEN;g++){
if(g%50==0||g==GEN){
long tot=0,spec=0; int allF=0; double msz=0,mv=0;
for(int c=0;c<NCOMP;c++){ int s=sizeof_(n[c]); tot+=s; for(int k=1;k<=F;k++) spec+=n[c][k];
int have=0; for(int k=1;k<=F;k++) if(n[c][k]>0) have++; if(have==F) allF++;
msz+=s; mv+=viab(n[c]); }
printf(" %-5d %10.3f %12.1f%% %9.1f %9.2f\n",g,(double)spec/tot,100.0*allF/NCOMP,msz/NCOMP,mv/NCOMP);
}
if(g==GEN) break;
for(int c=0;c<NCOMP;c++){
int m[F+1]; for(int k=0;k<=F;k++) m[k]=n[c][k];
int s=sizeof_(m), target=2*s; if(target>DMAX) target=DMAX;
for(int i=s;i<target;i++){
double Z=0; for(int k=0;k<=F;k++) Z+=m[k]*w[k]; if(Z<1e-12)break;
double u=urand()*Z,acc=0; int r=0; for(;r<=F;r++){ acc+=m[r]*w[r]; if(u<=acc) break; } if(r>F)r=F;
int off=r;
if(r==0){ if(urand()<MUDUP) off=1+(int)(xs()%F); } /* duplicate + diverge */
else { if(urand()<MULOSS) off=0; } /* revert */
m[off]++;
}
for(int k=0;k<=F;k++){ int d1=0; for(int i=0;i<m[k];i++) if(urand()<0.5) d1++; dau[2*c][k]=d1; dau[2*c+1][k]=m[k]-d1; }
}
double tot=0;
for(int i=0;i<2*NCOMP;i++){ int sz=sizeof_(dau[i]); double weight=viab(dau[i])/(1.0+SIZECOST*sz); if(sz<1)weight=0; tot+=weight; cum[i]=tot; }
if(tot<=0){ for(int c=0;c<NCOMP;c++){ n[c][0]=1; for(int k=1;k<=F;k++) n[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++) n[c][k]=dau[lo][k]; }
}
printf("\n High spec frac + high all-F + viability up = division of labor EMERGED (rung 5) in a real\n");
printf(" population — bridged by duplication (the rung-2 copying capacity repurposed). Low = still stalled.\n");
return 0;
}