/* gard.c — does HEREDITY emerge from an autocatalytic set? (rung 1 -> rung 2 as emergence)
*
* NO replicator, NO templating, NO heredity is coded. Given: a random catalytic chemistry
* (molecules catalyze each other's entry into an assembly) + a splitting rule. Observed:
* whether a lineage's COMPOSITION is transmitted across splits on its own (compositional
* inheritance = the composome/GARD idea, Segre-Lancet).
*
* Mechanism that would MAKE it emergent (not designed): if the catalytic dynamics have
* multiple attractor compositions ("composomes"), then a split perturbs the composition but
* autocatalytic REGROWTH pulls the daughter back to the parent's attractor -> the composition
* breeds true. Heredity without a genome.
*
* Strict test (no answer encoded):
* 1. how many DISTINCT mature compositions do random founders fall into? (>1 = heritable variety)
* 2. after split + regrow, is cosine(parent, daughter) >> cosine(parent, random assembly)?
* (composition transmitted through the split = heredity EMERGED)
* Honest prior: heredity may emerge but be LIMITED (Vasas-Szathmary: composomes low-evolvability).
* Report whatever happens, including a null.
*
* build: cc -O2 -Wall -std=c11 -o gard gard.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#define N 40 /* molecule types */
#define S 200 /* assembly size (molecules) */
#define MAT 6000 /* maturation steps (join/leave to reach attractor) */
#define PCAT 0.15 /* catalytic-network density */
#define RHO0 0.15 /* weaker basal -> autocatalysis dominates -> sharper, multiple attractors */
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 beta[N][N]; /* beta[i][j] = catalysis of type-i entry by present type j */
static double rate[N]; /* rate[i] = RHO0 + sum_j beta[i][j]*n[j], maintained incrementally */
static void build_chem(void){ /* the GIVEN random chemistry */
for(int i=0;i<N;i++) for(int j=0;j<N;j++) beta[i][j]=(urand()<PCAT)? exp(2.5*randn())*0.30 : 0.0;
}
static void recompute_rate(const int*n){ for(int i=0;i<N;i++){ double r=RHO0; for(int j=0;j<N;j++) r+=beta[i][j]*n[j]; rate[i]=r; } }
static int pick_add(void){ double tot=0; for(int i=0;i<N;i++) tot+=rate[i]; double u=urand()*tot,a=0; for(int i=0;i<N;i++){ a+=rate[i]; if(u<=a) return i; } return N-1; }
static int pick_rem(const int*n,int size){ double u=urand()*size,a=0; for(int j=0;j<N;j++){ a+=n[j]; if(u<=a) return j; } return N-1; }
static void addmol(int*n,int i){ n[i]++; for(int k=0;k<N;k++) rate[k]+=beta[k][i]; }
static void remmol(int*n,int j){ n[j]--; for(int k=0;k<N;k++) rate[k]-=beta[k][j]; }
/* mature an assembly of given size to its attractor composition (constant-size join/leave) */
static void mature(int*n,int size){ recompute_rate(n);
for(int t=0;t<MAT;t++){ int j=pick_rem(n,size); remmol(n,j); int i=pick_add(); addmol(n,i); }
}
static void grow_to(int*n,int*size,int target){ recompute_rate(n);
while(*size<target){ int i=pick_add(); addmol(n,i); (*size)++; }
}
static double cosine(const int*a,const int*b){ double d=0,na=0,nb=0; for(int i=0;i<N;i++){ d+=(double)a[i]*b[i]; na+=(double)a[i]*a[i]; nb+=(double)b[i]*b[i]; } if(na<=0||nb<=0)return 0; return d/(sqrt(na)*sqrt(nb)); }
int main(void){
RNG=0x9e3779b97f4a7c15ULL; build_chem();
/* --- founders: mature random assemblies, count distinct composomes --- */
#define K 50
static int fnd[K][N];
for(int k=0;k<K;k++){ int n[N]; memset(n,0,sizeof n); for(int s=0;s<S;s++){ n[(int)(xs()%N)]++; } mature(n,S); memcpy(fnd[k],n,sizeof n); }
int nclust=0; int rep[K];
for(int k=0;k<K;k++){ int found=0; for(int c=0;c<nclust;c++) if(cosine(fnd[k],fnd[rep[c]])>0.85){found=1;break;} if(!found) rep[nclust++]=k; }
/* --- heredity test: split a matured parent, regrow the daughter, compare compositions --- */
double sum_pd=0,sum_pr=0; int trials=200;
for(int tr=0;tr<trials;tr++){
int P[N]; memset(P,0,sizeof P); for(int s=0;s<S;s++) P[(int)(xs()%N)]++; mature(P,S);
int D[N]; memset(D,0,sizeof D); int ds=0; /* split: each molecule -> daughter w.p. 0.5 */
for(int i=0;i<N;i++) for(int c=0;c<P[i];c++) if(urand()<0.5){ D[i]++; ds++; }
if(ds<1){ D[0]=1; ds=1; }
grow_to(D,&ds,S); mature(D,S); /* autocatalytic regrowth + maturation */
int R[N]; memcpy(R,fnd[(int)(xs()%K)],sizeof R); /* a random matured assembly */
sum_pd += cosine(P,D); sum_pr += cosine(P,R);
}
double pd=sum_pd/trials, pr=sum_pr/trials;
printf("=== HEREDITY FROM AN AUTOCATALYTIC SET (rung1 -> rung2, emergence test) ===\n");
printf(" chemistry: %d molecule types, random catalytic network (density %.2f). NOTHING about\n",N,PCAT);
printf(" heredity/replication is coded — only chemistry + splitting.\n\n");
printf(" distinct composomes among %d random founders : %d\n",K,nclust);
printf(" parent<->daughter composition similarity : %.3f (after split + autocatalytic regrow)\n",pd);
printf(" parent<->random composition similarity : %.3f (baseline)\n",pr);
printf(" transmission signal (pd - pr) : %+.3f\n\n",pd-pr);
if(pd-pr>0.15 && nclust>1)
printf(" => HEREDITY EMERGED: composition is transmitted across splits (daughters resemble\n parents far more than random), with %d heritable composome types. No genome coded.\n",nclust);
else if(pd-pr>0.05)
printf(" => WEAK/LIMITED heredity: some transmission, but composomes are few/blurred\n (the Vasas-Szathmary limit). Honest partial result.\n");
else
printf(" => NO heredity emerged: daughters no more like parents than random. Autocatalysis\n alone did not transmit composition here. Honest null.\n");
return 0;
}