Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_units/tests/dimension.rs

6.4 KiB, 1 run

created by r1870400018:17301, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! External-oracle tests for the dimensional algebra.
2//!
3//! The expected values here come from physics and metrology, not from the
4//! implementation: force is `M·L·T^-2`, an electronvolt is 1.602176634e-19 J,
5//! half a turn is π radians, and adding a length to a time is a category error.
6
7use oxedyne_fe2o3_units::{
8 dimension::{
9 Base,
10 Dimension,
11 Ratio,
12 },
13 quantity::{
14 Quantity,
15 ELECTRONVOLT_J,
16 },
17};
18
19use oxedyne_fe2o3_core::prelude::*;
20
21/// Asserts two floats agree to a relative tolerance.
22fn close(a: f64, b: f64) -> bool {
23 if b == 0.0 {
24 a.abs() < 1.0e-12
25 } else {
26 ((a - b) / b).abs() < 1.0e-9
27 }
28}
29
30#[test]
31fn test_dimensionless_identity_01() -> Outcome<()> {
32 let d = Dimension::dimensionless();
33 assert!(d.is_dimensionless());
34 // Multiplying by the identity changes nothing.
35 let l = Dimension::length();
36 assert_eq!(l.mul(&d), l);
37 Ok(())
38}
39
40#[test]
41fn test_velocity_times_time_is_length_01() -> Outcome<()> {
42 // (L/T) * T = L.
43 let v = Dimension::velocity();
44 let t = Dimension::time();
45 assert_eq!(v.mul(&t), Dimension::length());
46 Ok(())
47}
48
49#[test]
50fn test_force_is_mass_times_acceleration_01() -> Outcome<()> {
51 // Force = M·L·T^-2, arrived at from F = m·a.
52 let m = Dimension::mass();
53 let a = Dimension::acceleration();
54 assert_eq!(m.mul(&a), Dimension::force());
55 // And the exponents are exactly what physics says.
56 let f = Dimension::force();
57 assert_eq!(f.exponent(Base::Mass), Ratio::int(1));
58 assert_eq!(f.exponent(Base::Length), Ratio::int(1));
59 assert_eq!(f.exponent(Base::Time), Ratio::int(-2));
60 Ok(())
61}
62
63#[test]
64fn test_area_is_length_squared_01() -> Outcome<()> {
65 // Area = L^2.
66 let l = Dimension::length();
67 assert_eq!(l.powi(2), Dimension::area());
68 assert_eq!(Dimension::area().exponent(Base::Length), Ratio::int(2));
69 Ok(())
70}
71
72#[test]
73fn test_root_of_area_is_length_01() -> Outcome<()> {
74 // sqrt(L^2) = L, exercising rational exponents.
75 let half = res!(Ratio::frac(1, 2));
76 assert_eq!(Dimension::area().pow(&half), Dimension::length());
77 Ok(())
78}
79
80#[test]
81fn test_quantity_velocity_times_time_01() -> Outcome<()> {
82 // 20 m/s for 3 s covers 60 m, and the result is a length.
83 let speed = res!(Quantity::metres(20.0, 4)).div(&res!(Quantity::seconds(1.0, 4)));
84 let time = res!(Quantity::seconds(3.0, 4));
85 let dist = speed.mul(&time);
86 assert!(close(dist.val(), 60.0));
87 assert_eq!(dist.dim(), Dimension::length());
88 Ok(())
89}
90
91#[test]
92fn test_quantity_force_from_mass_and_acceleration_01() -> Outcome<()> {
93 // 2 kg at 3 m/s^2 gives 6 N with the force dimension.
94 let mass = res!(Quantity::kilograms(2.0, 4));
95 let acc = res!(Quantity::new(3.0, 4, Dimension::acceleration()));
96 let force = mass.mul(&acc);
97 assert!(close(force.val(), 6.0));
98 assert_eq!(force.dim(), Dimension::force());
99 Ok(())
100}
101
102#[test]
103fn test_add_same_dimension_ok_01() -> Outcome<()> {
104 let a = res!(Quantity::metres(5.0, 4));
105 let b = res!(Quantity::metres(3.0, 4));
106 let sum = res!(a.add(&b));
107 assert!(close(sum.val(), 8.0));
108 assert_eq!(sum.dim(), Dimension::length());
109 Ok(())
110}
111
112#[test]
113fn test_add_mismatched_dimension_errors_01() -> Outcome<()> {
114 // Adding a length to a time must be rejected.
115 let length = res!(Quantity::metres(5.0, 4));
116 let time = res!(Quantity::seconds(3.0, 4));
117 assert!(length.add(&time).is_err());
118 Ok(())
119}
120
121#[test]
122fn test_sub_mismatched_dimension_errors_01() -> Outcome<()> {
123 let energy = res!(Quantity::joules(1.0, 4));
124 let mass = res!(Quantity::kilograms(1.0, 4));
125 assert!(energy.sub(&mass).is_err());
126 Ok(())
127}
128
129#[test]
130fn test_electronvolt_in_joules_01() -> Outcome<()> {
131 // 1 eV = 1.602176634e-19 J (exact by definition).
132 let ev = res!(Quantity::electronvolts(1.0, 10));
133 assert!(close(ev.val(), 1.602176634e-19));
134 assert!(close(ev.val(), ELECTRONVOLT_J));
135 assert_eq!(ev.dim(), Dimension::energy());
136 Ok(())
137}
138
139#[test]
140fn test_degrees_to_radians_01() -> Outcome<()> {
141 // 180 degrees = π radians.
142 let half_turn = res!(Quantity::degrees(180.0, 10));
143 assert!(close(half_turn.val(), std::f64::consts::PI));
144 assert_eq!(half_turn.dim(), Dimension::angle());
145 Ok(())
146}
147
148#[test]
149fn test_angle_is_not_dimensionless_01() -> Outcome<()> {
150 // A radian is tagged, so it cannot be added to a bare number.
151 let angle = res!(Quantity::radians(1.0, 4));
152 let bare = res!(Quantity::dimensionless(1.0, 4));
153 assert_ne!(angle.dim(), Dimension::dimensionless());
154 assert!(angle.add(&bare).is_err());
155 Ok(())
156}
157
158#[test]
159fn test_litre_is_cubic_metres_01() -> Outcome<()> {
160 // 1 L = 1e-3 m^3, with the volume dimension.
161 let vol = res!(Quantity::litres(1.0, 4));
162 assert!(close(vol.val(), 1.0e-3));
163 assert_eq!(vol.dim(), Dimension::volume());
164 Ok(())
165}
166
167#[test]
168fn test_minutes_and_hours_to_seconds_01() -> Outcome<()> {
169 let m = res!(Quantity::minutes(1.0, 4));
170 let h = res!(Quantity::hours(1.0, 4));
171 assert!(close(m.val(), 60.0));
172 assert!(close(h.val(), 3600.0));
173 assert_eq!(m.dim(), Dimension::time());
174 assert_eq!(h.dim(), Dimension::time());
175 Ok(())
176}
177
178#[test]
179fn test_sigfig_multiply_takes_minimum_01() -> Outcome<()> {
180 // 2.0 (2 sf) × 3.00 (3 sf) → 2 sf.
181 let a = res!(Quantity::dimensionless(2.0, 2));
182 let b = res!(Quantity::dimensionless(3.00, 3));
183 let p = a.mul(&b);
184 assert_eq!(p.sf(), 2);
185 assert!(close(p.rounded(), 6.0));
186 Ok(())
187}
188
189#[test]
190fn test_sigfig_divide_takes_minimum_01() -> Outcome<()> {
191 // 6.000 (4 sf) / 3.0 (2 sf) → 2 sf.
192 let a = res!(Quantity::dimensionless(6.000, 4));
193 let b = res!(Quantity::dimensionless(3.0, 2));
194 let q = a.div(&b);
195 assert_eq!(q.sf(), 2);
196 Ok(())
197}
198
199#[test]
200fn test_sigfig_add_by_decimal_place_01() -> Outcome<()> {
201 // 12.11 (4 sf, tied to hundredths) + 0.1 (1 sf, tied to tenths) = 12.2,
202 // which is coarse to the tenths and so carries 3 sf.
203 let a = res!(Quantity::metres(12.11, 4));
204 let b = res!(Quantity::metres(0.1, 1));
205 let sum = res!(a.add(&b));
206 assert_eq!(sum.sf(), 3);
207 assert!(close(sum.rounded(), 12.2));
208 Ok(())
209}
210
211#[test]
212fn test_dimension_display_force_01() -> Outcome<()> {
213 // The mismatch error names dimensions, so Display must be legible.
214 let s = fmt!("{}", Dimension::force());
215 assert!(s.contains("kg"));
216 assert!(s.contains("m"));
217 assert!(s.contains("s^-2"));
218 Ok(())
219}