1use std::f64::consts::PI;
5
6#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct Vec3 {
12 pub x: f64,
13 pub y: f64,
14 pub z: f64,
15}
16
17impl Vec3 {
18 pub fn new(x: f64, y: f64, z: f64) -> Self { Vec3 { x, y, z } }
19 pub fn zero() -> Self { Vec3 { x: 0.0, y: 0.0, z: 0.0 } }
20 pub fn dot(self, other: Self) -> f64 { self.x * other.x + self.y * other.y + self.z * other.z }
21 pub fn cross(self, other: Self) -> Self {
22 Vec3 {
23 x: self.y * other.z - self.z * other.y,
24 y: self.z * other.x - self.x * other.z,
25 z: self.x * other.y - self.y * other.x,
26 }
27 }
28 pub fn len_sq(self) -> f64 { self.dot(self) }
29 pub fn len(self) -> f64 { self.len_sq().sqrt() }
30 pub fn normalize(self) -> Self {
31 let l = self.len();
32 if l < 1e-300 { return Self::zero(); }
33 Vec3 { x: self.x / l, y: self.y / l, z: self.z / l }
34 }
35 pub fn dist(self, other: Self) -> f64 { (self - other).len() }
36}
37
38impl std::ops::Add for Vec3 {
39 type Output = Self;
40 fn add(self, o: Self) -> Self { Vec3 { x: self.x + o.x, y: self.y + o.y, z: self.z + o.z } }
41}
42impl std::ops::Sub for Vec3 {
43 type Output = Self;
44 fn sub(self, o: Self) -> Self { Vec3 { x: self.x - o.x, y: self.y - o.y, z: self.z - o.z } }
45}
46impl std::ops::Mul<f64> for Vec3 {
47 type Output = Self;
48 fn mul(self, s: f64) -> Self { Vec3 { x: self.x * s, y: self.y * s, z: self.z * s } }
49}
50impl std::ops::Div<f64> for Vec3 {
51 type Output = Self;
52 fn div(self, s: f64) -> Self { Vec3 { x: self.x / s, y: self.y / s, z: self.z / s } }
53}
54impl std::ops::Neg for Vec3 {
55 type Output = Self;
56 fn neg(self) -> Self { Vec3 { x: -self.x, y: -self.y, z: -self.z } }
57}
58impl std::ops::AddAssign for Vec3 {
59 fn add_assign(&mut self, o: Self) { self.x += o.x; self.y += o.y; self.z += o.z; }
60}
61impl std::ops::MulAssign<f64> for Vec3 {
62 fn mul_assign(&mut self, s: f64) { self.x *= s; self.y *= s; self.z *= s; }
63}
64
65#[derive(Clone, Debug)]
71pub struct CellularAutomaton<T: Clone> {
72 pub grid: Vec<Vec<T>>,
73 pub width: usize,
74 pub height: usize,
75}
76
77impl<T: Clone + Default> CellularAutomaton<T> {
78 pub fn new(width: usize, height: usize) -> Self {
79 Self {
80 grid: vec![vec![T::default(); width]; height],
81 width,
82 height,
83 }
84 }
85 pub fn get(&self, x: usize, y: usize) -> &T { &self.grid[y][x] }
86 pub fn set(&mut self, x: usize, y: usize, v: T) { self.grid[y][x] = v; }
87 pub fn get_wrapped(&self, x: i64, y: i64) -> &T {
88 let xi = ((x % self.width as i64 + self.width as i64) % self.width as i64) as usize;
89 let yi = ((y % self.height as i64 + self.height as i64) % self.height as i64) as usize;
90 &self.grid[yi][xi]
91 }
92}
93
94#[derive(Clone, Debug)]
100pub struct GameOfLife {
101 pub grid: Vec<Vec<bool>>,
102 pub width: usize,
103 pub height: usize,
104}
105
106impl GameOfLife {
107 pub fn new(width: usize, height: usize) -> Self {
108 Self { grid: vec![vec![false; width]; height], width, height }
109 }
110
111 pub fn from_pattern(width: usize, height: usize, pattern: &[(usize, usize)]) -> Self {
112 let mut g = Self::new(width, height);
113 for &(x, y) in pattern {
114 if x < width && y < height { g.grid[y][x] = true; }
115 }
116 g
117 }
118
119 pub fn count_neighbors(&self, x: usize, y: usize) -> u8 {
120 let mut count = 0u8;
121 for dy in -1i64..=1 {
122 for dx in -1i64..=1 {
123 if dx == 0 && dy == 0 { continue; }
124 let nx = ((x as i64 + dx).rem_euclid(self.width as i64)) as usize;
125 let ny = ((y as i64 + dy).rem_euclid(self.height as i64)) as usize;
126 if self.grid[ny][nx] { count += 1; }
127 }
128 }
129 count
130 }
131
132 pub fn step(&mut self) {
133 let old = self.grid.clone();
134 for y in 0..self.height {
135 for x in 0..self.width {
136 let n = {
137 let mut count = 0u8;
138 for dy in -1i64..=1 {
139 for dx in -1i64..=1 {
140 if dx == 0 && dy == 0 { continue; }
141 let nx = ((x as i64 + dx).rem_euclid(self.width as i64)) as usize;
142 let ny = ((y as i64 + dy).rem_euclid(self.height as i64)) as usize;
143 if old[ny][nx] { count += 1; }
144 }
145 }
146 count
147 };
148 self.grid[y][x] = if old[y][x] {
149 n == 2 || n == 3
150 } else {
151 n == 3
152 };
153 }
154 }
155 }
156
157 pub fn count_living(&self) -> usize {
158 self.grid.iter().flat_map(|row| row.iter()).filter(|&&c| c).count()
159 }
160
161 pub fn is_stable(&self) -> bool {
162 let prev = self.grid.clone();
163 let mut tmp = self.clone();
164 tmp.step();
165 tmp.grid == prev
166 }
167
168 pub fn place_glider(&mut self, ox: usize, oy: usize) {
170 let cells = [(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)];
171 for (dx, dy) in cells {
172 let x = ox + dx; let y = oy + dy;
173 if x < self.width && y < self.height { self.grid[y][x] = true; }
174 }
175 }
176
177 pub fn place_glider_gun(&mut self, ox: usize, oy: usize) {
179 let cells: &[(usize, usize)] = &[
180 (0,4),(0,5),(1,4),(1,5),
181 (10,4),(10,5),(10,6),(11,3),(11,7),(12,2),(12,8),(13,2),(13,8),
182 (14,5),(15,3),(15,7),(16,4),(16,5),(16,6),(17,5),
183 (20,2),(20,3),(20,4),(21,2),(21,3),(21,4),(22,1),(22,5),
184 (24,0),(24,1),(24,5),(24,6),
185 (34,2),(34,3),(35,2),(35,3),
186 ];
187 for &(dx, dy) in cells {
188 let x = ox + dx; let y = oy + dy;
189 if x < self.width && y < self.height { self.grid[y][x] = true; }
190 }
191 }
192
193 pub fn place_pulsar(&mut self, ox: usize, oy: usize) {
195 let cols = [2usize, 3, 4, 8, 9, 10];
196 let rows = [0usize, 5, 7, 12];
197 for &r in &rows {
198 for &c in &cols {
199 let x = ox + c; let y = oy + r;
200 if x < self.width && y < self.height { self.grid[y][x] = true; }
201 }
202 }
203 for &c in &rows {
205 for &r in &cols {
206 let x = ox + c; let y = oy + r;
207 if x < self.width && y < self.height { self.grid[y][x] = true; }
208 }
209 }
210 }
211
212 pub fn place_lwss(&mut self, ox: usize, oy: usize) {
214 let cells = [(1,0),(4,0),(0,1),(0,2),(4,2),(0,3),(1,3),(2,3),(3,3)];
215 for (dx, dy) in cells {
216 let x = ox + dx; let y = oy + dy;
217 if x < self.width && y < self.height { self.grid[y][x] = true; }
218 }
219 }
220
221 pub fn place_r_pentomino(&mut self, ox: usize, oy: usize) {
223 for (dx, dy) in [(1,0),(2,0),(0,1),(1,1),(1,2)] {
224 let x = ox + dx; let y = oy + dy;
225 if x < self.width && y < self.height { self.grid[y][x] = true; }
226 }
227 }
228
229 pub fn place_acorn(&mut self, ox: usize, oy: usize) {
231 for (dx, dy) in [(0,1),(1,3),(2,0),(2,1),(4,1),(5,1),(6,1)] {
232 let x = ox + dx; let y = oy + dy;
233 if x < self.width && y < self.height { self.grid[y][x] = true; }
234 }
235 }
236}
237
238#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
244pub enum WireWorldCell {
245 #[default]
246 Empty,
247 ElectronHead,
248 ElectronTail,
249 Conductor,
250}
251
252#[derive(Clone, Debug)]
254pub struct WireWorld {
255 pub grid: Vec<Vec<WireWorldCell>>,
256 pub width: usize,
257 pub height: usize,
258}
259
260impl WireWorld {
261 pub fn new(width: usize, height: usize) -> Self {
262 Self { grid: vec![vec![WireWorldCell::Empty; width]; height], width, height }
263 }
264
265 pub fn set(&mut self, x: usize, y: usize, cell: WireWorldCell) {
266 self.grid[y][x] = cell;
267 }
268
269 pub fn step(&mut self) {
270 let old = self.grid.clone();
271 for y in 0..self.height {
272 for x in 0..self.width {
273 self.grid[y][x] = match old[y][x] {
274 WireWorldCell::ElectronHead => WireWorldCell::ElectronTail,
275 WireWorldCell::ElectronTail => WireWorldCell::Conductor,
276 WireWorldCell::Conductor => {
277 let heads = self.count_heads_neighbor(&old, x, y);
278 if heads == 1 || heads == 2 {
279 WireWorldCell::ElectronHead
280 } else {
281 WireWorldCell::Conductor
282 }
283 }
284 WireWorldCell::Empty => WireWorldCell::Empty,
285 };
286 }
287 }
288 }
289
290 fn count_heads_neighbor(&self, grid: &[Vec<WireWorldCell>], x: usize, y: usize) -> usize {
291 let mut count = 0;
292 for dy in -1i64..=1 {
293 for dx in -1i64..=1 {
294 if dx == 0 && dy == 0 { continue; }
295 let nx = x as i64 + dx;
296 let ny = y as i64 + dy;
297 if nx >= 0 && nx < self.width as i64 && ny >= 0 && ny < self.height as i64 {
298 if grid[ny as usize][nx as usize] == WireWorldCell::ElectronHead { count += 1; }
299 }
300 }
301 }
302 count
303 }
304}
305
306#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
312pub enum ForestCell { #[default] Empty, Tree, Burning }
313
314#[derive(Clone, Debug)]
316pub struct ForestFire {
317 pub grid: Vec<Vec<ForestCell>>,
318 pub width: usize,
319 pub height: usize,
320 pub p_grow: f64, pub p_catch: f64, rng_state: u64,
323}
324
325impl ForestFire {
326 pub fn new(width: usize, height: usize, p_grow: f64, p_catch: f64, seed: u64) -> Self {
327 Self {
328 grid: vec![vec![ForestCell::Empty; width]; height],
329 width, height, p_grow, p_catch,
330 rng_state: seed,
331 }
332 }
333
334 fn rand(&mut self) -> f64 {
335 self.rng_state = self.rng_state.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
336 (self.rng_state >> 33) as f64 / (u32::MAX as f64)
337 }
338
339 pub fn step(&mut self) {
340 let old = self.grid.clone();
341 for y in 0..self.height {
342 for x in 0..self.width {
343 self.grid[y][x] = match old[y][x] {
344 ForestCell::Burning => ForestCell::Empty,
345 ForestCell::Empty => {
346 if self.rand() < self.p_grow { ForestCell::Tree } else { ForestCell::Empty }
347 }
348 ForestCell::Tree => {
349 let neighbor_burning = (-1i64..=1).any(|dy| {
350 (-1i64..=1).any(|dx| {
351 if dx == 0 && dy == 0 { return false; }
352 let nx = x as i64 + dx; let ny = y as i64 + dy;
353 nx >= 0 && nx < self.width as i64 && ny >= 0 && ny < self.height as i64
354 && old[ny as usize][nx as usize] == ForestCell::Burning
355 })
356 });
357 if neighbor_burning || self.rand() < self.p_catch {
358 ForestCell::Burning
359 } else {
360 ForestCell::Tree
361 }
362 }
363 };
364 }
365 }
366 }
367
368 pub fn count_trees(&self) -> usize {
369 self.grid.iter().flat_map(|r| r.iter()).filter(|&&c| c == ForestCell::Tree).count()
370 }
371
372 pub fn count_burning(&self) -> usize {
373 self.grid.iter().flat_map(|r| r.iter()).filter(|&&c| c == ForestCell::Burning).count()
374 }
375}
376
377#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
383pub enum BrianCell { #[default] Off, On, Dying }
384
385#[derive(Clone, Debug)]
386pub struct BriansBrain {
387 pub grid: Vec<Vec<BrianCell>>,
388 pub width: usize,
389 pub height: usize,
390}
391
392impl BriansBrain {
393 pub fn new(width: usize, height: usize) -> Self {
394 Self { grid: vec![vec![BrianCell::Off; width]; height], width, height }
395 }
396
397 pub fn step(&mut self) {
398 let old = self.grid.clone();
399 for y in 0..self.height {
400 for x in 0..self.width {
401 self.grid[y][x] = match old[y][x] {
402 BrianCell::On => BrianCell::Dying,
403 BrianCell::Dying => BrianCell::Off,
404 BrianCell::Off => {
405 let on_count = (-1i64..=1).flat_map(|dy| {
406 (-1i64..=1).map(move |dx| (dx, dy))
407 }).filter(|&(dx, dy)| {
408 if dx == 0 && dy == 0 { return false; }
409 let nx = x as i64 + dx; let ny = y as i64 + dy;
410 nx >= 0 && nx < self.width as i64 && ny >= 0 && ny < self.height as i64
411 && old[ny as usize][nx as usize] == BrianCell::On
412 }).count();
413 if on_count == 2 { BrianCell::On } else { BrianCell::Off }
414 }
415 };
416 }
417 }
418 }
419
420 pub fn count_on(&self) -> usize {
421 self.grid.iter().flat_map(|r| r.iter()).filter(|&&c| c == BrianCell::On).count()
422 }
423}
424
425#[derive(Clone, Copy, Debug, PartialEq, Eq)]
430pub enum AntDir { North, East, South, West }
431
432impl AntDir {
433 fn turn_right(self) -> Self {
434 match self { Self::North => Self::East, Self::East => Self::South, Self::South => Self::West, Self::West => Self::North }
435 }
436 fn turn_left(self) -> Self {
437 match self { Self::North => Self::West, Self::West => Self::South, Self::South => Self::East, Self::East => Self::North }
438 }
439 fn delta(self) -> (i64, i64) {
440 match self { Self::North => (0,-1), Self::East => (1,0), Self::South => (0,1), Self::West => (-1,0) }
441 }
442}
443
444#[derive(Clone, Debug)]
446pub struct LangtonsAnt {
447 pub grid: std::collections::HashMap<(i64, i64), bool>,
448 pub ant_pos: (i64, i64),
449 pub ant_dir: AntDir,
450 pub steps: u64,
451}
452
453impl LangtonsAnt {
454 pub fn new() -> Self {
455 Self {
456 grid: std::collections::HashMap::new(),
457 ant_pos: (0, 0),
458 ant_dir: AntDir::North,
459 steps: 0,
460 }
461 }
462
463 pub fn step(&mut self) {
464 let cell = self.grid.get(&self.ant_pos).copied().unwrap_or(false);
465 if !cell {
466 self.ant_dir = self.ant_dir.turn_right();
468 self.grid.insert(self.ant_pos, true);
469 } else {
470 self.ant_dir = self.ant_dir.turn_left();
472 self.grid.insert(self.ant_pos, false);
473 }
474 let (dx, dy) = self.ant_dir.delta();
475 self.ant_pos = (self.ant_pos.0 + dx, self.ant_pos.1 + dy);
476 self.steps += 1;
477 }
478
479 pub fn step_n(&mut self, n: usize) {
480 for _ in 0..n { self.step(); }
481 }
482
483 pub fn count_black(&self) -> usize {
484 self.grid.values().filter(|&&v| v).count()
485 }
486}
487
488impl Default for LangtonsAnt {
489 fn default() -> Self { Self::new() }
490}
491
492#[derive(Clone, Debug)]
498pub struct Totalistic1D {
499 pub rule: u8,
500}
501
502impl Totalistic1D {
503 pub fn new(rule: u8) -> Self { Self { rule } }
504
505 #[inline]
507 fn apply(&self, l: bool, c: bool, r: bool) -> bool {
508 let idx = ((l as u8) << 2) | ((c as u8) << 1) | (r as u8);
509 (self.rule >> idx) & 1 == 1
510 }
511
512 pub fn evolve(&self, initial: Vec<bool>, steps: usize) -> Vec<Vec<bool>> {
514 let n = initial.len();
515 let mut result = Vec::with_capacity(steps + 1);
516 result.push(initial);
517 for _ in 0..steps {
518 let prev = result.last().unwrap();
519 let mut next = vec![false; n];
520 for i in 0..n {
521 let l = if i == 0 { prev[n - 1] } else { prev[i - 1] };
522 let c = prev[i];
523 let r = if i == n - 1 { prev[0] } else { prev[i + 1] };
524 next[i] = self.apply(l, c, r);
525 }
526 result.push(next);
527 }
528 result
529 }
530
531 pub fn rule_30_random(x: u64) -> bool {
533 let ca = Totalistic1D::new(30);
535 let n = 128usize;
536 let mut row = vec![false; n];
537 row[n / 2] = true;
538 let steps = (x % 64) as usize + 10;
539 let result = ca.evolve(row, steps);
540 let last = result.last().unwrap();
541 last[n / 2]
542 }
543}
544
545#[derive(Clone, Debug)]
551pub struct Body {
552 pub position: Vec3,
553 pub velocity: Vec3,
554 pub mass: f64,
555 pub radius: f64,
556}
557
558#[derive(Clone, Debug)]
560pub struct NBodySimulation {
561 pub bodies: Vec<Body>,
562 pub dt: f64,
563 pub time: f64,
564 pub softening: f64, }
566
567impl NBodySimulation {
568 pub fn new(bodies: Vec<Body>, dt: f64) -> Self {
569 Self { bodies, dt, time: 0.0, softening: 1e-4 }
570 }
571}
572
573pub fn direct_gravity_step(sim: &mut NBodySimulation) {
575 let n = sim.bodies.len();
576 let eps2 = sim.softening * sim.softening;
577 let mut accels = vec![Vec3::zero(); n];
578 const G: f64 = 6.674e-11;
579 for i in 0..n {
580 for j in 0..n {
581 if i == j { continue; }
582 let r = sim.bodies[j].position - sim.bodies[i].position;
583 let r2 = r.len_sq() + eps2;
584 let r3 = r2 * r2.sqrt();
585 let a = G * sim.bodies[j].mass / r3;
586 accels[i] += r * a;
587 }
588 }
589 for i in 0..n {
590 sim.bodies[i].velocity += accels[i] * sim.dt;
591 let vel = sim.bodies[i].velocity;
592 sim.bodies[i].position += vel * sim.dt;
593 }
594 sim.time += sim.dt;
595}
596
597pub fn leapfrog_step(sim: &mut NBodySimulation) {
599 let n = sim.bodies.len();
600 let eps2 = sim.softening * sim.softening;
601 const G: f64 = 6.674e-11;
602 let compute_accels = |bodies: &[Body]| -> Vec<Vec3> {
603 let mut accels = vec![Vec3::zero(); bodies.len()];
604 for i in 0..bodies.len() {
605 for j in 0..bodies.len() {
606 if i == j { continue; }
607 let r = bodies[j].position - bodies[i].position;
608 let r2 = r.len_sq() + eps2;
609 let r3 = r2 * r2.sqrt();
610 accels[i] += r * (G * bodies[j].mass / r3);
611 }
612 }
613 accels
614 };
615 let acc0 = compute_accels(&sim.bodies);
617 for i in 0..n { sim.bodies[i].velocity += acc0[i] * (sim.dt * 0.5); }
618 for i in 0..n {
620 let v = sim.bodies[i].velocity;
621 sim.bodies[i].position += v * sim.dt;
622 }
623 let acc1 = compute_accels(&sim.bodies);
625 for i in 0..n { sim.bodies[i].velocity += acc1[i] * (sim.dt * 0.5); }
626 sim.time += sim.dt;
627}
628
629#[derive(Clone, Debug)]
635pub enum OctreeNode {
636 Empty,
637 Leaf { body_idx: usize, pos: Vec3, mass: f64 },
638 Internal {
639 center_of_mass: Vec3,
640 total_mass: f64,
641 children: Box<[OctreeNode; 8]>,
642 min: Vec3,
643 max: Vec3,
644 },
645}
646
647impl OctreeNode {
648 fn child_idx(center: Vec3, pos: Vec3) -> usize {
649 let x = if pos.x >= center.x { 1 } else { 0 };
650 let y = if pos.y >= center.y { 2 } else { 0 };
651 let z = if pos.z >= center.z { 4 } else { 0 };
652 x | y | z
653 }
654
655 fn child_bounds(min: Vec3, max: Vec3, idx: usize) -> (Vec3, Vec3) {
656 let center = (min + max) * 0.5;
657 let child_min = Vec3::new(
658 if idx & 1 != 0 { center.x } else { min.x },
659 if idx & 2 != 0 { center.y } else { min.y },
660 if idx & 4 != 0 { center.z } else { min.z },
661 );
662 let child_max = Vec3::new(
663 if idx & 1 != 0 { max.x } else { center.x },
664 if idx & 2 != 0 { max.y } else { center.y },
665 if idx & 4 != 0 { max.z } else { center.z },
666 );
667 (child_min, child_max)
668 }
669
670 pub fn insert(&mut self, body_idx: usize, pos: Vec3, mass: f64, min: Vec3, max: Vec3) {
671 match self {
672 OctreeNode::Empty => {
673 *self = OctreeNode::Leaf { body_idx, pos, mass };
674 }
675 OctreeNode::Leaf { body_idx: bi, pos: bp, mass: bm } => {
676 let old_bi = *bi; let old_pos = *bp; let old_mass = *bm;
677 let center = (min + max) * 0.5;
678 let children: [OctreeNode; 8] = Default::default();
679 *self = OctreeNode::Internal {
680 center_of_mass: (old_pos * old_mass + pos * mass) * (1.0 / (old_mass + mass)),
681 total_mass: old_mass + mass,
682 children: Box::new(children),
683 min, max,
684 };
685 if let OctreeNode::Internal { children, min, max, .. } = self {
686 let ci = Self::child_idx(((*min + *max) * 0.5), old_pos);
687 let (cmin, cmax) = Self::child_bounds(*min, *max, ci);
688 children[ci].insert(old_bi, old_pos, old_mass, cmin, cmax);
689 let ci2 = Self::child_idx(((*min + *max) * 0.5), pos);
690 let (cmin2, cmax2) = Self::child_bounds(*min, *max, ci2);
691 children[ci2].insert(body_idx, pos, mass, cmin2, cmax2);
692 }
693 }
694 OctreeNode::Internal { center_of_mass, total_mass, children, min, max } => {
695 *total_mass += mass;
696 *center_of_mass = (*center_of_mass * (*total_mass - mass) + pos * mass) * (1.0 / *total_mass);
697 let ci = Self::child_idx((*min + *max) * 0.5, pos);
698 let (cmin, cmax) = Self::child_bounds(*min, *max, ci);
699 children[ci].insert(body_idx, pos, mass, cmin, cmax);
700 }
701 }
702 }
703
704 pub fn compute_accel(&self, pos: Vec3, theta: f64, eps2: f64) -> Vec3 {
705 const G: f64 = 6.674e-11;
706 match self {
707 OctreeNode::Empty => Vec3::zero(),
708 OctreeNode::Leaf { pos: lp, mass, .. } => {
709 let r = *lp - pos;
710 let r2 = r.len_sq() + eps2;
711 if r2 < eps2 * 100.0 { return Vec3::zero(); }
712 r * (G * mass / (r2 * r2.sqrt()))
713 }
714 OctreeNode::Internal { center_of_mass, total_mass, children, min, max } => {
715 let size = (max.x - min.x).max((max.y - min.y).max(max.z - min.z));
716 let r = *center_of_mass - pos;
717 let dist = r.len();
718 if size / dist < theta {
719 let r2 = r.len_sq() + eps2;
720 r * (G * total_mass / (r2 * r2.sqrt()))
721 } else {
722 children.iter().map(|c| c.compute_accel(pos, theta, eps2)).fold(Vec3::zero(), |a, b| a + b)
723 }
724 }
725 }
726 }
727}
728
729impl Default for OctreeNode {
730 fn default() -> Self { OctreeNode::Empty }
731}
732
733pub fn barnes_hut_step(sim: &mut NBodySimulation, theta: f64) {
735 if sim.bodies.is_empty() { return; }
736 let mut min = sim.bodies[0].position;
738 let mut max = sim.bodies[0].position;
739 for b in &sim.bodies {
740 min.x = min.x.min(b.position.x); max.x = max.x.max(b.position.x);
741 min.y = min.y.min(b.position.y); max.y = max.y.max(b.position.y);
742 min.z = min.z.min(b.position.z); max.z = max.z.max(b.position.z);
743 }
744 let padding = 1.0;
745 min = min - Vec3::new(padding, padding, padding);
746 max = max + Vec3::new(padding, padding, padding);
747
748 let mut root = OctreeNode::Empty;
750 for (i, b) in sim.bodies.iter().enumerate() {
751 root.insert(i, b.position, b.mass, min, max);
752 }
753
754 let eps2 = sim.softening * sim.softening;
756 let n = sim.bodies.len();
757 let accels: Vec<Vec3> = (0..n)
758 .map(|i| root.compute_accel(sim.bodies[i].position, theta, eps2))
759 .collect();
760
761 for i in 0..n {
762 sim.bodies[i].velocity += accels[i] * sim.dt;
763 let vel = sim.bodies[i].velocity;
764 sim.bodies[i].position += vel * sim.dt;
765 }
766 sim.time += sim.dt;
767}
768
769#[derive(Clone, Debug)]
775pub struct OrbitalElements {
776 pub semi_major_axis: f64, pub eccentricity: f64, pub inclination: f64, pub lan: f64, pub arg_periapsis: f64, pub true_anomaly: f64, }
783
784pub fn from_state_vectors(r: Vec3, v: Vec3, mu: f64) -> OrbitalElements {
786 let h = r.cross(v); let r_len = r.len();
788 let v_len = v.len();
789
790 let e_vec = r * (v_len * v_len / mu - 1.0 / r_len) - v * (r.dot(v) / mu);
792 let ecc = e_vec.len();
793
794 let energy = v_len * v_len / 2.0 - mu / r_len;
795 let a = if energy.abs() > 1e-12 { -mu / (2.0 * energy) } else { 1e30 };
796
797 let i = (h.z / h.len()).acos().min(PI).max(0.0);
798 let n = Vec3::new(-h.y, h.x, 0.0); let n_len = n.len();
800
801 let lan = if n_len > 1e-12 {
802 let mut lan = (n.x / n_len).acos();
803 if n.y < 0.0 { lan = 2.0 * PI - lan; }
804 lan
805 } else { 0.0 };
806
807 let arg_p = if n_len > 1e-12 && ecc > 1e-12 {
808 let mut omega = (n.dot(e_vec) / (n_len * ecc)).clamp(-1.0, 1.0).acos();
809 if e_vec.z < 0.0 { omega = 2.0 * PI - omega; }
810 omega
811 } else { 0.0 };
812
813 let nu = if ecc > 1e-12 {
814 let mut nu = (e_vec.dot(r) / (ecc * r_len)).clamp(-1.0, 1.0).acos();
815 if r.dot(v) < 0.0 { nu = 2.0 * PI - nu; }
816 nu
817 } else { 0.0 };
818
819 OrbitalElements {
820 semi_major_axis: a,
821 eccentricity: ecc,
822 inclination: i,
823 lan,
824 arg_periapsis: arg_p,
825 true_anomaly: nu,
826 }
827}
828
829pub fn to_state_vectors(elems: &OrbitalElements, mu: f64) -> (Vec3, Vec3) {
831 let a = elems.semi_major_axis;
832 let e = elems.eccentricity;
833 let i = elems.inclination;
834 let lan = elems.lan;
835 let omega = elems.arg_periapsis;
836 let nu = elems.true_anomaly;
837
838 let p = a * (1.0 - e * e);
839 let r_perifocal = p / (1.0 + e * nu.cos());
840
841 let r_peri = Vec3::new(r_perifocal * nu.cos(), r_perifocal * nu.sin(), 0.0);
842 let v_peri = Vec3::new(
843 -(mu / p).sqrt() * nu.sin(),
844 (mu / p).sqrt() * (e + nu.cos()),
845 0.0,
846 );
847
848 let rx = |theta: f64, v: Vec3| -> Vec3 {
850 Vec3::new(v.x, v.y * theta.cos() - v.z * theta.sin(), v.y * theta.sin() + v.z * theta.cos())
851 };
852 let rz = |theta: f64, v: Vec3| -> Vec3 {
853 Vec3::new(v.x * theta.cos() - v.y * theta.sin(), v.x * theta.sin() + v.y * theta.cos(), v.z)
854 };
855
856 let r_rot = rz(-lan, rx(-i, rz(-omega, r_peri)));
857 let v_rot = rz(-lan, rx(-i, rz(-omega, v_peri)));
858
859 (r_rot, v_rot)
860}
861
862pub fn solve_kepler_equation(m_anom: f64, e: f64) -> f64 {
864 let mut e_anom = if e < 0.8 { m_anom } else { PI };
865 for _ in 0..100 {
866 let f = e_anom - e * e_anom.sin() - m_anom;
867 let df = 1.0 - e * e_anom.cos();
868 if df.abs() < 1e-12 { break; }
869 let delta = f / df;
870 e_anom -= delta;
871 if delta.abs() < 1e-12 { break; }
872 }
873 e_anom
874}
875
876#[derive(Clone, Debug)]
883pub struct GrayScott {
884 pub u: Vec<f64>,
885 pub v: Vec<f64>,
886 pub width: usize,
887 pub height: usize,
888 pub feed: f64,
889 pub kill: f64,
890 pub du: f64,
891 pub dv: f64,
892}
893
894#[derive(Clone, Copy, Debug)]
896pub struct GrayScottPreset {
897 pub feed: f64,
898 pub kill: f64,
899 pub name: &'static str,
900}
901
902impl GrayScott {
903 pub fn new(width: usize, height: usize, feed: f64, kill: f64) -> Self {
904 let n = width * height;
905 Self {
906 u: vec![1.0; n],
907 v: vec![0.0; n],
908 width, height, feed, kill,
909 du: 0.2,
910 dv: 0.1,
911 }
912 }
913
914 pub fn seed_center(&mut self) {
915 let cx = self.width / 2;
916 let cy = self.height / 2;
917 let r = 5.min(self.width.min(self.height) / 4);
918 for y in (cy.saturating_sub(r))..(cy + r).min(self.height) {
919 for x in (cx.saturating_sub(r))..(cx + r).min(self.width) {
920 let idx = y * self.width + x;
921 self.u[idx] = 0.5;
922 self.v[idx] = 0.25;
923 }
924 }
925 }
926
927 #[inline]
928 fn idx(&self, x: usize, y: usize) -> usize { y * self.width + x }
929
930 fn laplacian_u(&self, x: usize, y: usize) -> f64 {
931 let cx = x;
932 let cy = y;
933 let xm = if cx == 0 { self.width - 1 } else { cx - 1 };
934 let xp = if cx == self.width - 1 { 0 } else { cx + 1 };
935 let ym = if cy == 0 { self.height - 1 } else { cy - 1 };
936 let yp = if cy == self.height - 1 { 0 } else { cy + 1 };
937 self.u[self.idx(xm, cy)] + self.u[self.idx(xp, cy)]
938 + self.u[self.idx(cx, ym)] + self.u[self.idx(cx, yp)]
939 - 4.0 * self.u[self.idx(cx, cy)]
940 }
941
942 fn laplacian_v(&self, x: usize, y: usize) -> f64 {
943 let cx = x; let cy = y;
944 let xm = if cx == 0 { self.width - 1 } else { cx - 1 };
945 let xp = if cx == self.width - 1 { 0 } else { cx + 1 };
946 let ym = if cy == 0 { self.height - 1 } else { cy - 1 };
947 let yp = if cy == self.height - 1 { 0 } else { cy + 1 };
948 self.v[self.idx(xm, cy)] + self.v[self.idx(xp, cy)]
949 + self.v[self.idx(cx, ym)] + self.v[self.idx(cx, yp)]
950 - 4.0 * self.v[self.idx(cx, cy)]
951 }
952
953 pub fn step(&mut self, dt: f64) {
954 let n = self.width * self.height;
955 let mut u_new = vec![0.0f64; n];
956 let mut v_new = vec![0.0f64; n];
957 for y in 0..self.height {
958 for x in 0..self.width {
959 let i = self.idx(x, y);
960 let u = self.u[i];
961 let v = self.v[i];
962 let uvv = u * v * v;
963 u_new[i] = u + dt * (self.du * self.laplacian_u(x, y) - uvv + self.feed * (1.0 - u));
964 v_new[i] = v + dt * (self.dv * self.laplacian_v(x, y) + uvv - (self.feed + self.kill) * v);
965 u_new[i] = u_new[i].clamp(0.0, 1.0);
966 v_new[i] = v_new[i].clamp(0.0, 1.0);
967 }
968 }
969 self.u = u_new;
970 self.v = v_new;
971 }
972
973 pub fn preset_mitosis(width: usize, height: usize) -> Self {
975 Self::new(width, height, 0.0367, 0.0649)
976 }
977
978 pub fn preset_coral(width: usize, height: usize) -> Self {
980 Self::new(width, height, 0.0545, 0.062)
981 }
982
983 pub fn preset_worms(width: usize, height: usize) -> Self {
985 Self::new(width, height, 0.078, 0.061)
986 }
987
988 pub fn preset_maze(width: usize, height: usize) -> Self {
990 Self::new(width, height, 0.029, 0.057)
991 }
992
993 pub fn preset_solitons(width: usize, height: usize) -> Self {
995 Self::new(width, height, 0.03, 0.06)
996 }
997
998 pub fn preset_unstable(width: usize, height: usize) -> Self {
1000 Self::new(width, height, 0.02, 0.055)
1001 }
1002}
1003
1004#[derive(Clone, Debug)]
1010pub struct FitzHughNagumo {
1011 pub v: Vec<f64>, pub w: Vec<f64>, pub width: usize,
1014 pub height: usize,
1015 pub a: f64, pub b: f64, pub tau: f64, pub eps: f64, }
1020
1021impl FitzHughNagumo {
1022 pub fn new(width: usize, height: usize) -> Self {
1023 let n = width * height;
1024 Self {
1025 v: vec![0.0; n],
1026 w: vec![0.0; n],
1027 width, height,
1028 a: 0.7, b: 0.8, tau: 12.5, eps: 0.1,
1029 }
1030 }
1031
1032 pub fn stimulate(&mut self, x: usize, y: usize, radius: usize) {
1033 for dy in 0..=radius * 2 {
1034 for dx in 0..=radius * 2 {
1035 let nx = (x + dx).saturating_sub(radius);
1036 let ny = (y + dy).saturating_sub(radius);
1037 if nx < self.width && ny < self.height {
1038 let i = ny * self.width + nx;
1039 self.v[i] = 1.0;
1040 }
1041 }
1042 }
1043 }
1044
1045 fn laplacian(data: &[f64], x: usize, y: usize, w: usize, h: usize) -> f64 {
1046 let idx = |x: usize, y: usize| y * w + x;
1047 let xm = if x == 0 { w - 1 } else { x - 1 };
1048 let xp = if x == w - 1 { 0 } else { x + 1 };
1049 let ym = if y == 0 { h - 1 } else { y - 1 };
1050 let yp = if y == h - 1 { 0 } else { y + 1 };
1051 data[idx(xm, y)] + data[idx(xp, y)] + data[idx(x, ym)] + data[idx(x, yp)] - 4.0 * data[idx(x, y)]
1052 }
1053
1054 pub fn step(&mut self, dt: f64) {
1055 let n = self.width * self.height;
1056 let mut v_new = vec![0.0f64; n];
1057 let mut w_new = vec![0.0f64; n];
1058 for y in 0..self.height {
1059 for x in 0..self.width {
1060 let i = y * self.width + x;
1061 let v = self.v[i];
1062 let w = self.w[i];
1063 let lap_v = Self::laplacian(&self.v, x, y, self.width, self.height);
1064 let dv = v - v.powi(3) / 3.0 - w + self.eps * lap_v;
1065 let dw = (v + self.a - self.b * w) / self.tau;
1066 v_new[i] = v + dt * dv;
1067 w_new[i] = w + dt * dw;
1068 }
1069 }
1070 self.v = v_new;
1071 self.w = w_new;
1072 }
1073}
1074
1075#[derive(Clone, Debug)]
1081pub struct BzParams {
1082 pub alpha: f64,
1083 pub beta: f64,
1084 pub gamma: f64,
1085}
1086
1087impl Default for BzParams {
1088 fn default() -> Self { BzParams { alpha: 0.1, beta: 0.1, gamma: 0.1 } }
1089}
1090
1091#[derive(Clone, Debug)]
1093pub struct BelousovZhabotinsky {
1094 pub concentrations: Vec<Vec<f64>>,
1096 pub width: usize,
1097 pub height: usize,
1098 pub params: BzParams,
1099}
1100
1101impl BelousovZhabotinsky {
1102 pub fn new(width: usize, height: usize, params: BzParams) -> Self {
1103 let n = width * height;
1104 let mut concentrations = vec![vec![0.0f64; 3]; n];
1106 let mut state = 12345u64;
1107 for row in &mut concentrations {
1108 for v in row.iter_mut() {
1109 state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
1110 *v = (state >> 33) as f64 / (u32::MAX as f64);
1111 }
1112 }
1113 Self { concentrations, width, height, params }
1114 }
1115
1116 fn laplacian_conc(data: &[Vec<f64>], comp: usize, x: usize, y: usize, w: usize, h: usize) -> f64 {
1117 let idx = |x: usize, y: usize| y * w + x;
1118 let xm = if x == 0 { w - 1 } else { x - 1 };
1119 let xp = if x == w - 1 { 0 } else { x + 1 };
1120 let ym = if y == 0 { h - 1 } else { y - 1 };
1121 let yp = if y == h - 1 { 0 } else { y + 1 };
1122 data[idx(xm, y)][comp] + data[idx(xp, y)][comp]
1123 + data[idx(x, ym)][comp] + data[idx(x, yp)][comp]
1124 - 4.0 * data[idx(x, y)][comp]
1125 }
1126
1127 pub fn step(&mut self, dt: f64) {
1128 let n = self.width * self.height;
1129 let mut new_c = self.concentrations.clone();
1130 let alpha = self.params.alpha;
1131 let beta = self.params.beta;
1132 let gamma = self.params.gamma;
1133 for y in 0..self.height {
1134 for x in 0..self.width {
1135 let i = y * self.width + x;
1136 let u = self.concentrations[i][0];
1137 let v = self.concentrations[i][1];
1138 let w = self.concentrations[i][2];
1139 let lap_u = Self::laplacian_conc(&self.concentrations, 0, x, y, self.width, self.height);
1140 let lap_v = Self::laplacian_conc(&self.concentrations, 1, x, y, self.width, self.height);
1141 let lap_w = Self::laplacian_conc(&self.concentrations, 2, x, y, self.width, self.height);
1142 let du = u * (1.0 - u - alpha * v / (u + beta)) + 0.1 * lap_u;
1144 let dv = u - v + 0.05 * lap_v;
1145 let dw = gamma * (u - w) + 0.02 * lap_w;
1146 new_c[i][0] = (u + dt * du).clamp(0.0, 1.0);
1147 new_c[i][1] = (v + dt * dv).clamp(0.0, 1.0);
1148 new_c[i][2] = (w + dt * dw).clamp(0.0, 1.0);
1149 }
1150 }
1151 self.concentrations = new_c;
1152 }
1153}
1154
1155#[derive(Clone, Debug)]
1161pub struct SirModel {
1162 pub s: f64, pub i: f64, pub r: f64, pub n: f64, pub beta: f64, pub gamma: f64, pub time: f64,
1169}
1170
1171impl SirModel {
1172 pub fn new(s0: f64, i0: f64, n: f64, beta: f64, gamma: f64) -> Self {
1173 let r0 = n - s0 - i0;
1174 Self { s: s0, i: i0, r: r0.max(0.0), n, beta, gamma, time: 0.0 }
1175 }
1176
1177 pub fn step(&mut self, dt: f64) {
1178 let ds = -self.beta * self.s * self.i / self.n;
1179 let di = self.beta * self.s * self.i / self.n - self.gamma * self.i;
1180 let dr = self.gamma * self.i;
1181 self.s = (self.s + dt * ds).max(0.0);
1182 self.i = (self.i + dt * di).max(0.0);
1183 self.r = (self.r + dt * dr).max(0.0);
1184 self.time += dt;
1185 }
1186
1187 pub fn basic_reproduction_number(&self) -> f64 {
1188 self.beta / self.gamma
1189 }
1190
1191 pub fn time_to_peak(&self) -> f64 {
1192 let r0 = self.basic_reproduction_number();
1194 if r0 <= 1.0 { return f64::INFINITY; }
1195 let beta = self.beta; let gamma = self.gamma;
1196 (1.0 / (beta - gamma)) * (beta * self.s / (gamma * self.n)).ln().abs()
1197 }
1198
1199 pub fn herd_immunity_threshold(&self) -> f64 {
1200 1.0 - 1.0 / self.basic_reproduction_number()
1201 }
1202}
1203
1204#[derive(Clone, Debug)]
1206pub struct SeirdModel {
1207 pub s: f64, pub e: f64, pub i: f64, pub r: f64, pub d: f64, pub n: f64, pub beta: f64, pub sigma: f64, pub gamma: f64, pub delta: f64, pub time: f64,
1218}
1219
1220impl SeirdModel {
1221 pub fn new(s0: f64, e0: f64, i0: f64, n: f64, beta: f64, sigma: f64, gamma: f64, delta: f64) -> Self {
1222 Self { s: s0, e: e0, i: i0, r: 0.0, d: 0.0, n, beta, sigma, gamma, delta, time: 0.0 }
1223 }
1224
1225 pub fn step(&mut self, dt: f64) {
1226 let force = self.beta * self.s * self.i / self.n;
1227 let ds = -force;
1228 let de = force - self.sigma * self.e;
1229 let di = self.sigma * self.e - (self.gamma + self.delta) * self.i;
1230 let dr = self.gamma * self.i;
1231 let dd = self.delta * self.i;
1232 self.s = (self.s + dt * ds).max(0.0);
1233 self.e = (self.e + dt * de).max(0.0);
1234 self.i = (self.i + dt * di).max(0.0);
1235 self.r = (self.r + dt * dr).max(0.0);
1236 self.d = (self.d + dt * dd).max(0.0);
1237 self.time += dt;
1238 }
1239
1240 pub fn total_active(&self) -> f64 { self.s + self.e + self.i }
1241}
1242
1243#[derive(Clone, Copy, Debug, Default)]
1245pub struct SirCell {
1246 pub s: f64,
1247 pub i: f64,
1248 pub r: f64,
1249}
1250
1251#[derive(Clone, Debug)]
1253pub struct SirGrid {
1254 pub grid: Vec<SirCell>,
1255 pub width: usize,
1256 pub height: usize,
1257 pub beta: f64,
1258 pub gamma: f64,
1259 pub diffusion: f64,
1260 pub time: f64,
1261}
1262
1263impl SirGrid {
1264 pub fn new(width: usize, height: usize, beta: f64, gamma: f64) -> Self {
1265 let n = width * height;
1266 let cell = SirCell { s: 1.0, i: 0.0, r: 0.0 };
1267 Self { grid: vec![cell; n], width, height, beta, gamma, diffusion: 0.1, time: 0.0 }
1268 }
1269
1270 pub fn seed(&mut self, x: usize, y: usize, fraction: f64) {
1271 if x < self.width && y < self.height {
1272 let i = y * self.width + x;
1273 self.grid[i].i = fraction;
1274 self.grid[i].s = (1.0 - fraction).max(0.0);
1275 }
1276 }
1277
1278 fn idx(&self, x: usize, y: usize) -> usize { y * self.width + x }
1279
1280 pub fn step(&mut self, dt: f64) {
1281 let old = self.grid.clone();
1282 for y in 0..self.height {
1283 for x in 0..self.width {
1284 let i = self.idx(x, y);
1285 let cell = old[i];
1286 let force = self.beta * cell.s * cell.i;
1287 let xm = if x == 0 { self.width - 1 } else { x - 1 };
1289 let xp = if x == self.width - 1 { 0 } else { x + 1 };
1290 let ym = if y == 0 { self.height - 1 } else { y - 1 };
1291 let yp = if y == self.height - 1 { 0 } else { y + 1 };
1292 let lap_i = old[self.idx(xm, y)].i + old[self.idx(xp, y)].i
1293 + old[self.idx(x, ym)].i + old[self.idx(x, yp)].i - 4.0 * cell.i;
1294 self.grid[i].s = (cell.s - dt * force).max(0.0);
1295 self.grid[i].i = (cell.i + dt * (force - self.gamma * cell.i + self.diffusion * lap_i)).max(0.0);
1296 self.grid[i].r = (cell.r + dt * self.gamma * cell.i).max(0.0);
1297 }
1298 }
1299 self.time += dt;
1300 }
1301
1302 pub fn total_infected(&self) -> f64 {
1303 self.grid.iter().map(|c| c.i).sum()
1304 }
1305}
1306
1307#[derive(Clone, Debug)]
1313pub struct Vehicle {
1314 pub pos: f64,
1315 pub speed: f64,
1316 pub length: f64,
1317 pub desired_speed: f64,
1318 pub accel: f64,
1319 pub decel: f64,
1320}
1321
1322#[derive(Clone, Debug)]
1324pub struct NagelSchreckenberg {
1325 pub road: Vec<Option<usize>>, pub vehicles: Vec<(usize, u32)>, pub road_length: usize,
1328 pub v_max: u32,
1329 pub p_brake: f64, rng_state: u64,
1331}
1332
1333impl NagelSchreckenberg {
1334 pub fn new(road_length: usize, v_max: u32, p_brake: f64, density: f64, seed: u64) -> Self {
1335 let mut road = vec![None; road_length];
1336 let mut vehicles = Vec::new();
1337 let mut rng = seed;
1338 let n_vehicles = (density * road_length as f64) as usize;
1339 let mut positions: Vec<usize> = (0..road_length).collect();
1340 for i in (1..road_length).rev() {
1342 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
1343 let j = (rng >> 33) as usize % (i + 1);
1344 positions.swap(i, j);
1345 }
1346 for k in 0..n_vehicles.min(road_length) {
1347 let pos = positions[k];
1348 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
1349 let spd = (rng >> 33) as u32 % (v_max + 1);
1350 road[pos] = Some(k);
1351 vehicles.push((pos, spd));
1352 }
1353 Self { road, vehicles, road_length, v_max, p_brake, rng_state: rng }
1354 }
1355
1356 fn rand(&mut self) -> f64 {
1357 self.rng_state = self.rng_state.wrapping_mul(6364136223846793005).wrapping_add(1);
1358 (self.rng_state >> 33) as f64 / (u32::MAX as f64)
1359 }
1360
1361 pub fn step(&mut self) {
1362 let n_veh = self.vehicles.len();
1363 let road_len = self.road_length;
1364
1365 for v in &mut self.vehicles {
1367 if v.1 < self.v_max { v.1 += 1; }
1368 }
1369 let positions: Vec<usize> = self.vehicles.iter().map(|v| v.0).collect();
1371 for (k, v) in self.vehicles.iter_mut().enumerate() {
1372 let mut gap = road_len; for j in 1..=road_len {
1375 let next_pos = (v.0 + j) % road_len;
1376 if self.road[next_pos].is_some() { gap = j - 1; break; }
1377 }
1378 if v.1 as usize > gap { v.1 = gap as u32; }
1379 }
1380 let brakes: Vec<f64> = (0..self.vehicles.len()).map(|_| self.rand()).collect();
1382 for (i, v) in self.vehicles.iter_mut().enumerate() {
1383 if v.1 > 0 && brakes[i] < self.p_brake { v.1 -= 1; }
1384 }
1385 let mut new_road = vec![None; road_len];
1387 for (k, v) in self.vehicles.iter_mut().enumerate() {
1388 self.road[v.0] = None;
1389 v.0 = (v.0 + v.1 as usize) % road_len;
1390 new_road[v.0] = Some(k);
1391 }
1392 self.road = new_road;
1393 }
1394
1395 pub fn flow_rate(&self) -> f64 {
1396 let total_speed: f64 = self.vehicles.iter().map(|v| v.1 as f64).sum();
1397 total_speed / self.road_length as f64
1398 }
1399
1400 pub fn density(&self) -> f64 {
1401 self.vehicles.len() as f64 / self.road_length as f64
1402 }
1403
1404 pub fn average_speed(&self) -> f64 {
1405 if self.vehicles.is_empty() { return 0.0; }
1406 self.vehicles.iter().map(|v| v.1 as f64).sum::<f64>() / self.vehicles.len() as f64
1407 }
1408}
1409
1410#[derive(Clone, Debug)]
1412pub struct IntelligentDriverModel {
1413 pub t_desired: f64, pub a_desired: f64, pub b_desired: f64, pub v_desired: f64, pub s_min: f64, }
1419
1420impl IntelligentDriverModel {
1421 pub fn new() -> Self {
1422 Self {
1423 t_desired: 1.5,
1424 a_desired: 1.4,
1425 b_desired: 2.0,
1426 v_desired: 33.33, s_min: 2.0,
1428 }
1429 }
1430
1431 pub fn acceleration(&self, v: f64, delta_v: f64, s: f64) -> f64 {
1435 let s_star = self.s_min
1436 + (v * self.t_desired
1437 + v * delta_v / (2.0 * (self.a_desired * self.b_desired).sqrt()))
1438 .max(0.0);
1439 let free_term = (v / self.v_desired).powi(4);
1440 let interaction_term = (s_star / s.max(0.001)).powi(2);
1441 self.a_desired * (1.0 - free_term - interaction_term)
1442 }
1443}
1444
1445impl Default for IntelligentDriverModel {
1446 fn default() -> Self { Self::new() }
1447}
1448
1449#[cfg(test)]
1454mod tests {
1455 use super::*;
1456
1457 #[test]
1458 fn test_game_of_life_step() {
1459 let mut gol = GameOfLife::new(20, 20);
1460 gol.place_glider(1, 1);
1461 let initial_count = gol.count_living();
1462 assert_eq!(initial_count, 5);
1463 gol.step();
1464 assert_eq!(gol.count_living(), 5);
1466 }
1467
1468 #[test]
1469 fn test_game_of_life_still_life() {
1470 let mut gol = GameOfLife::new(10, 10);
1472 gol.grid[1][1] = true; gol.grid[1][2] = true;
1473 gol.grid[2][1] = true; gol.grid[2][2] = true;
1474 assert!(gol.is_stable());
1475 }
1476
1477 #[test]
1478 fn test_wolfram_rule30() {
1479 let ca = Totalistic1D::new(30);
1480 let init: Vec<bool> = (0..11).map(|i| i == 5).collect();
1481 let result = ca.evolve(init, 5);
1482 assert_eq!(result.len(), 6);
1483 }
1484
1485 #[test]
1486 fn test_wolfram_rule110() {
1487 let ca = Totalistic1D::new(110);
1488 let init = vec![false, false, false, false, true, false, false, false, false];
1489 let result = ca.evolve(init, 4);
1490 assert_eq!(result.len(), 5);
1491 }
1492
1493 #[test]
1494 fn test_langtons_ant_steps() {
1495 let mut ant = LangtonsAnt::new();
1496 ant.step_n(100);
1497 assert_eq!(ant.steps, 100);
1498 assert!(ant.count_black() > 0);
1500 }
1501
1502 #[test]
1503 fn test_langtons_ant_highway() {
1504 let mut ant = LangtonsAnt::new();
1506 ant.step_n(10000);
1507 assert_eq!(ant.steps, 10000);
1508 }
1509
1510 #[test]
1511 fn test_nbody_basic() {
1512 let bodies = vec![
1513 Body { position: Vec3::new(0.0, 0.0, 0.0), velocity: Vec3::zero(), mass: 1e10, radius: 1.0 },
1514 Body { position: Vec3::new(100.0, 0.0, 0.0), velocity: Vec3::zero(), mass: 1e10, radius: 1.0 },
1515 ];
1516 let mut sim = NBodySimulation::new(bodies, 1.0);
1517 direct_gravity_step(&mut sim);
1518 assert!(sim.bodies[0].velocity.x > 0.0);
1520 assert!(sim.bodies[1].velocity.x < 0.0);
1521 }
1522
1523 #[test]
1524 fn test_leapfrog_step() {
1525 let bodies = vec![
1526 Body { position: Vec3::new(0.0, 0.0, 0.0), velocity: Vec3::new(0.0, 1e-3, 0.0), mass: 1e12, radius: 1.0 },
1527 Body { position: Vec3::new(1000.0, 0.0, 0.0), velocity: Vec3::new(0.0, -1e-3, 0.0), mass: 1e12, radius: 1.0 },
1528 ];
1529 let mut sim = NBodySimulation::new(bodies, 0.1);
1530 for _ in 0..10 { leapfrog_step(&mut sim); }
1531 assert!((sim.time - 1.0).abs() < 1e-10);
1532 }
1533
1534 #[test]
1535 fn test_solve_kepler() {
1536 let e_anom = solve_kepler_equation(1.0, 0.0);
1538 assert!((e_anom - 1.0).abs() < 1e-10);
1539 }
1540
1541 #[test]
1542 fn test_orbital_round_trip() {
1543 let r = Vec3::new(6371e3 + 400e3, 0.0, 0.0); let v = Vec3::new(0.0, 7.66e3, 0.0); let mu = 3.986e14;
1546 let elems = from_state_vectors(r, v, mu);
1547 let (r2, v2) = to_state_vectors(&elems, mu);
1548 assert!((r.x - r2.x).abs() < 1.0, "r.x mismatch");
1549 assert!((v.y - v2.y).abs() < 1.0, "v.y mismatch");
1550 }
1551
1552 #[test]
1553 fn test_gray_scott_step() {
1554 let mut gs = GrayScott::new(20, 20, 0.055, 0.062);
1555 gs.seed_center();
1556 for _ in 0..10 { gs.step(1.0); }
1557 assert!(gs.u.iter().all(|&x| x >= 0.0 && x <= 1.0));
1559 assert!(gs.v.iter().all(|&x| x >= 0.0 && x <= 1.0));
1560 }
1561
1562 #[test]
1563 fn test_fitzhugh_nagumo_step() {
1564 let mut fhn = FitzHughNagumo::new(10, 10);
1565 fhn.stimulate(5, 5, 1);
1566 for _ in 0..20 { fhn.step(0.05); }
1567 assert!(fhn.v.iter().all(|v| v.is_finite()));
1569 }
1570
1571 #[test]
1572 fn test_sir_model() {
1573 let mut sir = SirModel::new(999.0, 1.0, 1000.0, 0.3, 0.1);
1574 let r0 = sir.basic_reproduction_number();
1575 assert!((r0 - 3.0).abs() < 1e-10);
1576 for _ in 0..100 { sir.step(1.0); }
1577 assert!(sir.r > 10.0);
1579 }
1580
1581 #[test]
1582 fn test_sir_herd_immunity() {
1583 let sir = SirModel::new(999.0, 1.0, 1000.0, 0.3, 0.1);
1584 let hit = sir.herd_immunity_threshold();
1585 assert!((hit - 2.0 / 3.0).abs() < 1e-10);
1586 }
1587
1588 #[test]
1589 fn test_seird_model() {
1590 let mut seird = SeirdModel::new(9990.0, 5.0, 5.0, 10000.0, 0.3, 0.2, 0.1, 0.01);
1591 for _ in 0..50 { seird.step(1.0); }
1592 assert!(seird.r > 0.0);
1593 assert!(seird.d > 0.0);
1594 }
1595
1596 #[test]
1597 fn test_nagel_schreckenberg() {
1598 let mut ns = NagelSchreckenberg::new(100, 5, 0.3, 0.3, 42);
1599 for _ in 0..20 { ns.step(); }
1600 assert!(ns.average_speed() >= 0.0);
1601 assert!(ns.flow_rate() >= 0.0);
1602 assert!(ns.density() >= 0.0 && ns.density() <= 1.0);
1603 }
1604
1605 #[test]
1606 fn test_idm_acceleration() {
1607 let idm = IntelligentDriverModel::new();
1608 let a_free = idm.acceleration(20.0, 0.0, 1000.0);
1610 assert!(a_free > 0.0, "should accelerate in free flow");
1611 let a_brake = idm.acceleration(20.0, 10.0, 5.0);
1613 assert!(a_brake < 0.0, "should decelerate when too close");
1614 }
1615
1616 #[test]
1617 fn test_bz_reaction() {
1618 let params = BzParams::default();
1619 let mut bz = BelousovZhabotinsky::new(10, 10, params);
1620 for _ in 0..5 { bz.step(0.01); }
1621 for cell in &bz.concentrations {
1623 for &c in cell { assert!(c >= 0.0 && c <= 1.0, "concentration out of bounds: {}", c); }
1624 }
1625 }
1626
1627 #[test]
1628 fn test_wireworld_step() {
1629 let mut ww = WireWorld::new(10, 5);
1630 ww.set(0, 2, WireWorldCell::Conductor);
1631 ww.set(1, 2, WireWorldCell::ElectronHead);
1632 ww.set(2, 2, WireWorldCell::Conductor);
1633 ww.step();
1634 assert_eq!(ww.grid[2][1], WireWorldCell::ElectronTail);
1635 assert_eq!(ww.grid[2][2], WireWorldCell::ElectronHead);
1636 }
1637
1638 #[test]
1639 fn test_forest_fire_step() {
1640 let mut ff = ForestFire::new(20, 20, 0.1, 0.001, 42);
1641 for row in ff.grid.iter_mut() {
1643 for cell in row.iter_mut() { *cell = ForestCell::Tree; }
1644 }
1645 ff.grid[10][10] = ForestCell::Burning;
1646 ff.step();
1647 let burning = ff.count_burning();
1649 assert!(burning > 0);
1650 }
1651
1652 #[test]
1653 fn test_brians_brain() {
1654 let mut bb = BriansBrain::new(10, 10);
1655 bb.grid[5][5] = BrianCell::On;
1656 bb.step();
1657 assert_eq!(bb.grid[5][5], BrianCell::Dying);
1658 bb.step();
1659 assert_eq!(bb.grid[5][5], BrianCell::Off);
1660 }
1661
1662 #[test]
1663 fn test_sir_grid() {
1664 let mut sg = SirGrid::new(20, 20, 0.3, 0.05);
1665 sg.seed(10, 10, 0.1);
1666 for _ in 0..50 { sg.step(1.0); }
1667 assert!(sg.total_infected() > 0.0);
1668 }
1669
1670 #[test]
1671 fn test_vec3_ops() {
1672 let a = Vec3::new(1.0, 0.0, 0.0);
1673 let b = Vec3::new(0.0, 1.0, 0.0);
1674 let c = a.cross(b);
1675 assert!((c.z - 1.0).abs() < 1e-10);
1676 assert!((a.dot(b)).abs() < 1e-10);
1677 }
1678}