/* substrate.c — ONE representation for all five rungs. Milestone 1: does it express rung 1 and 2?
*
* WHY. SYNTHESIS's standing admission is that no rung ever produced the next, and the cold read of
* Part C found why it is structural rather than accidental: EVERY RUNG HAS A DIFFERENT SUBSTRATE.
* Bit strings, count vectors, reaction sets, gene-type lists, role counts. A rung's output is not
* expressible as the next rung's input, so the ladder can only ever be narrated across disconnected
* models. rafcomp.c got two rungs co-present by hand-translating one into the other's terms; doing
* that for a whole ladder means one representation that expresses all of them.
*
* THE CONSTRAINT TONIGHT ADDED. genome.c, replicase.c and replisome.c all failed for one reason:
* nothing in those models was expensive enough to be worth remembering, because payoff was scored on
* the present configuration and any configuration was cheap to rediscover. linkage.c, the one that
* worked, is the one whose payoff required a COMBINATION. So the substrate must make combinations
* expensive to rediscover, or memory can never pay in it.
*
* THE CHOICE. A reaction fires only when a SPECIFIC SET of species is present — reactants AND a
* catalyst. That is combinatorial by construction, and it already covers the ladder:
* rung 1 a self-sustaining reaction subset (a RAF)
* rung 2 a template: the reaction A -> A + A catalysed by A
* rung 3 species ARE sequences; mutation is a variant-producing reaction
* rung 4 a container is a sub-multiset copied as a unit — AND IS ITSELF A SPECIES
* rung 5 a specialist is a species participating in only part of the reaction set
* The recursive move is the last-but-one: containers are species. That is what lets a rung's output
* be the next rung's input instead of being hand-translated.
*
* MILESTONE 1 (this file) — the test of the test. A new substrate is worthless if it does not
* faithfully express what we already have, so before anything else:
* S1 the engine must REPRODUCE rung1.c's percolation threshold. Same construction, same sweep. If
* the curve differs, the substrate does not express rung 1 and nothing built on it can be
* trusted.
* S2 a self-templating species (rung 2) must be EXPRESSIBLE in the same engine with no special
* case — just a reaction whose product is its own catalyst — and must self-sustain when its
* inputs are food.
* S3 both must be able to coexist in ONE network, which is the whole point: it is the precondition
* for rung 2 arising INSIDE a rung-1 system rather than being a separate model.
* Milestones 2+ (containers-as-species, then rungs 3-5) are not attempted here. A substrate that
* fails S1 is not worth extending.
*
* build: cc -O2 -Wall -std=c11 -o substrate substrate.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#define NSPEC 60 /* species; rung1.c used N=60 */
#define NFOOD 8 /* food species, always present; rung1.c used NF=8 */
#define NRXN 300 /* reactions; rung1.c used M=300 */
#define INST 60 /* network instances per point; rung1.c used INST=60 */
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); }
/* ---- THE TYPE. A reaction is (reactant a, reactant b) -> product, enabled by any catalyst.
Species are integers. Everything in the ladder is meant to be expressed as species+reactions:
a template is a reaction whose product catalyses itself; a container will be a species whose
contents are a multiset (milestone 2). ---- */
typedef struct { int a, b, prod; unsigned char cat[NSPEC]; } Rxn;
static Rxn R[NRXN];
static int nrxn;
static unsigned char present[NSPEC], active[NRXN];
static void net_clear(void){ nrxn=0; }
static int net_add(int a,int b,int prod){
if(nrxn>=NRXN) return -1;
R[nrxn].a=a; R[nrxn].b=b; R[nrxn].prod=prod;
memset(R[nrxn].cat,0,sizeof R[nrxn].cat);
return nrxn++;
}
static void net_catalyse(int j,int sp){ R[j].cat[sp]=1; }
/* rung1.c's construction, verbatim in shape: random reactions, each species catalyses each
reaction with probability p. */
static void net_random_n(double p,int count){
net_clear();
if(count>NRXN) count=NRXN;
for(int j=0;j<count;j++){
int id=net_add(rint_(NSPEC),rint_(NSPEC),NFOOD+rint_(NSPEC-NFOOD));
if(id<0) break;
for(int m=0;m<NSPEC;m++) if(urand()<p) net_catalyse(id,m);
}
}
static void net_random(double p){ net_random_n(p,NRXN); }
/* maxRAF by prune-to-fixpoint — rung1.c's algorithm, operating on the substrate's own type. */
static int max_raf(void){
for(int j=0;j<nrxn;j++) active[j]=1;
for(;;){
for(int m=0;m<NSPEC;m++) present[m]=(m<NFOOD)?1:0;
int grew=1;
while(grew){ grew=0;
for(int j=0;j<nrxn;j++){ if(!active[j]) continue;
if(!present[R[j].a]||!present[R[j].b]||present[R[j].prod]) continue;
int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&present[m]){cz=1;break;}
if(cz){ present[R[j].prod]=1; grew=1; }
}
}
int changed=0;
for(int j=0;j<nrxn;j++){ if(!active[j]) continue;
int sup=present[R[j].a]&&present[R[j].b];
int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&present[m]){cz=1;break;}
if(!(sup&&cz)){ active[j]=0; changed=1; }
}
if(!changed) break;
}
int n=0; for(int j=0;j<nrxn;j++) n+=active[j];
return n;
}
int main(void)
{
printf("=== SUBSTRATE, MILESTONE 1: does one representation express rung 1 AND rung 2? ===\n");
printf(" Species are integers; a reaction is (a,b)->prod enabled by any present catalyst.\n");
printf(" Nothing is special-cased per rung. %d species (%d food), %d reactions.\n\n",NSPEC,NFOOD,NRXN);
/* ---- S1: reproduce rung1.c's percolation threshold ---- */
printf(" S1 — reproduce rung1.c's threshold (its published curve in the right column)\n");
printf(" %-8s %10s %10s %s\n","p","maxRAF","P(RAF>0)","rung1.c");
const char *ref[6]={"~0 (empty)","~0","rising","rising","system-spanning","system-spanning"};
double ps[6]={0.006,0.012,0.021,0.030,0.042,0.054};
int ok_low=0, ok_high=0;
for(int i=0;i<6;i++){
double sz=0; int nz=0;
for(int t=0;t<INST;t++){
RNG=0x9e3779b97f4a7c15ULL + (unsigned long long)(i*10007+t)*0x2545F4914F6CDD1DULL;
net_random(ps[i]); int s=max_raf(); sz+=s; if(s>0) nz++;
}
double m=sz/INST, f=(double)nz/INST;
printf(" %-8.4f %10.1f %10.2f %s\n",ps[i],m,f,ref[i]);
if(i==0 && m<1.0) ok_low=1;
if(i==5 && m>20.0) ok_high=1;
}
printf(" => %s\n\n", (ok_low&&ok_high)?
"MATCHES: empty below, system-spanning above — rung 1 is expressed faithfully."
:"DIVERGES from rung1.c — the substrate does not express rung 1. Stop here.");
/* ---- S2: a template is just a reaction that catalyses itself. No special case. ---- */
printf(" S2 — express rung 2 (a template) in the SAME type: a reaction whose product is its\n");
printf(" own catalyst. Food-only reactants, so it needs nothing but the food set.\n");
net_clear();
int T=NFOOD; /* the template species */
int t1=net_add(0,1,T); net_catalyse(t1,T); /* food + food -> T, catalysed by T */
int self=max_raf();
printf(" template alone, uncatalysed start : maxRAF %d (expected 0 — needs a seed)\n",self);
net_catalyse(t1,0); /* also catalysed by a food species = a seed */
int seeded=max_raf();
printf(" with a food-species seed : maxRAF %d (expected 1 — self-sustains)\n",seeded);
int s2 = (self==0 && seeded==1);
printf(" => %s\n\n", s2?
"EXPRESSED: rung 2 is a reaction shape, not a new model. And it reproduces the known\n bootstrap problem — a self-catalysing template cannot start itself without a seed."
:"NOT EXPRESSED as expected — check the encoding before building on it.");
/* ---- S3: both in ONE network, which is the precondition for chaining ---- */
printf(" S3 — rung 1 and rung 2 in ONE network (the point: rung 2 could arise INSIDE rung 1)\n");
int both=0, tin=0;
{
/* BUG FIXED: net_random filled all NRXN slots, so the following net_add returned -1 and
active[-1] was read — undefined behaviour, reported as "no". Leave a slot free. */
int hits=0, tries=8;
for(int t=0;t<tries;t++){
RNG=0xfeedface12345678ULL + (unsigned long long)t*0x9E3779B97F4A7C15ULL;
net_random_n(0.030,NRXN-1);
int j=net_add(0,1,T);
if(j<0){ printf(" reaction table full — cannot express both\n"); break; }
net_catalyse(j,T); net_catalyse(j,0);
int n=max_raf();
if(active[j] && n>1) hits++;
if(t==0) printf(" mixed network maxRAF %d, template reaction in the RAF: %s\n",n,active[j]?"YES":"no");
}
printf(" across %d seeds, template retained in the RAF: %d\n",tries,hits);
tin=(hits>0); both=(hits>=tries-1);
}
printf(" => %s\n\n", both?
"COEXIST: a self-templating species sits inside an autocatalytic set, in one type, with no\n translation between them. That is the precondition the ladder has never had."
:"did NOT coexist in this instance — retry across seeds before concluding.");
printf(" MILESTONE 1 %s\n", (ok_low&&ok_high&&s2&&both)?"PASSED":"NOT PASSED");
printf(" Next, and NOT attempted here: containers as species (rung 4), which is the recursive\n");
printf(" move that lets a compartment be produced, counted and selected like anything else —\n");
printf(" and is what would let a rung's output actually BE the next rung's input.\n");
return 0;
}