epoch.go raw

   1  // Package epoch formalizes the synchronization between binary-aligned
   2  // (2^b) and decimal-aligned (10^a) cadences in dendrite's subsystems.
   3  //
   4  // Binary and decimal are multiplicatively incommensurable: log2(10) is
   5  // transcendental, so no integer power of 2 exactly equals an integer
   6  // power of 10. When two subsystems operate at cadences from different
   7  // bases — binary for memory/crypto/lattice, decimal for metrics/timing/
   8  // human interfaces — they drift apart at every step. The drift is the
   9  // phase angle θ = ln(2^b / 10^a), and it only resolves to zero at the
  10  // synchronization epoch: the product 10^a × 2^b.
  11  //
  12  // On the real line, the ratio 2^b / 10^a is an irreducible gap.
  13  // In the complex plane, the gap becomes a rotation: the binary and
  14  // decimal clocks trace circles of different periods, and the "missing"
  15  // alignment lives in the imaginary component of the complex logarithm.
  16  // The epoch is the point where both clocks return to the real axis
  17  // simultaneously.
  18  package epoch
  19  
  20  import (
  21  	"fmt"
  22  
  23  	"git.smesh.lol/gnarl-hamadryad/ratio"
  24  )
  25  
  26  // Epoch represents a synchronization period between a decimal cadence
  27  // (10^DecExp) and a binary cadence (2^BinExp). The synchronization
  28  // period is DecFactor × BinFactor = 10^DecExp × 2^BinExp.
  29  type Epoch struct {
  30  	DecExp    int   // exponent of 10
  31  	BinExp    int   // exponent of 2
  32  	DecFactor int64 // 10^DecExp, precomputed
  33  	BinFactor int64 // 2^BinExp, precomputed
  34  	Period    int64 // DecFactor × BinFactor
  35  }
  36  
  37  // New creates an Epoch from the decimal exponent a and binary exponent b.
  38  // The synchronization period is 10^a × 2^b.
  39  // Panics if either exponent is negative or the result overflows int64.
  40  func New(decExp, binExp int) Epoch {
  41  	if decExp < 0 || binExp < 0 {
  42  		panic("epoch: negative exponent")
  43  	}
  44  	dec := pow10(decExp)
  45  	bin := pow2(binExp)
  46  	// Overflow check: if Period / dec != bin, we overflowed.
  47  	period := dec * bin
  48  	if dec != 0 && period/dec != bin {
  49  		panic("epoch: period overflow")
  50  	}
  51  	return Epoch{
  52  		DecExp:    decExp,
  53  		BinExp:    binExp,
  54  		DecFactor: dec,
  55  		BinFactor: bin,
  56  		Period:    period,
  57  	}
  58  }
  59  
  60  // Named epochs for dendrite subsystem synchronization.
  61  var (
  62  	// Colony is the generation synchronization epoch: 10^2 × 2^8 = 25600.
  63  	// Binary-aligned logging every 256 steps, decimal-aligned crypto
  64  	// verification every 100 steps. At step 25600 both clocks land on zero.
  65  	Colony = New(2, 8)
  66  
  67  	// EngineDissolveLCM is the tick synchronization epoch for concurrent
  68  	// engine (2^4 = 16ms) and dissolve (10^2 = 100ms) loops.
  69  	// 10^2 × 2^4 = 1600. The raw LCM(16, 100) = 400; the epoch is 4×LCM,
  70  	// the smallest period expressible as a pure 10^a × 2^b product.
  71  	EngineDissolveLCM = New(2, 4)
  72  
  73  	// FitnessSampling is the epoch-aligned sample limit for binary
  74  	// similarity: 10^2 × 2^8 = 25600. Replaces the ad-hoc 10000.
  75  	FitnessSampling = New(2, 8)
  76  
  77  	// CryptoWalk128 is the walk step count for Security128 (N=256=2^8):
  78  	// 2^10 = 1024. Pure binary — the walk operates in a 2^8-dimensional
  79  	// space, so the walk length shares the binary base.
  80  	CryptoWalk128 = New(0, 10)
  81  
  82  	// CryptoWalk192 is the walk step count for Security192 (N=384):
  83  	// 10^2 × 2^4 = 1600. N=384=3×2^7 is not a pure power of 2,
  84  	// so the epoch carries a decimal component.
  85  	CryptoWalk192 = New(2, 4)
  86  
  87  	// CryptoWalk256 is the walk step count for Security256 (N=512=2^9):
  88  	// 10^3 × 2^1 = 2000. Already epoch-aligned.
  89  	CryptoWalk256 = New(3, 1)
  90  
  91  	// EWMACap is the denominator cap for EWMA rescaling:
  92  	// 10^9 × 2^0 = 1_000_000_000. Pure decimal — the cap exists to
  93  	// provide ~9 decimal digits of precision for human-readable output.
  94  	EWMACap = New(9, 0)
  95  )
  96  
  97  // OnBoundary reports whether step n falls on the epoch boundary.
  98  // Returns false for n <= 0.
  99  func (e Epoch) OnBoundary(n int64) bool {
 100  	return n > 0 && n%e.Period == 0
 101  }
 102  
 103  // OnDecimal reports whether step n falls on the decimal cadence boundary.
 104  // Returns false for n <= 0.
 105  func (e Epoch) OnDecimal(n int64) bool {
 106  	return n > 0 && n%e.DecFactor == 0
 107  }
 108  
 109  // OnBinary reports whether step n falls on the binary cadence boundary.
 110  // Returns false for n <= 0.
 111  func (e Epoch) OnBinary(n int64) bool {
 112  	return n > 0 && n%e.BinFactor == 0
 113  }
 114  
 115  // Phase returns the drift between the binary and decimal clocks at step n,
 116  // as an exact Ratio in [-1/2, 1/2].
 117  //
 118  // At epoch boundaries, Phase returns 0/1.
 119  // At intermediate steps, Phase is the difference between the fractional
 120  // positions of the two clocks:
 121  //
 122  //	Phase(n) = (n mod BinFactor)/BinFactor − (n mod DecFactor)/DecFactor
 123  //
 124  // This is the discrete analog of the transcendental phase angle
 125  // θ = ln(2^b / 10^a) that separates binary and decimal in the
 126  // complex plane. All arithmetic is exact rational.
 127  func (e Epoch) Phase(n int64) ratio.Ratio {
 128  	binFrac := ratio.New(n%e.BinFactor, e.BinFactor)
 129  	decFrac := ratio.New(n%e.DecFactor, e.DecFactor)
 130  	return binFrac.Sub(decFrac)
 131  }
 132  
 133  // ComplexPhase holds the real and imaginary components of the
 134  // binary/decimal clock relationship at a step.
 135  type ComplexPhase struct {
 136  	Re ratio.Ratio // drift: binFrac - decFrac
 137  	Im ratio.Ratio // cross-correlation: binFrac * decFrac
 138  }
 139  
 140  // ImagPhase returns the cross-correlation between the binary and
 141  // decimal clocks at step n, as an exact Ratio in [0, 1/4].
 142  //
 143  // The imaginary phase is the product of the two fractional positions:
 144  //
 145  //	ImagPhase(n) = (n mod BinFactor)/BinFactor × (n mod DecFactor)/DecFactor
 146  //
 147  // This is maximal (1/4) when both clocks are at their midpoints
 148  // simultaneously, and zero at any factor boundary. It captures
 149  // the "when both clocks peak together" correlation that the real
 150  // Phase (their difference) cannot represent.
 151  //
 152  // On the complex plane: the real phase is the signed distance between
 153  // the two clock hands. The imaginary phase is their geometric mean
 154  // position — when both are high, the system is in a correlated state
 155  // that lives on the imaginary axis.
 156  func (e Epoch) ImagPhase(n int64) ratio.Ratio {
 157  	binFrac := ratio.New(n%e.BinFactor, e.BinFactor)
 158  	decFrac := ratio.New(n%e.DecFactor, e.DecFactor)
 159  	return binFrac.Mul(decFrac)
 160  }
 161  
 162  // ComplexPhase returns both the real drift and imaginary cross-correlation
 163  // at step n.
 164  func (e Epoch) ComplexPhase(n int64) ComplexPhase {
 165  	return ComplexPhase{
 166  		Re: e.Phase(n),
 167  		Im: e.ImagPhase(n),
 168  	}
 169  }
 170  
 171  // BinaryStepsPerDecimal returns the exact ratio BinFactor / DecFactor.
 172  // For Colony: 256/100 = 64/25 = 2.56.
 173  func (e Epoch) BinaryStepsPerDecimal() ratio.Ratio {
 174  	return ratio.New(e.BinFactor, e.DecFactor)
 175  }
 176  
 177  // DecimalStepsPerBinary returns the exact ratio DecFactor / BinFactor.
 178  // For Colony: 100/256 = 25/64.
 179  func (e Epoch) DecimalStepsPerBinary() ratio.Ratio {
 180  	return ratio.New(e.DecFactor, e.BinFactor)
 181  }
 182  
 183  // String returns "10^a×2^b=N" for debugging.
 184  func (e Epoch) String() string {
 185  	return fmt.Sprintf("10^%d×2^%d=%d", e.DecExp, e.BinExp, e.Period)
 186  }
 187  
 188  // pow10 returns 10^n using integer multiplication. Panics on overflow.
 189  func pow10(n int) int64 {
 190  	var r int64 = 1
 191  	for range n {
 192  		next := r * 10
 193  		if next/10 != r {
 194  			panic("epoch: 10^n overflow")
 195  		}
 196  		r = next
 197  	}
 198  	return r
 199  }
 200  
 201  // pow2 returns 2^n using bit shift. Panics if n >= 63.
 202  func pow2(n int) int64 {
 203  	if n >= 63 {
 204  		panic("epoch: 2^n overflow")
 205  	}
 206  	return 1 << n
 207  }
 208