vibe_code/
utils.rs

1//! Provides utility functions for timing and random number generation.
2
3use std::sync::OnceLock;
4use std::time::Instant;
5
6/// A global, thread-safe timestamp of when the application started.
7/// This is used as a monotonic time source for performance metrics.
8static TIMER_START: OnceLock<Instant> = OnceLock::new();
9
10/// Initializes the global timer. Must be called once at startup.
11///
12/// # Returns
13/// - `Ok(())` if the timer was successfully initialized.
14/// - `Err(&'static str)` if the timer was already initialized.
15pub fn timer_init() -> Result<(), &'static str> {
16    TIMER_START
17        .set(Instant::now())
18        .map_err(|_| "Timer already initialized")
19}
20
21/// Returns the number of nanoseconds elapsed since `timer_init()` was called.
22///
23/// # Panics
24/// Panics if the timer has not been initialized.
25#[inline(always)]
26pub fn elapsed_ns() -> u64 {
27    let start = TIMER_START
28        .get()
29        .expect("Timer not initialized. Call timer_init() first.");
30    Instant::now().duration_since(*start).as_nanos() as u64
31}
32
33// Constants used for the random number generation algorithm.
34const VIBE_MULT_A: u64 = 0x9e3779b97f4a7c15;
35const VIBE_MULT_B: u64 = 0xc6a4a7935bd1e995;
36const VIBE_MULT_C: u64 = 0xe7037ed1a0b428db;
37
38/// A fast, non-cryptographic random number generator.
39#[derive(Clone, Copy, Debug)]
40pub struct VibeRng {
41    state_a: u64,
42    state_b: u64,
43    counter: u64,
44}
45
46/// Strategies for biasing the random number generator.
47/// Used by the task router to select nodes.
48#[derive(Clone, Copy, Debug)]
49pub enum BiasStrategy {
50    None,
51    Exponential,
52    Weighted,
53    Power(f64),
54    Stepped,
55}
56
57impl VibeRng {
58    /// Creates a new `VibeRng` instance from a given seed.
59    #[inline(always)]
60    pub fn new(seed: u64) -> Self {
61        let mut z = seed.wrapping_add(VIBE_MULT_A);
62
63        z = (z ^ (z >> 30)).wrapping_mul(VIBE_MULT_B);
64        z = (z ^ (z >> 27)).wrapping_mul(VIBE_MULT_C);
65        z = z ^ (z >> 31);
66        let state_a = z;
67
68        z = state_a.wrapping_add(VIBE_MULT_A);
69        z = (z ^ (z >> 29)).wrapping_mul(VIBE_MULT_C);
70        z = (z ^ (z >> 26)).wrapping_mul(VIBE_MULT_B);
71        z = z ^ (z >> 30);
72        let state_b = z;
73
74        z = state_b.wrapping_add(VIBE_MULT_A);
75        z = (z ^ (z >> 28)).wrapping_mul(VIBE_MULT_B);
76        let counter = z ^ (z >> 32);
77
78        Self {
79            state_a,
80            state_b,
81            counter,
82        }
83    }
84
85    /// Generates a random number within a given range (inclusive).
86    #[inline(always)]
87    pub fn range(&mut self, min: u64, max: u64) -> u64 {
88        if min >= max {
89            return min;
90        }
91
92        let range = max - min + 1;
93        let result = match range {
94            1 => 0,
95            2..=256 if range.is_power_of_two() => vibe_range_pow2(self, range),
96            2..=256 => vibe_range_small(self, range),
97            _ => vibe_range_unbiased(self, range),
98        };
99        min + result
100    }
101
102    /// Generates a biased random number within a given range.
103    #[inline(always)]
104    pub fn range_biased(&mut self, min: u64, max: u64, bias: BiasStrategy) -> u64 {
105        if min >= max {
106            return min;
107        }
108
109        match bias {
110            BiasStrategy::None => self.range(min, max),
111            _ => self.range_biased_impl(min, max, bias),
112        }
113    }
114
115    /// Generates the next raw `u64` random number.
116    #[inline(always)]
117    pub fn next_raw(&mut self) -> u64 {
118        let s0 = self.state_a;
119        let mut s1 = self.state_b;
120
121        s1 ^= s0;
122        self.state_a = s0.rotate_left(26) ^ s1 ^ (s1 << 9);
123        self.state_b = s1.rotate_left(13);
124
125        let result = s0.wrapping_add(s1);
126        let result = result ^ (result >> 31);
127        let result = result.wrapping_mul(VIBE_MULT_B);
128        let result = result ^ (result >> 29);
129
130        self.counter = self.counter.wrapping_add(1);
131        result
132    }
133
134    /// Generates the next `f64` random number between 0.0 and 1.0 (fast but less precision).
135    #[inline(always)]
136    fn next_f64_fast(&mut self) -> f64 {
137        const SCALE: f64 = 1.0 / (1u32 << 24) as f64;
138        ((self.next_raw() >> 40) as u32 as f64) * SCALE
139    }
140
141    /// Generates the next `f64` random number between 0.0 and 1.0 (full precision).
142    #[inline(always)]
143    pub fn next_f64(&mut self) -> f64 {
144        const SCALE: f64 = 1.0 / (1u64 << 53) as f64;
145        ((self.next_raw() >> 11) as f64) * SCALE
146    }
147
148    /// Internal implementation for biased range generation.
149    #[inline(always)]
150    fn range_biased_impl(&mut self, min: u64, max: u64, bias: BiasStrategy) -> u64 {
151        let full_range = max - min + 1;
152        let quarter_size = full_range / 4;
153        let quarter_end = min + quarter_size.saturating_sub(1);
154
155        match bias {
156            BiasStrategy::Weighted => {
157                if self.next_raw() % 100 < 60 {
158                    self.range(min, quarter_end.min(max))
159                } else {
160                    let rest_start = (min + quarter_size).min(max);
161                    if rest_start > max {
162                        min
163                    } else {
164                        self.range(rest_start, max)
165                    }
166                }
167            }
168
169            BiasStrategy::Exponential => {
170                let u = self.next_f64_fast();
171                let biased_u = (-u * 3.0).exp();
172                let offset = (biased_u * full_range as f64) as u64;
173                min + offset.min(full_range - 1)
174            }
175
176            BiasStrategy::Power(power) => {
177                let u = self.next_f64_fast();
178                let biased_u = if power < 1.0 {
179                    1.0 - (1.0 - u).powf(1.0 / power)
180                } else {
181                    u.powf(power)
182                };
183                let offset = (biased_u * full_range as f64) as u64;
184                min + offset.min(full_range - 1)
185            }
186
187            BiasStrategy::Stepped => {
188                if (self.next_raw() & 3) < 3 {
189                    self.range(min, quarter_end.min(max))
190                } else {
191                    self.range(min, max)
192                }
193            }
194
195            BiasStrategy::None => unreachable!(),
196        }
197    }
198}
199
200/// Unbiased range generation for large ranges.
201#[inline(always)]
202fn vibe_range_unbiased(rng: &mut VibeRng, range: u64) -> u64 {
203    if range <= 1 {
204        return 0;
205    }
206
207    let mut random = rng.next_raw();
208    let mut multiresult = (random as u128) * (range as u128);
209    let mut leftover = multiresult as u64;
210
211    if leftover < range {
212        let threshold = (0u64.wrapping_sub(range)) % range;
213        while leftover < threshold {
214            random = rng.next_raw();
215            multiresult = (random as u128) * (range as u128);
216            leftover = multiresult as u64;
217        }
218    }
219    (multiresult >> 64) as u64
220}
221
222/// Fast range generation for powers of two.
223#[inline(always)]
224fn vibe_range_pow2(rng: &mut VibeRng, range: u64) -> u64 {
225    debug_assert!(range.is_power_of_two());
226    rng.next_raw() & (range - 1)
227}
228
229/// Fast range generation for small ranges.
230#[inline(always)]
231fn vibe_range_small(rng: &mut VibeRng, range: u64) -> u64 {
232    if range.is_power_of_two() {
233        return vibe_range_pow2(rng, range);
234    }
235
236    let mask = range.next_power_of_two() - 1;
237    loop {
238        let candidate = rng.next_raw() & mask;
239        if candidate < range {
240            return candidate;
241        }
242    }
243}