/* replisome.c — the closed genotype/phenotype loop. Can it bootstrap?
*
* WHERE THIS COMES FROM. genome.c v1/v2 gave templates a fair test and they failed twice, but the
* second failure exposed something I had been getting for free: TEMPLATE COPYING WAS FREE PHYSICS
* (child = par, perfect, no cost). In reality copying a genome faithfully needs a REPLICASE, which
* is itself a catalyst, which is itself specified by a template. That is the loop, and it is exactly
* rung 1→2.
*
* Two flaws in genome.c are fixed here:
* (1) a picked template ALWAYS became a catalyst when reading was on, so reading was a dead end for
* the template lineage — it could be useful or persist, never both. Now a picked template is
* READ with probability PREAD and otherwise COPIED TRUE, exactly as a catalyst is copied true.
* Reading is a FUNCTION templates provide, not a route by which they propagate.
* (2) template fidelity was free. Now it is CONDITIONAL on the compartment holding a catalyst of
* the replicase type: with one, templates copy at MU*RLO; without, at MU*RHI.
*
* THE RAZOR. This is where encoding the answer is easiest, so, precisely:
* SUPPLIED as physics: "a type-REPTYPE catalyst improves template fidelity in its compartment" —
* a function, the same shape as ribozyme.c's supplied fold→catalysis map.
* NOT SUPPLIED: any reward for being or holding a replicase. REPTYPE is EXCLUDED FROM THE TARGET
* SET, so it contributes nothing to payoff directly — it is pure overhead unless the template
* loop pays for itself. It is not seeded, not protected, not favoured in replication. Non-folders
* still replicate faster, so the parasite advantage is intact and the replicase is a PUBLIC GOOD
* within its compartment, free-ridable exactly as catalysis was in ribozyme.c.
* MEASURED: whether lineages acquire and HOLD the replicase, and whether function is higher for it.
*
* THE CONTROL, built to today's matched-condition lesson. Turning the replicase effect on also
* changes the average template mutation rate, which would confound any gain. So the control applies
* the IDENTICAL two-rate structure conditioned on a COIN FLIP per compartment instead of on
* replicase presence — same mutation-rate distribution, no type to select for. The realized
* low-fidelity fraction is reported for both arms so the match is visible rather than assumed.
*
* PRE-REGISTERED:
* R1 replicase frequency RISES above the control's. Lineages hold a type that pays them nothing
* directly, because it protects the templates that carry everything else.
* R2 target repertoire is HIGHER with the replicase effect than with the coin-flip control.
* R3 TEST OF THE TEST: catalysts and templates coexist, and the two arms' low-fidelity fractions
* are comparable. If they diverge sharply the comparison is confounded by mutation load.
* R4 the advantage should GROW with base mu — the replicase matters most where templates decay fast.
*
* HONEST PRIOR: partial. The replicase is a public good inside its compartment, so free-riding should
* bite exactly as it did in ribozyme.c — one level up. A null here is a real result.
*
* build: cc -O2 -Wall -std=c11 -o replicase replisome.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#define NFOLD 16
#define NPAIR (NFOLD/2)
#define THETA 6
#define NTYPE 8
#define REPTYPE 0 /* the replicase type — excluded from the target set */
#define NTARG 3 /* target types, drawn from types 1..7 */
#define NCOMP 600
#define CSZ 8
#define GEN 200
#define BETA 2.0
#define PREAD 0.50
#define RLO 0.25 /* template mutation multiplier WITH a replicase present */
#define RHI 1.50 /* ... and without */
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 TARGET[NTYPE];
static int phi(uint32_t s){ int c=0; for(int i=0;i<NPAIR;i++){ int a=(s>>i)&1,b=(s>>(NFOLD-1-i))&1; if(a!=b)c++; } return c; }
static int isfold(uint32_t s){ return phi(s)>=THETA; }
static int typeof_(uint32_t s){ return (int)((s>>NFOLD)&7); }
static uint32_t mut(uint32_t s,double mu){ for(int i=0;i<NFOLD+3;i++) if(urand()<mu) s^=(1u<<i); return s; }
/* Each slot holds a PAIR of strands — a minimal physical complex, copied together (linkage).
PRIVATE: a template's fidelity is high only if ITS OWN partner is a replicase.
SHARED : a template's fidelity is high if ANY catalyst in the compartment is a replicase
(this is replicase.c's behaviour, which gave a complete null).
One knob: the SCALE at which the replicase's benefit is privatised. */
static uint32_t A[NCOMP][CSZ], B[NCOMP][CSZ], nA[NCOMP][CSZ], nB[NCOMP][CSZ];
static double cum[NCOMP];
static int payoff_of(int c){
int seen[NTYPE]; for(int t=0;t<NTYPE;t++) seen[t]=0;
int d=0;
for(int k=0;k<CSZ;k++){
if(isfold(A[c][k])){ int t=typeof_(A[c][k]); if(!seen[t]){ seen[t]=1; if(TARGET[t]) d++; } }
if(isfold(B[c][k])){ int t=typeof_(B[c][k]); if(!seen[t]){ seen[t]=1; if(TARGET[t]) d++; } }
}
return d;
}
static int is_rep(uint32_t s){ return isfold(s) && typeof_(s)==REPTYPE; }
static int comp_has_rep(int c){ for(int k=0;k<CSZ;k++) if(is_rep(A[c][k])||is_rep(B[c][k])) return 1; return 0; }
static double rep_freq_of(int c){ int n=0; for(int k=0;k<CSZ;k++){ n+=is_rep(A[c][k]); n+=is_rep(B[c][k]); } return (double)n/(2*CSZ); }
/* mode 0 = PRIVATE (replisome scale), 1 = SHARED (compartment scale, = replicase.c) */
static void run(int mode,double mu,double *rep_freq,double *pay_out,double *lofrac)
{
for(int t=0;t<NTYPE;t++) TARGET[t]=0;
for(int got=0;got<NTARG;){ int t=1+(int)(xs()%(NTYPE-1)); if(!TARGET[t]){ TARGET[t]=1; got++; } }
for(int c=0;c<NCOMP;c++) for(int k=0;k<CSZ;k++){
uint32_t v=0,w=0;
for(int i=0;i<NFOLD+3;i++){ if(urand()<0.5) v|=(1u<<i); if(urand()<0.5) w|=(1u<<i); }
A[c][k]=v; B[c][k]=w;
}
double losum=0; long lon=0;
for(int g=0;g<GEN;g++){
for(int c=0;c<NCOMP;c++){
int shared_rep = comp_has_rep(c);
double tot=0,cw[CSZ];
for(int k=0;k<CSZ;k++){
double q = 2.0 + BETA*(1.0-(double)phi(A[c][k])/NPAIR) + BETA*(1.0-(double)phi(B[c][k])/NPAIR);
tot+=q; cw[k]=tot;
}
int mach=-1; for(int k=0;k<CSZ;k++){ if(isfold(A[c][k])){mach=k;break;} if(isfold(B[c][k])){mach=k;break;} }
for(int k=0;k<CSZ;k++){
double r=urand()*tot; int lo=0,hi=CSZ-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cw[mid]<r) lo=mid+1; else hi=mid; }
/* the PAIR is copied together — linkage, so a complex is inherited as a unit */
int priv = is_rep(A[c][lo]) || is_rep(B[c][lo]);
int lo_fid = (mode==0)? priv : shared_rep;
losum+=lo_fid; lon++;
double tmu = mu*(lo_fid?RLO:RHI);
uint32_t src[2]={A[c][lo],B[c][lo]}, out[2];
for(int h=0;h<2;h++){
uint32_t par=src[h], child; double emu;
if(!isfold(par)){
if(mach>=0 && urand()<PREAD){
uint32_t fold = isfold(A[c][mach])? A[c][mach] : B[c][mach];
child=(fold&((1u<<NFOLD)-1))|((uint32_t)typeof_(par)<<NFOLD); emu=mu;
} else { child=par; emu=tmu; }
} else { child=par; emu=mu; }
out[h]=mut(child,emu);
}
nA[c][k]=out[0]; nB[c][k]=out[1];
}
}
for(int c=0;c<NCOMP;c++) for(int k=0;k<CSZ;k++){ A[c][k]=nA[c][k]; B[c][k]=nB[c][k]; }
double tot=0;
for(int c=0;c<NCOMP;c++){ tot += 0.05 + (double)payoff_of(c); cum[c]=tot; }
for(int c=0;c<NCOMP;c++){
double r=urand()*tot; int lo=0,hi=NCOMP-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
for(int k=0;k<CSZ;k++){ int q=(int)(xs()%CSZ); nA[c][k]=A[lo][q]; nB[c][k]=B[lo][q]; }
}
for(int c=0;c<NCOMP;c++) for(int k=0;k<CSZ;k++){ A[c][k]=nA[c][k]; B[c][k]=nB[c][k]; }
}
double rf=0,pay=0;
for(int c=0;c<NCOMP;c++){ rf+=rep_freq_of(c); pay+=payoff_of(c); }
*rep_freq=rf/NCOMP; *pay_out=pay/NCOMP; *lofrac=losum/(double)lon;
}
int main(void)
{
unsigned long long seeds[6];
seeds[0]=0x9e3779b97f4a7c15ULL; seeds[1]=0xd1b54a32d192ed03ULL; seeds[2]=0x2545f4914f6cdd1dULL;
for(int i=3;i<6;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1));
int NS=6;
printf("=== REPLISOME SCALE: does privatising the replicase to its own complex close the loop? ===\n");
printf(" replicase.c gave a complete null: a replicase improves fidelity for EVERY template in its\n");
printf(" compartment including free-riders', so within-compartment selection purges it. A boundary\n");
printf(" can only privatise a good it can exclude non-contributors from, and everything inside a\n");
printf(" compartment is inside it. So: shrink the scale.\n");
printf(" Each slot is a PAIR copied together (linkage). PRIVATE = a pair gets high fidelity only if\n");
printf(" ITS OWN partner is a replicase. SHARED = replicase.c's rule. One knob: the scale.\n");
printf(" NOTE the confound direction: SHARED grants low-mutation more often at equal replicase\n");
printf(" frequency, so SHARED enjoys LESS mutation load. If PRIVATE still wins, it wins against it.\n\n");
printf(" mu | PRIVATE rep-freq / payoff | SHARED rep-freq / payoff | lo-fid P / S\n");
printf(" ------+---------------------------+--------------------------+-------------\n");
double mus[]={0.005,0.01,0.02,0.04,0.08};
int nM=5, w1=0, w2=0; double g0=0,g1=0;
for(int i=0;i<nM;i++){
double a1=0,a2=0,a3=0,b1=0,b2=0,b3=0;
for(int s=0;s<NS;s++){
double rf,pv,lf;
RNG=seeds[s]; run(0,mus[i],&rf,&pv,&lf); a1+=rf; a2+=pv; a3+=lf;
RNG=seeds[s]; run(1,mus[i],&rf,&pv,&lf); b1+=rf; b2+=pv; b3+=lf;
}
a1/=NS;a2/=NS;a3/=NS;b1/=NS;b2/=NS;b3/=NS;
printf(" %.3f | %.3f / %.2f | %.3f / %.2f | %.2f / %.2f\n",
mus[i],a1,a2,b1,b2,a3,b3);
if(a1>b1+0.01) w1++;
if(a2>b2+0.05) w2++;
if(i==0) g0=a1-b1;
g1=a1-b1;
}
printf("\n rep-freq = fraction of strands that are replicases; payoff = target types (max %d)\n\n",NTARG);
printf(" P1 PRIVATE holds more replicase than SHARED : %d/%d\n",w1,nM);
printf(" P2 PRIVATE payoff higher : %d/%d\n",w2,nM);
printf(" P3 replicase-frequency gap vs mu : %+.3f -> %+.3f\n\n",g0,g1);
if(w1>=nM-1 && w2>=nM-1)
printf(" => SCALE IS THE ANSWER. Privatising the replicase to its own complex — and copying that\n"
" complex as a unit — lets lineages hold a catalyst that pays them nothing directly,\n"
" and carry more function for it. The same good, the same cost, the same free-riders:\n"
" only the SCOPE of the benefit changed, and that was the whole difference. It also\n"
" wins against the confound, since SHARED enjoys the lower mutation load.\n");
else if(w1>=nM-1)
printf(" => REPLICASE HELD, FUNCTION NOT RAISED (%d/%d vs %d/%d). Privatisation retains the\n"
" replicase but the loop does not yet pay off in function. Partial.\n",w1,nM,w2,nM);
else
printf(" => HONEST NULL. Shrinking the scale of privatisation does not rescue the replicase\n"
" either. Three designs, three nulls: the genotype/phenotype loop does not bootstrap\n"
" in this family of models, and the missing ingredient is still not identified.\n");
return 0;
}