/* chain12.c — THE QUESTION: does rung 1 PRODUCE rung 2?
*
* SYNTHESIS's oldest admission, unchanged since the arc began: "No rung ever emerged from the rung
* below. Each was a separate model, and the ladder was a story told across disconnected pieces."
* rafcomp.c got two rungs CO-PRESENT by hand-translating one into the other's terms. The substrate
* milestones removed the structural reason that was necessary — all five rungs now live in one type.
* So for the first time the question is askable in one model rather than narrated across two.
*
* THE TEST. Run a random catalytic network in the counts substrate at a density where a RAF exists
* (rung 1 — a self-sustaining set, which rung1.c showed self-organises). Then ask whether
* SELF-TEMPLATING species are preferentially maintained: a species X is self-templating when some
* reaction produces X and X catalyses that same reaction. That is exactly rung 2 — a form that
* templates copies of itself — expressed in the same type, with no separate model.
*
* If self-templaters end up ENRICHED relative to their share of the network, the autocatalytic set
* is preferentially sustaining the seeds of heredity: rung 1 produces rung 2.
* If they end at their chance share, rung 1 sustains itself and nothing more, and the ladder's
* oldest admission stands.
*
* THE GUARD, and it is what makes a positive believable. "Enriched relative to chance" needs the
* right null, and the wrong one is easy: self-templating reactions are catalysed BY DEFINITION, so
* they are more likely to fire than an average reaction whether or not anything is being selected.
* The control therefore uses a SCRAMBLED network with the identical number of self-templating
* reactions and the identical catalysis density, but with the self-templating property assigned to
* RANDOM species instead of the ones that carry it. Same firing advantage, no relationship between
* being self-templating and being sustained. That is the four-check tally applied at design time:
* the knob must move one variable.
*
* A null here is the expected outcome and a real result — it would say the substrate makes the
* question askable without making the answer yes.
*
* build: cc -O2 -Wall -std=c11 -o chain12 chain12.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#define NSPEC 40
#define NFOOD 6
#define NRXN 160
#define TOTAL 3000
#define ROUNDS 20000
static double PCAT_RUN=0.004;
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); }
typedef struct { int a,b,prod; unsigned char cat[NSPEC]; } Rxn;
static Rxn R[NRXN];
static unsigned char selftem[NSPEC]; /* species X: some reaction makes X and X catalyses it */
/* scramble=0: self-templating is whatever the network actually has.
scramble=1: the SAME COUNT of self-templating reactions, but the property is reassigned to random
species — identical firing advantage, no link between being self-templating and being sustained. */
static void net_random(int scramble){
memset(selftem,0,sizeof selftem);
for(int j=0;j<NRXN;j++){
R[j].a=rint_(NSPEC); R[j].b=rint_(NSPEC); R[j].prod=NFOOD+rint_(NSPEC-NFOOD);
memset(R[j].cat,0,sizeof R[j].cat);
for(int m=0;m<NSPEC;m++) if(urand()<PCAT_RUN) R[j].cat[m]=1;
}
/* find the genuine self-templaters */
int hits[NRXN], nh=0;
for(int j=0;j<NRXN;j++) if(R[j].cat[R[j].prod]) hits[nh++]=j;
if(!scramble){
for(int i=0;i<nh;i++) selftem[R[hits[i]].prod]=1;
} else {
/* keep the same number of self-templating reactions, but point the property elsewhere:
the reaction still catalyses itself (same firing advantage) while the SPECIES we count as
self-templating is drawn at random. */
for(int i=0;i<nh;i++) selftem[NFOOD+rint_(NSPEC-NFOOD)]=1;
}
}
static int n[NSPEC];
static void seed(void){ memset(n,0,sizeof n); for(int i=0;i<TOTAL;i++) n[rint_(NSPEC)]++; }
static void react(void){
for(int t=0;t<ROUNDS;t++){
int tot=0; for(int m=0;m<NSPEC;m++) tot+=n[m];
if(tot<2) return;
int pa=rint_(tot),pb=rint_(tot),a=-1,b=-1,acc=0;
for(int m=0;m<NSPEC;m++){ acc+=n[m]; if(a<0&&pa<acc)a=m; if(b<0&&pb<acc)b=m; }
if(a<0||b<0) continue;
int j0=rint_(NRXN);
for(int d=0;d<NRXN;d++){
int j=(j0+d)%NRXN;
if(!((R[j].a==a&&R[j].b==b)||(R[j].a==b&&R[j].b==a))) continue;
int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&n[m]>0){cz=1;break;}
if(!cz) break;
int ev=rint_(tot),e=-1; acc=0;
for(int m=0;m<NSPEC;m++){ acc+=n[m]; if(ev<acc){ e=m; break; } }
if(e>=NFOOD && n[e]>0) n[e]--;
n[R[j].prod]++;
break;
}
}
}
/* share of sustained abundance that is self-templating, and the chance share it is compared to */
static void measure(double *got,double *chance){
int tot=0,st=0;
for(int m=NFOOD;m<NSPEC;m++){ tot+=n[m]; if(selftem[m]) st+=n[m]; }
*got = tot? (double)st/tot : 0.0;
int ns=0; for(int m=NFOOD;m<NSPEC;m++) if(selftem[m]) ns++;
*chance = (double)ns/(NSPEC-NFOOD); /* their share of the species pool */
}
int main(void)
{
printf("=== REPLICATION: does the rung1->rung2 enrichment hold across seeds and catalysis density? ===\n");
printf(" First run: 1.433x real vs 1.046x scrambled, n=25 of 60 seeds, one PCAT. Promising and\n");
printf(" unreplicated. Here: 200 seeds per cell and a PCAT sweep. The control is a PERMUTATION —\n");
printf(" both arms share the seed so network and dynamics are byte-identical; only which species\n");
printf(" are LABELLED self-templating differs.\n");
printf(" Also reported: how many networks contained any self-templater, since the rest are\n");
printf(" excluded and that conditioning should be visible rather than hidden.\n\n");
double pcats[4]={0.002,0.004,0.008,0.016};
int seeds=200, held=0, tested=0;
printf(" PCAT | networks w/ self-tem | REAL enrich | SCRAMBLED | difference\n");
printf(" ------+----------------------+-------------+-----------+-----------\n");
for(int pi=0;pi<4;pi++){
PCAT_RUN=pcats[pi];
double eg=0,ec=0; int ng=0,nc=0;
for(int s=0;s<seeds;s++){
double g,ch;
RNG=0x9e3779b97f4a7c15ULL+(unsigned long long)(pi*7919+s)*0x2545F4914F6CDD1DULL;
net_random(0); seed(); react(); measure(&g,&ch);
if(ch>0.01){ eg+=g/ch; ng++; }
RNG=0x9e3779b97f4a7c15ULL+(unsigned long long)(pi*7919+s)*0x2545F4914F6CDD1DULL;
net_random(1); seed(); react(); measure(&g,&ch);
if(ch>0.01){ ec+=g/ch; nc++; }
}
if(ng) eg/=ng;
if(nc) ec/=nc;
double d=eg-ec;
printf(" %.3f | %3d of %3d | %.3fx | %.3fx | %+.3f\n",
pcats[pi],ng,seeds,eg,ec,d);
if(ng>=seeds/10){ tested++; if(d>0.10) held++; }
}
printf("\n held (difference > 0.10) in %d of %d cells with enough networks\n\n",held,tested);
if(tested==0)
printf(" => TOO FEW NETWORKS CONTAINED A SELF-TEMPLATER AT ANY DENSITY. Nothing measured.\n");
else if(held==tested)
printf(" => IT HOLDS. The enrichment survives 200 seeds per cell across the whole catalysis\n"
" sweep, against a permutation null on byte-identical dynamics. The first result was\n"
" not a small-n artefact.\n");
else if(held>0)
printf(" => PARTIAL: holds in %d of %d cells. Report the density dependence as-is; the effect\n"
" is real somewhere and absent elsewhere, which is itself informative.\n",held,tested);
else
printf(" => IT DOES NOT HOLD. At 200 seeds per cell the enrichment does not separate from its\n"
" permutation control anywhere in the sweep. The first result was a small-n artefact\n"
" and chain12's claim must be RETRACTED — the ladder's oldest admission stands.\n");
return 0;
}