eng_units/
complex_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 energy_unit;
18
19use crate::units::amount_of_substance_unit::AmountOfSubstanceUnit;
20use crate::units::electric_current_unit::ElectricCurrentUnit;
21use crate::units::length_unit::LengthUnit;
22use crate::units::luminous_intensity_unit::LuminousIntensityUnit;
23use crate::units::mass_unit::MassUnit;
24use crate::units::temperature_unit::TemperatureDeltaUnit;
25use crate::units::time_unit::TimeUnit;
26use crate::EngUnit;
27
28#[derive(Debug, Clone, Copy)]
29pub struct ComplexUnit {
30    pub prefix_multiplier: f64,
31    pub amount_of_substance_count: i32,
32    pub amount_of_substance_unit: AmountOfSubstanceUnit,
33    pub electric_current_count: i32,
34    pub electric_current_unit: ElectricCurrentUnit,
35    pub length_count: i32,
36    pub length_unit: LengthUnit,
37    pub luminous_intensity_count: i32,
38    pub luminous_intensity_unit: LuminousIntensityUnit,
39    pub mass_count: i32,
40    pub mass_unit: MassUnit,
41    pub temperature_count: i32,
42    pub temperature_unit: TemperatureDeltaUnit,
43    pub time_count: i32,
44    pub time_unit: TimeUnit,
45    pub unit_string: &'static str,
46}
47
48impl ComplexUnit {
49    pub fn unit_to_string(&self) -> String {
50        self.unit_string.to_string()
51    }
52}
53
54pub const JOULE: ComplexUnit = ComplexUnit {
55    prefix_multiplier: 1.0,
56    amount_of_substance_count: 0,
57    amount_of_substance_unit: AmountOfSubstanceUnit::None,
58    electric_current_count: 0,
59    electric_current_unit: ElectricCurrentUnit::None,
60    length_count: 2,
61    length_unit: LengthUnit::Meter,
62    luminous_intensity_count: 0,
63    luminous_intensity_unit: LuminousIntensityUnit::None,
64    mass_count: 1,
65    mass_unit: MassUnit::Kilogram,
66    temperature_count: 0,
67    temperature_unit: TemperatureDeltaUnit::None,
68    time_count: -2,
69    time_unit: TimeUnit::Second,
70    unit_string: "J",
71};
72
73pub const KILOJOULE: ComplexUnit = ComplexUnit {
74    prefix_multiplier: 1.0 / 1000.0,
75    amount_of_substance_count: 0,
76    amount_of_substance_unit: AmountOfSubstanceUnit::None,
77    electric_current_count: 0,
78    electric_current_unit: ElectricCurrentUnit::None,
79    length_count: 2,
80    length_unit: LengthUnit::Meter,
81    luminous_intensity_count: 0,
82    luminous_intensity_unit: LuminousIntensityUnit::None,
83    mass_count: 1,
84    mass_unit: MassUnit::Kilogram,
85    temperature_count: 0,
86    temperature_unit: TemperatureDeltaUnit::None,
87    time_count: -2,
88    time_unit: TimeUnit::Second,
89    unit_string: "kJ",
90};
91
92pub fn can_extract_normal(unit: &EngUnit, complex: &ComplexUnit) -> bool {
93    if complex.amount_of_substance_count > 0 {
94        if unit.amount_of_substance_count < complex.amount_of_substance_count {
95            return false;
96        }
97    } else if complex.amount_of_substance_count < 0 {
98        if unit.amount_of_substance_count > complex.amount_of_substance_count {
99            return false;
100        }
101    }
102
103    if complex.electric_current_count > 0 {
104        if unit.electric_current_count < complex.electric_current_count {
105            return false;
106        }
107    } else if complex.electric_current_count < 0 {
108        if unit.electric_current_count > complex.electric_current_count {
109            return false;
110        }
111    }
112
113    if complex.length_count > 0 {
114        if unit.length_count < complex.length_count {
115            return false;
116        }
117    } else if complex.length_count < 0 {
118        if unit.length_count > complex.length_count {
119            return false;
120        }
121    }
122
123    if complex.luminous_intensity_count > 0 {
124        if unit.luminous_intensity_count < complex.luminous_intensity_count {
125            return false;
126        }
127    } else if complex.luminous_intensity_count < 0 {
128        if unit.luminous_intensity_count > complex.luminous_intensity_count {
129            return false;
130        }
131    }
132
133    if complex.mass_count > 0 {
134        if unit.mass_count < complex.mass_count {
135            return false;
136        }
137    } else if complex.mass_count < 0 {
138        if unit.mass_count > complex.mass_count {
139            return false;
140        }
141    }
142
143    if complex.temperature_count > 0 {
144        if unit.temperature_count < complex.temperature_count {
145            return false;
146        }
147    } else if complex.temperature_count < 0 {
148        if unit.temperature_count > complex.temperature_count {
149            return false;
150        }
151    }
152
153    if complex.time_count > 0 {
154        if unit.time_count < complex.time_count {
155            return false;
156        }
157    } else if complex.time_count < 0 {
158        if unit.time_count > complex.time_count {
159            return false;
160        }
161    }
162    true
163}
164
165pub fn extract_numerator(unit: &EngUnit, complex: ComplexUnit) -> Option<EngUnit> {
166    if !can_extract_normal(&unit, &complex) {
167        return None;
168    }
169    let new_unit = unit.convert(complex.amount_of_substance_unit);
170    let new_unit = new_unit.convert(complex.electric_current_unit);
171    let new_unit = new_unit.convert(complex.length_unit);
172    let new_unit = new_unit.convert(complex.luminous_intensity_unit);
173    let new_unit = new_unit.convert(complex.mass_unit);
174    let new_unit = new_unit.convert(complex.temperature_unit);
175    let new_unit = new_unit.convert(complex.time_unit);
176
177    let mut new_unit = new_unit;
178    new_unit.value *= complex.prefix_multiplier;
179    new_unit.amount_of_substance_count -= complex.amount_of_substance_count;
180    new_unit.electric_current_count -= complex.electric_current_count;
181    new_unit.length_count -= complex.length_count;
182    new_unit.luminous_intensity_count -= complex.luminous_intensity_count;
183    new_unit.mass_count -= complex.mass_count;
184    new_unit.temperature_count -= complex.temperature_count;
185    new_unit.time_count -= complex.time_count;
186
187    new_unit.unit_numerator.push(complex.clone());
188    Some(new_unit)
189}
190
191#[cfg(test)]
192
193mod tests {
194    use super::*;
195    use crate::*;
196
197    #[test]
198    fn test_1() {
199        let u1 = kJ!(1.0);
200        let u2 = extract_numerator(&u1, JOULE);
201        assert!(u2.is_some());
202        let u2 = u2.unwrap();
203        assert_eq!(1000.0, u2.value);
204        assert_eq!("1000.00 J", u2.to_string());
205    }
206
207    #[test]
208    fn test_2() {
209        let u1 = kJ!(1.0);
210        let u2 = extract_numerator(&u1, KILOJOULE);
211        assert!(u2.is_some());
212        let u2 = u2.unwrap();
213        assert_eq!(1.0, u2.value);
214        assert_eq!("1.00 kJ", u2.to_string());
215    }
216}