OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / basal-agent / fluctuate.c

418 lines · c ref:
/* fluctuate.c — does MEMORY pay when the world CHANGES? Scored on FUNCTION, against a SCRAMBLED
 *               control, with the reading knockout reduced to ONE variable.
 *
 * WHERE THIS COMES FROM. threecarrier.c's fourth attempt reported that under a fluctuating target
 * set, turning reading ON raised the working fraction 0.294 -> 0.478 and function 1.27 -> 1.47.
 * That number is NOT usable as it stands, for two reasons found by reading its source:
 *
 *  (1) WRONG METRIC. Its pre-registered M1/M2 scored TEMPLATE ABUNDANCE. Reading converts a template
 *      replication event into a catalyst, so the reading-OFF arm holds MORE templates (0.023 vs
 *      0.012) precisely because they are useless there. Template count measures templates NOT BEING
 *      USED. genome.md records this identical mistake; it has now been made three times.
 *
 *  (2) THE KNOCKOUT MOVED TWO VARIABLES. threecarrier.c:238-239 reads
 *          if(ko!=2 && nc>0 && urand()<0.5){ ch.role=CAT; ch.type=p.type; }
 *          else if(urand()>f) ch.type=rint_(NTYPE);          // copy error
 *      An if/else. With reading ON a read child SKIPS the copy-error branch; with reading OFF every
 *      template replication takes it. So "reading off" also raises template mutation load. That is
 *      the one-knob-two-variables fault the 2026-07-31 post-mortem counted four separate times.
 *
 * WHAT THIS FILE CHANGES, AND NOTHING ELSE. Chemistry, sizes, rates, seeding and between-compartment
 * selection are IDENTICAL to threecarrier.c so the numbers stay comparable:
 *   - copy error now applies to EVERY template replication, independent of reading. Reading is a
 *     separate, subsequent event. The knockout is one variable.
 *   - scored on FUNCTION and WORKING FRACTION, time-averaged over the second half of the run
 *     (many switch cycles, not one phase-dependent snapshot). Template fraction is still reported,
 *     and is explicitly NOT a criterion.
 *   - 24 seeds, not 5, with the paired per-seed difference and its spread reported.
 *
 * THE CONTROL, WHICH IS THE POINT. genome.c ran this same question and looked positive until the
 * SCRAMBLED control inverted it: reading that produces a catalyst of a RANDOM type BEAT faithful
 * reading at every mutation rate, because the gain was the mechanism turning non-folders into
 * folders, and the information contributed nothing. threecarrier.c never ran that control. Comparing
 * read-ON against read-OFF cannot separate the information from the conversion, because reading also
 * adds a catalyst, which raises nc, which raises both growth (GROW*nc) and fidelity (nc>0 => FHI).
 * So the headline comparison here is FAITHFUL vs SCRAMBLED, not ON vs OFF.
 *
 * genome.md also named the missing ingredient and designed the fix deliberately left unrun: "a
 * genome is only worth having when a SPECIFIC configuration beats a random one". threecarrier.c
 * supplied that (NTARG specific types out of NTYPE) but never ran the control it was designed for.
 * This does.
 *
 * AND THE PREDICTION THAT MAKES A NULL INFORMATIVE. Under a FRESH random redraw, a stored type is a
 * target again with probability NTARG/NTYPE = 0.25 -- exactly the odds a scrambled read gets by
 * dice. So a fresh-redraw world offers memory no edge in principle, and worse: right after a switch
 * the template pool is enriched for the OLD targets, which is worse than random. If memory pays
 * anywhere it is in a world that REPEATS. So the fluctuating condition is run twice:
 *   FRESH  -- targets redrawn at random every switch (threecarrier.c's condition)
 *   RECUR  -- cycled among NENV fixed target sets, so a type not needed now IS needed again
 * Both are pre-registered before running, so a null on FRESH plus a positive on RECUR is a stated
 * prediction confirmed, not a story told afterwards.
 *
 * SUPPLIED vs MUST EMERGE:
 *  SUPPLIED: the three carriers seeded complete and correct, the reading physics, between-compartment
 *    selection on current function, and the environment's switching schedule.
 *  MUST EMERGE: any advantage of holding information. NOTHING rewards templates directly -- they
 *    fold nothing and cost a slot. Parasites are not seeded; they arise by mutation and replicate
 *    BETA faster, so the tragedy is live in every arm.
 *
 * PRE-REGISTERED:
 *  P1 THE PRIMARY TEST: under fluctuation, FAITHFUL reading beats SCRAMBLED reading on function.
 *     One variable: the type the read catalyst gets. If this fails, the information is doing
 *     nothing and any ON-vs-OFF gain is the conversion, exactly as in genome.c.
 *  P2 reading of any kind beats no reading. REPORTED BUT CONFOUNDED by construction (conversion +
 *     information + the nc it feeds). It is not evidence for memory and is not read as such.
 *  P3 fluctuation actually costs function relative to static. If it does not, nothing was survived.
 *  P4 THE STATED ALTERNATIVE: if P1 is null under FRESH but holds under RECUR, then memory pays only
 *     when the world repeats itself -- a condition on the ENVIRONMENT, not on the chemistry.
 *
 * TESTS OF THE TEST (any failure voids the arm it belongs to; a void result is reported as void):
 *  G1 reads actually happen -- nonzero in every reading arm, exactly zero in every OFF arm.
 *  G2 templates actually exist. If the template fraction is ~0 there is no third carrier and nothing
 *     was tested. This is what voided threecarrier.c's second and third attempts.
 *  G3 the environment actually changes -- switches occur and consecutive target sets differ.
 *  G4 the arms differ at all.
 *
 * Do not tune to force a positive. A null with G1-G4 passing is a real result: it says memory cannot
 * pay under instantaneous selection even when the world moves, which is the fifth 1->2 constraint
 * stated more strongly than the post-mortem could state it.
 *
 * build:  cc -O2 -Wall -Wextra -std=c11 -o fluctuate fluctuate.c -lm
 */
#include <math.h>
#include <stdio.h>
#include <string.h>

#define NCOMP   600
#define CAP     16
#define GEN     400
#define BURN    200        /* measurement window is generations BURN..GEN-1 */
#define NTYPE   16
#define NTARG   4
#define NENV    3          /* RECUR: this many fixed target sets, cycled */
#define SWEVERY 15
#define BETA    2.0        /* parasite replication advantage */
#define MUT     0.02
#define FLO     0.85       /* template copy fidelity with no catalyst present */
#define FHI     0.995      /* ... with one (the replicase coupling) */
#define GROW    0.55
#define DEATH   0.15
#define PREAD   0.5        /* per-replication chance a template is read */
#define NSEED   24

enum { CAT=0, TEM=1, PAR=2 };
enum { ENV_STATIC=0, ENV_FRESH=1, ENV_RECUR=2 };
enum { READ_OFF=0, READ_FAITHFUL=1, READ_SCRAMBLED=2 };

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 { unsigned char role, type; } Unit;
static Unit cell[NCOMP][CAP], nxt[NCOMP][CAP];
static int  sz[NCOMP], nsz[NCOMP];
static double mat[NCOMP], nmat[NCOMP];

static int TARGET[NTYPE];
static int ENVSET[NENV][NTYPE];

typedef struct {
	double func, work, tfrac, parfrac;   /* time-averaged over the measurement window */
	double early, late;                  /* function just after / just before a switch */
	double reads;                        /* read events per compartment per generation */
	double redund, gapfill;              /* fraction of reads that were redundant / filled a gap */
	double switches, overlap;            /* did the environment actually change? */
} Res;

static void counts(int c,int *nc,int *nt,int *np){
	*nc=*nt=*np=0;
	for(int k=0;k<sz[c];k++){
		if(cell[c][k].role==CAT) (*nc)++;
		else if(cell[c][k].role==TEM) (*nt)++;
		else (*np)++;
	}
}
/* function = how many of the SPECIFIC target types are present among catalysts. Identical to
   threecarrier.c. Nothing rewards templates; they fold nothing and still cost a slot. */
static int func_of(int c){
	int seen[NTYPE]; memset(seen,0,sizeof seen);
	for(int k=0;k<sz[c];k++) if(cell[c][k].role==CAT) seen[cell[c][k].type]=1;
	int f=0; for(int t=0;t<NTYPE;t++) if(seen[t]&&TARGET[t]) f++;
	return f;
}
static void drawset(int *set){
	for(int t=0;t<NTYPE;t++) set[t]=0;
	for(int got=0;got<NTARG;){ int t=rint_(NTYPE); if(!set[t]){ set[t]=1; got++; } }
}
static int overlap_of(const int *a,const int *b){
	int o=0; for(int t=0;t<NTYPE;t++) if(a[t]&&b[t]) o++; return o;
}

static void run(int envmode,int readmode,Res *R)
{
	int prev[NTYPE]; int envi=0;
	double nsw=0, novl=0, reads=0, redund=0, gapfill=0;
	double fsum=0, wsum=0, tsum=0, psum=0; long nmeas=0;
	double phase_f[SWEVERY]; long phase_n[SWEVERY];
	memset(phase_f,0,sizeof phase_f); memset(phase_n,0,sizeof phase_n);

	if(envmode==ENV_RECUR){
		for(int e=0;e<NENV;e++) drawset(ENVSET[e]);
		memcpy(TARGET,ENVSET[0],sizeof TARGET);
	} else drawset(TARGET);

	/* SEEDED COMPLETE AND CORRECT: catalysts and templates covering the target set. Identical to
	   threecarrier.c -- nothing here has to bootstrap; the question is only whether it holds. */
	for(int c=0;c<NCOMP;c++){
		sz[c]=CAP; mat[c]=0;
		for(int k=0;k<CAP;k++){
			int tg=-1,seen=0;
			for(int t=0;t<NTYPE&&tg<0;t++) if(TARGET[t]){ if(seen==(k/2)%NTARG) tg=t; seen++; }
			cell[c][k].role = (k%2)?TEM:CAT;
			cell[c][k].type = (unsigned char)(tg<0?0:tg);
		}
	}

	int lastsw=0;
	for(int g=0;g<GEN;g++){
		if(envmode!=ENV_STATIC && g>0 && g%SWEVERY==0){
			memcpy(prev,TARGET,sizeof prev);
			if(envmode==ENV_FRESH) drawset(TARGET);
			else { envi=(envi+1)%NENV; memcpy(TARGET,ENVSET[envi],sizeof TARGET); }
			nsw++; novl += overlap_of(prev,TARGET); lastsw=g;
		}
		for(int c=0;c<NCOMP;c++){
			int nc,nt,np; counts(c,&nc,&nt,&np);
			mat[c] += GROW*nc;                                  /* metabolism -> boundary */
			double f = (nc>0)? FHI : FLO;                       /* catalyst -> template fidelity */
			double tot=0,cum[CAP];
			for(int k=0;k<sz[c];k++){ tot += (cell[c][k].role==PAR)? 1.0+BETA : 1.0; cum[k]=tot; }
			/* which catalyst types this compartment ALREADY has, fixed at the start of the generation
			   so both arms are scored against the same reference. Used only to characterize WHY an
			   arm wins -- it feeds no dynamics. */
			int present[NTYPE]; memset(present,0,sizeof present);
			for(int k=0;k<sz[c];k++) if(cell[c][k].role==CAT) present[cell[c][k].type]=1;
			int newn=0;
			for(int k=0;k<sz[c];k++) if(urand()>=DEATH) nxt[c][newn++]=cell[c][k];
			while(mat[c]>=1.0 && newn<CAP && sz[c]>0){
				double r=urand()*tot; int lo=0,hi=sz[c]-1;
				while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
				Unit p=cell[c][lo], ch=p;
				if(p.role==TEM){
					/* THE FIX. Copy error applies to EVERY template replication, independent of
					   reading; reading is a separate, subsequent event. In threecarrier.c these were
					   an if/else, so the reading knockout also changed mutation load. Both RNG draws
					   are taken unconditionally so the arms stay on the same stream as long as
					   possible -- the read modes then differ in exactly one thing: the type the
					   produced catalyst gets. */
					if(urand()>f) ch.type=(unsigned char)rint_(NTYPE);   /* copy error */
					double rread = urand();
					unsigned char rtype = (unsigned char)rint_(NTYPE);
					if(readmode!=READ_OFF && nc>0 && rread<PREAD){
						ch.role=CAT;                                     /* template -> function */
						if(readmode==READ_SCRAMBLED) ch.type=rtype;      /* the control: no information */
						reads++;
						/* did this read add anything? A read that reproduces a type the compartment
						   already has changes no function; one that supplies a MISSING target type
						   raises it by one. This is the measurement that decides whether a scrambled
						   win is the diversity effect genome.md named. */
						if(present[ch.type]) redund++;
						else if(TARGET[ch.type]) gapfill++;
					}
				}
				if(urand()<MUT){ ch.role=(unsigned char)rint_(3); ch.type=(unsigned char)rint_(NTYPE); }
				nxt[c][newn++]=ch; mat[c]-=1.0;
			}
			nsz[c]=newn; nmat[c]=mat[c];
		}
		for(int c=0;c<NCOMP;c++){ sz[c]=nsz[c]; mat[c]=nmat[c]; for(int k=0;k<sz[c];k++) cell[c][k]=nxt[c][k]; }
		/* between-compartment: reproduce in proportion to CURRENT function. Identical to
		   threecarrier.c, and it is the instantaneous selection the deferred-benefit obstacle is
		   about -- deliberately left in place, since removing it would answer a different question. */
		{
			double tot=0,cum[NCOMP];
			for(int c=0;c<NCOMP;c++){ tot += 0.02 + func_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; }
				int m = sz[lo]<CAP? sz[lo] : CAP;
				nsz[c]=m; nmat[c]=mat[lo]*0.5;
				for(int k=0;k<m;k++) nxt[c][k]=cell[lo][rint_(sz[lo]>0?sz[lo]:1)];
			}
			for(int c=0;c<NCOMP;c++){ sz[c]=nsz[c]; mat[c]=nmat[c]; for(int k=0;k<sz[c];k++) cell[c][k]=nxt[c][k]; }
		}
		if(g>=BURN){
			int ok=0,tp=0,tt=0,tu=0; double fs=0;
			for(int c=0;c<NCOMP;c++){
				int nc,nt,np; counts(c,&nc,&nt,&np);
				int fc=func_of(c);
				if(fc>=2) ok++;
				fs+=fc; tp+=np; tt+=nt; tu+=sz[c];
			}
			double mf=fs/NCOMP;
			fsum+=mf; wsum+=(double)ok/NCOMP;
			tsum += tu? (double)tt/tu : 0; psum += tu? (double)tp/tu : 0;
			nmeas++;
			if(envmode!=ENV_STATIC){ int ph=g-lastsw; if(ph<SWEVERY){ phase_f[ph]+=mf; phase_n[ph]++; } }
		}
	}
	R->func=fsum/nmeas; R->work=wsum/nmeas; R->tfrac=tsum/nmeas; R->parfrac=psum/nmeas;
	R->reads = reads/((double)NCOMP*GEN);
	R->redund  = reads? redund/reads  : 0;
	R->gapfill = reads? gapfill/reads : 0;
	R->switches=nsw; R->overlap = nsw? novl/nsw : 0;
	/* where memory should pay if it pays anywhere: the generations right after a switch, versus
	   right before the next one. */
	double e=0,l=0; long en=0,ln=0;
	for(int ph=0;ph<5;ph++){ e+=phase_f[ph]; en+=phase_n[ph]; }
	for(int ph=SWEVERY-5;ph<SWEVERY;ph++){ l+=phase_f[ph]; ln+=phase_n[ph]; }
	R->early = en? e/en : 0; R->late = ln? l/ln : 0;
}

typedef struct { const char *nm; int env, rd; } Arm;

static void stats(const double *v,int n,double *mu,double *sd){
	double s=0; for(int i=0;i<n;i++) s+=v[i]; *mu=s/n;
	double q=0; for(int i=0;i<n;i++){ double d=v[i]-*mu; q+=d*d; }
	*sd = n>1? sqrt(q/(n-1)) : 0;
}
/* paired difference a-b across seeds: mean, standard error, and how many seeds favour a */
static void paired(const double *a,const double *b,int n,double *mu,double *sem,int *win){
	double d[NSEED]; *win=0;
	for(int i=0;i<n;i++){ d[i]=a[i]-b[i]; if(d[i]>0) (*win)++; }
	double sd; stats(d,n,mu,&sd); *sem = n>1? sd/sqrt((double)n) : 0;
}

int main(void)
{
	Arm arms[7]={
		{"STATIC    faithful",ENV_STATIC,   READ_FAITHFUL },
		{"FRESH     faithful",ENV_FRESH,    READ_FAITHFUL },
		{"FRESH    scrambled",ENV_FRESH,    READ_SCRAMBLED},
		{"FRESH          off",ENV_FRESH,    READ_OFF      },
		{"RECUR     faithful",ENV_RECUR,    READ_FAITHFUL },
		{"RECUR    scrambled",ENV_RECUR,    READ_SCRAMBLED},
		{"RECUR          off",ENV_RECUR,    READ_OFF      }};
	enum { A_STAT=0, A_FF, A_FS, A_FO, A_RF, A_RS, A_RO };

	static double F[7][NSEED], W[7][NSEED], T[7][NSEED], P[7][NSEED];
	static double EA[7][NSEED], LA[7][NSEED], RD[7][NSEED], OV[7][NSEED];

	printf("=== fluctuate.c — does memory pay when the world changes? ===\n");
	printf("  Scored on FUNCTION, not template abundance (the metric that voided threecarrier.c's\n");
	printf("  fourth attempt). Reading knockout reduced to ONE variable. %d seeds.\n", NSEED);
	printf("  THE HEADLINE IS FAITHFUL vs SCRAMBLED: reading also converts a template into a catalyst,\n");
	printf("  which raises nc and so raises growth AND fidelity. Only the scrambled control holds that\n");
	printf("  constant and isolates the information. genome.c's positive died to this control.\n\n");
	printf("  %d of %d types needed at a time; environment switches every %d generations;\n", NTARG,NTYPE,SWEVERY);
	printf("  FRESH redraws at random, RECUR cycles %d fixed sets. Measured over generations %d-%d.\n\n",
	       NENV,BURN,GEN-1);

	for(int k=0;k<7;k++){
		for(int s=0;s<NSEED;s++){
			Res R;
			RNG = 0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(s+1));
			for(int w=0;w<8;w++) xs();                     /* warm the stream */
			run(arms[k].env,arms[k].rd,&R);
			F[k][s]=R.func; W[k][s]=R.work; T[k][s]=R.tfrac; P[k][s]=R.parfrac;
			EA[k][s]=R.early; LA[k][s]=R.late; RD[k][s]=R.reads; OV[k][s]=R.overlap;
		}
	}

	printf("   arm                | function (max %d) |  working  | reads/c/g | templates | parasites\n",NTARG);
	printf("   -------------------+-------------------+-----------+-----------+-----------+----------\n");
	double fmu[7],fsd[7],wmu[7],wsd[7],tmu[7],tsd[7],pmu[7],psd[7],rmu[7],rsd[7];
	for(int k=0;k<7;k++){
		stats(F[k],NSEED,&fmu[k],&fsd[k]); stats(W[k],NSEED,&wmu[k],&wsd[k]);
		stats(T[k],NSEED,&tmu[k],&tsd[k]); stats(P[k],NSEED,&pmu[k],&psd[k]);
		stats(RD[k],NSEED,&rmu[k],&rsd[k]);
		printf("   %s |    %.3f +- %.3f  |   %.3f   |   %.3f   |   %.3f   |   %.3f\n",
		       arms[k].nm,fmu[k],fsd[k],wmu[k],rmu[k],tmu[k],pmu[k]);
	}
	printf("   (template fraction is REPORTED, and is deliberately NOT a criterion — see header)\n\n");

	/* ---- tests of the test ---- */
	double omu,osd; stats(OV[A_FF],NSEED,&omu,&osd);
	double omu2,osd2; stats(OV[A_RF],NSEED,&omu2,&osd2);
	int g1 = (rmu[A_FF]>0 && rmu[A_FS]>0 && rmu[A_RF]>0 && rmu[A_RS]>0
	          && rmu[A_FO]==0 && rmu[A_RO]==0);
	int g2 = (tmu[A_FF]>0.002 && tmu[A_RF]>0.002);
	int g3 = (omu < NTARG - 0.05) && (omu2 < NTARG - 0.05);
	printf("   G1 reads happen in reading arms, never in OFF arms : %s (%.3f/%.3f on, %.3f/%.3f off)\n",
	       g1?"PASS":"FAIL", rmu[A_FF],rmu[A_RF],rmu[A_FO],rmu[A_RO]);
	printf("   G2 templates actually exist (else no third carrier): %s (%.4f fresh, %.4f recur)\n",
	       g2?"PASS":"FAIL", tmu[A_FF],tmu[A_RF]);
	printf("   G3 the environment actually changes                 : %s (overlap %.2f fresh, %.2f recur, of %d)\n",
	       g3?"PASS":"FAIL", omu,omu2,NTARG);

	double d,se; int win;
	paired(F[A_FF],F[A_FS],NSEED,&d,&se,&win);
	double d_p1f=d, se_p1f=se; int w_p1f=win;
	paired(F[A_RF],F[A_RS],NSEED,&d,&se,&win);
	double d_p1r=d, se_p1r=se; int w_p1r=win;
	paired(F[A_FF],F[A_FO],NSEED,&d,&se,&win);
	double d_p2f=d, se_p2f=se;
	paired(F[A_RF],F[A_RO],NSEED,&d,&se,&win);
	double d_p2r=d, se_p2r=se;
	paired(F[A_STAT],F[A_FF],NSEED,&d,&se,&win);
	double d_p3=d, se_p3=se;

	int g4 = fabs(d_p1f)>2*se_p1f || fabs(d_p1r)>2*se_p1r || fabs(d_p2f)>2*se_p2f || fabs(d_p3)>2*se_p3;
	printf("   G4 the arms differ at all                           : %s\n\n", g4?"PASS":"FAIL");

	if(!(g1&&g2&&g3&&g4)){
		printf("   => VOID. A test of the test failed, so nothing below is a result. Fix the test, not\n"
		       "      the hypothesis.\n");
		return 0;
	}

	int p1f = d_p1f > 2*se_p1f;
	int p1r = d_p1r > 2*se_p1r;
	printf("   P1 FRESH: faithful beats scrambled : %s  %+.3f +- %.3f  (%d/%d seeds)\n",
	       p1f?"YES":"no", d_p1f, se_p1f, w_p1f, NSEED);
	printf("   P1 RECUR: faithful beats scrambled : %s  %+.3f +- %.3f  (%d/%d seeds)\n",
	       p1r?"YES":"no", d_p1r, se_p1r, w_p1r, NSEED);
	printf("   P2 reading beats no reading (CONFOUNDED — conversion, not memory):\n");
	printf("        FRESH %+.3f +- %.3f     RECUR %+.3f +- %.3f\n", d_p2f,se_p2f,d_p2r,se_p2r);
	printf("   P3 fluctuation costs function      : %s  static-minus-fresh %+.3f +- %.3f\n",
	       (d_p3>2*se_p3)?"YES":"no", d_p3, se_p3);
	printf("   post-switch -> pre-switch function (where memory should pay if anywhere):\n");
	{
		double ea,es,la,ls;
		stats(EA[A_FF],NSEED,&ea,&es); stats(LA[A_FF],NSEED,&la,&ls);
		double ea2,es2,la2,ls2;
		stats(EA[A_FS],NSEED,&ea2,&es2); stats(LA[A_FS],NSEED,&la2,&ls2);
		double ea3,es3,la3,ls3;
		stats(EA[A_RF],NSEED,&ea3,&es3); stats(LA[A_RF],NSEED,&la3,&ls3);
		double ea4,es4,la4,ls4;
		stats(EA[A_RS],NSEED,&ea4,&es4); stats(LA[A_RS],NSEED,&la4,&ls4);
		printf("        FRESH faithful %.3f -> %.3f | scrambled %.3f -> %.3f\n", ea,la,ea2,la2);
		printf("        RECUR faithful %.3f -> %.3f | scrambled %.3f -> %.3f\n", ea3,la3,ea4,la4);
	}
	printf("\n");

	if(p1f && p1r)
		printf("   => THE INFORMATION IS DOING WORK, IN BOTH WORLDS. Faithful reading beats a scrambled\n"
		       "      read that converts identically, so it is the type carried by the template and not\n"
		       "      the conversion. This is the control genome.c died to, passed.\n");
	else if(!p1f && p1r)
		printf("   => MEMORY PAYS ONLY WHEN THE WORLD REPEATS. P4 as pre-registered: a fresh random\n"
		       "      redraw gives a stored type the same %.2f odds a dice roll gets, so there is nothing\n"
		       "      to be faithful to; a recurring world makes a specific past configuration worth more\n"
		       "      than a random one, and faithful reading separates from its control. The condition is\n"
		       "      on the ENVIRONMENT, not the chemistry.\n", (double)NTARG/NTYPE);
	else if(p1f && !p1r)
		printf("   => BACKWARDS AND UNEXPLAINED. Faithful wins under a random redraw but not under a\n"
		       "      repeating world, which inverts the stated prediction. Do not narrate this;\n"
		       "      characterize it before claiming anything.\n");
	else
		printf("   => HONEST NULL, AND IT IS genome.c's RESULT AGAIN. Faithful reading does not beat a\n"
		       "      scrambled read in either world (%+.3f fresh, %+.3f recur), so whatever ON-vs-OFF\n"
		       "      gain exists is the CONVERSION of a template into a catalyst, not the information it\n"
		       "      carries. threecarrier.c's 0.294 -> 0.478 is therefore not evidence for memory.\n"
		       "      Memory still cannot pay under instantaneous selection, and a moving world does not\n"
		       "      rescue it.\n", d_p1f, d_p1r);
	return 0;
}