Order of Magnitude Conversion
P
//! Length Unit Conversion
//!
//! This module provides conversion between metric length units ranging from
//! meters to yottameters (10^24 meters).
//!
//! Available units: Meter, Kilometer, Megameter, Gigameter, Terameter,
//! Petameter, Exameter, Zettameter, Yottameter
//!
//! ## Spelling Convention
//!
//! This module uses **American spellings** (meter, kilometer, etc.) for all
//! official API elements including enum variants and documentation, following
//! standard programming conventions and SI guidelines.
//!
//! However, the `FromStr` implementation **accepts both American and British spellings**
//! for maximum compatibility:
//! - American: "meter", "kilometer", "megameter", etc.
//! - British: "metre", "kilometre", "megametre", etc.
//!
//! ```
//! use the_algorithms_rust::conversions::MetricLengthUnit;
//! use std::str::FromStr;
//!
//! // Both spellings work!
//! let american: MetricLengthUnit = "megameter".parse().unwrap();
//! let british: MetricLengthUnit = "megametre".parse().unwrap();
//! assert_eq!(american, british); // Same enum variant
//! ```
//!
//! # References
//!
//! - [Meter - Wikipedia](https://en.wikipedia.org/wiki/Meter)
//! - [Kilometer - Wikipedia](https://en.wikipedia.org/wiki/Kilometer)
//! - [Orders of Magnitude (Length) - Wikipedia](https://en.wikipedia.org/wiki/Orders_of_magnitude_(length))
use std::fmt;
use std::str::FromStr;
/// Represents different metric length units.
///
/// Each variant corresponds to a specific power of 10 relative to the base unit (meter).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetricLengthUnit {
/// Meter (m) - base unit, 10^0 meters
Meter,
/// Kilometer (km) - 10^3 meters
Kilometer,
/// Megameter (Mm) - 10^6 meters
Megameter,
/// Gigameter (Gm) - 10^9 meters
Gigameter,
/// Terameter (Tm) - 10^12 meters
Terameter,
/// Petameter (Pm) - 10^15 meters
Petameter,
/// Exameter (Em) - 10^18 meters
Exameter,
/// Zettameter (Zm) - 10^21 meters
Zettameter,
/// Yottameter (Ym) - 10^24 meters
Yottameter,
}
impl MetricLengthUnit {
/// Returns the exponent (power of 10) for this unit relative to meters.
///
/// # Example
///
/// ```
/// use the_algorithms_rust::conversions::MetricLengthUnit;
///
/// assert_eq!(MetricLengthUnit::Meter.exponent(), 0);
/// assert_eq!(MetricLengthUnit::Kilometer.exponent(), 3);
/// assert_eq!(MetricLengthUnit::Megameter.exponent(), 6);
/// ```
pub fn exponent(&self) -> i32 {
match self {
MetricLengthUnit::Meter => 0,
MetricLengthUnit::Kilometer => 3,
MetricLengthUnit::Megameter => 6,
MetricLengthUnit::Gigameter => 9,
MetricLengthUnit::Terameter => 12,
MetricLengthUnit::Petameter => 15,
MetricLengthUnit::Exameter => 18,
MetricLengthUnit::Zettameter => 21,
MetricLengthUnit::Yottameter => 24,
}
}
/// Returns the standard abbreviation for this unit.
///
/// # Example
///
/// ```
/// use the_algorithms_rust::conversions::MetricLengthUnit;
///
/// assert_eq!(MetricLengthUnit::Meter.symbol(), "m");
/// assert_eq!(MetricLengthUnit::Kilometer.symbol(), "km");
/// ```
pub fn symbol(&self) -> &'static str {
match self {
MetricLengthUnit::Meter => "m",
MetricLengthUnit::Kilometer => "km",
MetricLengthUnit::Megameter => "Mm",
MetricLengthUnit::Gigameter => "Gm",
MetricLengthUnit::Terameter => "Tm",
MetricLengthUnit::Petameter => "Pm",
MetricLengthUnit::Exameter => "Em",
MetricLengthUnit::Zettameter => "Zm",
MetricLengthUnit::Yottameter => "Ym",
}
}
}
impl FromStr for MetricLengthUnit {
type Err = String;
/// Parses a unit from a string (case-insensitive, handles plurals).
///
/// Accepts both full names (e.g., "meter", "meters") and symbols (e.g., "m", "km").
/// **Accepts both American and British spellings** (e.g., "megameter" and "megametre").
///
/// # Example
///
/// ```
/// use the_algorithms_rust::conversions::MetricLengthUnit;
/// use std::str::FromStr;
///
/// // American spellings (official)
/// assert_eq!(MetricLengthUnit::from_str("meter").unwrap(), MetricLengthUnit::Meter);
/// assert_eq!(MetricLengthUnit::from_str("megameter").unwrap(), MetricLengthUnit::Megameter);
///
/// // British spellings (also accepted)
/// assert_eq!(MetricLengthUnit::from_str("metre").unwrap(), MetricLengthUnit::Meter);
/// assert_eq!(MetricLengthUnit::from_str("megametre").unwrap(), MetricLengthUnit::Megameter);
///
/// // Symbols and case-insensitive
/// assert_eq!(MetricLengthUnit::from_str("km").unwrap(), MetricLengthUnit::Kilometer);
/// assert_eq!(MetricLengthUnit::from_str("METERS").unwrap(), MetricLengthUnit::Meter);
/// ```
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Sanitize: lowercase and remove trailing 's'
let sanitized = s.to_lowercase().trim_end_matches('s').to_string();
match sanitized.as_str() {
"meter" | "metre" | "m" => Ok(MetricLengthUnit::Meter),
"kilometer" | "kilometre" | "km" => Ok(MetricLengthUnit::Kilometer),
"megameter" | "megametre" | "mm" => Ok(MetricLengthUnit::Megameter),
"gigameter" | "gigametre" | "gm" => Ok(MetricLengthUnit::Gigameter),
"terameter" | "terametre" | "tm" => Ok(MetricLengthUnit::Terameter),
"petameter" | "petametre" | "pm" => Ok(MetricLengthUnit::Petameter),
"exameter" | "exametre" | "em" => Ok(MetricLengthUnit::Exameter),
"zettameter" | "zettametre" | "zm" => Ok(MetricLengthUnit::Zettameter),
"yottameter" | "yottametre" | "ym" => Ok(MetricLengthUnit::Yottameter),
_ => Err(format!(
"Invalid unit: '{s}'. Valid units are: m, km, Mm, Gm, Tm, Pm, Em, Zm, Ym"
)),
}
}
}
impl fmt::Display for MetricLengthUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.symbol())
}
}
/// Converts a length value from one unit to another.
///
/// # Arguments
///
/// * `value` - The numeric value to convert
/// * `from` - The unit to convert from
/// * `to` - The unit to convert to
///
/// # Returns
///
/// The converted value in the target unit
///
/// # Example
///
/// ```
/// use the_algorithms_rust::conversions::{MetricLengthUnit, convert_metric_length};
///
/// let result = convert_metric_length(1.0, MetricLengthUnit::Meter, MetricLengthUnit::Kilometer);
/// assert_eq!(result, 0.001);
///
/// let result = convert_metric_length(1.0, MetricLengthUnit::Kilometer, MetricLengthUnit::Meter);
/// assert_eq!(result, 1000.0);
/// ```
pub fn convert_metric_length(value: f64, from: MetricLengthUnit, to: MetricLengthUnit) -> f64 {
let from_exp = from.exponent();
let to_exp = to.exponent();
let exponent = from_exp - to_exp;
value * 10_f64.powi(exponent)
}
/// Converts a length value from one unit to another using string unit names.
///
/// This function accepts both full unit names and abbreviations, and is case-insensitive.
/// It also handles plural forms (e.g., "meters" and "meter" both work).
///
/// # Arguments
///
/// * `value` - The numeric value to convert
/// * `from_type` - The unit to convert from (as a string)
/// * `to_type` - The unit to convert to (as a string)
///
/// # Returns
///
/// `Ok(f64)` with the converted value, or `Err(String)` if either unit is invalid
///
/// # Example
///
/// ```
/// use the_algorithms_rust::conversions::metric_length_conversion;
///
/// let result = metric_length_conversion(1.0, "meter", "kilometer").unwrap();
/// assert_eq!(result, 0.001);
///
/// let result = metric_length_conversion(1.0, "km", "m").unwrap();
/// assert_eq!(result, 1000.0);
///
/// // Case insensitive and handles plurals
/// let result = metric_length_conversion(5.0, "METERS", "kilometers").unwrap();
/// assert_eq!(result, 0.005);
///
/// // Invalid unit returns error
/// assert!(metric_length_conversion(1.0, "wrongUnit", "meter").is_err());
/// ```
pub fn metric_length_conversion(value: f64, from_type: &str, to_type: &str) -> Result<f64, String> {
let from_unit = MetricLengthUnit::from_str(from_type).map_err(|_| {
format!(
"Invalid 'from_type' value: '{from_type}'.\nConversion abbreviations are: m, km, Mm, Gm, Tm, Pm, Em, Zm, Ym"
)
})?;
let to_unit = MetricLengthUnit::from_str(to_type).map_err(|_| {
format!(
"Invalid 'to_type' value: '{to_type}'.\nConversion abbreviations are: m, km, Mm, Gm, Tm, Pm, Em, Zm, Ym"
)
})?;
Ok(convert_metric_length(value, from_unit, to_unit))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unit_exponents() {
assert_eq!(MetricLengthUnit::Meter.exponent(), 0);
assert_eq!(MetricLengthUnit::Kilometer.exponent(), 3);
assert_eq!(MetricLengthUnit::Megameter.exponent(), 6);
assert_eq!(MetricLengthUnit::Gigameter.exponent(), 9);
assert_eq!(MetricLengthUnit::Terameter.exponent(), 12);
assert_eq!(MetricLengthUnit::Petameter.exponent(), 15);
assert_eq!(MetricLengthUnit::Exameter.exponent(), 18);
assert_eq!(MetricLengthUnit::Zettameter.exponent(), 21);
assert_eq!(MetricLengthUnit::Yottameter.exponent(), 24);
}
#[test]
fn test_unit_symbols() {
assert_eq!(MetricLengthUnit::Meter.symbol(), "m");
assert_eq!(MetricLengthUnit::Kilometer.symbol(), "km");
assert_eq!(MetricLengthUnit::Megameter.symbol(), "Mm");
assert_eq!(MetricLengthUnit::Gigameter.symbol(), "Gm");
assert_eq!(MetricLengthUnit::Terameter.symbol(), "Tm");
assert_eq!(MetricLengthUnit::Petameter.symbol(), "Pm");
assert_eq!(MetricLengthUnit::Exameter.symbol(), "Em");
assert_eq!(MetricLengthUnit::Zettameter.symbol(), "Zm");
assert_eq!(MetricLengthUnit::Yottameter.symbol(), "Ym");
}
#[test]
fn test_from_str_full_names() {
assert_eq!(
MetricLengthUnit::from_str("meter").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("kilometer").unwrap(),
MetricLengthUnit::Kilometer
);
assert_eq!(
MetricLengthUnit::from_str("megameter").unwrap(),
MetricLengthUnit::Megameter
);
}
#[test]
fn test_from_str_symbols() {
assert_eq!(
MetricLengthUnit::from_str("m").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("km").unwrap(),
MetricLengthUnit::Kilometer
);
assert_eq!(
MetricLengthUnit::from_str("Mm").unwrap(),
MetricLengthUnit::Megameter
);
assert_eq!(
MetricLengthUnit::from_str("Gm").unwrap(),
MetricLengthUnit::Gigameter
);
}
#[test]
fn test_from_str_case_insensitive() {
assert_eq!(
MetricLengthUnit::from_str("METER").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("KiLoMeTeR").unwrap(),
MetricLengthUnit::Kilometer
);
assert_eq!(
MetricLengthUnit::from_str("KM").unwrap(),
MetricLengthUnit::Kilometer
);
}
#[test]
fn test_from_str_plurals() {
assert_eq!(
MetricLengthUnit::from_str("meters").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("kilometers").unwrap(),
MetricLengthUnit::Kilometer
);
}
#[test]
fn test_from_str_british_spellings() {
// Test that British spellings work (uses 're' instead of 'er')
assert_eq!(
MetricLengthUnit::from_str("metre").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("kilometre").unwrap(),
MetricLengthUnit::Kilometer
);
assert_eq!(
MetricLengthUnit::from_str("megametre").unwrap(),
MetricLengthUnit::Megameter
);
assert_eq!(
MetricLengthUnit::from_str("gigametre").unwrap(),
MetricLengthUnit::Gigameter
);
assert_eq!(
MetricLengthUnit::from_str("terametre").unwrap(),
MetricLengthUnit::Terameter
);
assert_eq!(
MetricLengthUnit::from_str("petametre").unwrap(),
MetricLengthUnit::Petameter
);
assert_eq!(
MetricLengthUnit::from_str("exametre").unwrap(),
MetricLengthUnit::Exameter
);
assert_eq!(
MetricLengthUnit::from_str("zettametre").unwrap(),
MetricLengthUnit::Zettameter
);
assert_eq!(
MetricLengthUnit::from_str("yottametre").unwrap(),
MetricLengthUnit::Yottameter
);
// British spellings with plurals
assert_eq!(
MetricLengthUnit::from_str("metres").unwrap(),
MetricLengthUnit::Meter
);
assert_eq!(
MetricLengthUnit::from_str("kilometres").unwrap(),
MetricLengthUnit::Kilometer
);
}
#[test]
fn test_from_str_american_and_british_equivalent() {
// Verify American and British spellings parse to the same enum variant
let american = MetricLengthUnit::from_str("megameter").unwrap();
let british = MetricLengthUnit::from_str("megametre").unwrap();
assert_eq!(american, british);
assert_eq!(american, MetricLengthUnit::Megameter);
}
#[test]
fn test_from_str_invalid() {
assert!(MetricLengthUnit::from_str("wrongUnit").is_err());
assert!(MetricLengthUnit::from_str("inch").is_err());
assert!(MetricLengthUnit::from_str("").is_err());
}
#[test]
fn test_convert_length_meter_to_kilometer() {
let result =
convert_metric_length(1.0, MetricLengthUnit::Meter, MetricLengthUnit::Kilometer);
assert_eq!(result, 0.001);
}
#[test]
fn test_convert_length_meter_to_megameter() {
let result =
convert_metric_length(1.0, MetricLengthUnit::Meter, MetricLengthUnit::Megameter);
assert_eq!(result, 1e-6);
}
#[test]
fn test_convert_length_gigameter_to_meter() {
let result =
convert_metric_length(1.0, MetricLengthUnit::Gigameter, MetricLengthUnit::Meter);
assert_eq!(result, 1_000_000_000.0);
}
#[test]
fn test_convert_length_gigameter_to_terameter() {
let result = convert_metric_length(
1.0,
MetricLengthUnit::Gigameter,
MetricLengthUnit::Terameter,
);
assert_eq!(result, 0.001);
}
#[test]
fn test_convert_length_petameter_to_terameter() {
let result = convert_metric_length(
1.0,
MetricLengthUnit::Petameter,
MetricLengthUnit::Terameter,
);
assert_eq!(result, 1000.0);
}
#[test]
fn test_convert_length_petameter_to_exameter() {
let result =
convert_metric_length(1.0, MetricLengthUnit::Petameter, MetricLengthUnit::Exameter);
assert_eq!(result, 0.001);
}
#[test]
fn test_convert_length_terameter_to_zettameter() {
let result = convert_metric_length(
1.0,
MetricLengthUnit::Terameter,
MetricLengthUnit::Zettameter,
);
assert_eq!(result, 1e-9);
}
#[test]
fn test_convert_length_yottameter_to_zettameter() {
let result = convert_metric_length(
1.0,
MetricLengthUnit::Yottameter,
MetricLengthUnit::Zettameter,
);
assert_eq!(result, 1000.0);
}
#[test]
fn test_convert_length_same_unit() {
let result = convert_metric_length(
42.0,
MetricLengthUnit::Kilometer,
MetricLengthUnit::Kilometer,
);
assert_eq!(result, 42.0);
}
#[test]
fn test_length_conversion_str_basic() {
let result = metric_length_conversion(1.0, "meter", "kilometer").unwrap();
assert_eq!(result, 0.001);
}
#[test]
fn test_length_conversion_str_symbols() {
let result = metric_length_conversion(1.0, "m", "km").unwrap();
assert_eq!(result, 0.001);
}
#[test]
fn test_length_conversion_str_case_insensitive() {
let result = metric_length_conversion(1.0, "METER", "KILOMETER").unwrap();
assert_eq!(result, 0.001);
}
#[test]
fn test_length_conversion_str_plurals() {
let result = metric_length_conversion(5.0, "meters", "kilometers").unwrap();
assert_eq!(result, 0.005);
}
#[test]
fn test_length_conversion_str_british_spellings() {
// Test that British spellings work in string-based API
let result = metric_length_conversion(1.0, "metre", "kilometre").unwrap();
assert_eq!(result, 0.001);
let result = metric_length_conversion(1000.0, "kilometre", "metre").unwrap();
assert_eq!(result, 1_000_000.0);
let result = metric_length_conversion(1.0, "gigametre", "megametre").unwrap();
assert_eq!(result, 1000.0);
// Mix American and British
let result = metric_length_conversion(1.0, "meter", "kilometre").unwrap();
assert_eq!(result, 0.001);
let result = metric_length_conversion(1.0, "megametre", "meter").unwrap();
assert_eq!(result, 1_000_000.0);
}
#[test]
fn test_length_conversion_str_invalid_from() {
let result = metric_length_conversion(1.0, "wrongUnit", "meter");
assert!(result.is_err());
assert!(result.unwrap_err().contains("Invalid 'from_type'"));
}
#[test]
fn test_length_conversion_str_invalid_to() {
let result = metric_length_conversion(1.0, "meter", "inch");
assert!(result.is_err());
assert!(result.unwrap_err().contains("Invalid 'to_type'"));
}
#[test]
fn test_length_conversion_str_large_values() {
let result = metric_length_conversion(1000.0, "km", "m").unwrap();
assert_eq!(result, 1_000_000.0);
}
#[test]
fn test_length_conversion_str_small_values() {
let result = metric_length_conversion(0.001, "m", "km").unwrap();
assert_eq!(result, 0.000001);
}
#[test]
fn test_all_conversions_reversible() {
let units = [
MetricLengthUnit::Meter,
MetricLengthUnit::Kilometer,
MetricLengthUnit::Megameter,
MetricLengthUnit::Gigameter,
MetricLengthUnit::Terameter,
];
for &from in &units {
for &to in &units {
let forward = convert_metric_length(100.0, from, to);
let backward = convert_metric_length(forward, to, from);
assert!((backward - 100.0).abs() < 1e-9, "Conversion not reversible");
}
}
}
}