/* flagella.c — the flagellation-constraint bridge for 4.5 -> 5 (germ-soma differentiation).
*
* Volvocine biology: a cell can't swim (motility/viability) and divide (reproduction/fecundity)
* at once (flagellar basal bodies become the spindle). As colonies GROW, motility demand rises
* superlinearly, so the tradeoff between the two functions becomes CONVEX. Michod's result:
* division of labor (germ vs soma) beats generalists exactly when the viability<->fecundity
* tradeoff is CONVEX. So the constraint doesn't bridge OVER the valley — at large size it
* REGRADES the landscape until the valley dissolves and differentiation flows downhill.
*
* Model: colony of cells, each allocates a in [0,1] to reproduction, (1-a) to motility.
* cell fecundity = a^beta, viability = (1-a)^beta. Colony fitness = (sum viability)*(sum fecundity)
* (need BOTH). beta = tradeoff convexity, RISING with size (the flagellation constraint).
* Evolve the colony's cell-allocations; sweep beta. Prediction: beta<1 (small colony, concave) ->
* all cells generalist (a~0.5, no DoL); beta>1 (large colony, convex) -> cells SPLIT into
* germ (a~1) and soma (a~0) = division of labor EMERGES. Crossing 4.5 -> 5, driven by the constraint.
*
* build: cc -O2 -Wall -std=c11 -o flagella flagella.c -lm
*/
#include <math.h>
#include <stdio.h>
#define POP 120
#define NCELL 12
#define GEN 300
#define ELITE 30
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 double randn(void){ double u=urand(),v=urand(); if(u<1e-300)u=1e-300; return sqrt(-2*log(u))*cos(6.283185307*v); }
static double a[POP][NCELL], na[POP][NCELL], fit[POP]; static int idx[POP];
static int cmp(const void*x,const void*y){ double d=fit[*(const int*)y]-fit[*(const int*)x]; return d<0?-1:d>0?1:0; }
static double colony_fit(const double *c,double beta){
double V=0,B=0;
for(int i=0;i<NCELL;i++){ double x=c[i]; if(x<0)x=0; if(x>1)x=1; V+=pow(1-x,beta); B+=pow(x,beta); }
return V*B;
}
int main(void){
printf("=== FLAGELLATION-CONSTRAINT BRIDGE (4.5 -> 5: germ-soma differentiation) ===\n");
printf(" beta = tradeoff convexity (rises with colony size). Evolve cell allocations.\n\n");
printf(" %-8s %-10s %-14s %s\n","beta","specialized","germ+soma both?","evolved cell allocations");
double betas[9]={0.6,0.8,0.9,1.0,1.1,1.3,1.6,2.0,2.5};
for(int bi=0;bi<9;bi++){
double beta=betas[bi];
RNG=0x9e3779b97f4a7c15ULL + (unsigned long long)bi*0x2545F4914F6CDD1DULL;
for(int p=0;p<POP;p++) for(int i=0;i<NCELL;i++) a[p][i]=urand();
for(int g=0;g<GEN;g++){
for(int p=0;p<POP;p++){ fit[p]=colony_fit(a[p],beta); idx[p]=p; }
qsort(idx,POP,sizeof(int),cmp);
for(int p=0;p<POP;p++){ int par=idx[(int)(xs()%ELITE)];
for(int i=0;i<NCELL;i++){ double v=a[par][i]+0.08*randn(); if(v<0)v=0; if(v>1)v=1; na[p][i]=v; } }
for(int i=0;i<NCELL;i++) na[0][i]=a[idx[0]][i]; /* elitism */
for(int p=0;p<POP;p++) for(int i=0;i<NCELL;i++) a[p][i]=na[p][i];
}
for(int p=0;p<POP;p++){ fit[p]=colony_fit(a[p],beta); idx[p]=p; } qsort(idx,POP,sizeof(int),cmp);
double *best=a[idx[0]];
int nsoma=0,ngerm=0,nspec=0;
for(int i=0;i<NCELL;i++){ if(best[i]<0.2){nsoma++;nspec++;} else if(best[i]>0.8){ngerm++;nspec++;} }
char alloc[128]={0}; int pos=0;
for(int i=0;i<NCELL;i++) pos+=snprintf(alloc+pos,sizeof(alloc)-pos,"%.1f ",best[i]);
printf(" %-8.2f %2d/%d %-6s %-14s %s\n",beta,nspec,NCELL,"",(nsoma>0&&ngerm>0)?"YES (division of labor)":"no",alloc);
}
printf("\n beta < 1 (small colony, concave tradeoff): cells stay ~0.5 = generalists (no DoL).\n");
printf(" beta > 1 (large colony, convex tradeoff): cells split into germ (~1) and soma (~0) —\n");
printf(" division of labor EMERGES. The flagellation constraint regrades the landscape so the\n");
printf(" 4.5->5 valley DISSOLVES; differentiation flows downhill. That is how Pleodorina got soma.\n");
return 0;
}