use super::conv_factors::{base, derived};
use super::si::*;
use crate::prelude::Ex;
impl Length {
pub fn meters(val: &Ex) -> Self {
Length(val.clone())
}
pub fn kilometers(val: &Ex) -> Self {
Length(val * 1000)
}
pub fn centimeters(val: &Ex) -> Self {
Length(val * &val.context().rational(1, 100))
}
pub fn millimeters(val: &Ex) -> Self {
Length(val * &val.context().rational(1, 1000))
}
pub fn micrometers(val: &Ex) -> Self {
Length(val * &val.context().rational(1, 1_000_000))
}
pub fn inches(val: &Ex) -> Self {
Length(val * &val.context().rational(base::INCH.0, base::INCH.1))
}
pub fn feet(val: &Ex) -> Self {
Length(val * &val.context().rational(derived::FOOT.0, derived::FOOT.1))
}
pub fn yards(val: &Ex) -> Self {
Length(val * &val.context().rational(derived::YARD.0, derived::YARD.1))
}
pub fn miles(val: &Ex) -> Self {
Length(val * &val.context().rational(derived::MILE.0, derived::MILE.1))
}
pub fn nautical_miles(val: &Ex) -> Self {
Length(
val * &val
.context()
.rational(base::NAUTICAL_MILE.0, base::NAUTICAL_MILE.1),
)
}
pub fn fathoms(val: &Ex) -> Self {
Length(val * &val.context().rational(derived::FATHOM.0, derived::FATHOM.1))
}
pub fn mils(val: &Ex) -> Self {
Length(val * &val.context().rational(127, 5_000_000))
}
}
impl Mass {
pub fn kilograms(val: &Ex) -> Self {
Mass(val.clone())
}
pub fn grams(val: &Ex) -> Self {
Mass(val * &val.context().rational(1, 1000))
}
pub fn milligrams(val: &Ex) -> Self {
Mass(val * &val.context().rational(1, 1_000_000))
}
pub fn tonnes(val: &Ex) -> Self {
Mass(val * 1000)
}
pub fn pounds(val: &Ex) -> Self {
Mass(val * &val.context().rational(base::POUND.0, base::POUND.1))
}
pub fn ounces(val: &Ex) -> Self {
Mass(val * &val.context().rational(derived::OUNCE.0, derived::OUNCE.1))
}
pub fn short_tons(val: &Ex) -> Self {
Mass(
val * &val
.context()
.rational(derived::SHORT_TON.0, derived::SHORT_TON.1),
)
}
pub fn long_tons(val: &Ex) -> Self {
Mass(
val * &val
.context()
.rational(derived::LONG_TON.0, derived::LONG_TON.1),
)
}
pub fn grains(val: &Ex) -> Self {
Mass(val * &val.context().rational(derived::GRAIN.0, derived::GRAIN.1))
}
pub fn slugs(val: &Ex) -> Self {
Mass(val * &val.context().rational(derived::SLUG.0, derived::SLUG.1))
}
}
impl Time {
pub fn seconds(val: &Ex) -> Self {
Time(val.clone())
}
pub fn milliseconds(val: &Ex) -> Self {
Time(val * &val.context().rational(1, 1000))
}
pub fn microseconds(val: &Ex) -> Self {
Time(val * &val.context().rational(1, 1_000_000))
}
pub fn nanoseconds(val: &Ex) -> Self {
Time(val * &val.context().rational(1, 1_000_000_000))
}
pub fn minutes(val: &Ex) -> Self {
Time(val * 60)
}
pub fn hours(val: &Ex) -> Self {
Time(val * 3600)
}
pub fn days(val: &Ex) -> Self {
Time(val * 86400)
}
}
impl Angle {
pub fn radians(val: &Ex) -> Self {
Angle(val.clone())
}
pub fn degrees(val: &Ex) -> Self {
Angle(val * &val.context().pi() / 180)
}
pub fn revolutions(val: &Ex) -> Self {
Angle(val * &(2 * &val.context().pi()))
}
}
impl Velocity {
pub fn meters_per_second(val: &Ex) -> Self {
Velocity(val.clone())
}
pub fn kilometers_per_hour(val: &Ex) -> Self {
Velocity(val * &val.context().rational(5, 18))
}
pub fn miles_per_hour(val: &Ex) -> Self {
Velocity(val * &val.context().rational(derived::MPH.0, derived::MPH.1))
}
pub fn knots(val: &Ex) -> Self {
Velocity(val * &val.context().rational(derived::KNOT.0, derived::KNOT.1))
}
pub fn feet_per_second(val: &Ex) -> Self {
Velocity(val * &val.context().rational(derived::FOOT.0, derived::FOOT.1))
}
}
impl Force {
pub fn newtons(val: &Ex) -> Self {
Force(val.clone())
}
pub fn kilonewtons(val: &Ex) -> Self {
Force(val * 1000)
}
pub fn pound_force(val: &Ex) -> Self {
Force(
val * &val
.context()
.rational(derived::POUND_FORCE.0, derived::POUND_FORCE.1),
)
}
pub fn kilogram_force(val: &Ex) -> Self {
Force(
val * &val
.context()
.rational(derived::KILOGRAM_FORCE.0, derived::KILOGRAM_FORCE.1),
)
}
pub fn dynes(val: &Ex) -> Self {
Force(val * &val.context().rational(derived::DYNE.0, derived::DYNE.1))
}
}
impl Energy {
pub fn joules(val: &Ex) -> Self {
Energy(val.clone())
}
pub fn kilojoules(val: &Ex) -> Self {
Energy(val * 1000)
}
pub fn kilowatt_hours(val: &Ex) -> Self {
Energy(val * 3_600_000)
}
pub fn calories(val: &Ex) -> Self {
Energy(
val * &val
.context()
.rational(base::CALORIE_TH.0, base::CALORIE_TH.1),
)
}
pub fn kilocalories(val: &Ex) -> Self {
Energy(val * 4184)
}
pub fn btu(val: &Ex) -> Self {
Energy(val * &val.context().rational(base::BTU_IT.0, base::BTU_IT.1))
}
pub fn ergs(val: &Ex) -> Self {
Energy(val * &val.context().rational(derived::ERG.0, derived::ERG.1))
}
pub fn foot_pounds(val: &Ex) -> Self {
Energy(
val * &val
.context()
.rational(derived::FOOT_POUND.0, derived::FOOT_POUND.1),
)
}
}
impl Power {
pub fn watts(val: &Ex) -> Self {
Power(val.clone())
}
pub fn kilowatts(val: &Ex) -> Self {
Power(val * 1000)
}
pub fn megawatts(val: &Ex) -> Self {
Power(val * 1_000_000)
}
pub fn horsepower(val: &Ex) -> Self {
Power(
val * &val
.context()
.rational(derived::HORSEPOWER.0, derived::HORSEPOWER.1),
)
}
pub fn metric_horsepower(val: &Ex) -> Self {
Power(
val * &val
.context()
.rational(derived::METRIC_HORSEPOWER.0, derived::METRIC_HORSEPOWER.1),
)
}
}
impl Voltage {
pub fn volts(val: &Ex) -> Self {
Voltage(val.clone())
}
pub fn millivolts(val: &Ex) -> Self {
Voltage(val * &val.context().rational(1, 1000))
}
pub fn kilovolts(val: &Ex) -> Self {
Voltage(val * 1000)
}
}
impl Current {
pub fn amperes(val: &Ex) -> Self {
Current(val.clone())
}
pub fn milliamperes(val: &Ex) -> Self {
Current(val * &val.context().rational(1, 1000))
}
pub fn microamperes(val: &Ex) -> Self {
Current(val * &val.context().rational(1, 1_000_000))
}
}
impl Resistance {
pub fn ohms(val: &Ex) -> Self {
Resistance(val.clone())
}
pub fn milliohms(val: &Ex) -> Self {
Resistance(val * &val.context().rational(1, 1000))
}
pub fn kilohms(val: &Ex) -> Self {
Resistance(val * 1000)
}
pub fn megohms(val: &Ex) -> Self {
Resistance(val * 1_000_000)
}
}
impl Pressure {
pub fn pascals(val: &Ex) -> Self {
Pressure(val.clone())
}
pub fn kilopascals(val: &Ex) -> Self {
Pressure(val * 1000)
}
pub fn megapascals(val: &Ex) -> Self {
Pressure(val * 1_000_000)
}
pub fn bars(val: &Ex) -> Self {
Pressure(val * 100_000)
}
pub fn atmospheres(val: &Ex) -> Self {
Pressure(val * 101_325)
}
pub fn psi(val: &Ex) -> Self {
Pressure(val * &val.context().rational(derived::PSI.0, derived::PSI.1))
}
pub fn torr(val: &Ex) -> Self {
Pressure(val * &val.context().rational(derived::TORR.0, derived::TORR.1))
}
}
impl Frequency {
pub fn hertz(val: &Ex) -> Self {
Frequency(val.clone())
}
pub fn kilohertz(val: &Ex) -> Self {
Frequency(val * 1000)
}
pub fn megahertz(val: &Ex) -> Self {
Frequency(val * 1_000_000)
}
pub fn gigahertz(val: &Ex) -> Self {
Frequency(val * 1_000_000_000)
}
pub fn rpm(val: &Ex) -> Self {
Frequency(val * &val.context().rational(1, 60))
}
pub fn bpm(val: &Ex) -> Self {
Frequency(val * &val.context().rational(1, 60))
}
}
impl Temperature {
pub fn kelvins(val: &Ex) -> Self {
Temperature(val.clone())
}
pub fn from_celsius(val: &Ex) -> Self {
Temperature(val + &val.context().rational(27315, 100))
}
pub fn from_fahrenheit(val: &Ex) -> Self {
Temperature(&(val + &val.context().rational(45967, 100)) * &val.context().rational(5, 9))
}
pub fn from_rankine(val: &Ex) -> Self {
Temperature(val * &val.context().rational(5, 9))
}
}
impl Torque {
pub fn newton_meters(val: &Ex) -> Self {
Torque(val.clone())
}
}
impl Acceleration {
pub fn meters_per_second_squared(val: &Ex) -> Self {
Acceleration(val.clone())
}
pub fn standard_gravity(val: &Ex) -> Self {
Acceleration(val * &val.context().rational(base::G_N.0, base::G_N.1))
}
pub fn feet_per_second_squared(val: &Ex) -> Self {
Acceleration(val * &val.context().rational(derived::FOOT.0, derived::FOOT.1))
}
}
impl Area {
pub fn square_meters(val: &Ex) -> Self {
Area(val.clone())
}
pub fn square_kilometers(val: &Ex) -> Self {
Area(val * 1_000_000)
}
pub fn square_centimeters(val: &Ex) -> Self {
Area(val * &val.context().rational(1, 10_000))
}
pub fn hectares(val: &Ex) -> Self {
Area(val * 10_000)
}
}
impl Volume {
pub fn cubic_meters(val: &Ex) -> Self {
Volume(val.clone())
}
pub fn liters(val: &Ex) -> Self {
Volume(val * &val.context().rational(1, 1000))
}
pub fn milliliters(val: &Ex) -> Self {
Volume(val * &val.context().rational(1, 1_000_000))
}
pub fn us_gallons(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(derived::US_GALLON.0, derived::US_GALLON.1),
)
}
pub fn us_quarts(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(derived::US_QUART.0, derived::US_QUART.1),
)
}
pub fn us_pints(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(derived::US_PINT.0, derived::US_PINT.1),
)
}
pub fn us_fluid_ounces(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(derived::US_FLUID_OUNCE.0, derived::US_FLUID_OUNCE.1),
)
}
pub fn imperial_gallons(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(base::IMPERIAL_GALLON.0, base::IMPERIAL_GALLON.1),
)
}
pub fn us_tablespoons(val: &Ex) -> Self {
Volume(
val * &val
.context()
.rational(derived::US_TABLESPOON.0, derived::US_TABLESPOON.1),
)
}
}
impl Momentum {
pub fn kilogram_meters_per_second(val: &Ex) -> Self {
Momentum(val.clone())
}
}
impl AngularVelocity {
pub fn radians_per_second(val: &Ex) -> Self {
AngularVelocity(val.clone())
}
pub fn rpm(val: &Ex) -> Self {
AngularVelocity(&(val * &val.context().pi()) * &val.context().rational(2, 60))
}
pub fn degrees_per_second(val: &Ex) -> Self {
AngularVelocity(val * &val.context().pi() / 180)
}
}
impl Charge {
pub fn coulombs(val: &Ex) -> Self {
Charge(val.clone())
}
pub fn milliampere_hours(val: &Ex) -> Self {
Charge(val * &val.context().rational(36, 10))
}
pub fn ampere_hours(val: &Ex) -> Self {
Charge(val * 3600)
}
}
impl Capacitance {
pub fn farads(val: &Ex) -> Self {
Capacitance(val.clone())
}
pub fn microfarads(val: &Ex) -> Self {
Capacitance(val * &val.context().rational(1, 1_000_000))
}
pub fn nanofarads(val: &Ex) -> Self {
Capacitance(val * &val.context().rational(1, 1_000_000_000))
}
pub fn picofarads(val: &Ex) -> Self {
Capacitance(val * &val.context().rational(1, 1_000_000_000_000))
}
}
impl Inductance {
pub fn henrys(val: &Ex) -> Self {
Inductance(val.clone())
}
pub fn millihenrys(val: &Ex) -> Self {
Inductance(val * &val.context().rational(1, 1000))
}
pub fn microhenrys(val: &Ex) -> Self {
Inductance(val * &val.context().rational(1, 1_000_000))
}
}
impl MagneticFlux {
pub fn webers(val: &Ex) -> Self {
MagneticFlux(val.clone())
}
}
impl Stiffness {
pub fn newtons_per_meter(val: &Ex) -> Self {
Stiffness(val.clone())
}
pub fn kilonewtons_per_meter(val: &Ex) -> Self {
Stiffness(val * 1000)
}
}
impl Damping {
pub fn newton_seconds_per_meter(val: &Ex) -> Self {
Damping(val.clone())
}
}
impl Dimensionless {
pub fn percent(val: &Ex) -> Self {
Dimensionless(val * &val.context().rational(1, 100))
}
pub fn per_mille(val: &Ex) -> Self {
Dimensionless(val * &val.context().rational(1, 1000))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_meters_identity() {
let v = crate::api::context::Context::new().int(5);
let l = Length::meters(&v);
assert_eq!(l.into_inner().eval().to_string(), "5");
}
#[test]
fn length_kilometers_scales() {
let v = crate::api::context::Context::new().int(3);
let l = Length::kilometers(&v);
assert_eq!(l.into_inner().eval().to_string(), "3000");
}
#[test]
fn length_centimeters_scales() {
let v = crate::api::context::Context::new().int(200);
let l = Length::centimeters(&v);
assert_eq!(l.into_inner().eval().to_string(), "2");
}
#[test]
fn length_inches_exact() {
let v = crate::api::context::Context::new().int(1);
let l = Length::inches(&v);
assert_eq!(l.into_inner().eval().to_string(), "127/5000");
}
#[test]
fn mass_pounds_exact() {
let v = crate::api::context::Context::new().int(1);
let m = Mass::pounds(&v);
assert_eq!(m.into_inner().eval().to_string(), "45359237/100000000");
}
#[test]
fn time_hours_scales() {
let v = crate::api::context::Context::new().int(2);
let t = Time::hours(&v);
assert_eq!(t.into_inner().eval().to_string(), "7200");
}
#[test]
fn temperature_from_celsius() {
let v = crate::api::context::Context::new().int(0);
let t = Temperature::from_celsius(&v);
let result = t.into_inner().eval().to_string();
assert!(
result == "5463/20" || result == "27315/100",
"Expected 5463/20 or 27315/100, got {result}"
);
}
#[test]
fn temperature_from_fahrenheit_boiling() {
let v = crate::api::context::Context::new().int(212);
let t = Temperature::from_fahrenheit(&v);
let result = t.into_inner().eval().to_string();
assert!(
result == "7463/20" || result == "37315/100",
"Expected 7463/20 or 37315/100, got {result}"
);
}
#[test]
fn pressure_atmospheres_exact() {
let v = crate::api::context::Context::new().int(1);
let p = Pressure::atmospheres(&v);
assert_eq!(p.into_inner().eval().to_string(), "101325");
}
#[test]
fn velocity_kmh_to_ms() {
let v = crate::api::context::Context::new().int(90);
let vel = Velocity::kilometers_per_hour(&v);
assert_eq!(vel.into_inner().eval().to_string(), "25");
}
#[test]
fn energy_kwh_to_joules() {
let v = crate::api::context::Context::new().int(1);
let e = Energy::kilowatt_hours(&v);
assert_eq!(e.into_inner().eval().to_string(), "3600000");
}
#[test]
fn frequency_rpm_to_hertz() {
let v = crate::api::context::Context::new().int(120);
let f = Frequency::rpm(&v);
assert_eq!(f.into_inner().eval().to_string(), "2");
}
#[test]
fn symbolic_length_preserves_variable() {
let x = crate::api::context::Context::new().symbol("x");
let l = Length::kilometers(&x);
let inner = l.into_inner();
assert!(inner.to_string().contains("x"));
}
#[test]
fn volume_liters_scales() {
let v = crate::api::context::Context::new().int(1000);
let vol = Volume::liters(&v);
assert_eq!(vol.into_inner().eval().to_string(), "1");
}
#[test]
fn dimensionless_percent() {
let v = crate::api::context::Context::new().int(50);
let d = Dimensionless::percent(&v);
assert_eq!(d.into_inner().eval().to_string(), "1/2");
}
#[test]
fn charge_ampere_hours() {
let v = crate::api::context::Context::new().int(1);
let c = Charge::ampere_hours(&v);
assert_eq!(c.into_inner().eval().to_string(), "3600");
}
#[test]
fn area_hectares() {
let v = crate::api::context::Context::new().int(2);
let a = Area::hectares(&v);
assert_eq!(a.into_inner().eval().to_string(), "20000");
}
}