Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
* [Roman Numerals](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/roman_numerals.rs)
* [Speed](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/speed.rs)
* [Time Units](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/time_units.rs)
* [Temperature](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/temperature.rs)
* Data Structures
* [AVL Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/avl_tree.rs)
* [B-Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/b_tree.rs)
Expand Down
2 changes: 2 additions & 0 deletions src/conversions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod rgb_cmyk_conversion;
mod rgb_hsv_conversion;
mod roman_numerals;
mod speed;
mod temperature;
mod time_units;

pub use self::binary_to_decimal::binary_to_decimal;
Expand All @@ -40,4 +41,5 @@ pub use self::rgb_cmyk_conversion::rgb_to_cmyk;
pub use self::rgb_hsv_conversion::{hsv_to_rgb, rgb_to_hsv, ColorError, Hsv, Rgb};
pub use self::roman_numerals::{int_to_roman, roman_to_int};
pub use self::speed::{convert_speed, SpeedUnit};
pub use self::temperature::{convert_temperature, TemperatureUnit};
pub use self::time_units::convert_time;
254 changes: 254 additions & 0 deletions src/conversions/temperature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
//! Convert between different units of temperature
//!
//! Supports conversions between 8 temperature scales using Kelvin as an intermediary:
//! - Kelvin (K) - SI base unit, absolute scale
//! - Celsius (°C) - Standard metric scale
//! - Fahrenheit (°F) - Imperial scale
//! - Rankine (°R) - Absolute Fahrenheit scale
//! - Delisle (°De) - Historical inverted scale (higher values = colder)
//! - Newton (°N) - Historical scale by Isaac Newton
//! - Réaumur (°Ré) - Historical European scale
//! - Rømer (°Rø) - Historical Danish scale

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TemperatureUnit {
Kelvin,
Celsius,
Fahrenheit,
Rankine,
Delisle,
Newton,
Reaumur,
Romer,
}

impl TemperatureUnit {
fn to_kelvin(self, value: f64) -> f64 {
match self {
TemperatureUnit::Kelvin => value,
TemperatureUnit::Celsius => value + 273.15,
TemperatureUnit::Fahrenheit => (value + 459.67) * 5.0 / 9.0,
TemperatureUnit::Rankine => value * 5.0 / 9.0,
TemperatureUnit::Delisle => 373.15 - value * 2.0 / 3.0,
TemperatureUnit::Newton => value * 100.0 / 33.0 + 273.15,
TemperatureUnit::Reaumur => value * 5.0 / 4.0 + 273.15,
TemperatureUnit::Romer => (value - 7.5) * 40.0 / 21.0 + 273.15,
}
}

fn kelvin_to_unit(self, kelvin: f64) -> f64 {
match self {
TemperatureUnit::Kelvin => kelvin,
TemperatureUnit::Celsius => kelvin - 273.15,
TemperatureUnit::Fahrenheit => kelvin * 9.0 / 5.0 - 459.67,
TemperatureUnit::Rankine => kelvin * 9.0 / 5.0,
TemperatureUnit::Delisle => (373.15 - kelvin) * 3.0 / 2.0,
TemperatureUnit::Newton => (kelvin - 273.15) * 33.0 / 100.0,
TemperatureUnit::Reaumur => (kelvin - 273.15) * 4.0 / 5.0,
TemperatureUnit::Romer => (kelvin - 273.15) * 21.0 / 40.0 + 7.5,
}
}
}

pub fn convert_temperature(value: f64, from: TemperatureUnit, to: TemperatureUnit) -> f64 {
let kelvin = from.to_kelvin(value);
to.kelvin_to_unit(kelvin)
}

#[cfg(test)]
mod tests {
use super::*;

const EPSILON: f64 = 1e-10;

fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPSILON
}

#[test]
fn test_celsius_conversions() {
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
32.0
));
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
212.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
273.15
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Rankine),
491.67
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Delisle),
150.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Newton),
0.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Reaumur),
0.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Romer),
7.5
));
}

#[test]
fn test_fahrenheit_conversions() {
assert!(approx_eq(
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius),
0.0
));
assert!(approx_eq(
convert_temperature(212.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius),
100.0
));
assert!(approx_eq(
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Kelvin),
273.15
));
assert!(approx_eq(
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Rankine),
491.67
));
}

#[test]
fn test_kelvin_conversions() {
assert!(approx_eq(
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Celsius),
0.0
));
assert!(approx_eq(
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit),
32.0
));
assert!(approx_eq(
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Rankine),
491.67
));
}

#[test]
fn test_round_trip_conversions() {
let temp = 25.0;
let units = [
TemperatureUnit::Celsius,
TemperatureUnit::Fahrenheit,
TemperatureUnit::Kelvin,
TemperatureUnit::Rankine,
TemperatureUnit::Delisle,
TemperatureUnit::Newton,
TemperatureUnit::Reaumur,
TemperatureUnit::Romer,
];

for from_unit in units.iter() {
for to_unit in units.iter() {
let converted = convert_temperature(temp, *from_unit, *to_unit);
let back = convert_temperature(converted, *to_unit, *from_unit);
assert!(
approx_eq(back, temp),
"Round trip failed: {from_unit:?} -> {to_unit:?} -> {from_unit:?}: {back} != {temp}"
);
}
}
}

#[test]
fn test_special_temperatures() {
// Absolute zero
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Kelvin, TemperatureUnit::Celsius),
-273.15
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit),
-459.67
));

// Water freezing point
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
32.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
273.15
));

// Water boiling point
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
212.0
));
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
373.15
));

// Celsius equals Fahrenheit
assert!(approx_eq(
convert_temperature(-40.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
-40.0
));
}

#[test]
fn test_historical_scales() {
// Delisle (inverted scale)
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Delisle),
0.0
));
assert!(approx_eq(
convert_temperature(0.0, TemperatureUnit::Delisle, TemperatureUnit::Celsius),
100.0
));

// Newton scale
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Newton),
33.0
));
assert!(approx_eq(
convert_temperature(33.0, TemperatureUnit::Newton, TemperatureUnit::Celsius),
100.0
));

// Rømer scale
assert!(approx_eq(
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Romer),
60.0
));
assert!(approx_eq(
convert_temperature(60.0, TemperatureUnit::Romer, TemperatureUnit::Celsius),
100.0
));
}

#[test]
fn test_same_unit_conversion() {
let temp = 42.0;
for unit in [
TemperatureUnit::Celsius,
TemperatureUnit::Fahrenheit,
TemperatureUnit::Kelvin,
TemperatureUnit::Rankine,
TemperatureUnit::Delisle,
TemperatureUnit::Newton,
TemperatureUnit::Reaumur,
TemperatureUnit::Romer,
] {
assert!(approx_eq(convert_temperature(temp, unit, unit), temp));
}
}
}