eng_units/units/
temperature_unit.rs

1// eng-units - engineering unit conversion and calculation library
2// Copyright (C) 2023 Frank Pereny
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17use crate::units::AmountOfSubstanceUnit;
18use crate::units::ElectricCurrentUnit;
19use crate::units::IsEngUnitType;
20use crate::units::LengthUnit;
21use crate::units::LuminousIntensityUnit;
22use crate::units::MassUnit;
23use crate::units::TimeUnit;
24
25#[macro_export]
26macro_rules! temperature {
27    ($value:literal, $unit:expr) => {{
28        let mut unit = EngUnit::new();
29        unit.value = $value;
30        unit.temperature_count = 1;
31        if $unit == TemperatureDeltaUnit::None {
32            unit.temperature_count = 0;
33        }
34        unit.temperature_unit = $unit;
35        unit
36    }};
37}
38
39#[derive(Copy, Clone, Debug, PartialEq)]
40pub enum TemperatureDeltaUnit {
41    C,
42    R,
43    F,
44    K,
45    None,
46}
47
48impl<
49        T: IsEngUnitType
50            + Into<AmountOfSubstanceUnit>
51            + Into<ElectricCurrentUnit>
52            + Into<LengthUnit>
53            + Into<LuminousIntensityUnit>
54            + Into<MassUnit>
55            + Into<TemperatureDeltaUnit>
56            + Into<TimeUnit>,
57    > From<&T> for TemperatureDeltaUnit
58{
59    fn from(value: &T) -> Self {
60        if T::is_amount_unit() {
61            value.into()
62        } else {
63            Self::None
64        }
65    }
66}
67impl IsEngUnitType for TemperatureDeltaUnit {
68    fn is_temperature_unit() -> bool {
69        true
70    }
71}
72impl From<AmountOfSubstanceUnit> for TemperatureDeltaUnit {
73    fn from(_: AmountOfSubstanceUnit) -> Self {
74        TemperatureDeltaUnit::None
75    }
76}
77impl From<ElectricCurrentUnit> for TemperatureDeltaUnit {
78    fn from(_: ElectricCurrentUnit) -> Self {
79        TemperatureDeltaUnit::None
80    }
81}
82impl From<LengthUnit> for TemperatureDeltaUnit {
83    fn from(_: LengthUnit) -> Self {
84        TemperatureDeltaUnit::None
85    }
86}
87impl From<LuminousIntensityUnit> for TemperatureDeltaUnit {
88    fn from(_: LuminousIntensityUnit) -> Self {
89        TemperatureDeltaUnit::None
90    }
91}
92impl From<MassUnit> for TemperatureDeltaUnit {
93    fn from(_: MassUnit) -> Self {
94        TemperatureDeltaUnit::None
95    }
96}
97impl From<TimeUnit> for TemperatureDeltaUnit {
98    fn from(_: TimeUnit) -> Self {
99        TemperatureDeltaUnit::None
100    }
101}
102
103impl TemperatureDeltaUnit {
104    pub fn to_string(&self) -> &'static str {
105        match self {
106            TemperatureDeltaUnit::R => "R",
107            TemperatureDeltaUnit::K => "K",
108            TemperatureDeltaUnit::C => "°C",
109            TemperatureDeltaUnit::F => "°F",
110            TemperatureDeltaUnit::None => "",
111        }
112    }
113
114    pub fn to_latex(&self) -> &'static str {
115        match self {
116            TemperatureDeltaUnit::R => "R",
117            TemperatureDeltaUnit::K => "K",
118            TemperatureDeltaUnit::C => "^\\circ C",
119            TemperatureDeltaUnit::F => "^\\circ F",
120            TemperatureDeltaUnit::None => "",
121        }
122    }
123
124    pub fn conversion_factor(from: &TemperatureDeltaUnit, to: &TemperatureDeltaUnit) -> f64 {
125        match from {
126            TemperatureDeltaUnit::R => match to {
127                TemperatureDeltaUnit::R => 1.0,
128                TemperatureDeltaUnit::K => 5.0 / 9.0,
129                TemperatureDeltaUnit::F => 1.0,
130                TemperatureDeltaUnit::C => 5.0 / 9.0,
131                TemperatureDeltaUnit::None => 1.0,
132            },
133            TemperatureDeltaUnit::F => match to {
134                TemperatureDeltaUnit::R => 1.0,
135                TemperatureDeltaUnit::K => 5.0 / 9.0,
136                TemperatureDeltaUnit::F => 1.0,
137                TemperatureDeltaUnit::C => 5.0 / 9.0,
138                TemperatureDeltaUnit::None => 1.0,
139            },
140            TemperatureDeltaUnit::K => match to {
141                TemperatureDeltaUnit::R => 9.0 / 5.0,
142                TemperatureDeltaUnit::K => 1.0,
143                TemperatureDeltaUnit::F => 9.0 / 5.0,
144                TemperatureDeltaUnit::C => 1.0,
145                TemperatureDeltaUnit::None => 1.0,
146            },
147            TemperatureDeltaUnit::C => match to {
148                TemperatureDeltaUnit::R => 9.0 / 5.0,
149                TemperatureDeltaUnit::K => 1.0,
150                TemperatureDeltaUnit::F => 9.0 / 5.0,
151                TemperatureDeltaUnit::C => 1.0,
152                TemperatureDeltaUnit::None => 1.0,
153            },
154            TemperatureDeltaUnit::None => 1.0,
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use crate::units::amount_of_substance_unit::AmountOfSubstanceUnit;
162    use crate::units::electric_current_unit::ElectricCurrentUnit;
163    use crate::units::length_unit::LengthUnit;
164    use crate::units::luminous_intensity_unit::LuminousIntensityUnit;
165    use crate::units::mass_unit::MassUnit;
166    use crate::units::temperature_unit::TemperatureDeltaUnit;
167    use crate::units::time_unit::TimeUnit;
168    use crate::units::EngUnit;
169
170    #[test]
171    fn conversion_r_to_r() {
172        let val = TemperatureDeltaUnit::conversion_factor(
173            &TemperatureDeltaUnit::R,
174            &TemperatureDeltaUnit::R,
175        );
176        assert_eq!(1.0, val);
177    }
178
179    #[test]
180    fn conversion_f_to_f() {
181        let val = TemperatureDeltaUnit::conversion_factor(
182            &TemperatureDeltaUnit::F,
183            &TemperatureDeltaUnit::F,
184        );
185        assert_eq!(1.0, val);
186    }
187
188    #[test]
189    fn conversion_k_to_k() {
190        let val = TemperatureDeltaUnit::conversion_factor(
191            &TemperatureDeltaUnit::K,
192            &TemperatureDeltaUnit::K,
193        );
194        assert_eq!(1.0, val);
195    }
196
197    #[test]
198    fn conversion_k_to_f() {
199        let val = TemperatureDeltaUnit::conversion_factor(
200            &TemperatureDeltaUnit::K,
201            &TemperatureDeltaUnit::F,
202        );
203        assert_eq!(9.0 / 5.0, val);
204    }
205
206    #[test]
207    fn conversion_r_to_c() {
208        let val = TemperatureDeltaUnit::conversion_factor(
209            &TemperatureDeltaUnit::R,
210            &TemperatureDeltaUnit::C,
211        );
212        assert_eq!(5.0 / 9.0, val);
213    }
214
215    #[test]
216    fn conversion_r_to_none() {
217        let val = TemperatureDeltaUnit::conversion_factor(
218            &TemperatureDeltaUnit::R,
219            &TemperatureDeltaUnit::None,
220        );
221        assert_eq!(1.0, val);
222    }
223
224    #[test]
225    fn conversion_f_to_none() {
226        let val = TemperatureDeltaUnit::conversion_factor(
227            &TemperatureDeltaUnit::F,
228            &TemperatureDeltaUnit::None,
229        );
230        assert_eq!(1.0, val);
231    }
232
233    #[test]
234    fn conversion_c_to_none() {
235        let val = TemperatureDeltaUnit::conversion_factor(
236            &TemperatureDeltaUnit::C,
237            &TemperatureDeltaUnit::None,
238        );
239        assert_eq!(1.0, val);
240    }
241
242    #[test]
243    fn conversion_c_to_f() {
244        let val = TemperatureDeltaUnit::conversion_factor(
245            &TemperatureDeltaUnit::C,
246            &TemperatureDeltaUnit::F,
247        );
248        assert_eq!(9.0 / 5.0, val);
249    }
250
251    #[test]
252    fn conversion_f_to_c() {
253        let val = TemperatureDeltaUnit::conversion_factor(
254            &TemperatureDeltaUnit::F,
255            &TemperatureDeltaUnit::C,
256        );
257        assert_eq!(5.0 / 9.0, val);
258    }
259
260    #[test]
261    fn conversion_c_to_k() {
262        let val = TemperatureDeltaUnit::conversion_factor(
263            &TemperatureDeltaUnit::C,
264            &TemperatureDeltaUnit::K,
265        );
266        assert_eq!(1.0, val);
267    }
268
269    #[test]
270    fn conversion_k_to_c() {
271        let val = TemperatureDeltaUnit::conversion_factor(
272            &TemperatureDeltaUnit::K,
273            &TemperatureDeltaUnit::C,
274        );
275        assert_eq!(1.0, val);
276    }
277
278    #[test]
279    fn conversion_k_to_r() {
280        let val = TemperatureDeltaUnit::conversion_factor(
281            &TemperatureDeltaUnit::K,
282            &TemperatureDeltaUnit::R,
283        );
284        assert_eq!(9.0 / 5.0, val);
285    }
286
287    #[test]
288    fn conversion_r_to_k() {
289        let val = TemperatureDeltaUnit::conversion_factor(
290            &TemperatureDeltaUnit::R,
291            &TemperatureDeltaUnit::K,
292        );
293        assert_eq!(5.0 / 9.0, val);
294    }
295
296    #[test]
297    fn conversion_c_to_r() {
298        let val = TemperatureDeltaUnit::conversion_factor(
299            &TemperatureDeltaUnit::C,
300            &TemperatureDeltaUnit::R,
301        );
302        assert_eq!(9.0 / 5.0, val);
303    }
304
305    #[test]
306    fn conversion_f_to_k() {
307        let val = TemperatureDeltaUnit::conversion_factor(
308            &TemperatureDeltaUnit::F,
309            &TemperatureDeltaUnit::K,
310        );
311        assert_eq!(5.0 / 9.0, val);
312    }
313
314    #[test]
315    fn conversion_f_to_r() {
316        let val = TemperatureDeltaUnit::conversion_factor(
317            &TemperatureDeltaUnit::F,
318            &TemperatureDeltaUnit::R,
319        );
320        assert_eq!(1.0, val);
321    }
322
323    #[test]
324    fn conversion_r_to_f() {
325        let val = TemperatureDeltaUnit::conversion_factor(
326            &TemperatureDeltaUnit::R,
327            &TemperatureDeltaUnit::F,
328        );
329        assert_eq!(1.0, val);
330    }
331
332    #[test]
333    fn from_is_none() {
334        let val = TemperatureDeltaUnit::conversion_factor(
335            &TemperatureDeltaUnit::None,
336            &TemperatureDeltaUnit::K,
337        );
338        assert_eq!(1.0, val);
339    }
340
341    #[test]
342    fn to_is_none() {
343        let val = TemperatureDeltaUnit::conversion_factor(
344            &TemperatureDeltaUnit::K,
345            &TemperatureDeltaUnit::None,
346        );
347        assert_eq!(1.0, val);
348    }
349
350    #[test]
351    fn both_are_none() {
352        let val = TemperatureDeltaUnit::conversion_factor(
353            &TemperatureDeltaUnit::None,
354            &TemperatureDeltaUnit::None,
355        );
356        assert_eq!(1.0, val);
357    }
358
359    #[test]
360    fn macro_create_temperature_1() {
361        let unit = temperature!(123.45, TemperatureDeltaUnit::C);
362        assert_eq!(123.45, unit.value);
363        assert_eq!("123.45 °C", unit.to_string());
364        assert_eq!(TimeUnit::None, unit.time_unit);
365        assert_eq!(AmountOfSubstanceUnit::None, unit.amount_of_substance_unit);
366        assert_eq!(ElectricCurrentUnit::None, unit.electric_current_unit);
367        assert_eq!(LengthUnit::None, unit.length_unit);
368        assert_eq!(LuminousIntensityUnit::None, unit.luminous_intensity_unit);
369        assert_eq!(MassUnit::None, unit.mass_unit);
370        assert_eq!(TemperatureDeltaUnit::C, unit.temperature_unit);
371        assert_eq!(TimeUnit::None, unit.time_unit);
372        assert_eq!(1, unit.temperature_count);
373        assert_eq!(0, unit.time_count);
374        assert_eq!(0, unit.length_count);
375        assert_eq!(0, unit.mass_count);
376        assert_eq!(0, unit.luminous_intensity_count);
377        assert_eq!(0, unit.amount_of_substance_count);
378        assert_eq!(0, unit.electric_current_count);
379    }
380
381    #[test]
382    fn macro_create_temperature_2() {
383        let unit = temperature!(123.45, TemperatureDeltaUnit::K);
384        assert_eq!(123.45, unit.value);
385        assert_eq!("123.45 K", unit.to_string());
386        assert_eq!(TimeUnit::None, unit.time_unit);
387        assert_eq!(AmountOfSubstanceUnit::None, unit.amount_of_substance_unit);
388        assert_eq!(ElectricCurrentUnit::None, unit.electric_current_unit);
389        assert_eq!(LengthUnit::None, unit.length_unit);
390        assert_eq!(LuminousIntensityUnit::None, unit.luminous_intensity_unit);
391        assert_eq!(MassUnit::None, unit.mass_unit);
392        assert_eq!(TemperatureDeltaUnit::K, unit.temperature_unit);
393        assert_eq!(TimeUnit::None, unit.time_unit);
394        assert_eq!(1, unit.temperature_count);
395        assert_eq!(0, unit.time_count);
396        assert_eq!(0, unit.length_count);
397        assert_eq!(0, unit.mass_count);
398        assert_eq!(0, unit.luminous_intensity_count);
399        assert_eq!(0, unit.amount_of_substance_count);
400        assert_eq!(0, unit.electric_current_count);
401    }
402
403    #[test]
404    fn macro_create_temperature_3() {
405        let unit = temperature!(123.45, TemperatureDeltaUnit::F);
406        assert_eq!(123.45, unit.value);
407        assert_eq!("123.45 °F", unit.to_string());
408        assert_eq!(TimeUnit::None, unit.time_unit);
409        assert_eq!(AmountOfSubstanceUnit::None, unit.amount_of_substance_unit);
410        assert_eq!(ElectricCurrentUnit::None, unit.electric_current_unit);
411        assert_eq!(LengthUnit::None, unit.length_unit);
412        assert_eq!(LuminousIntensityUnit::None, unit.luminous_intensity_unit);
413        assert_eq!(MassUnit::None, unit.mass_unit);
414        assert_eq!(TemperatureDeltaUnit::F, unit.temperature_unit);
415        assert_eq!(TimeUnit::None, unit.time_unit);
416        assert_eq!(1, unit.temperature_count);
417        assert_eq!(0, unit.time_count);
418        assert_eq!(0, unit.length_count);
419        assert_eq!(0, unit.mass_count);
420        assert_eq!(0, unit.luminous_intensity_count);
421        assert_eq!(0, unit.amount_of_substance_count);
422        assert_eq!(0, unit.electric_current_count);
423    }
424
425    #[test]
426    fn macro_create_temperature_4() {
427        let unit = temperature!(123.45, TemperatureDeltaUnit::R);
428        assert_eq!(123.45, unit.value);
429        assert_eq!("123.45 R", unit.to_string());
430        assert_eq!(TimeUnit::None, unit.time_unit);
431        assert_eq!(AmountOfSubstanceUnit::None, unit.amount_of_substance_unit);
432        assert_eq!(ElectricCurrentUnit::None, unit.electric_current_unit);
433        assert_eq!(LengthUnit::None, unit.length_unit);
434        assert_eq!(LuminousIntensityUnit::None, unit.luminous_intensity_unit);
435        assert_eq!(MassUnit::None, unit.mass_unit);
436        assert_eq!(TemperatureDeltaUnit::R, unit.temperature_unit);
437        assert_eq!(TimeUnit::None, unit.time_unit);
438        assert_eq!(1, unit.temperature_count);
439        assert_eq!(0, unit.time_count);
440        assert_eq!(0, unit.length_count);
441        assert_eq!(0, unit.mass_count);
442        assert_eq!(0, unit.luminous_intensity_count);
443        assert_eq!(0, unit.amount_of_substance_count);
444        assert_eq!(0, unit.electric_current_count);
445    }
446
447    #[test]
448    fn macro_create_temperature_5() {
449        let unit = temperature!(123.45, TemperatureDeltaUnit::None);
450        assert_eq!(123.45, unit.value);
451        assert_eq!("123.45", unit.to_string());
452        assert_eq!(TimeUnit::None, unit.time_unit);
453        assert_eq!(AmountOfSubstanceUnit::None, unit.amount_of_substance_unit);
454        assert_eq!(ElectricCurrentUnit::None, unit.electric_current_unit);
455        assert_eq!(LengthUnit::None, unit.length_unit);
456        assert_eq!(LuminousIntensityUnit::None, unit.luminous_intensity_unit);
457        assert_eq!(MassUnit::None, unit.mass_unit);
458        assert_eq!(TemperatureDeltaUnit::None, unit.temperature_unit);
459        assert_eq!(TimeUnit::None, unit.time_unit);
460        assert_eq!(0, unit.temperature_count);
461        assert_eq!(0, unit.time_count);
462        assert_eq!(0, unit.length_count);
463        assert_eq!(0, unit.mass_count);
464        assert_eq!(0, unit.luminous_intensity_count);
465        assert_eq!(0, unit.amount_of_substance_count);
466        assert_eq!(0, unit.electric_current_count);
467    }
468
469    #[test]
470    fn test_unit_strings() {
471        let unit = temperature!(123.45, TemperatureDeltaUnit::C);
472        assert_eq!("°C", unit.unit_to_string());
473        let unit = temperature!(123.45, TemperatureDeltaUnit::K);
474        assert_eq!("K", unit.unit_to_string());
475        let unit = temperature!(123.45, TemperatureDeltaUnit::R);
476        assert_eq!("R", unit.unit_to_string());
477        let unit = temperature!(123.45, TemperatureDeltaUnit::F);
478        assert_eq!("°F", unit.unit_to_string());
479        let unit = temperature!(123.45, TemperatureDeltaUnit::None);
480        assert_eq!("", unit.unit_to_string());
481    }
482
483    #[test]
484    fn test_unit_strings_2() {
485        assert_eq!("°C", TemperatureDeltaUnit::C.to_string());
486        assert_eq!("°F", TemperatureDeltaUnit::F.to_string());
487        assert_eq!("R", TemperatureDeltaUnit::R.to_string());
488        assert_eq!("K", TemperatureDeltaUnit::K.to_string());
489        assert_eq!("", TemperatureDeltaUnit::None.to_string());
490    }
491
492    #[test]
493    fn unit_clone() {
494        let unit = temperature!(123.45, TemperatureDeltaUnit::R);
495        let unit_2 = unit.clone();
496        assert_eq!(123.45, unit_2.value);
497        assert_eq!("123.45 R", unit_2.to_string());
498        assert_eq!(TimeUnit::None, unit_2.time_unit);
499        assert_eq!(AmountOfSubstanceUnit::None, unit_2.amount_of_substance_unit);
500        assert_eq!(ElectricCurrentUnit::None, unit_2.electric_current_unit);
501        assert_eq!(LengthUnit::None, unit_2.length_unit);
502        assert_eq!(LuminousIntensityUnit::None, unit_2.luminous_intensity_unit);
503        assert_eq!(MassUnit::None, unit_2.mass_unit);
504        assert_eq!(TemperatureDeltaUnit::R, unit_2.temperature_unit);
505        assert_eq!(TimeUnit::None, unit_2.time_unit);
506        assert_eq!(1, unit_2.temperature_count);
507        assert_eq!(0, unit_2.time_count);
508        assert_eq!(0, unit_2.length_count);
509        assert_eq!(0, unit_2.mass_count);
510        assert_eq!(0, unit_2.luminous_intensity_count);
511        assert_eq!(0, unit_2.amount_of_substance_count);
512        assert_eq!(0, unit_2.electric_current_count);
513    }
514
515    #[test]
516    fn test_equality() {
517        let a = temperature!(123.45, TemperatureDeltaUnit::K);
518        let b = temperature!(123.45, TemperatureDeltaUnit::K);
519        assert_eq!(a.temperature_unit, b.temperature_unit);
520
521        let a = temperature!(123.45, TemperatureDeltaUnit::C);
522        let b = temperature!(123.45, TemperatureDeltaUnit::C);
523        assert_eq!(a.temperature_unit, b.temperature_unit);
524
525        let a = temperature!(123.45, TemperatureDeltaUnit::R);
526        let b = temperature!(123.45, TemperatureDeltaUnit::R);
527        assert_eq!(a.temperature_unit, b.temperature_unit);
528
529        let a = temperature!(123.45, TemperatureDeltaUnit::F);
530        let b = temperature!(123.45, TemperatureDeltaUnit::F);
531        assert_eq!(a.temperature_unit, b.temperature_unit);
532    }
533
534    #[test]
535    fn test_debug() {
536        let a = temperature!(123.45, TemperatureDeltaUnit::K);
537        let s = format!("{a:?}");
538        assert!(!s.is_empty())
539    }
540
541    #[test]
542    fn test_latex_1() {
543        let a = temperature!(123.45, TemperatureDeltaUnit::K);
544        assert_eq!("$123.45\\ K$", a.to_latex());
545    }
546
547    #[test]
548    fn test_latex_2() {
549        let a = temperature!(123.45, TemperatureDeltaUnit::R);
550        assert_eq!("$123.45\\ R$", a.to_latex());
551    }
552
553    #[test]
554    fn test_latex_3() {
555        let a = temperature!(123.45, TemperatureDeltaUnit::C);
556        assert_eq!("$123.45\\ ^\\circ C$", a.to_latex());
557    }
558
559    #[test]
560    fn test_latex_4() {
561        let a = temperature!(123.45, TemperatureDeltaUnit::F);
562        assert_eq!("$123.45\\ ^\\circ F$", a.to_latex());
563    }
564
565    #[test]
566    fn test_latex_5() {
567        let a = temperature!(123.45, TemperatureDeltaUnit::None);
568        assert_eq!("$123.45$", a.to_latex());
569    }
570}