sciforge_hub/tools/
utils.rs1pub fn format_scientific(value: f64, precision: usize) -> String {
6 if value == 0.0 {
7 return "0.0".to_string();
8 }
9 let exp = value.abs().log10().floor() as i32;
10 let mantissa = value / 10f64.powi(exp);
11 format!("{mantissa:.precision$}e{exp}")
12}
13
14pub fn format_si(value: f64) -> String {
16 let prefixes = [
17 (1e24, "Y"),
18 (1e21, "Z"),
19 (1e18, "E"),
20 (1e15, "P"),
21 (1e12, "T"),
22 (1e9, "G"),
23 (1e6, "M"),
24 (1e3, "k"),
25 (1.0, ""),
26 (1e-3, "m"),
27 (1e-6, "µ"),
28 (1e-9, "n"),
29 (1e-12, "p"),
30 (1e-15, "f"),
31 (1e-18, "a"),
32 ];
33 let abs = value.abs();
34 for &(threshold, prefix) in &prefixes {
35 if abs >= threshold {
36 return format!("{:.3}{prefix}", value / threshold);
37 }
38 }
39 format_scientific(value, 3)
40}
41
42pub fn linspace(start: f64, end: f64, n: usize) -> Vec<f64> {
44 if n <= 1 {
45 return vec![start];
46 }
47 let step = (end - start) / (n - 1) as f64;
48 (0..n).map(|i| start + i as f64 * step).collect()
49}
50
51pub fn logspace(start_exp: f64, end_exp: f64, n: usize) -> Vec<f64> {
53 linspace(start_exp, end_exp, n)
54 .into_iter()
55 .map(|e| 10f64.powf(e))
56 .collect()
57}
58
59pub fn clamp_vec(data: &mut [f64], min: f64, max: f64) {
61 for v in data.iter_mut() {
62 *v = v.clamp(min, max);
63 }
64}
65
66pub fn normalize(data: &[f64]) -> Vec<f64> {
68 let min = data.iter().copied().fold(f64::INFINITY, f64::min);
69 let max = data.iter().copied().fold(f64::NEG_INFINITY, f64::max);
70 let range = max - min;
71 if range == 0.0 {
72 return vec![0.0; data.len()];
73 }
74 data.iter().map(|&v| (v - min) / range).collect()
75}
76
77pub fn cumulative_sum(data: &[f64]) -> Vec<f64> {
79 let mut result = Vec::with_capacity(data.len());
80 let mut sum = 0.0;
81 for &v in data {
82 sum += v;
83 result.push(sum);
84 }
85 result
86}
87
88pub fn moving_average(data: &[f64], window: usize) -> Vec<f64> {
90 if window == 0 || data.is_empty() {
91 return data.to_vec();
92 }
93 let n = data.len();
94 (0..n)
95 .map(|i| {
96 let lo = i.saturating_sub(window / 2);
97 let hi = (i + window / 2 + 1).min(n);
98 data[lo..hi].iter().sum::<f64>() / (hi - lo) as f64
99 })
100 .collect()
101}
102
103pub fn relative_error(computed: f64, exact: f64) -> f64 {
105 if exact == 0.0 {
106 return computed.abs();
107 }
108 ((computed - exact) / exact).abs()
109}
110
111pub fn approx_equal(a: f64, b: f64, tolerance: f64) -> bool {
113 (a - b).abs() <= tolerance
114}