#pragma once #include #include #include #include #include #include #include #include #include #ifdef __CUDACC__ #define HD __host__ __device__ #else #define HD #endif namespace show { constexpr uint64_t A = 0x5DEECE66DULL; constexpr uint64_t B = 11; constexpr uint64_t MASK48 = (1ULL << 48) - 1; constexpr uint64_t SLIME_XOR = 987234911; constexpr uint32_t REJECTION_START = 2147483640U; constexpr int MAX_N = 8; // Explicit Java int wrapping and sign extension; no signed overflow in C++. HD inline uint64_t javaLong(uint32_t v) { return (v & 0x80000000U) ? uint64_t(v) | 0xFFFFFFFF00000000ULL : uint64_t(v); } HD inline uint64_t xTerm(int32_t x) { const uint32_t u = uint32_t(x); return javaLong(u*u*4987142U) + javaLong(u*5947611U); } HD inline uint64_t zTerm(int32_t z) { const uint32_t u = uint32_t(z); return javaLong(u*u)*4392871ULL + javaLong(u*389711U); } HD inline uint64_t term(int32_t x, int32_t z) { return xTerm(x) + zTerm(z); } // Stage 01/02/03/04: the original two-XOR JavaRandom path, with defined arithmetic. struct JavaRandom { uint64_t state; HD explicit JavaRandom(uint64_t input) : state((input ^ A) & MASK48) {} HD uint32_t next31() { state = (state*A+B) & MASK48; return uint32_t(state >> 17); } HD bool slime() { for (;;) { const uint32_t q = next31(); if (q < REJECTION_START) return q % 10 == 0; } } }; HD inline bool original(uint64_t seed, uint64_t t) { JavaRandom random((seed+t) ^ SLIME_XOR); return random.slime(); } // State equations used by the Random-properties discussion and analytical demos. HD inline uint64_t firstState(uint64_t seed, uint64_t t) { const uint64_t u = ((seed+t) ^ (SLIME_XOR ^ A)) & MASK48; return (u*A+B) & MASK48; } HD inline bool fromState(uint64_t state) { for (;;) { const uint32_t q = uint32_t(state >> 17); if (q < REJECTION_START) return q % 10 == 0; state = (state*A+B) & MASK48; } } HD inline bool fused(uint64_t seed, uint64_t t) { return fromState(firstState(seed,t)); } template HD inline bool solid(uint64_t seed, int32_t x, int32_t z, int n) { for (int dx=0; dx=argc) throw std::runtime_error("missing value: "+key); const std::string v=argv[i]; if(key=="--seed") a.seed=positive(v); else if(key=="--seeds") a.seeds=positive(v); else if(key=="--x0") a.x0=std::stoi(v); else if(key=="--z0") a.z0=std::stoi(v); else if(key=="--width") a.width=positive(v); else if(key=="--height") a.height=positive(v); else if(key=="-n") a.n=std::stoi(v); else if(key=="--repeats") a.repeats=std::stoi(v); else if(key=="--cpu-threads") a.cpuThreads=std::stoi(v); else if(key=="--capacity") a.capacity=positive(v); else if(key=="--modes") a.modes=v; else throw std::runtime_error("unknown option: "+key); } if(a.n<1 || a.n>MAX_N || !a.seeds || !a.width || !a.height || a.repeats<1 || a.cpuThreads<1 || a.cpuThreads>256 || !a.capacity || a.capacity>10000000 || a.width>10000000 || a.height>10000000 || a.seeds>10000000) throw std::runtime_error("invalid range/size/count"); if(a.seeds-1 > UINT64_MAX-a.seed || a.seeds > UINT64_MAX/a.positions()) throw std::runtime_error("seed/work range overflow"); // Include the expanded 16x16 tile's unused fringe so CUDA precomputation is safe. if(int64_t(a.x0)+int64_t((a.width+15)/16*16)+a.n-2 > INT32_MAX || int64_t(a.z0)+int64_t((a.height+15)/16*16)+a.n-2 > INT32_MAX) throw std::runtime_error("coordinate footprint/tile exceeds int32"); return a; } struct Hit { uint64_t seed; int32_t x,z; bool operator<(const Hit& o) const { return std::tie(seed,x,z)(std::chrono::steady_clock::now()-t).count(); } inline double median(std::vector v) { std::sort(v.begin(),v.end()); return v.size()%2 ? v[v.size()/2] : (v[v.size()/2-1]+v[v.size()/2])/2; } inline void array(const std::vector& v) { std::cout << '['; for(size_t i=0;i& v) { std::cout << '['; for(size_t i=0;i