From f6db9223ab098c89b59a0e826fcc488b68f34aaa Mon Sep 17 00:00:00 2001 From: fderuiter <127706008+fderuiter@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:04:23 +0000 Subject: [PATCH] Refactor biology module for safety and error handling Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .../game_theory/evolutionary/replicator.rs | 1 - .../src/applied/game_theory/mean_field/mod.rs | 4 +- math_explorer/src/biology/diffusion.rs | 158 +++++++++++++----- .../src/biology/morphogenesis/mod.rs | 23 +-- .../src/biology/morphogenesis/solvers.rs | 26 ++- .../src/biology/reaction_diffusion.rs | 25 ++- .../src/physics/medical/dose_calculation.rs | 1 + math_explorer/src/physics/solid_state/bcs.rs | 1 + .../tests/bench_reaction_diffusion.rs | 4 +- math_explorer/tests/test_lorahub.rs | 1 - math_explorer/tests/test_turing_regression.rs | 4 +- math_explorer/tests/test_turing_structure.rs | 4 +- math_explorer_gui/src/app.rs | 3 +- .../src/tabs/neuroscience/spike_analysis.rs | 28 +++- .../src/tabs/number_theory/prime_spiral.rs | 6 +- 15 files changed, 209 insertions(+), 80 deletions(-) diff --git a/math_explorer/src/applied/game_theory/evolutionary/replicator.rs b/math_explorer/src/applied/game_theory/evolutionary/replicator.rs index f4672acf..538b7471 100644 --- a/math_explorer/src/applied/game_theory/evolutionary/replicator.rs +++ b/math_explorer/src/applied/game_theory/evolutionary/replicator.rs @@ -146,7 +146,6 @@ impl OdeSystem> for ReplicatorDynamics { #[cfg(test)] mod tests { use super::*; - use crate::pure_math::analysis::ode::Euler; #[test] fn test_rock_paper_scissors() { diff --git a/math_explorer/src/applied/game_theory/mean_field/mod.rs b/math_explorer/src/applied/game_theory/mean_field/mod.rs index cde94e93..2ab539fc 100644 --- a/math_explorer/src/applied/game_theory/mean_field/mod.rs +++ b/math_explorer/src/applied/game_theory/mean_field/mod.rs @@ -104,7 +104,7 @@ mod tests { let term_fn = |x: f64, _m: f64| -> f64 { x * x }; let init_dist = |x: f64| -> f64 { (-x * x * 5.0).exp() }; - let (u, m) = mfg.solve(cost_fn, term_fn, init_dist, 5); + let (u, _m) = mfg.solve(cost_fn, term_fn, init_dist, 5); assert_eq!(u.nrows(), 50); assert_eq!(u.ncols(), 101); @@ -121,7 +121,7 @@ mod tests { let term_fn = |x: f64, _m: f64| -> f64 { x * x }; let init_dist = |x: f64| -> f64 { (-x * x * 5.0).exp() }; - let (u, m) = solver.solve(&config, &cost_fn, &term_fn, &init_dist); + let (u, _m) = solver.solve(&config, &cost_fn, &term_fn, &init_dist); assert_eq!(u.nrows(), 50); assert_eq!(u.ncols(), 101); diff --git a/math_explorer/src/biology/diffusion.rs b/math_explorer/src/biology/diffusion.rs index bbd8684d..4688c3fe 100644 --- a/math_explorer/src/biology/diffusion.rs +++ b/math_explorer/src/biology/diffusion.rs @@ -3,6 +3,21 @@ //! This module provides strategies for computing the spatial diffusion term $D \nabla^2 u$ //! in reaction-diffusion systems. +use thiserror::Error; + +/// Errors that can occur during spatial diffusion operations. +#[derive(Debug, Clone, PartialEq, Error)] +pub enum SpatialDiffusionError { + #[error("Buffer size mismatch for species {species}: expected >= {expected}, got {actual}")] + BufferSizeMismatch { + species: usize, + expected: usize, + actual: usize, + }, + #[error("Grid dimensions invalid: {0}")] + InvalidDimensions(String), +} + /// Defines a strategy for computing spatial diffusion. pub trait SpatialDiffusion { /// Computes diffusion terms for each point and calls the closure. @@ -12,7 +27,12 @@ pub trait SpatialDiffusion { /// * `index`: The linear index of the point. /// * `current_vals`: Current values of species at index. /// * `diff_vals`: The diffusion terms ($D \nabla^2 u$). - fn map_diffusion(&self, state: [&[f64]; N], coeffs: [f64; N], op: F) + fn map_diffusion( + &self, + state: [&[f64]; N], + coeffs: [f64; N], + op: F, + ) -> Result<(), SpatialDiffusionError> where F: FnMut(usize, [f64; N], [f64; N]); @@ -24,7 +44,12 @@ pub trait SpatialDiffusion { /// * `state` - Input concentration slices. /// * `out` - Output buffers for diffusion terms. /// * `coeffs` - Diffusion coefficients. - fn apply(&self, state: [&[f64]; N], out: [&mut [f64]; N], coeffs: [f64; N]) { + fn apply( + &self, + state: [&[f64]; N], + out: [&mut [f64]; N], + coeffs: [f64; N], + ) -> Result<(), SpatialDiffusionError> { // Default implementation: calculate diffusion and write to buffer self.map_diffusion(state, coeffs, |i, _, diffs| { for s in 0..N { @@ -32,7 +57,7 @@ pub trait SpatialDiffusion { out[s][i] = diffs[s]; } } - }); + }) } } @@ -69,31 +94,54 @@ impl crate::biology::reaction_diffusion::DiffusionModel for FiniteDifference1D { state: &crate::biology::reaction_diffusion::ChemicalState, out: &mut crate::biology::reaction_diffusion::ChemicalState, coeffs: &[f64], - ) { + ) -> Result<(), crate::biology::reaction_diffusion::DiffusionError> { + use crate::biology::reaction_diffusion::DiffusionError; + let n_species = state.num_species(); + if coeffs.len() != n_species { + return Err(DiffusionError::SpeciesCountMismatch { + expected: n_species, + actual: coeffs.len(), + }); + } + let dx_sq = self.dx * self.dx; let inv_dx_sq = 1.0 / dx_sq; for (s, d) in coeffs.iter().enumerate().take(n_species) { let u = &state.concentrations[s]; let out_u = &mut out.concentrations[s]; + + if u.len() != out_u.len() { + return Err(DiffusionError::GridSizeMismatch { + state_grid: u.len(), + model_grid: out_u.len(), + }); + } + let d = *d; apply_1d_stencil(u, out_u, d, inv_dx_sq); } + Ok(()) } } impl SpatialDiffusion for FiniteDifference1D { - fn map_diffusion(&self, state: [&[f64]; N], coeffs: [f64; N], mut op: F) + fn map_diffusion( + &self, + state: [&[f64]; N], + coeffs: [f64; N], + mut op: F, + ) -> Result<(), SpatialDiffusionError> where F: FnMut(usize, [f64; N], [f64; N]), { if N == 0 { - return; + return Ok(()); } let n = state[0].len(); if n == 0 { - return; + return Ok(()); } let dx_sq = self.dx * self.dx; @@ -107,11 +155,13 @@ impl SpatialDiffusion for FiniteDifference1D { // Verify all buffers are large enough for (s, buffer) in state.iter().enumerate().take(N) { - assert!( - buffer.len() >= n, - "Buffer too small for diffusion (species {})", - s - ); + if buffer.len() < n { + return Err(SpatialDiffusionError::BufferSizeMismatch { + species: s, + expected: n, + actual: buffer.len(), + }); + } } // 1. Left Boundary (i=0) @@ -177,6 +227,7 @@ impl SpatialDiffusion for FiniteDifference1D { } op(i, current_vals, diff_vals); } + Ok(()) } } @@ -307,27 +358,35 @@ impl crate::biology::reaction_diffusion::DiffusionModel for FiniteDifference2D { state: &crate::biology::reaction_diffusion::ChemicalState, out: &mut crate::biology::reaction_diffusion::ChemicalState, coeffs: &[f64], - ) { + ) -> Result<(), crate::biology::reaction_diffusion::DiffusionError> { + use crate::biology::reaction_diffusion::DiffusionError; + let n_species = state.num_species(); if n_species == 0 { - return; + return Ok(()); } // Ensure grid size matches let n_grid = self.width * self.height; - // In release mode, these checks are cheap. In debug, they catch errors. - // We use assert! because dimension mismatch is a critical bug. - assert_eq!( - state.grid_size(), - n_grid, - "ChemicalState grid size mismatch with FiniteDifference2D" - ); - assert_eq!(out.grid_size(), n_grid, "Output state grid size mismatch"); - assert_eq!( - coeffs.len(), - n_species, - "Diffusion coefficients count mismatch" - ); + + if state.grid_size() != n_grid { + return Err(DiffusionError::GridSizeMismatch { + state_grid: state.grid_size(), + model_grid: n_grid, + }); + } + if out.grid_size() != n_grid { + return Err(DiffusionError::GridSizeMismatch { + state_grid: out.grid_size(), + model_grid: n_grid, + }); + } + if coeffs.len() != n_species { + return Err(DiffusionError::SpeciesCountMismatch { + expected: n_species, + actual: coeffs.len(), + }); + } for (s, coeff) in coeffs.iter().enumerate().take(n_species) { let src = &state.concentrations[s]; @@ -336,24 +395,34 @@ impl crate::biology::reaction_diffusion::DiffusionModel for FiniteDifference2D { apply_2d_stencil_optimized(self.width, self.height, self.dx, self.dy, src, dst, coeff); } + Ok(()) } } impl SpatialDiffusion for FiniteDifference2D { - fn map_diffusion(&self, state: [&[f64]; N], coeffs: [f64; N], mut op: F) + fn map_diffusion( + &self, + state: [&[f64]; N], + coeffs: [f64; N], + mut op: F, + ) -> Result<(), SpatialDiffusionError> where F: FnMut(usize, [f64; N], [f64; N]), { if N == 0 { - return; + return Ok(()); } let n = self.width * self.height; if n == 0 { - return; + return Ok(()); } // Basic check for one buffer length to ensure safety if state[0].len() != n { - panic!("Buffer size mismatch in FiniteDifference2D"); + return Err(SpatialDiffusionError::BufferSizeMismatch { + species: 0, + expected: n, + actual: state[0].len(), + }); } let inv_dx_sq = 1.0 / (self.dx * self.dx); @@ -371,11 +440,13 @@ impl SpatialDiffusion for FiniteDifference2D { // Verify all buffers are large enough to avoid UB in unsafe block for (s, buffer) in state.iter().enumerate().take(N) { - assert!( - buffer.len() >= n, - "Buffer too small for diffusion (species {})", - s - ); + if buffer.len() < n { + return Err(SpatialDiffusionError::BufferSizeMismatch { + species: s, + expected: n, + actual: buffer.len(), + }); + } } iter_stencil_2d( @@ -405,6 +476,7 @@ impl SpatialDiffusion for FiniteDifference2D { op(idx, current_vals, diff_vals); }, ); + Ok(()) } } @@ -429,7 +501,8 @@ mod tests_2d { [u.as_slice(), v.as_slice()], [out_u.as_mut_slice(), out_v.as_mut_slice()], [1.0, 1.0], - ); + ) + .unwrap(); for val in out_u { assert_eq!(val, 0.0); @@ -463,7 +536,8 @@ mod tests_2d { [u.as_slice(), v.as_slice()], [out_u.as_mut_slice(), out_v.as_mut_slice()], [1.0, 1.0], - ); + ) + .unwrap(); // Interior points should be exactly 4.0 // (1,1) is index 1*5 + 1 = 6. @@ -513,7 +587,8 @@ mod tests_2d { [u.as_slice(), v.as_slice()], [out_u_1.as_mut_slice(), out_v_1.as_mut_slice()], [d_u, d_v], - ); + ) + .unwrap(); for i in 0..n { out_u_1[i] = u[i] + dt * (out_u_1[i] + 1.0); // Dummy reaction +1 out_v_1[i] = v[i] + dt * (out_v_1[i] + 2.0); // Dummy reaction +2 @@ -533,7 +608,8 @@ mod tests_2d { out_u_2[i] = u_curr + dt * (diff_u + reac_u); out_v_2[i] = v_curr + dt * (diff_v + reac_v); }, - ); + ) + .unwrap(); for i in 0..n { assert!((out_u_1[i] - out_u_2[i]).abs() < 1e-10); @@ -561,7 +637,7 @@ mod tests_2d { // Apply diffusion let coeffs = [0.1, 0.2]; - DiffusionModel::apply(&diff, &state, &mut out, &coeffs); + DiffusionModel::apply(&diff, &state, &mut out, &coeffs).unwrap(); // Check center point diffusion // Laplacian at center: (0+0+0+0 - 4*1) = -4 diff --git a/math_explorer/src/biology/morphogenesis/mod.rs b/math_explorer/src/biology/morphogenesis/mod.rs index 2d4c3fcf..1114c00c 100644 --- a/math_explorer/src/biology/morphogenesis/mod.rs +++ b/math_explorer/src/biology/morphogenesis/mod.rs @@ -173,10 +173,10 @@ impl, S: TuringSolverStrategy> Turin } /// Updates the grid using the solver strategy. - pub fn step(&mut self, dt: f64) { + pub fn step(&mut self, dt: f64) -> Result<(), solvers::MorphogenesisError> { let n = self.state.len(); if n == 0 { - return; + return Ok(()); } // Ensure buffers are the right size @@ -193,10 +193,11 @@ impl, S: TuringSolverStrategy> Turin self.d_u, self.d_v, dt, - ); + )?; // Swap buffers (states) std::mem::swap(&mut self.state, &mut self.next_state); + Ok(()) } } @@ -229,11 +230,13 @@ impl, S: TuringSolverStrategy> OdeSy let out_v = &mut out.v; // 1. Compute Diffusion - self.diffusion.apply( - [u.as_slice(), v.as_slice()], - [out_u.as_mut_slice(), out_v.as_mut_slice()], - [self.d_u, self.d_v], - ); + self.diffusion + .apply( + [u.as_slice(), v.as_slice()], + [out_u.as_mut_slice(), out_v.as_mut_slice()], + [self.d_u, self.d_v], + ) + .expect("SpatialDiffusion::apply failed in derivative_in_place"); // 2. Compute Reaction and Accumulate // SAFETY: @@ -269,7 +272,7 @@ impl, S: TuringSolverStrategy> TimeS fn step(&mut self, dt: f64) { // Delegate to the optimized inherent method - self.step(dt); + self.step(dt).expect("TuringSystem::step failed"); } } @@ -310,7 +313,7 @@ mod tests { // Run for a few steps let dt = 0.01; for _ in 0..5 { - system.step(dt); + system.step(dt).unwrap(); } // Capture output diff --git a/math_explorer/src/biology/morphogenesis/solvers.rs b/math_explorer/src/biology/morphogenesis/solvers.rs index 6f2ee797..59abdb21 100644 --- a/math_explorer/src/biology/morphogenesis/solvers.rs +++ b/math_explorer/src/biology/morphogenesis/solvers.rs @@ -1,6 +1,16 @@ use super::reaction::ReactionKinetics; use super::state::TuringState; -use crate::biology::diffusion::SpatialDiffusion; +use crate::biology::diffusion::{SpatialDiffusion, SpatialDiffusionError}; +use thiserror::Error; + +/// Errors that can occur during time-stepping. +#[derive(Debug, Clone, PartialEq, Error)] +pub enum MorphogenesisError { + #[error("Spatial diffusion error: {0}")] + DiffusionError(#[from] SpatialDiffusionError), + #[error("State size mismatch: expected {expected}, got {actual}")] + StateSizeMismatch { expected: usize, actual: usize }, +} /// Strategy for time-stepping the Turing System. /// @@ -26,7 +36,7 @@ pub trait TuringSolverStrategy { d_u: f64, d_v: f64, dt: f64, - ); + ) -> Result<(), MorphogenesisError>; } /// A fused Euler integration solver. @@ -53,15 +63,18 @@ impl TuringSolverStrategy for FusedEulerSolver { d_u: f64, d_v: f64, dt: f64, - ) { + ) -> Result<(), MorphogenesisError> { let n = state.len(); if n == 0 { - return; + return Ok(()); } // Ensure buffers are the right size (safety check, though caller should handle) if next_state.len() != n { - return; + return Err(MorphogenesisError::StateSizeMismatch { + expected: n, + actual: next_state.len(), + }); } let u = &state.u; @@ -90,6 +103,7 @@ impl TuringSolverStrategy for FusedEulerSolver { next_v[i] = v_curr + dt * (diff_v + reac_v); } }, - ); + )?; + Ok(()) } } diff --git a/math_explorer/src/biology/reaction_diffusion.rs b/math_explorer/src/biology/reaction_diffusion.rs index 47a0ed7b..d453da32 100644 --- a/math_explorer/src/biology/reaction_diffusion.rs +++ b/math_explorer/src/biology/reaction_diffusion.rs @@ -7,6 +7,19 @@ use crate::pure_math::analysis::ode::solvers::Euler; use crate::pure_math::analysis::ode::traits::{OdeSystem, Solver, VectorOperations}; use std::ops::{Add, AddAssign, Mul, MulAssign}; +use thiserror::Error; + +/// Errors that can occur during diffusion. +#[derive(Debug, Clone, PartialEq, Error)] +pub enum DiffusionError { + #[error("Grid size mismatch: state {state_grid} != model {model_grid}")] + GridSizeMismatch { + state_grid: usize, + model_grid: usize, + }, + #[error("Species count mismatch: expected {expected}, got {actual}")] + SpeciesCountMismatch { expected: usize, actual: usize }, +} /// Represents the state of a multi-species chemical system. /// @@ -207,7 +220,12 @@ pub trait DiffusionModel { /// * `state`: Current chemical state. /// * `out`: Output buffer for the diffusion term (D * Laplacian). /// * `coeffs`: Diffusion coefficients for each species. - fn apply(&self, state: &ChemicalState, out: &mut ChemicalState, coeffs: &[f64]); + fn apply( + &self, + state: &ChemicalState, + out: &mut ChemicalState, + coeffs: &[f64], + ) -> Result<(), DiffusionError>; } /// Defines a strategy for time integration of the Reaction-Diffusion system. @@ -265,7 +283,10 @@ impl OdeSystem fn derivative_in_place(&self, _t: f64, state: &ChemicalState, out: &mut ChemicalState) { // Compute Diffusion - self.diffusion.apply(state, out, &self.diffusion_coeffs); + // Panic if diffusion fails (violates OdeSystem contract) + self.diffusion + .apply(state, out, &self.diffusion_coeffs) + .expect("Diffusion application failed: dimensions must match"); // Add Reaction // Optimized: Use batch processing to allow vectorization and avoid gather/scatter diff --git a/math_explorer/src/physics/medical/dose_calculation.rs b/math_explorer/src/physics/medical/dose_calculation.rs index 67c34818..9e48cd1f 100644 --- a/math_explorer/src/physics/medical/dose_calculation.rs +++ b/math_explorer/src/physics/medical/dose_calculation.rs @@ -27,6 +27,7 @@ pub fn point_kernel(radius: f64, amplitude: f64, beta: f64) -> Result (f } #[cfg(test)] +#[allow(deprecated)] mod tests { use super::*; diff --git a/math_explorer/tests/bench_reaction_diffusion.rs b/math_explorer/tests/bench_reaction_diffusion.rs index b8d94f2e..e46e8d46 100644 --- a/math_explorer/tests/bench_reaction_diffusion.rs +++ b/math_explorer/tests/bench_reaction_diffusion.rs @@ -23,12 +23,12 @@ fn bench_turing_2d_step() { // Warmup for _ in 0..10 { - system.step(dt); + system.step(dt).unwrap(); } let start = Instant::now(); for _ in 0..iterations { - system.step(dt); + system.step(dt).unwrap(); } let duration = start.elapsed(); diff --git a/math_explorer/tests/test_lorahub.rs b/math_explorer/tests/test_lorahub.rs index da47bddc..5f6d92c3 100644 --- a/math_explorer/tests/test_lorahub.rs +++ b/math_explorer/tests/test_lorahub.rs @@ -1,6 +1,5 @@ use math_explorer::applied::lorahub::{LoraEnsemble, LoraStateDict}; use nalgebra::DMatrix; -use std::collections::HashMap; #[test] fn test_lorahub_linear_combination() { diff --git a/math_explorer/tests/test_turing_regression.rs b/math_explorer/tests/test_turing_regression.rs index f97369ab..9b24f673 100644 --- a/math_explorer/tests/test_turing_regression.rs +++ b/math_explorer/tests/test_turing_regression.rs @@ -33,7 +33,7 @@ fn test_turing_regression() { } for _ in 0..iterations { - system.step(0.01); + system.step(0.01).unwrap(); } // Verify exactness @@ -57,7 +57,7 @@ fn test_custom_kinetics_strategy() { // Run simulation for _ in 0..iterations { - system.step(0.1); + system.step(0.1).unwrap(); } // Check that values changed (diffusion + reaction happened) diff --git a/math_explorer/tests/test_turing_structure.rs b/math_explorer/tests/test_turing_structure.rs index 41007345..da02588b 100644 --- a/math_explorer/tests/test_turing_structure.rs +++ b/math_explorer/tests/test_turing_structure.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { - use math_explorer::biology::morphogenesis::{TuringState, TuringSystem}; - use math_explorer::pure_math::analysis::ode::{RungeKutta4, Solver, TimeStepper}; + use math_explorer::biology::morphogenesis::TuringSystem; + use math_explorer::pure_math::analysis::ode::{RungeKutta4, TimeStepper}; #[test] fn test_turing_system_generic_step() { diff --git a/math_explorer_gui/src/app.rs b/math_explorer_gui/src/app.rs index a1f9a6ee..8cff3ddd 100644 --- a/math_explorer_gui/src/app.rs +++ b/math_explorer_gui/src/app.rs @@ -2,8 +2,7 @@ use crate::tabs::{ chaos::ChaosTab, clinical_trials::ClinicalTrialsTab, epidemiology::EpidemiologyTab, fluid_dynamics::FluidDynamicsTab, game_theory::GameTheoryTab, medical::MedicalTab, morphogenesis::MorphogenesisTab, mri::MriTab, neuroscience::NeuroscienceTab, - number_theory::NumberTheoryTab, quantum::QuantumTab, solid_state::SolidStateTab, - ExplorerTab, + number_theory::NumberTheoryTab, quantum::QuantumTab, solid_state::SolidStateTab, ExplorerTab, }; use eframe::egui; diff --git a/math_explorer_gui/src/tabs/neuroscience/spike_analysis.rs b/math_explorer_gui/src/tabs/neuroscience/spike_analysis.rs index 2b7fb155..1fcf9ad1 100644 --- a/math_explorer_gui/src/tabs/neuroscience/spike_analysis.rs +++ b/math_explorer_gui/src/tabs/neuroscience/spike_analysis.rs @@ -63,7 +63,10 @@ impl NeuroscienceTool for SpikeAnalysisTool { ui.separator(); ui.horizontal(|ui| { - if ui.button(if self.is_running { "Pause" } else { "Run" }).clicked() { + if ui + .button(if self.is_running { "Pause" } else { "Run" }) + .clicked() + { self.is_running = !self.is_running; } if ui.button("Reset").clicked() { @@ -104,13 +107,24 @@ impl NeuroscienceTool for SpikeAnalysisTool { plot_ui.line(Line::new("Voltage", PlotPoints::new(self.history.clone()))); // Threshold Line - plot_ui.hline(egui_plot::HLine::new("Threshold", self.spike_threshold).color(egui::Color32::RED)); + plot_ui.hline( + egui_plot::HLine::new("Threshold", self.spike_threshold) + .color(egui::Color32::RED), + ); // Spike Markers (Raster) // Using VLine might be too much if there are many spikes, maybe Points? // Let's use Points at (time, threshold) - let spike_points: Vec<[f64; 2]> = self.spike_times.iter().map(|&t| [t, self.spike_threshold]).collect(); - plot_ui.points(egui_plot::Points::new("Spikes", PlotPoints::new(spike_points)).radius(3.0).color(egui::Color32::GREEN)); + let spike_points: Vec<[f64; 2]> = self + .spike_times + .iter() + .map(|&t| [t, self.spike_threshold]) + .collect(); + plot_ui.points( + egui_plot::Points::new("Spikes", PlotPoints::new(spike_points)) + .radius(3.0) + .color(egui::Color32::GREEN), + ); }); }); @@ -124,7 +138,9 @@ impl NeuroscienceTool for SpikeAnalysisTool { .show(ui, |plot_ui| { if !self.isis.is_empty() { let bars = self.compute_histogram(20); // 20 bins - plot_ui.bar_chart(BarChart::new("ISI Distribution", bars).color(egui::Color32::GOLD)); + plot_ui.bar_chart( + BarChart::new("ISI Distribution", bars).color(egui::Color32::GOLD), + ); } }); }); @@ -151,7 +167,7 @@ impl SpikeAnalysisTool { // Recording every 10th step = 0.1ms resolution // Or just check time % 0.1 < dt if (self.time * 10.0).round() % 1.0 == 0.0 { - self.history.push([self.time, v]); + self.history.push([self.time, v]); } // Manage History Size (keep last 5000 points = 500ms window approx if subsampled) diff --git a/math_explorer_gui/src/tabs/number_theory/prime_spiral.rs b/math_explorer_gui/src/tabs/number_theory/prime_spiral.rs index 60c0c2d7..a62acc97 100644 --- a/math_explorer_gui/src/tabs/number_theory/prime_spiral.rs +++ b/math_explorer_gui/src/tabs/number_theory/prime_spiral.rs @@ -76,9 +76,9 @@ impl PrimeSpiralWidget { if is_prime_lookup[i] { pixels[py * size + px] = Color32::WHITE; } - if i == 1 { - pixels[py * size + px] = Color32::RED; - } + if i == 1 { + pixels[py * size + px] = Color32::RED; + } } }