eng_units/
units.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
17pub mod amount_of_substance_unit;
18pub mod electric_current_unit;
19pub mod length_unit;
20pub mod luminous_intensity_unit;
21pub mod mass_unit;
22pub mod temperature_unit;
23pub mod time_unit;
24
25use crate::complex_units::ComplexUnit;
26use crate::units::amount_of_substance_unit::AmountOfSubstanceUnit;
27use crate::units::electric_current_unit::ElectricCurrentUnit;
28use crate::units::length_unit::LengthUnit;
29use crate::units::luminous_intensity_unit::LuminousIntensityUnit;
30use crate::units::mass_unit::MassUnit;
31use crate::units::temperature_unit::TemperatureDeltaUnit;
32use crate::units::time_unit::TimeUnit;
33
34use std::cmp::Ordering;
35use std::fmt::Display;
36use std::ops;
37
38#[derive(Clone, Debug)]
39pub struct EngUnit {
40    pub value: f64,
41    pub amount_of_substance_count: i32,
42    pub amount_of_substance_unit: AmountOfSubstanceUnit,
43    pub electric_current_count: i32,
44    pub electric_current_unit: ElectricCurrentUnit,
45    pub length_count: i32,
46    pub length_unit: LengthUnit,
47    pub luminous_intensity_count: i32,
48    pub luminous_intensity_unit: LuminousIntensityUnit,
49    pub mass_count: i32,
50    pub mass_unit: MassUnit,
51    pub temperature_count: i32,
52    pub temperature_unit: TemperatureDeltaUnit,
53    pub time_count: i32,
54    pub time_unit: TimeUnit,
55    pub unit_numerator: Vec<ComplexUnit>,
56    pub unit_denominator: Vec<ComplexUnit>,
57}
58
59impl Default for EngUnit {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl Display for EngUnit {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        if self.has_units() || self.has_custom_untits() {
68            write!(f, "{:.2} {}", self.value, self.unit_to_string())
69        } else {
70            write!(f, "{:.2}", self.value)
71        }
72    }
73}
74
75pub trait Convert<T> {
76    fn conversion_factor(from_unit: &T, to_unit: &T) -> f64;
77}
78
79pub trait IsEngUnitType {
80    fn is_amount_unit() -> bool {
81        false
82    }
83    fn is_electric_current_unit() -> bool {
84        false
85    }
86    fn is_length_unit() -> bool {
87        false
88    }
89    fn is_luminous_unit() -> bool {
90        false
91    }
92    fn is_mass_unit() -> bool {
93        false
94    }
95    fn is_temperature_unit() -> bool {
96        false
97    }
98    fn is_time_unit() -> bool {
99        false
100    }
101}
102
103impl EngUnit {
104    pub fn new() -> Self {
105        Self {
106            value: 1.0,
107            length_count: 0,
108            length_unit: LengthUnit::None,
109            time_count: 0,
110            time_unit: TimeUnit::None,
111            mass_count: 0,
112            mass_unit: MassUnit::None,
113            temperature_count: 0,
114            temperature_unit: TemperatureDeltaUnit::None,
115            electric_current_count: 0,
116            electric_current_unit: ElectricCurrentUnit::None,
117            luminous_intensity_count: 0,
118            luminous_intensity_unit: LuminousIntensityUnit::None,
119            amount_of_substance_count: 0,
120            amount_of_substance_unit: AmountOfSubstanceUnit::None,
121            unit_numerator: Vec::new(),
122            unit_denominator: Vec::new(),
123        }
124    }
125
126    fn has_custom_untits(&self) -> bool {
127        self.unit_numerator.len() > 0 || self.unit_denominator.len() > 0
128    }
129
130    fn to_amount_unit<
131        T: Into<AmountOfSubstanceUnit>
132            + Into<ElectricCurrentUnit>
133            + Into<LengthUnit>
134            + Into<LuminousIntensityUnit>
135            + Into<MassUnit>
136            + Into<TemperatureDeltaUnit>
137            + Into<TimeUnit>,
138    >(
139        unit: T,
140    ) -> AmountOfSubstanceUnit {
141        unit.into()
142    }
143    fn to_electric_unit<
144        T: Into<AmountOfSubstanceUnit>
145            + Into<ElectricCurrentUnit>
146            + Into<LengthUnit>
147            + Into<LuminousIntensityUnit>
148            + Into<MassUnit>
149            + Into<TemperatureDeltaUnit>
150            + Into<TimeUnit>,
151    >(
152        unit: T,
153    ) -> ElectricCurrentUnit {
154        unit.into()
155    }
156    fn to_length_unit<
157        T: Into<AmountOfSubstanceUnit>
158            + Into<ElectricCurrentUnit>
159            + Into<LengthUnit>
160            + Into<LuminousIntensityUnit>
161            + Into<MassUnit>
162            + Into<TemperatureDeltaUnit>
163            + Into<TimeUnit>,
164    >(
165        unit: T,
166    ) -> LengthUnit {
167        unit.into()
168    }
169    fn to_luminous_unit<
170        T: Into<AmountOfSubstanceUnit>
171            + Into<ElectricCurrentUnit>
172            + Into<LengthUnit>
173            + Into<LuminousIntensityUnit>
174            + Into<MassUnit>
175            + Into<TemperatureDeltaUnit>
176            + Into<TimeUnit>,
177    >(
178        unit: T,
179    ) -> LuminousIntensityUnit {
180        unit.into()
181    }
182    fn to_mass_unit<
183        T: Into<AmountOfSubstanceUnit>
184            + Into<ElectricCurrentUnit>
185            + Into<LengthUnit>
186            + Into<LuminousIntensityUnit>
187            + Into<MassUnit>
188            + Into<TemperatureDeltaUnit>
189            + Into<TimeUnit>,
190    >(
191        unit: T,
192    ) -> MassUnit {
193        unit.into()
194    }
195    fn to_temperature_unit<
196        T: Into<AmountOfSubstanceUnit>
197            + Into<ElectricCurrentUnit>
198            + Into<LengthUnit>
199            + Into<LuminousIntensityUnit>
200            + Into<MassUnit>
201            + Into<TemperatureDeltaUnit>
202            + Into<TimeUnit>,
203    >(
204        unit: T,
205    ) -> TemperatureDeltaUnit {
206        unit.into()
207    }
208
209    fn to_time_unit<
210        T: Into<AmountOfSubstanceUnit>
211            + Into<ElectricCurrentUnit>
212            + Into<LengthUnit>
213            + Into<LuminousIntensityUnit>
214            + Into<MassUnit>
215            + Into<TemperatureDeltaUnit>
216            + Into<TimeUnit>,
217    >(
218        unit: T,
219    ) -> TimeUnit {
220        unit.into()
221    }
222
223    pub fn convert<
224        T: IsEngUnitType
225            + Into<AmountOfSubstanceUnit>
226            + Into<ElectricCurrentUnit>
227            + Into<LengthUnit>
228            + Into<LuminousIntensityUnit>
229            + Into<MassUnit>
230            + Into<TemperatureDeltaUnit>
231            + Into<TimeUnit>,
232    >(
233        &self,
234        to_unit: T,
235    ) -> Self {
236        let mut new_unit = self.clone();
237        if T::is_amount_unit() {
238            let from_unit = &self.amount_of_substance_unit;
239            let to_unit = EngUnit::to_amount_unit(to_unit);
240            let mut conversion_factor =
241                AmountOfSubstanceUnit::conversion_factor(from_unit, &to_unit);
242            conversion_factor = f64::powf(conversion_factor, self.amount_of_substance_count as f64);
243            match self.amount_of_substance_count.cmp(&0) {
244                Ordering::Greater => {
245                    new_unit.value *= conversion_factor;
246                }
247                Ordering::Less => {
248                    new_unit.value /= conversion_factor;
249                }
250                Ordering::Equal => {}
251            }
252            new_unit.amount_of_substance_unit = to_unit;
253        } else if T::is_electric_current_unit() {
254            let from_unit = &self.electric_current_unit;
255            let to_unit = EngUnit::to_electric_unit(to_unit);
256            let mut conversion_factor = ElectricCurrentUnit::conversion_factor(from_unit, &to_unit);
257            conversion_factor = f64::powf(conversion_factor, self.electric_current_count as f64);
258            match self.electric_current_count.cmp(&0) {
259                Ordering::Greater => {
260                    new_unit.value *= conversion_factor;
261                }
262                Ordering::Less => {
263                    new_unit.value /= conversion_factor;
264                }
265                Ordering::Equal => {}
266            }
267            new_unit.electric_current_unit = to_unit;
268        } else if T::is_length_unit() {
269            let from_unit = &self.length_unit;
270            let to_unit = EngUnit::to_length_unit(to_unit);
271            let mut conversion_factor = LengthUnit::conversion_factor(from_unit, &to_unit);
272            conversion_factor = f64::powf(conversion_factor, self.length_count as f64);
273            match self.length_count.cmp(&0) {
274                Ordering::Greater => {
275                    new_unit.value *= conversion_factor;
276                }
277                Ordering::Less => {
278                    new_unit.value /= conversion_factor;
279                }
280                Ordering::Equal => {}
281            }
282            new_unit.length_unit = to_unit;
283        } else if T::is_luminous_unit() {
284            let from_unit = &self.luminous_intensity_unit;
285            let to_unit = EngUnit::to_luminous_unit(to_unit);
286            let mut conversion_factor =
287                LuminousIntensityUnit::conversion_factor(from_unit, &to_unit);
288            conversion_factor = f64::powf(conversion_factor, self.luminous_intensity_count as f64);
289            match self.luminous_intensity_count.cmp(&0) {
290                Ordering::Greater => {
291                    new_unit.value *= conversion_factor;
292                }
293                Ordering::Less => {
294                    new_unit.value /= conversion_factor;
295                }
296                Ordering::Equal => {}
297            }
298            new_unit.luminous_intensity_unit = to_unit;
299        } else if T::is_mass_unit() {
300            let from_unit = &self.mass_unit;
301            let to_unit = EngUnit::to_mass_unit(to_unit);
302            let mut conversion_factor = MassUnit::conversion_factor(from_unit, &to_unit);
303            conversion_factor = f64::powf(conversion_factor, self.mass_count as f64);
304            match self.mass_count.cmp(&0) {
305                Ordering::Greater => {
306                    new_unit.value *= conversion_factor;
307                }
308                Ordering::Less => {
309                    new_unit.value /= conversion_factor;
310                }
311                Ordering::Equal => {}
312            }
313            new_unit.mass_unit = to_unit;
314        } else if T::is_temperature_unit() {
315            let from_unit = &self.temperature_unit;
316            let to_unit = EngUnit::to_temperature_unit(to_unit);
317            let mut conversion_factor =
318                TemperatureDeltaUnit::conversion_factor(from_unit, &to_unit);
319            conversion_factor = f64::powf(conversion_factor, self.temperature_count as f64);
320            match self.temperature_count.cmp(&0) {
321                Ordering::Greater => {
322                    new_unit.value *= conversion_factor;
323                }
324                Ordering::Less => {
325                    new_unit.value /= conversion_factor;
326                }
327                Ordering::Equal => {}
328            }
329            new_unit.temperature_unit = to_unit;
330        } else if T::is_time_unit() {
331            let from_unit = &self.time_unit;
332            let to_unit = EngUnit::to_time_unit(to_unit);
333            let conversion_factor = TimeUnit::conversion_factor(from_unit, &to_unit);
334            new_unit.value *= f64::powf(conversion_factor, self.time_count as f64);
335            match self.time_count.cmp(&0) {
336                Ordering::Greater => {
337                    new_unit.value *= conversion_factor;
338                }
339                Ordering::Less => {
340                    new_unit.value /= conversion_factor;
341                }
342                Ordering::Equal => {}
343            }
344            new_unit.time_unit = to_unit;
345        }
346        new_unit
347    }
348
349    fn has_units(&self) -> bool {
350        if self.length_count != 0 {
351            return true;
352        }
353        if self.time_count != 0 {
354            return true;
355        }
356        if self.mass_count != 0 {
357            return true;
358        }
359        if self.temperature_count != 0 {
360            return true;
361        }
362        if self.electric_current_count != 0 {
363            return true;
364        }
365        if self.luminous_intensity_count != 0 {
366            return true;
367        }
368        if self.amount_of_substance_count != 0 {
369            return true;
370        }
371        false
372    }
373
374    pub fn unit_to_string(&self) -> String {
375        let mut s_numerator: Vec<String> = Vec::new();
376        let mut s_denominator: Vec<String> = Vec::new();
377
378        for u in &self.unit_numerator {
379            s_numerator.push(u.unit_to_string())
380        }
381
382        if self.amount_of_substance_count >= 2 {
383            let s = format!(
384                "{}^{}",
385                self.amount_of_substance_unit.to_string(),
386                self.amount_of_substance_count
387            );
388            s_numerator.push(s);
389        } else if self.amount_of_substance_count == 1 {
390            let s = self.amount_of_substance_unit.to_string();
391            s_numerator.push(s.to_string());
392        }
393        if self.electric_current_count >= 2 {
394            let s = format!(
395                "{}^{}",
396                self.electric_current_unit.to_string(),
397                self.electric_current_count
398            );
399            s_numerator.push(s);
400        } else if self.electric_current_count == 1 {
401            let s = self.electric_current_unit.to_string();
402            s_numerator.push(s.to_string());
403        }
404        if self.mass_count >= 2 {
405            let s = format!("{}^{}", self.mass_unit.to_string(), self.mass_count);
406            s_numerator.push(s);
407        } else if self.mass_count == 1 {
408            let s = self.mass_unit.to_string();
409            s_numerator.push(s.to_string());
410        }
411        if self.length_count >= 2 {
412            let s = format!("{}^{}", self.length_unit.to_string(), self.length_count);
413            s_numerator.push(s);
414        } else if self.length_count == 1 {
415            let s = self.length_unit.to_string();
416            s_numerator.push(s.to_string());
417        }
418        if self.luminous_intensity_count >= 2 {
419            let s = format!(
420                "{}^{}",
421                self.luminous_intensity_unit.to_string(),
422                self.luminous_intensity_count
423            );
424            s_numerator.push(s);
425        } else if self.luminous_intensity_count == 1 {
426            let s = self.luminous_intensity_unit.to_string();
427            s_numerator.push(s.to_string());
428        }
429        if self.time_count >= 2 {
430            let s = format!("{}^{}", self.time_unit.to_string(), self.time_count);
431            s_numerator.push(s);
432        } else if self.time_count == 1 {
433            let s = self.time_unit.to_string();
434            s_numerator.push(s.to_string());
435        }
436        if self.temperature_count >= 2 {
437            let s = format!(
438                "{}^{}",
439                self.temperature_unit.to_string(),
440                self.temperature_count
441            );
442            s_numerator.push(s);
443        } else if self.temperature_count == 1 {
444            let s = self.temperature_unit.to_string();
445            s_numerator.push(s.to_string());
446        }
447
448        // String Denominator
449        for u in &self.unit_denominator {
450            s_denominator.push(u.unit_to_string())
451        }
452        if self.amount_of_substance_count <= -2 {
453            let s = format!(
454                "{}^{}",
455                self.amount_of_substance_unit.to_string(),
456                self.amount_of_substance_count
457            );
458            s_denominator.push(s);
459        } else if self.amount_of_substance_count == -1 {
460            let s = self.amount_of_substance_unit.to_string();
461            s_denominator.push(s.to_string());
462        }
463
464        if self.electric_current_count <= -2 {
465            let s = format!(
466                "{}^{}",
467                self.electric_current_unit.to_string(),
468                self.electric_current_count
469            );
470            s_denominator.push(s);
471        } else if self.electric_current_count == -1 {
472            let s = self.electric_current_unit.to_string();
473            s_denominator.push(s.to_string());
474        }
475
476        if self.length_count <= -2 {
477            let s = format!("{}^{}", self.length_unit.to_string(), self.length_count);
478            s_denominator.push(s);
479        } else if self.length_count == -1 {
480            let s = self.length_unit.to_string();
481            s_denominator.push(s.to_string());
482        }
483
484        if self.luminous_intensity_count <= -2 {
485            let s = format!(
486                "{}^{}",
487                self.luminous_intensity_unit.to_string(),
488                self.luminous_intensity_count
489            );
490            s_denominator.push(s);
491        } else if self.luminous_intensity_count == -1 {
492            let s = self.luminous_intensity_unit.to_string();
493            s_denominator.push(s.to_string());
494        }
495
496        if self.mass_count <= -2 {
497            let s = format!("{}^{}", self.mass_unit.to_string(), self.mass_count);
498            s_denominator.push(s);
499        } else if self.mass_count == -1 {
500            let s = self.mass_unit.to_string();
501            s_denominator.push(s.to_string());
502        }
503
504        if self.time_count <= -2 {
505            let s = format!(
506                "{}^{}",
507                self.time_unit.to_string(),
508                i32::abs(self.time_count)
509            );
510            s_denominator.push(s);
511        } else if self.time_count == -1 {
512            let s = self.time_unit.to_string();
513            s_denominator.push(s.to_string());
514        }
515
516        if self.temperature_count <= -2 {
517            let s = format!(
518                "{}^{}",
519                self.temperature_unit.to_string(),
520                self.temperature_count
521            );
522            s_denominator.push(s);
523        } else if self.temperature_count == -1 {
524            let s = self.temperature_unit.to_string();
525            s_denominator.push(s.to_string());
526        }
527
528        let mut s_output = String::new();
529        for s in s_numerator.iter() {
530            s_output.push_str(s);
531            s_output.push('·');
532        }
533        if !s_numerator.is_empty() {
534            let mut chars = s_output.chars();
535            chars.next_back();
536            s_output = chars.as_str().to_string();
537        }
538
539        if !s_denominator.is_empty() {
540            s_output.push('/');
541        }
542        for s in s_denominator.iter() {
543            s_output.push_str(s);
544            s_output.push('·');
545        }
546        if !s_denominator.is_empty() {
547            let mut chars = s_output.chars();
548            chars.next_back();
549            s_output = chars.as_str().to_string();
550        }
551        s_output
552    }
553
554    pub fn to_latex(&self) -> String {
555        if !self.has_units() {
556            return format!("${}$", self.value);
557        }
558
559        let mut s_numerator: Vec<String> = Vec::new();
560        let mut s_denominator: Vec<String> = Vec::new();
561
562        // String Numerator
563        if self.mass_count >= 2 {
564            let s = format!("{}^{}", self.mass_unit.to_string(), self.mass_count);
565            s_numerator.push(s);
566        } else if self.mass_count == 1 {
567            let s = self.mass_unit.to_string();
568            s_numerator.push(s.to_string());
569        }
570
571        if self.temperature_count >= 2 {
572            let s = format!(
573                "{}^{}",
574                self.temperature_unit.to_string(),
575                self.temperature_count
576            );
577            s_numerator.push(s);
578        } else if self.temperature_count == 1 {
579            let s = self.temperature_unit.to_latex();
580            s_numerator.push(s.to_string());
581        }
582
583        if self.time_count >= 2 {
584            let s = format!("{}^{}", self.time_unit.to_string(), self.time_count);
585            s_numerator.push(s);
586        } else if self.time_count == 1 {
587            let s = self.time_unit.to_string();
588            s_numerator.push(s.to_string());
589        }
590
591        // String Denominator
592        if self.mass_count <= -2 {
593            let s = format!("{}^{}", self.mass_unit.to_string(), self.mass_count);
594            s_denominator.push(s);
595        } else if self.mass_count == -1 {
596            let s = self.mass_unit.to_string();
597            s_denominator.push(s.to_string());
598        }
599
600        if self.temperature_count <= -2 {
601            let s = format!(
602                "{}^{}",
603                self.temperature_unit.to_string(),
604                self.temperature_count
605            );
606            s_denominator.push(s);
607        } else if self.temperature_count == -1 {
608            let s = self.temperature_unit.to_string();
609            s_denominator.push(s.to_string());
610        }
611
612        if self.time_count <= -2 {
613            let s = format!(
614                "{}^{}",
615                self.time_unit.to_string(),
616                i32::abs(self.time_count)
617            );
618            s_denominator.push(s);
619        } else if self.time_count == -1 {
620            let s = self.time_unit.to_string();
621            s_denominator.push(s.to_string());
622        }
623
624        let mut s_output = format!("${}\\ ", self.value);
625        for s in s_numerator.iter() {
626            s_output.push_str(s);
627        }
628
629        if !s_denominator.is_empty() {
630            s_output.push('/');
631        }
632        for s in s_denominator.iter() {
633            s_output.push_str(s);
634        }
635        s_output.push('$');
636        s_output
637    }
638
639    fn multiply_units(self, other: &EngUnit) -> EngUnit {
640        let mut new_unit = EngUnit::new();
641        new_unit.amount_of_substance_count =
642            self.amount_of_substance_count + other.amount_of_substance_count;
643        new_unit.electric_current_count =
644            self.electric_current_count + other.electric_current_count;
645        new_unit.length_count = self.length_count + other.length_count;
646        new_unit.luminous_intensity_count =
647            self.luminous_intensity_count + other.luminous_intensity_count;
648        new_unit.mass_count = self.mass_count + other.mass_count;
649        new_unit.temperature_count = self.temperature_count + other.temperature_count;
650        new_unit.time_count = self.time_count + other.time_count;
651
652        let mut amount_conversion_factor = AmountOfSubstanceUnit::conversion_factor(
653            &other.amount_of_substance_unit,
654            &self.amount_of_substance_unit,
655        );
656        if other.amount_of_substance_count < 0 {
657            amount_conversion_factor = 1.0 / amount_conversion_factor;
658        }
659
660        let mut electric_conversion_factor = ElectricCurrentUnit::conversion_factor(
661            &other.electric_current_unit,
662            &self.electric_current_unit,
663        );
664        if other.electric_current_count < 0 {
665            electric_conversion_factor = 1.0 / electric_conversion_factor;
666        }
667
668        let mut length_conversion_factor =
669            LengthUnit::conversion_factor(&other.length_unit, &self.length_unit);
670        if other.length_count < 0 {
671            length_conversion_factor = 1.0 / length_conversion_factor;
672        }
673
674        let mut luminous_conversion_factor = LuminousIntensityUnit::conversion_factor(
675            &other.luminous_intensity_unit,
676            &self.luminous_intensity_unit,
677        );
678        if other.luminous_intensity_count < 0 {
679            luminous_conversion_factor = 1.0 / luminous_conversion_factor;
680        }
681
682        let mut mass_conversion_factor =
683            MassUnit::conversion_factor(&other.mass_unit, &self.mass_unit);
684        if other.mass_count < 0 {
685            mass_conversion_factor = 1.0 / mass_conversion_factor;
686        }
687
688        let mut temperature_conversion_factor = TemperatureDeltaUnit::conversion_factor(
689            &other.temperature_unit,
690            &self.temperature_unit,
691        );
692        if other.temperature_count < 0 {
693            temperature_conversion_factor = 1.0 / temperature_conversion_factor;
694        }
695
696        let mut time_conversion_factor =
697            TimeUnit::conversion_factor(&other.time_unit, &self.time_unit);
698        if other.time_count < 0 {
699            time_conversion_factor = 1.0 / time_conversion_factor;
700        }
701
702        new_unit.value = self.value * other.value;
703        new_unit.value *= amount_conversion_factor;
704        new_unit.value *= electric_conversion_factor;
705        new_unit.value *= length_conversion_factor;
706        new_unit.value *= luminous_conversion_factor;
707        new_unit.value *= mass_conversion_factor;
708        new_unit.value *= temperature_conversion_factor;
709        new_unit.value *= time_conversion_factor;
710
711        if new_unit.amount_of_substance_count != 0 {
712            if self.amount_of_substance_count != 0 {
713                new_unit.amount_of_substance_unit = self.amount_of_substance_unit;
714            } else {
715                new_unit.amount_of_substance_unit = other.amount_of_substance_unit.clone();
716            }
717        } else {
718            new_unit.amount_of_substance_unit = AmountOfSubstanceUnit::None;
719        }
720
721        if new_unit.electric_current_count != 0 {
722            if self.electric_current_count != 0 {
723                new_unit.electric_current_unit = self.electric_current_unit;
724            } else {
725                new_unit.electric_current_unit = other.electric_current_unit.clone();
726            }
727        } else {
728            new_unit.electric_current_unit = ElectricCurrentUnit::None;
729        }
730
731        if new_unit.length_count != 0 {
732            if self.length_count != 0 {
733                new_unit.length_unit = self.length_unit;
734            } else {
735                new_unit.length_unit = other.length_unit.clone();
736            }
737        } else {
738            new_unit.length_unit = LengthUnit::None;
739        }
740
741        if new_unit.luminous_intensity_count != 0 {
742            if self.luminous_intensity_count != 0 {
743                new_unit.luminous_intensity_unit = self.luminous_intensity_unit;
744            } else {
745                new_unit.luminous_intensity_unit = other.luminous_intensity_unit.clone();
746            }
747        } else {
748            new_unit.luminous_intensity_unit = LuminousIntensityUnit::None;
749        }
750
751        if new_unit.mass_count != 0 {
752            if self.mass_count != 0 {
753                new_unit.mass_unit = self.mass_unit;
754            } else {
755                new_unit.mass_unit = other.mass_unit.clone();
756            }
757        } else {
758            new_unit.mass_unit = MassUnit::None;
759        }
760
761        if new_unit.temperature_count != 0 {
762            if self.temperature_count != 0 {
763                new_unit.temperature_unit = self.temperature_unit;
764            } else {
765                new_unit.temperature_unit = other.temperature_unit.clone();
766            }
767        } else {
768            new_unit.temperature_unit = TemperatureDeltaUnit::None;
769        }
770
771        if new_unit.time_count != 0 {
772            if self.time_count != 0 {
773                new_unit.time_unit = self.time_unit;
774            } else {
775                new_unit.time_unit = other.time_unit.clone();
776            }
777        } else {
778            new_unit.time_unit = TimeUnit::None;
779        }
780        new_unit
781    }
782
783    fn divide_units(self, other: &EngUnit) -> EngUnit {
784        let recip = other.reciprocal();
785        self.multiply_units(&recip)
786    }
787
788    pub fn reciprocal(&self) -> EngUnit {
789        let mut recip = self.clone();
790        recip.value = 1.0 / recip.value;
791        recip.amount_of_substance_count *= -1;
792        recip.electric_current_count *= -1;
793        recip.length_count *= -1;
794        recip.luminous_intensity_count *= -1;
795        recip.mass_count *= -1;
796        recip.temperature_count *= -1;
797        recip.time_count *= -1;
798        recip
799    }
800
801    pub fn get_complex_unit() {}
802}
803
804impl ops::Mul for EngUnit {
805    type Output = EngUnit;
806    fn mul(self, rhs: Self) -> Self::Output {
807        self.multiply_units(&rhs)
808    }
809}
810
811impl ops::Mul for &EngUnit {
812    type Output = EngUnit;
813    fn mul(self, rhs: Self) -> Self::Output {
814        let new_unit = self.clone();
815        new_unit.multiply_units(rhs)
816    }
817}
818
819impl ops::Div for EngUnit {
820    type Output = EngUnit;
821    fn div(self, rhs: Self) -> Self::Output {
822        self.divide_units(&rhs)
823    }
824}
825
826impl ops::Div for &EngUnit {
827    type Output = EngUnit;
828    fn div(self, rhs: Self) -> Self::Output {
829        let new_unit = self.clone();
830        new_unit.divide_units(rhs)
831    }
832}
833
834impl ops::Mul<f64> for EngUnit {
835    type Output = EngUnit;
836    fn mul(self, rhs: f64) -> Self::Output {
837        let mut new_unit = self.clone();
838        new_unit.value *= rhs;
839        new_unit
840    }
841}
842
843impl ops::Mul<f64> for &EngUnit {
844    type Output = EngUnit;
845    fn mul(self, rhs: f64) -> Self::Output {
846        let mut new_unit = self.clone();
847        new_unit.value *= rhs;
848        new_unit
849    }
850}
851
852impl ops::Mul<EngUnit> for f64 {
853    type Output = EngUnit;
854    fn mul(self, lhs: EngUnit) -> Self::Output {
855        let mut new_unit = lhs.clone();
856        new_unit.value *= self;
857        new_unit
858    }
859}
860
861impl ops::Mul<&EngUnit> for f64 {
862    type Output = EngUnit;
863    fn mul(self, lhs: &EngUnit) -> Self::Output {
864        let mut new_unit = lhs.clone();
865        new_unit.value *= self;
866        new_unit
867    }
868}
869
870pub fn same_units(unit_1: &EngUnit, unit_2: &EngUnit) -> bool {
871    if unit_1.amount_of_substance_unit != unit_2.amount_of_substance_unit {
872        return false;
873    }
874    if unit_1.electric_current_unit != unit_2.electric_current_unit {
875        return false;
876    }
877    if unit_1.length_unit != unit_2.length_unit {
878        return false;
879    }
880    if unit_1.luminous_intensity_unit != unit_2.luminous_intensity_unit {
881        return false;
882    }
883    if unit_1.mass_unit != unit_2.mass_unit {
884        return false;
885    }
886    if unit_1.temperature_unit != unit_2.temperature_unit {
887        return false;
888    }
889    if unit_1.time_unit != unit_2.time_unit {
890        return false;
891    }
892
893    if unit_1.amount_of_substance_count != unit_2.amount_of_substance_count {
894        return false;
895    }
896    if unit_1.electric_current_count != unit_2.electric_current_count {
897        return false;
898    }
899    if unit_1.length_count != unit_2.length_count {
900        return false;
901    }
902    if unit_1.luminous_intensity_count != unit_2.luminous_intensity_count {
903        return false;
904    }
905    if unit_1.mass_count != unit_2.mass_count {
906        return false;
907    }
908    if unit_1.temperature_count != unit_2.temperature_count {
909        return false;
910    }
911    if unit_1.time_count != unit_2.time_count {
912        return false;
913    }
914    true
915}
916
917#[cfg(test)]
918mod tests {
919    use crate::temperature;
920    use crate::*;
921
922    #[test]
923    fn new_eng_unt() {
924        let new_unit = EngUnit::new();
925        assert_eq!(0, new_unit.length_count);
926        assert_eq!(0, new_unit.time_count);
927        assert_eq!(0, new_unit.mass_count);
928        assert_eq!(0, new_unit.temperature_count);
929        assert_eq!(0, new_unit.electric_current_count);
930        assert_eq!(0, new_unit.luminous_intensity_count);
931        assert_eq!(0, new_unit.amount_of_substance_count);
932        assert_eq!(false, new_unit.has_units());
933    }
934
935    #[test]
936    fn multiply_numerator_check() {
937        let unit_1 = EngUnit::new();
938        let mut unit_2 = EngUnit::new();
939        unit_2.length_count = 1;
940        unit_2.mass_count = 1;
941        unit_2.time_count = 1;
942        unit_2.temperature_count = 1;
943        unit_2.electric_current_count = 1;
944        unit_2.luminous_intensity_count = 1;
945        unit_2.amount_of_substance_count = 1;
946
947        let unit3 = unit_1.multiply_units(&unit_2);
948        assert_eq!(1, unit3.length_count);
949        assert_eq!(1, unit3.mass_count);
950        assert_eq!(1, unit3.time_count);
951        assert_eq!(1, unit3.temperature_count);
952        assert_eq!(1, unit3.electric_current_count);
953        assert_eq!(1, unit3.luminous_intensity_count);
954        assert_eq!(1, unit3.amount_of_substance_count);
955        assert_eq!(true, unit3.has_units());
956    }
957
958    #[test]
959    fn multiply_operator_numerator_check() {
960        let unit_1 = EngUnit::new();
961        let mut unit_2 = EngUnit::new();
962        unit_2.length_count = 1;
963        unit_2.mass_count = 1;
964        unit_2.time_count = 1;
965        unit_2.temperature_count = 1;
966        unit_2.electric_current_count = 1;
967        unit_2.luminous_intensity_count = 1;
968        unit_2.amount_of_substance_count = 1;
969
970        let unit3 = unit_1 * unit_2;
971        assert_eq!(1, unit3.length_count);
972        assert_eq!(1, unit3.mass_count);
973        assert_eq!(1, unit3.time_count);
974        assert_eq!(1, unit3.temperature_count);
975        assert_eq!(1, unit3.electric_current_count);
976        assert_eq!(1, unit3.luminous_intensity_count);
977        assert_eq!(1, unit3.amount_of_substance_count);
978        assert_eq!(true, unit3.has_units());
979    }
980
981    #[test]
982    fn multiply_denominator_check() {
983        let unit_1 = EngUnit::new();
984        let mut unit_2 = EngUnit::new();
985        unit_2.length_count = -1;
986        unit_2.mass_count = -1;
987        unit_2.time_count = -1;
988        unit_2.temperature_count = -1;
989        unit_2.electric_current_count = -1;
990        unit_2.luminous_intensity_count = -1;
991        unit_2.amount_of_substance_count = -1;
992
993        let unit3 = unit_1.multiply_units(&unit_2);
994        assert_eq!(-1, unit3.length_count);
995        assert_eq!(-1, unit3.mass_count);
996        assert_eq!(-1, unit3.time_count);
997        assert_eq!(-1, unit3.temperature_count);
998        assert_eq!(-1, unit3.electric_current_count);
999        assert_eq!(-1, unit3.luminous_intensity_count);
1000        assert_eq!(-1, unit3.amount_of_substance_count);
1001        assert_eq!(true, unit3.has_units());
1002    }
1003
1004    #[test]
1005    fn multiply_operator_denominator_check() {
1006        let unit_1 = EngUnit::new();
1007        let mut unit_2 = EngUnit::new();
1008        unit_2.length_count = -1;
1009        unit_2.mass_count = -1;
1010        unit_2.time_count = -1;
1011        unit_2.temperature_count = -1;
1012        unit_2.electric_current_count = -1;
1013        unit_2.luminous_intensity_count = -1;
1014        unit_2.amount_of_substance_count = -1;
1015
1016        let unit3 = unit_1 * unit_2;
1017        assert_eq!(-1, unit3.length_count);
1018        assert_eq!(-1, unit3.mass_count);
1019        assert_eq!(-1, unit3.time_count);
1020        assert_eq!(-1, unit3.temperature_count);
1021        assert_eq!(-1, unit3.electric_current_count);
1022        assert_eq!(-1, unit3.luminous_intensity_count);
1023        assert_eq!(-1, unit3.amount_of_substance_count);
1024        assert_eq!(true, unit3.has_units());
1025    }
1026
1027    #[test]
1028    fn multiply_cancel_out_check() {
1029        let mut unit_1 = EngUnit::new();
1030        unit_1.length_count = -1;
1031        unit_1.mass_count = -1;
1032        unit_1.time_count = -1;
1033        unit_1.temperature_count = -1;
1034        unit_1.electric_current_count = -1;
1035        unit_1.luminous_intensity_count = -1;
1036        unit_1.amount_of_substance_count = -1;
1037        let mut unit_2 = EngUnit::new();
1038        unit_2.length_count = 1;
1039        unit_2.mass_count = 1;
1040        unit_2.time_count = 1;
1041        unit_2.temperature_count = 1;
1042        unit_2.electric_current_count = 1;
1043        unit_2.luminous_intensity_count = 1;
1044        unit_2.amount_of_substance_count = 1;
1045
1046        let unit3 = unit_1.multiply_units(&unit_2);
1047        assert_eq!(0, unit3.length_count);
1048        assert_eq!(0, unit3.mass_count);
1049        assert_eq!(0, unit3.time_count);
1050        assert_eq!(0, unit3.temperature_count);
1051        assert_eq!(0, unit3.electric_current_count);
1052        assert_eq!(0, unit3.luminous_intensity_count);
1053        assert_eq!(0, unit3.amount_of_substance_count);
1054    }
1055
1056    #[test]
1057    fn multiply_operator_cancel_out_check() {
1058        let mut unit_1 = EngUnit::new();
1059        unit_1.length_count = -1;
1060        unit_1.mass_count = -1;
1061        unit_1.time_count = -1;
1062        unit_1.temperature_count = -1;
1063        unit_1.electric_current_count = -1;
1064        unit_1.luminous_intensity_count = -1;
1065        unit_1.amount_of_substance_count = -1;
1066        let mut unit_2 = EngUnit::new();
1067        unit_2.length_count = 1;
1068        unit_2.mass_count = 1;
1069        unit_2.time_count = 1;
1070        unit_2.temperature_count = 1;
1071        unit_2.electric_current_count = 1;
1072        unit_2.luminous_intensity_count = 1;
1073        unit_2.amount_of_substance_count = 1;
1074
1075        let unit3 = unit_1 * unit_2;
1076        assert_eq!(0, unit3.length_count);
1077        assert_eq!(0, unit3.mass_count);
1078        assert_eq!(0, unit3.time_count);
1079        assert_eq!(0, unit3.temperature_count);
1080        assert_eq!(0, unit3.electric_current_count);
1081        assert_eq!(0, unit3.luminous_intensity_count);
1082        assert_eq!(0, unit3.amount_of_substance_count);
1083    }
1084
1085    #[test]
1086    fn temperature_unit_new() {
1087        let mut unit_1 = EngUnit::new();
1088        unit_1.value = 100.0;
1089        unit_1.temperature_count = 1;
1090        unit_1.temperature_unit = TemperatureDeltaUnit::C;
1091        let unit_2 = EngUnit::new();
1092        let unit_3 = unit_1 * unit_2;
1093        assert_eq!(100.0, unit_3.value);
1094        assert_eq!(1, unit_3.temperature_count);
1095        assert_eq!(TemperatureDeltaUnit::C, unit_3.temperature_unit);
1096        assert_eq!(true, unit_3.has_units())
1097    }
1098
1099    #[test]
1100    fn temperature_c_div_c() {
1101        let unit_1 = temperature!(1.0, TemperatureDeltaUnit::C);
1102        let unit_2 = temperature!(2.0, TemperatureDeltaUnit::C);
1103        let unit_3 = unit_1 / unit_2;
1104        assert_eq!(0.5, unit_3.value);
1105        assert_eq!("0.50", unit_3.to_string());
1106        assert_eq!(0, unit_3.temperature_count);
1107        assert_eq!(TemperatureDeltaUnit::None, unit_3.temperature_unit);
1108        assert!(!unit_3.has_units())
1109    }
1110
1111    #[test]
1112    fn temperature_k_div_r() {
1113        let unit_1 = temperature!(1.0, TemperatureDeltaUnit::K);
1114        let unit_2 = temperature!(1.0, TemperatureDeltaUnit::R);
1115        let unit_3 = unit_1 / unit_2;
1116        let expected = 9.0 / 5.0;
1117        assert!(f64::abs(expected - unit_3.value) < 0.000001);
1118        assert_eq!("1.80", unit_3.to_string());
1119        assert_eq!(TemperatureDeltaUnit::None, unit_3.temperature_unit);
1120        assert!(!unit_3.has_units())
1121    }
1122}