1use glam::Vec2;
8
9pub struct Rng {
16 state: u64,
17}
18
19impl Rng {
20 pub fn new(seed: u64) -> Self {
22 Self { state: seed.wrapping_add(1) }
23 }
24
25 pub fn next_u32(&mut self) -> u32 {
27 self.state = self.state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
29 ((self.state >> 33) ^ (self.state >> 17)) as u32
30 }
31
32 pub fn uniform(&mut self) -> f64 {
34 (self.next_u32() as f64) / (u32::MAX as f64 + 1.0)
35 }
36
37 pub fn normal(&mut self) -> f64 {
39 let u1 = self.uniform().max(1e-15);
40 let u2 = self.uniform();
41 (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
42 }
43
44 pub fn normal_with(&mut self, mean: f64, std_dev: f64) -> f64 {
46 mean + std_dev * self.normal()
47 }
48}
49
50pub struct BrownianMotion {
59 pub dimension: usize,
61 pub dt: f64,
63 pub variance: f64,
65}
66
67impl BrownianMotion {
68 pub fn new(dimension: usize, dt: f64) -> Self {
70 Self { dimension, dt, variance: 1.0 }
71 }
72
73 pub fn with_variance(dimension: usize, dt: f64, variance: f64) -> Self {
75 Self { dimension, dt, variance }
76 }
77
78 pub fn step(&self, rng: &mut Rng) -> Vec<f64> {
80 let scale = (self.variance * self.dt).sqrt();
81 (0..self.dimension).map(|_| rng.normal() * scale).collect()
82 }
83
84 pub fn path(&self, rng: &mut Rng, steps: usize) -> Vec<Vec<f64>> {
86 let mut trajectory = Vec::with_capacity(steps + 1);
87 let mut current = vec![0.0; self.dimension];
88 trajectory.push(current.clone());
89 for _ in 0..steps {
90 let dw = self.step(rng);
91 for (c, d) in current.iter_mut().zip(dw.iter()) {
92 *c += d;
93 }
94 trajectory.push(current.clone());
95 }
96 trajectory
97 }
98
99 pub fn path_from(&self, rng: &mut Rng, start: &[f64], steps: usize) -> Vec<Vec<f64>> {
101 let mut trajectory = Vec::with_capacity(steps + 1);
102 let mut current = start.to_vec();
103 trajectory.push(current.clone());
104 for _ in 0..steps {
105 let dw = self.step(rng);
106 for (c, d) in current.iter_mut().zip(dw.iter()) {
107 *c += d;
108 }
109 trajectory.push(current.clone());
110 }
111 trajectory
112 }
113}
114
115pub struct BrownianMotion2D {
121 pub dt: f64,
122 pub variance: f64,
123}
124
125impl BrownianMotion2D {
126 pub fn new(dt: f64) -> Self {
127 Self { dt, variance: 1.0 }
128 }
129
130 pub fn with_variance(dt: f64, variance: f64) -> Self {
131 Self { dt, variance }
132 }
133
134 pub fn step(&self, rng: &mut Rng) -> Vec2 {
136 let scale = (self.variance * self.dt).sqrt() as f32;
137 Vec2::new(rng.normal() as f32 * scale, rng.normal() as f32 * scale)
138 }
139
140 pub fn path(&self, rng: &mut Rng, steps: usize) -> Vec<Vec2> {
142 let mut trajectory = Vec::with_capacity(steps + 1);
143 let mut pos = Vec2::ZERO;
144 trajectory.push(pos);
145 for _ in 0..steps {
146 pos += self.step(rng);
147 trajectory.push(pos);
148 }
149 trajectory
150 }
151
152 pub fn path_from(&self, rng: &mut Rng, start: Vec2, steps: usize) -> Vec<Vec2> {
154 let mut trajectory = Vec::with_capacity(steps + 1);
155 let mut pos = start;
156 trajectory.push(pos);
157 for _ in 0..steps {
158 pos += self.step(rng);
159 trajectory.push(pos);
160 }
161 trajectory
162 }
163}
164
165pub struct BrownianBridge {
174 pub start: f64,
176 pub end: f64,
178 pub steps: usize,
180}
181
182impl BrownianBridge {
183 pub fn new(start: f64, end: f64, steps: usize) -> Self {
184 Self { start, end, steps }
185 }
186
187 pub fn path(&self, rng: &mut Rng) -> Vec<f64> {
189 let n = self.steps;
190 if n == 0 {
191 return vec![self.start];
192 }
193
194 let dt = 1.0 / n as f64;
195 let scale = dt.sqrt();
196
197 let mut w = Vec::with_capacity(n + 1);
199 w.push(0.0);
200 for _ in 0..n {
201 let prev = *w.last().unwrap();
202 w.push(prev + rng.normal() * scale);
203 }
204 let w_t = w[n];
205
206 let mut bridge = Vec::with_capacity(n + 1);
208 for k in 0..=n {
209 let t_frac = k as f64 / n as f64;
210 let val = self.start + (self.end - self.start) * t_frac + w[k] - t_frac * w_t;
211 bridge.push(val);
212 }
213 bridge
214 }
215
216 pub fn path_2d(&self, rng: &mut Rng, start: Vec2, end: Vec2) -> Vec<Vec2> {
218 let bx = BrownianBridge::new(start.x as f64, end.x as f64, self.steps);
219 let by = BrownianBridge::new(start.y as f64, end.y as f64, self.steps);
220 let px = bx.path(rng);
221 let py = by.path(rng);
222 px.iter()
223 .zip(py.iter())
224 .map(|(&x, &y)| Vec2::new(x as f32, y as f32))
225 .collect()
226 }
227}
228
229pub fn fractional_brownian(h: f64, steps: usize, rng: &mut Rng) -> Vec<f64> {
238 if steps == 0 {
239 return vec![0.0];
240 }
241
242 let n = steps;
243 let two_h = 2.0 * h;
244
245 let mut cov = vec![vec![0.0; n]; n];
247 for i in 0..n {
248 for j in 0..n {
249 let si = (i + 1) as f64;
250 let sj = (j + 1) as f64;
251 let diff = (si - sj).abs();
252 cov[i][j] = 0.5 * (si.powf(two_h) + sj.powf(two_h) - diff.powf(two_h));
253 }
254 }
255
256 let mut l = vec![vec![0.0; n]; n];
258 for i in 0..n {
259 for j in 0..=i {
260 let mut sum = 0.0;
261 for k in 0..j {
262 sum += l[i][k] * l[j][k];
263 }
264 if i == j {
265 let diag = cov[i][i] - sum;
266 l[i][j] = if diag > 0.0 { diag.sqrt() } else { 0.0 };
267 } else {
268 l[i][j] = if l[j][j].abs() > 1e-15 {
269 (cov[i][j] - sum) / l[j][j]
270 } else {
271 0.0
272 };
273 }
274 }
275 }
276
277 let z: Vec<f64> = (0..n).map(|_| rng.normal()).collect();
279
280 let mut path = Vec::with_capacity(n + 1);
282 path.push(0.0);
283 for i in 0..n {
284 let mut val = 0.0;
285 for j in 0..=i {
286 val += l[i][j] * z[j];
287 }
288 path.push(val);
289 }
290 path
291}
292
293pub struct BrownianRenderer {
299 pub character: char,
301 pub color: [f32; 4],
303 pub glow_radius: f32,
305}
306
307impl BrownianRenderer {
308 pub fn new() -> Self {
309 Self {
310 character: '·',
311 color: [0.4, 0.8, 1.0, 1.0],
312 glow_radius: 0.6,
313 }
314 }
315
316 pub fn with_character(mut self, ch: char) -> Self {
317 self.character = ch;
318 self
319 }
320
321 pub fn with_color(mut self, color: [f32; 4]) -> Self {
322 self.color = color;
323 self
324 }
325
326 pub fn render_path_2d(&self, path: &[Vec2]) -> Vec<(Vec2, char, [f32; 4])> {
328 let len = path.len();
329 if len == 0 {
330 return Vec::new();
331 }
332
333 let mut glyphs = Vec::with_capacity(len);
334 for (i, &pos) in path.iter().enumerate() {
335 let alpha = (i as f32 / len as f32) * self.color[3];
337 let color = [self.color[0], self.color[1], self.color[2], alpha];
338 glyphs.push((pos, self.character, color));
339 }
340 glyphs
341 }
342
343 pub fn render_path_nd(&self, path: &[Vec<f64>]) -> Vec<(Vec2, char, [f32; 4])> {
345 let len = path.len();
346 if len == 0 {
347 return Vec::new();
348 }
349
350 let mut glyphs = Vec::with_capacity(len);
351 for (i, point) in path.iter().enumerate() {
352 let x = point.first().copied().unwrap_or(0.0) as f32;
353 let y = point.get(1).copied().unwrap_or(0.0) as f32;
354 let pos = Vec2::new(x, y);
355 let alpha = (i as f32 / len as f32) * self.color[3];
356 let color = [self.color[0], self.color[1], self.color[2], alpha];
357 glyphs.push((pos, self.character, color));
358 }
359 glyphs
360 }
361
362 pub fn render_path_1d(&self, path: &[f64], x_scale: f32, y_scale: f32) -> Vec<(Vec2, char, [f32; 4])> {
364 let len = path.len();
365 if len == 0 {
366 return Vec::new();
367 }
368
369 let mut glyphs = Vec::with_capacity(len);
370 for (i, &val) in path.iter().enumerate() {
371 let pos = Vec2::new(i as f32 * x_scale, val as f32 * y_scale);
372 let alpha = ((i as f32 + 1.0) / len as f32) * self.color[3];
373 let color = [self.color[0], self.color[1], self.color[2], alpha];
374 glyphs.push((pos, self.character, color));
375 }
376 glyphs
377 }
378}
379
380impl Default for BrownianRenderer {
381 fn default() -> Self {
382 Self::new()
383 }
384}
385
386#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_rng_uniform_range() {
396 let mut rng = Rng::new(42);
397 for _ in 0..1000 {
398 let u = rng.uniform();
399 assert!(u >= 0.0 && u < 1.0, "uniform out of range: {}", u);
400 }
401 }
402
403 #[test]
404 fn test_rng_normal_mean_and_variance() {
405 let mut rng = Rng::new(123);
406 let n = 50_000;
407 let samples: Vec<f64> = (0..n).map(|_| rng.normal()).collect();
408 let mean = samples.iter().sum::<f64>() / n as f64;
409 let var = samples.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
410 assert!(mean.abs() < 0.05, "normal mean too far from 0: {}", mean);
411 assert!((var - 1.0).abs() < 0.1, "normal variance too far from 1: {}", var);
412 }
413
414 #[test]
415 fn test_brownian_motion_dimensions() {
416 let bm = BrownianMotion::new(3, 0.01);
417 let mut rng = Rng::new(7);
418 let step = bm.step(&mut rng);
419 assert_eq!(step.len(), 3);
420
421 let path = bm.path(&mut rng, 100);
422 assert_eq!(path.len(), 101);
423 assert_eq!(path[0], vec![0.0, 0.0, 0.0]);
424 }
425
426 #[test]
427 fn test_brownian_variance_scales_with_time() {
428 let dt = 0.01;
430 let steps = 1000; let trials = 2000;
432 let mut rng = Rng::new(999);
433 let bm = BrownianMotion::new(1, dt);
434
435 let mut endpoints = Vec::with_capacity(trials);
436 for _ in 0..trials {
437 let path = bm.path(&mut rng, steps);
438 endpoints.push(path[steps][0]);
439 }
440
441 let mean = endpoints.iter().sum::<f64>() / trials as f64;
442 let var = endpoints.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / trials as f64;
443 let expected_var = dt * steps as f64; assert!(mean.abs() < 0.5, "BM mean should be ~0, got {}", mean);
446 assert!(
447 (var - expected_var).abs() < 2.0,
448 "BM variance should be ~{}, got {}",
449 expected_var,
450 var
451 );
452 }
453
454 #[test]
455 fn test_brownian_motion_2d() {
456 let bm = BrownianMotion2D::new(0.01);
457 let mut rng = Rng::new(55);
458 let path = bm.path(&mut rng, 200);
459 assert_eq!(path.len(), 201);
460 assert_eq!(path[0], Vec2::ZERO);
461 }
462
463 #[test]
464 fn test_brownian_bridge_endpoints() {
465 let bridge = BrownianBridge::new(0.0, 5.0, 500);
466 let mut rng = Rng::new(42);
467 let path = bridge.path(&mut rng);
468 assert_eq!(path.len(), 501);
469 assert!((path[0] - 0.0).abs() < 1e-10, "bridge should start at 0");
470 assert!((path[500] - 5.0).abs() < 1e-10, "bridge should end at 5, got {}", path[500]);
471 }
472
473 #[test]
474 fn test_fractional_brownian_standard() {
475 let mut rng = Rng::new(42);
477 let path = fractional_brownian(0.5, 100, &mut rng);
478 assert_eq!(path.len(), 101);
479 assert_eq!(path[0], 0.0);
480 }
481
482 #[test]
483 fn test_fractional_brownian_persistent() {
484 let mut rng = Rng::new(42);
486 let path = fractional_brownian(0.8, 200, &mut rng);
487 assert_eq!(path.len(), 201);
488 assert_eq!(path[0], 0.0);
489 assert!(path.iter().any(|&v| v != 0.0));
491 }
492
493 #[test]
494 fn test_fractional_brownian_antipersistent() {
495 let mut rng = Rng::new(42);
497 let path = fractional_brownian(0.2, 200, &mut rng);
498 assert_eq!(path.len(), 201);
499 assert_eq!(path[0], 0.0);
500 }
501
502 #[test]
503 fn test_brownian_renderer() {
504 let renderer = BrownianRenderer::new().with_character('*');
505 let bm = BrownianMotion2D::new(0.01);
506 let mut rng = Rng::new(10);
507 let path = bm.path(&mut rng, 50);
508 let glyphs = renderer.render_path_2d(&path);
509 assert_eq!(glyphs.len(), 51);
510 assert_eq!(glyphs[0].1, '*');
511 }
512
513 #[test]
514 fn test_brownian_bridge_2d() {
515 let bridge = BrownianBridge::new(0.0, 0.0, 100);
516 let mut rng = Rng::new(77);
517 let start = Vec2::new(1.0, 2.0);
518 let end = Vec2::new(5.0, 8.0);
519 let path = bridge.path_2d(&mut rng, start, end);
520 assert_eq!(path.len(), 101);
521 assert!((path[0].x - 1.0).abs() < 1e-4);
522 assert!((path[0].y - 2.0).abs() < 1e-4);
523 assert!((path[100].x - 5.0).abs() < 1e-4);
524 assert!((path[100].y - 8.0).abs() < 1e-4);
525 }
526
527 #[test]
528 fn test_render_path_1d() {
529 let renderer = BrownianRenderer::new();
530 let path = vec![0.0, 1.0, -0.5, 2.0];
531 let glyphs = renderer.render_path_1d(&path, 1.0, 1.0);
532 assert_eq!(glyphs.len(), 4);
533 assert!((glyphs[0].0.x - 0.0).abs() < 1e-5);
534 assert!((glyphs[2].0.y - (-0.5)).abs() < 1e-5);
535 }
536}