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