rs_sci/
lib.rs

1pub mod calculus;
2pub mod complex;
3pub mod consts;
4pub mod linear;
5pub mod stats;
6pub mod units;
7pub mod algebra;
8
9#[cfg(test)]
10mod tests {
11    use super::*;
12    use crate::complex::Complex;
13    use crate::consts::Const;
14    use crate::linear::{Matrix, Vector};
15    use crate::stats::Stats;
16    use crate::units::*;
17    use std::f64::consts::PI;
18
19    // consts
20    #[test]
21    fn test_mathematical_constants() {
22        assert_eq!(Const::pi(Some(7)), 3.1415927);
23        assert_eq!(Const::e(Some(7)), 2.7182818);
24        assert_eq!(Const::golden_ratio(Some(8)), 1.61803399);
25
26        assert!((Const::PI - std::f64::consts::PI).abs() < 1e-10);
27        assert!((Const::E - std::f64::consts::E).abs() < 1e-10);
28        assert!((Const::GOLDEN_RATIO - 1.618033988749895).abs() < 1e-10);
29    }
30
31    #[test]
32    fn test_physical_constants() {
33        assert!((Const::GRAVITATIONAL_CONSTANT - 6.67430e-11).abs() < 1e-15);
34        assert!(
35            (Const::REDUCED_PLANCK_CONSTANT - Const::PLANCK_CONSTANT / (2.0 * Const::PI)).abs()
36                < 1e-40
37        );
38        assert!((Const::BOLTZMANN_CONSTANT - 1.380649e-23).abs() < 1e-28);
39        assert!((Const::PROTON_MASS - 1.67262192369e-27).abs() < 1e-35);
40        assert!((Const::AVOGADRO_CONSTANT - 6.02214076e23).abs() < 1e15);
41    }
42
43    #[test]
44    fn test_precision_handling() {
45        assert_eq!(Const::pi(Some(2)), 3.14);
46        assert_eq!(Const::pi(Some(4)), 3.1416);
47        assert_eq!(Const::e(Some(2)), 2.72);
48        assert_eq!(Const::e(Some(4)), 2.7183);
49        assert_eq!(Const::golden_ratio(Some(2)), 1.62);
50        assert_eq!(Const::golden_ratio(Some(4)), 1.6180);
51    }
52
53    #[test]
54    fn test_constant_unit_conversions() {
55        let g_in_nm3_kg_s2 = Const::gravitational_constant(Force::Newton);
56        assert!(g_in_nm3_kg_s2.to_newtons() > 0.0);
57
58        let h_in_joule_sec = Const::planck_constant(Energy::Joule);
59        assert!(h_in_joule_sec.to_joules() > 0.0);
60
61        let kb_in_joule_per_k = Const::boltzmann_constant(Energy::Joule);
62        assert!(kb_in_joule_per_k.to_joules() > 0.0);
63
64        let me_in_atomic_units = Const::electron_mass(Mass::Atomic);
65        assert!(me_in_atomic_units.to_kg() > 0.0);
66
67        let mp_in_atomic_units = Const::proton_mass(Mass::Atomic);
68        assert!(mp_in_atomic_units.to_kg() > 0.0);
69
70        let na_in_hz = Const::avogadro_constant(Frequency::Hertz);
71        assert!(na_in_hz.to_hertz() > 0.0);
72    }
73
74    #[test]
75    fn test_derived_constants() {
76        let fine_structure = Const::ELEMENTARY_CHARGE.powi(2)
77            / (4.0
78                * Const::PI
79                * 8.854187812813e-12
80                * Const::REDUCED_PLANCK_CONSTANT
81                * Const::SPEED_OF_LIGHT);
82        assert!((fine_structure - 0.0072973525693).abs() < 1e-10);
83    }
84
85    #[test]
86    fn test_constant_relationships() {
87        let thermal_energy_at_room_temp = Const::BOLTZMANN_CONSTANT * 300.0;
88        assert!(thermal_energy_at_room_temp > 0.0);
89
90        let electron_rest_energy = Const::ELECTRON_MASS * Const::SPEED_OF_LIGHT.powi(2);
91        assert!(electron_rest_energy > 0.0);
92
93        let proton_electron_mass_ratio = Const::PROTON_MASS / Const::ELECTRON_MASS;
94        assert!((proton_electron_mass_ratio - 1836.15267343).abs() < 1e-6);
95    }
96
97    #[test]
98    fn test_constant_conversions() {
99        let c_in_kmh = Const::speed_of_light(Speed::KmH);
100        let e_in_ev = Const::elementary_charge(ElectricCharge::ElementaryCharge);
101        let m_in_kg = Const::electron_mass(Mass::Kg);
102        assert!(c_in_kmh.to_mps() > 0.0);
103        assert!(e_in_ev.to_coulombs() > 0.0);
104        assert!(m_in_kg.to_kg() > 0.0);
105    }
106
107    // units
108    #[test]
109    fn test_mass_conversions() {
110        let kg = Mass::Kg;
111        assert!((kg.clone().convert_to(Mass::Pound).to_kg() - 1.0).abs() < 1e-10);
112        assert!((kg.convert_to(Mass::Gram).to_kg() - 1.0).abs() < 1e-10);
113
114        let ton_in_kg = Mass::Ton.to_kg();
115        assert!((ton_in_kg - 1000.0).abs() < 1e-10);
116
117        assert!((Mass::Pound.convert_to(Mass::Ounce).to_kg() - 0.45359237).abs() < 1e-8);
118    }
119
120    #[test]
121    fn test_length_conversions() {
122        let meter = Length::Meter;
123        assert!((meter.convert_to(Length::Foot).to_meters() - 1.0).abs() < 1e-10);
124        assert!((Length::Mile.convert_to(Length::Kilometer).to_meters() - 1609.344).abs() < 1e-10);
125        assert!((Length::Inch.convert_to(Length::Centimeter).to_meters() - 0.0254).abs() < 1e-10);
126
127        let nautical_mile = Length::NauticalMile;
128        let meters = nautical_mile.to_meters();
129        assert!((meters - 1852.0).abs() < 1e-10);
130    }
131
132    #[test]
133    fn test_temperature_conversions() {
134        let celsius = Temperature::Celsius;
135        assert!((celsius.to_kelvin(0.0) - 273.15).abs() < 1e-10);
136        assert!((celsius.to_fahrenheit(100.0) - 212.0).abs() < 1e-10);
137        assert!((Temperature::Fahrenheit.to_celsius(32.0) - 0.0).abs() < 1e-10);
138        assert!((Temperature::Kelvin.to_celsius(273.15) - 0.0).abs() < 1e-10);
139    }
140
141    #[test]
142    fn test_time_conversions() {
143        let second = Time::Second;
144        assert!((second.convert_to(Time::Millisecond).to_seconds() - 1.0).abs() < 1e-10);
145        assert!((Time::Hour.convert_to(Time::Minute).to_seconds() - 3600.0).abs() < 1e-10);
146        assert!((Time::Day.convert_to(Time::Hour).to_seconds() - 86400.0).abs() < 1e-10);
147
148        assert!((Time::JulianYear.convert_to(Time::Day).to_seconds() - 31557600.0).abs() < 1e-10);
149        assert!((Time::SiderealDay.convert_to(Time::Hour).to_seconds() - 86164.0905).abs() < 1e-6);
150        assert!(
151            (Time::SiderealYear.convert_to(Time::Day).to_seconds() - 31558149.504).abs() < 1e-6
152        );
153    }
154
155    #[test]
156    fn test_length_astronomical() {
157        let light_year = Length::LightYear;
158        assert!((light_year.convert_to(Length::Parsec).to_meters() - 9.461e15).abs() < 1e12);
159        assert!(
160            (Length::AstroUnit.convert_to(Length::Kilometer).to_meters() - 149597870700.0).abs()
161                < 1e5
162        );
163        assert!((Length::Parsec.convert_to(Length::LightYear).to_meters() - 3.086e16).abs() < 1e13);
164    }
165
166    #[test]
167    fn test_length_microscopic() {
168        assert!((Length::Angstrom.convert_to(Length::Nanometer).to_meters() - 1e-10).abs() < 1e-15);
169        assert!((Length::Fermi.convert_to(Length::Angstrom).to_meters() - 1e-15).abs() < 1e-20);
170        assert!((Length::Micron.convert_to(Length::Millimeter).to_meters() - 1e-6).abs() < 1e-10);
171    }
172
173    #[test]
174    fn test_area_conversions() {
175        let hectare = Area::Hectare;
176        assert!((hectare.to_square_meters() - 10000.0).abs() < 1e-10);
177        assert!((Area::Acre.to_square_meters() - 4046.8564224).abs() < 1e-6);
178        assert!((Area::SquareMile.to_hectares() - 258.9988110336).abs() < 1e-6);
179    }
180
181    #[test]
182    fn test_volume_conversions() {
183        assert!((Volume::Liter.to_cubic_meters() - 0.001).abs() < 1e-10);
184        assert!((Volume::Gallon.to_liters() - 3.785411784).abs() < 1e-6);
185        assert!((Volume::CubicFoot.to_cubic_meters() - 0.028316846592).abs() < 1e-10);
186
187        let custom_volume = Length::Meter.cubed();
188        assert!((custom_volume.to_liters() - 1000.0).abs() < 1e-6);
189    }
190
191    #[test]
192    fn test_speed_conversions() {
193        assert!((Speed::C.to_mps() - 299792458.0).abs() < 1e-6);
194        assert!((Speed::Mach.to_kmh() - 1234.8).abs() < 1e-1);
195        assert!((Speed::Knot.to_mps() - 0.514444).abs() < 1e-6);
196    }
197
198    #[test]
199    fn test_force_and_energy() {
200        let newton = Force::Newton;
201        assert!((newton.to_pounds() - 0.224809).abs() < 1e-6);
202        assert!((Force::Kg.to_newtons() - 9.80665).abs() < 1e-6);
203
204        let joule = Energy::Joule;
205        assert!((joule.to_calories() - 0.239006).abs() < 1e-6);
206        assert!((Energy::Ev.to_joules() - 1.602176634e-19).abs() < 1e-25);
207    }
208
209    #[test]
210    fn test_pressure_conversions() {
211        assert!((Pressure::Bar.to_pascal() - 100000.0).abs() < 1e-6);
212        assert!((Pressure::Atmospheric.to_pascal() - 101325.0).abs() < 1e-6);
213        assert!((Pressure::Psi.to_pascal() - 6894.757293168361).abs() < 1e-6);
214        assert!((Pressure::Torr.to_pascal() - 133.322).abs() < 1e-3);
215    }
216
217    #[test]
218    fn test_electrical_units() {
219        let coulomb = ElectricCharge::Coulomb;
220        assert!((coulomb.to_elementary_charges() - 6.241509074e18).abs() < 1e12);
221
222        let ampere = Current::Ampere;
223        assert!((ampere.to_milliamperes() - 1000.0).abs() < 1e-10);
224    }
225
226    #[test]
227    fn test_frequency_and_acceleration() {
228        let hz = Frequency::Hertz;
229        assert!((hz.to_kilohertz() - 0.001).abs() < 1e-10);
230        assert!((Frequency::Gigahertz.to_hertz() - 1e9).abs() < 1e-6);
231
232        let g = Acceleration::G;
233        assert!((g.to_mps2() - 9.80665).abs() < 1e-6);
234        assert!((Acceleration::MetersPerSecondSquared.to_g() - 0.101972).abs() < 1e-6);
235    }
236
237    #[test]
238    fn test_compound_unit_conversions() {
239        let work = Energy::Custom(Force::Newton, Length::Meter);
240        assert!((work.to_joules() - 1.0).abs() < 1e-10);
241
242        let power = Energy::Joule.per_time(Time::Second);
243        assert!((power.to_watts() - 1.0).abs() < 1e-10);
244
245        let accel = Length::Meter.per_time_squared(Time::Second, Time::Second);
246        assert!((accel.to_mps2() - 1.0).abs() < 1e-10);
247
248        let freq = Time::Second.frequency();
249        assert!((freq.to_hertz() - 1.0).abs() < 1e-10);
250    }
251
252    // linear algebra
253    #[test]
254    fn test_vector_operations() {
255        let v1 = Vector::new(vec![1.0, 2.0, 3.0]);
256        let v2 = Vector::new(vec![4.0, 5.0, 6.0]);
257        let v3 = Vector::new(vec![2.0, 1.0, -1.0]);
258
259        let cross = v1.cross(&v2).unwrap();
260        assert!((cross[0] + 3.0).abs() < 1e-10);
261        assert!((cross[1] - 6.0).abs() < 1e-10);
262        assert!((cross[2] - (-3.0)).abs() < 1e-10);
263
264        let v4 = Vector::new(vec![1.0, 2.0]);
265        assert!(v1.dot(&v4).is_err());
266        assert!(v1.cross(&v4).is_err());
267
268        let normalized = v3.normalize();
269        assert!((normalized.magnitude() - 1.0).abs() < 1e-10);
270
271        let sum = (&v1 + &v2).unwrap();
272        assert!((sum[0] - 5.0).abs() < 1e-10);
273        assert!((sum[1] - 7.0).abs() < 1e-10);
274        assert!((sum[2] - 9.0).abs() < 1e-10);
275    }
276
277    #[test]
278    fn test_matrix_operations() {
279        let m = Matrix::new(vec![
280            vec![4.0, -2.0, 1.0],
281            vec![-2.0, 4.0, -2.0],
282            vec![1.0, -2.0, 4.0],
283        ])
284        .unwrap();
285
286        let (l, u) = m.lu_decomposition().unwrap();
287        let lu_product = (l * u).unwrap();
288        for i in 0..3 {
289            for j in 0..3 {
290                assert!((m[(i, j)] - lu_product[(i, j)]).abs() < 1e-10);
291            }
292        }
293
294        let (q, r) = m.qr_decomposition().unwrap();
295        let qr_product = (q * r).unwrap();
296        for i in 0..3 {
297            for j in 0..3 {
298                assert!((m[(i, j)] - qr_product[(i, j)]).abs() < 1e-10);
299            }
300        }
301
302        assert!(m.is_symmetric());
303        assert!(m.is_positive_definite());
304        assert!((m.trace().unwrap() - 12.0).abs() < 1e-10);
305
306        let l = m.cholesky().unwrap();
307        let l_transpose = l.transpose();
308        let ll_product = (l * l_transpose).unwrap();
309        for i in 0..3 {
310            for j in 0..3 {
311                assert!((m[(i, j)] - ll_product[(i, j)]).abs() < 1e-10);
312            }
313        }
314    }
315
316    #[test]
317    fn test_matrix_transformations() {
318        let m = Matrix::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]]).unwrap();
319
320        // Test matrix-vector multiplication
321        let v = Vector::new(vec![1.0, 2.0]);
322        let mv = (&m * &v).unwrap();
323        assert!((mv[0] - 5.0).abs() < 1e-10);
324        assert!((mv[1] - 11.0).abs() < 1e-10);
325
326        // Test matrix inverse
327        let inv = m.inverse().unwrap();
328        let identity = (m.clone() * inv.clone()).unwrap();
329        assert!((identity[(0, 0)] - 1.0).abs() < 1e-10);
330        assert!((identity[(1, 1)] - 1.0).abs() < 1e-10);
331        assert!(identity[(0, 1)].abs() < 1e-10);
332        assert!(identity[(1, 0)].abs() < 1e-10);
333
334        // Test matrix rank
335        assert_eq!(m.rank().unwrap(), 2);
336
337        let singular_matrix = Matrix::new(vec![vec![1.0, 2.0], vec![2.0, 4.0]]).unwrap();
338        assert_eq!(singular_matrix.rank().unwrap(), 1);
339    }
340
341    #[test]
342    fn test_matrix_special_cases() {
343        assert!(Matrix::new(vec![]).is_err());
344
345        let rect_matrix = Matrix::new(vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]]).unwrap();
346        assert!(rect_matrix.determinant().is_err());
347        assert!(rect_matrix.inverse().is_err());
348        assert!(rect_matrix.lu_decomposition().is_err());
349
350        let singular = Matrix::new(vec![vec![1.0, 1.0], vec![1.0, 1.0]]).unwrap();
351        assert!(singular.inverse().is_err());
352
353        let non_pd = Matrix::new(vec![vec![1.0, 2.0], vec![2.0, 1.0]]).unwrap();
354        assert!(!non_pd.is_positive_definite());
355    }
356
357    #[test]
358    fn test_matrix_decompositions() {
359        let m = Matrix::new(vec![vec![4.0, 2.0], vec![2.0, 4.0]]).unwrap();
360
361        let (l, u) = m.lu_decomposition().unwrap();
362        assert!((l[(1, 0)] * u[(0, 1)] + l[(1, 1)] * u[(1, 1)] - m[(1, 1)]).abs() < 1e-10);
363
364        let inverse = m.inverse().unwrap();
365        assert!((inverse[(0, 0)] * m[(0, 0)] + inverse[(0, 1)] * m[(1, 0)] - 1.0).abs() < 1e-10);
366    }
367
368    // statistics
369    #[test]
370    fn test_central_tendency() {
371        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
372
373        assert!((Stats::mean(&data).unwrap() - 3.0).abs() < 1e-10);
374        assert!((Stats::median(&data).unwrap() - 3.0).abs() < 1e-10);
375
376        let data_with_mode = vec![1.0, 2.0, 2.0, 3.0, 4.0];
377        let mode = Stats::mode(&data_with_mode).unwrap();
378        assert_eq!(mode, vec![2.0]);
379    }
380
381    #[test]
382    fn test_dispersion() {
383        let data = vec![2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
384
385        assert!((Stats::variance(&data).unwrap() - 4.571428571428571).abs() < 1e-10);
386        assert!((Stats::std_dev(&data).unwrap() - 2.138089935299395).abs() < 1e-10);
387        assert!((Stats::range(&data).unwrap() - 7.0).abs() < 1e-10);
388    }
389
390    #[test]
391    fn test_distribution_characteristics() {
392        let data = vec![1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0];
393
394        let quartiles = Stats::quartiles(&data).unwrap();
395        assert!((quartiles.0 - 2.0).abs() < 1e-10); // Q1
396        assert!((quartiles.1 - 3.0).abs() < 1e-10); // Q2 (median)
397        assert!((quartiles.2 - 4.0).abs() < 1e-10); // Q3
398    }
399
400    #[test]
401    fn test_effect_size() {
402        let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
403        let data2 = vec![2.0, 3.0, 4.0, 5.0, 6.0];
404
405        let d = Stats::cohens_d(&data1, &data2).unwrap();
406        assert!(d < 0.0); // data1 mean < data2 mean
407    }
408
409    #[test]
410    fn test_regression() {
411        let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
412        let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
413
414        let (slope, intercept) = Stats::linear_regression(&x, &y).unwrap();
415        assert!((slope - 2.0).abs() < 1e-10);
416        assert!(intercept.abs() < 1e-10);
417    }
418
419    #[test]
420    fn test_time_series() {
421        let data = vec![1.0, 2.0, 1.0, 2.0, 1.0];
422        let ac1 = Stats::autocorrelation(&data, 1).unwrap();
423        assert!(ac1 < 0.0);
424
425        let ac2 = Stats::autocorrelation(&data, 2).unwrap();
426        assert!(ac2 > 0.0);
427    }
428
429    // complex numbers
430    #[test]
431    fn test_complex_arithmetic() {
432        let z1 = Complex::new(1.0, 2.0);
433        let z2 = Complex::new(3.0, 4.0);
434
435        let sum = z1 + z2;
436        assert!((sum.re() as f32 - 4.0).abs() < 1e-10);
437        assert!((sum.im() as f32 - 6.0).abs() < 1e-10);
438
439        let product = z1 * z2;
440        assert!((product.re() as f32 - (-5.0)).abs() < 1e-10);
441        assert!((product.im() as f32 - 10.0).abs() < 1e-10);
442    }
443
444    #[test]
445    fn test_complex_properties() {
446        let z = Complex::new(3.0, 4.0);
447
448        assert!((z.modulus() - 5.0).abs() < 1e-10);
449        assert!((z.argument() - (4.0_f64).atan2(3.0)).abs() < 1e-10);
450
451        let conjugate = z.conjugate();
452        assert!((conjugate.re() - 3.0).abs() < 1e-10);
453        assert!((conjugate.im() + 4.0).abs() < 1e-10);
454    }
455
456    #[test]
457    fn test_complex_functions() {
458        let z = Complex::new(0.0, PI);
459
460        let exp_z = z.exp();
461        assert!((exp_z.re() + 1.0).abs() < 1e-10);
462        assert!(exp_z.im().abs() < 1e-10);
463
464        let z1 = Complex::new(1.0, 1.0);
465        let ln_z = z1.ln();
466
467        let expected_re = (2.0_f64).sqrt().ln();
468        let expected_im = PI / 4.0;
469
470        assert!((ln_z.re() - expected_re).abs() < 1e-10);
471        assert!((ln_z.im() - expected_im).abs() < 1e-10);
472
473        let z2 = Complex::new(1.0, 0.0);
474        let ln_z2 = z2.ln();
475        assert!(ln_z2.re().abs() < 1e-10);
476        assert!(ln_z2.im().abs() < 1e-10);
477    }
478
479    // calc
480    #[test]
481    fn test_derivatives() {
482        let f = |x: f64| x * x;
483        assert!((calculus::Calculus::derivative(&f, 2.0, 1e-6) - 4.0).abs() < 1e-4);
484        assert!((calculus::Calculus::derivative(&f, -2.0, 1e-6) + 4.0).abs() < 1e-4);
485        assert!((calculus::Calculus::second_derivative(&f, 0.0, 1e-6) - 2.0).abs() < 1e-4);
486    }
487
488    #[test]
489    fn test_integrals() {
490        let f = |x: f64| x * x;
491
492        let simpson = calculus::Calculus::integrate_simpson(&f, 0.0, 1.0, 1000);
493        assert!((simpson - 1.0 / 3.0).abs() < 1e-4);
494
495        let trapezoid = calculus::Calculus::integrate_trapezoid(&f, 0.0, 1.0, 1000);
496        assert!((trapezoid - 1.0 / 3.0).abs() < 1e-3);
497
498        let rectangle = calculus::Calculus::integrate_rectangle(&f, 0.0, 1.0, 1000);
499        assert!((rectangle - 1.0 / 3.0).abs() < 1e-3);
500    }
501
502    #[test]
503    fn test_differential_equations() {
504        let f = |_x: f64, y: f64| y;
505
506        let euler = calculus::Calculus::euler(&f, 0.0, 1.0, 0.01, 100);
507        assert!((euler.last().unwrap().1 - f64::exp(1.0)).abs() < 0.1);
508
509        let rk4 = calculus::Calculus::runge_kutta4(&f, 0.0, 1.0, 0.01, 100);
510        assert!((rk4.last().unwrap().1 - f64::exp(1.0)).abs() < 0.01);
511    }
512}