/* rung1.c — the autocatalytic set (RAF): self-producing closure ignites at a threshold.
*
* Rung 1 of the ladder, built on the corrected principle: GIVEN lawful rules (a random
* catalytic reaction network) + EMERGENT coherence (the largest self-sustaining subset
* ignites at a critical catalysis density — a percolation transition, the "laser" of
* self-production). The structure (reactions) is supplied; the AGENT (the RAF) emerges.
*
* A RAF (Reflexively Autocatalytic, Food-generated set; Kauffman; Hordijk & Steel) is a
* subset of reactions R such that every reaction in R is (a) SUPPORTED — its reactants
* are producible from the food set F using R, and (b) CATALYZED by a molecule producible
* from F using R. The maxRAF = prune-to-fixpoint: repeatedly drop reactions that aren't
* supported+catalyzed given what's currently producible.
*
* Sweep the catalysis probability p. Below a critical p the maxRAF is empty (no self-
* production — dies); above it a system-spanning RAF snaps on. That threshold is the
* coherence of self-production — the minimal bootstrap, condensing out of GIVEN chemistry.
*
* build: cc -O2 -Wall -std=c11 -o rung1 rung1.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 60 /* molecule types */
#define NF 8 /* food types (0..NF-1, always available) */
#define M 300 /* reactions (2 reactants -> 1 product) */
#define INST 60 /* random network instances averaged per p */
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 rint_(int n){ return (int)(xs()%(unsigned)n); }
static int r1[M],r2[M],prod[M];
static unsigned char cat[M][N]; /* cat[j][m]=1 : molecule m catalyzes reaction j */
static int active[M];
static unsigned char W[N];
/* build a random catalytic reaction network; each molecule catalyzes each reaction w.p. p */
static void build_net(double p){
for(int j=0;j<M;j++){
r1[j]=rint_(N); r2[j]=rint_(N);
prod[j]=NF+rint_(N-NF); /* products are non-food (must be made) */
for(int m=0;m<N;m++) cat[j][m]=(urand()<p)?1:0;
}
}
/* maxRAF size (number of reactions) via prune-to-fixpoint */
static int max_raf(void){
for(int j=0;j<M;j++) active[j]=1;
for(;;){
/* closure: producible set W from food, using catalyzed+supported active reactions */
for(int m=0;m<N;m++) W[m]=(m<NF)?1:0;
int grew=1;
while(grew){ grew=0;
for(int j=0;j<M;j++){ if(!active[j]) continue;
if(!W[r1[j]]||!W[r2[j]]||W[prod[j]]) continue;
int cz=0; for(int m=0;m<N;m++) if(cat[j][m]&&W[m]){cz=1;break;}
if(cz){ W[prod[j]]=1; grew=1; }
}
}
/* prune reactions not supported+catalyzed within W */
int changed=0;
for(int j=0;j<M;j++){ if(!active[j]) continue;
int sup=W[r1[j]]&&W[r2[j]];
int cz=0; for(int m=0;m<N;m++) if(cat[j][m]&&W[m]){cz=1;break;}
if(!(sup&&cz)){ active[j]=0; changed=1; }
}
if(!changed) break;
}
int n=0; for(int j=0;j<M;j++) n+=active[j];
return n;
}
int main(void){
printf("=== RUNG 1: AUTOCATALYTIC SET (RAF) — self-production ignites at a threshold ===\n");
printf(" N=%d molecules, %d food, M=%d reactions; maxRAF size vs catalysis density\n",N,NF,M);
printf(" %-7s %8s %10s %8s\n","p","f=p*M","maxRAF","P(RAF>0)");
for(int pi=1;pi<=18;pi++){
double p=pi*0.003;
double sz=0; int nz=0;
for(int inst=0;inst<INST;inst++){
RNG=0x9e3779b97f4a7c15ULL + (unsigned long long)(pi*10007+inst)*0x2545F4914F6CDD1DULL;
build_net(p); int s=max_raf(); sz+=s; if(s>0) nz++;
}
double meansz=sz/INST, frac=(double)nz/INST, f=p*M;
int bar=(int)(meansz*0.4+0.5); char b[80]; for(int k=0;k<bar&&k<70;k++)b[k]='#'; b[bar<70?bar:70]=0;
printf(" %-7.4f %8.2f %10.1f %8.2f %s\n",p,f,meansz,frac,b);
}
printf("\n Below a critical catalysis density the maxRAF is empty (no self-production).\n");
printf(" Above it a system-spanning autocatalytic set SNAPS ON — the coherence of\n");
printf(" self-production condensing out of given chemistry. That is rung 1.\n");
return 0;
}