use crate::Number;
use fastnum::{D128, dec128 as d};
use std::cmp::Reverse;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum UnitType {
Time,
Length,
Area,
Volume,
Mass,
DigitalStorage,
DataTransferRate,
FlopCount,
FlopRate,
Energy,
Power,
ElectricCurrent,
Resistance,
Voltage,
Pressure,
Frequency,
Speed,
Temperature,
}
impl UnitType {
fn primitive(&self) -> Vec<(Unit, isize)> {
let units = match self {
Time => vec![(Second, 1)],
Length => vec![(Meter, 1)],
Area => vec![(Meter, 2)],
Volume => vec![(Meter, 3)],
Mass => vec![(Kilogram, 1)],
DigitalStorage => vec![(Bit, 1)],
DataTransferRate => vec![(Bit, 1), (Second, -1)],
FlopCount => vec![(Flop, 1)],
FlopRate => vec![(Flop, 1), (Second, -1)],
Energy => vec![(Meter, 2), (Kilogram, 1), (Second, -2)],
Power => vec![(Meter, 2), (Kilogram, 1), (Second, -3)],
ElectricCurrent => vec![(Ampere, 1)],
Resistance => vec![(Meter, 2), (Kilogram, 1), (Second, -3), (Ampere, -2)],
Voltage => vec![(Meter, 2), (Kilogram, 1), (Second, -3), (Ampere, -1)],
Pressure => vec![(Kilogram, 1), (Second, -2), (Meter, -1)],
Frequency => vec![(Second, -1)],
Speed => vec![(Meter, 1), (Second, -1)],
Temperature => vec![(Kelvin, 1)],
};
#[cfg(debug_assertions)]
{
let u0 = units.clone();
let mut u1 = units.clone();
sort_units(&mut u1);
assert!(u0 == u1)
}
units
}
}
use UnitType::*;
pub fn sort_units(primitives: &mut Vec<(Unit, isize)>) {
primitives.sort_by_key(|u| {
(
u.1 < 0, Reverse(u.1.abs()), u.0, )
});
}
pub fn primitive_unit(unit: &[(Unit, isize)]) -> Vec<(Unit, isize)> {
let mut primitives: Vec<(Unit, isize)> = Vec::new();
for (unit, exponent) in unit {
for (primitive, primitive_exponent) in unit.category().primitive() {
let existing = primitives.iter_mut().find(|(u, _)| u == &primitive);
match existing {
Some(existing) => existing.1 += primitive_exponent * exponent,
None => primitives.push((primitive, primitive_exponent * exponent)),
}
}
}
primitives.retain(|(_, exponent)| exponent != &0);
sort_units(&mut primitives);
primitives
}
macro_rules! create_units {
( $( $variant:ident : $properties:expr ),*, ) => {
#[derive(Clone, Copy, PartialEq, Debug, Eq, PartialOrd, Ord)]
pub enum Unit {
$($variant),*
}
use Unit::*;
impl Unit {
pub fn category(&self) -> UnitType {
match self {
$(
Unit::$variant => $properties.0
),*
}
}
pub fn weight(&self) -> D128 {
match self {
$(
Unit::$variant => $properties.1
),*
}
}
pub(crate) fn singular(&self) -> &str {
match self {
$(
Unit::$variant => $properties.2
),*
}
}
pub(crate) fn plural(&self) -> &str {
match self {
$(
Unit::$variant => $properties.3
),*
}
}
}
}
}
macro_rules! inexact {
($a:tt / $b:tt) => {
d!($a) / d!($b)
};
}
create_units!(
Nanosecond: (Time, d!(0.000000001), "nanosecond", "nanoseconds"),
Microsecond: (Time, d!(0.000001), "microsecond", "microseconds"),
Millisecond: (Time, d!(0.001), "millisecond", "milliseconds"),
Second: (Time, d!(1), "second", "seconds"),
Minute: (Time, d!(60), "minute", "minutes"),
Hour: (Time, d!(3600), "hour", "hours"),
Day: (Time, d!(86400), "day", "days"),
Week: (Time, d!(604800), "week", "weeks"),
Month: (Time, d!(2629746), "month", "months"),
Quarter: (Time, d!(7889238), "quarter", "quarters"),
Year: (Time, d!(31556952), "year", "years"),
Decade: (Time, d!(315569520), "decade", "decades"),
Century: (Time, d!(3155695200), "century", "centuries"),
Millennium: (Time, d!(31556952000), "millennium", "millennia"),
Millimeter: (Length, d!(0.001), "millimeter", "millimeters"),
Centimeter: (Length, d!(0.01), "centimeter", "centimeters"),
Decimeter: (Length, d!(0.1), "decimeter", "decimeters"),
Meter: (Length, d!(1), "meter", "meters"),
Kilometer: (Length, d!(1000), "kilometer", "kilometers"),
Inch: (Length, d!(0.0254), "inch", "inches"),
Foot: (Length, d!(0.3048), "foot", "feet"),
Yard: (Length, d!(0.9144), "yard", "yards"),
Mile: (Length, d!(1609.344), "mile", "miles"),
Marathon: (Length, d!(42195), "marathon", "marathons"),
NauticalMile: (Length, d!(1852), "nautical mile", "nautical miles"),
LightYear: (Length, d!(9460730472580800), "light year", "light years"),
LightSecond: (Length, d!(299792458), "light second", "light seconds"),
SquareMillimeter: (Area, d!(0.000001), "square millimeter", "square millimeters"),
SquareCentimeter: (Area, d!(0.0001), "square centimeter", "square centimeters"),
SquareDecimeter: (Area, d!(0.01), "square decimeter", "square decimeters"),
SquareMeter: (Area, d!(1), "square meter", "square meters"),
SquareKilometer: (Area, d!(1000000), "square kilometer", "square kilometers"),
SquareInch: (Area, d!(0.00064516), "square inch", "square inches"),
SquareFoot: (Area, d!(0.09290304), "square foot", "square feet"),
SquareYard: (Area, d!(0.83612736), "square yard", "square yards"),
SquareMile: (Area, d!(2589988.110336), "square mile", "square miles"),
Are: (Area, d!(100), "are", "ares"),
Decare: (Area, d!(1000), "decare", "decares"),
Hectare: (Area, d!(10000), "hectare", "hectares"),
Acre: (Area, d!(4046.8564224), "acre", "acres"),
CubicMillimeter: (Volume, d!(0.000000001), "cubic millimeter", "cubic millimeters"),
CubicCentimeter: (Volume, d!(0.000001), "cubic centimeter", "cubic centimeters"),
CubicDecimeter: (Volume, d!(0.001), "cubic decimeter", "cubic decimeters"),
CubicMeter: (Volume, d!(1), "cubic meter", "cubic meters"),
CubicKilometer: (Volume, d!(1000000000), "cubic kilometer", "cubic kilometers"),
CubicInch: (Volume, d!(0.000016387064), "cubic inch", "cubic inches"),
CubicFoot: (Volume, d!(0.028316846592), "cubic foot", "cubic feet"),
CubicYard: (Volume, d!(0.764554857984), "cubic yard", "cubic yards"),
CubicMile: (Volume, d!(4168181825.440579584), "cubic mile", "cubic miles"),
Milliliter: (Volume, d!(0.000001), "milliliter", "milliliters"),
Centiliter: (Volume, d!(0.00001), "centiliter", "centiliters"),
Deciliter: (Volume, d!(0.0001), "deciliter", "deciliters"),
Liter: (Volume, d!(0.001), "liter", "liters"),
Teaspoon: (Volume, d!(0.00000492892159375), "teaspoon", "teaspoons"),
Tablespoon: (Volume, d!(0.00001478676478125), "tablespoon", "tablespoons"),
FluidOunce: (Volume, d!(0.0000295735295625), "fluid ounce", "fluid ounces"),
Cup: (Volume, d!(0.0002365882365), "cup", "cups"),
Pint: (Volume, d!(0.000473176473), "pint", "pints"),
Quart: (Volume, d!(0.000946352946), "quart", "quarts"),
Gallon: (Volume, d!(0.003785411784), "gallon", "gallons"),
OilBarrel: (Volume, d!(0.158987294928), "oil barrel", "oil barrels"),
Milligram: (Mass, d!(0.000001), "milligram", "milligrams"),
Gram: (Mass, d!(0.001), "gram", "grams"),
Hectogram: (Mass, d!(0.1), "hectogram", "hectograms"),
Kilogram: (Mass, d!(1), "kilogram", "kilograms"),
MetricTon: (Mass, d!(1000), "metric ton", "metric tons"),
Ounce: (Mass, d!(0.028349523125), "ounce", "ounces"),
Pound: (Mass, d!(0.45359237), "pound", "pounds"),
Stone: (Mass, d!(6.35029318), "stone", "stones"),
ShortTon: (Mass, d!(907.18474), "short ton", "short tons"),
LongTon: (Mass, d!(1016.0469088), "long ton", "long tons"),
Bit: (DigitalStorage, d!(1), "bit", "bits"),
Kilobit: (DigitalStorage, d!(1000), "kilobit", "kilobits"),
Megabit: (DigitalStorage, d!(1000000), "megabit", "megabits"),
Gigabit: (DigitalStorage, d!(1000000000), "gigabit", "gigabits"),
Terabit: (DigitalStorage, d!(1000000000000), "terabit", "terabits"),
Petabit: (DigitalStorage, d!(1000000000000000), "petabit", "petabits"),
Exabit: (DigitalStorage, d!(1000000000000000000), "exabit", "exabits"),
Zettabit: (DigitalStorage, d!(1000000000000000000000), "zettabit", "zettabits"),
Yottabit: (DigitalStorage, d!(1000000000000000000000000), "yottabit", "yottabits"),
Kibibit: (DigitalStorage, d!(1024), "kibibit", "kibibits"),
Mebibit: (DigitalStorage, d!(1048576), "mebibit", "mebibits"),
Gibibit: (DigitalStorage, d!(1073741824), "gibibit", "gibibits"),
Tebibit: (DigitalStorage, d!(1099511627776), "tebibit", "tebibits"),
Pebibit: (DigitalStorage, d!(1125899906842624), "pebibit", "pebibits"),
Exbibit: (DigitalStorage, d!(1152921504606846976), "exbibit", "exbibits"),
Zebibit: (DigitalStorage, d!(1180591620717411303424), "zebibit", "zebibits"),
Yobibit: (DigitalStorage, d!(1208925819614629174706176), "yobibit", "yobibits"),
Byte: (DigitalStorage, d!(8), "byte", "bytes"),
Kilobyte: (DigitalStorage, d!(8000), "kilobyte", "kilobytes"),
Megabyte: (DigitalStorage, d!(8000000), "megabyte", "megabytes"),
Gigabyte: (DigitalStorage, d!(8000000000), "gigabyte", "gigabytes"),
Terabyte: (DigitalStorage, d!(8000000000000), "terabyte", "terabytes"),
Petabyte: (DigitalStorage, d!(8000000000000000), "petabyte", "petabytes"),
Exabyte: (DigitalStorage, d!(8000000000000000000), "exabyte", "exabytes"),
Zettabyte: (DigitalStorage, d!(8000000000000000000000), "zettabyte", "zettabytes"),
Yottabyte: (DigitalStorage, d!(8000000000000000000000000), "yottabyte", "yottabytes"),
Kibibyte: (DigitalStorage, d!(8192), "kibibyte", "kibibytes"),
Mebibyte: (DigitalStorage, d!(8388608), "mebibyte", "mebibytes"),
Gibibyte: (DigitalStorage, d!(8589934592), "gibibyte", "gibibytes"),
Tebibyte: (DigitalStorage, d!(8796093022208), "tebibyte", "tebibytes"),
Pebibyte: (DigitalStorage, d!(9007199254740992), "pebibyte", "pebibytes"),
Exbibyte: (DigitalStorage, d!(9223372036854775808), "exbibyte", "exbibytes"),
Zebibyte: (DigitalStorage, d!(9444732965739290427392), "zebibyte", "zebibytes"),
Yobibyte: (DigitalStorage, d!(9671406556917033397649408), "yobibyte", "yobibytes"),
BitsPerSecond: (DataTransferRate, d!(1), "bit per second", "bits per second"),
KilobitsPerSecond: (DataTransferRate, d!(1000), "kilobit per second", "kilobits per second"),
MegabitsPerSecond: (DataTransferRate, d!(1000000), "megabit per second", "megabits per second"),
GigabitsPerSecond: (DataTransferRate, d!(1000000000), "gigabit per second", "gigabits per second"),
TerabitsPerSecond: (DataTransferRate, d!(1000000000000), "terabit per second", "terabits per second"),
PetabitsPerSecond: (DataTransferRate, d!(1000000000000000), "petabit per second", "petabits per second"),
ExabitsPerSecond: (DataTransferRate, d!(1000000000000000000), "exabit per second", "exabits per second"),
ZettabitsPerSecond: (DataTransferRate, d!(1000000000000000000000), "zettabit per second", "zettabits per second"),
YottabitsPerSecond: (DataTransferRate, d!(1000000000000000000000000), "yottabit per second", "yottabits per second"),
KibibitsPerSecond: (DataTransferRate, d!(1024), "kibibit per second", "kibibits per second"),
MebibitsPerSecond: (DataTransferRate, d!(1048576), "mebibit per second", "mebibits per second"),
GibibitsPerSecond: (DataTransferRate, d!(1073741824), "gibibit per second", "gibibits per second"),
TebibitsPerSecond: (DataTransferRate, d!(1099511627776), "tebibit per second", "tebibits per second"),
PebibitsPerSecond: (DataTransferRate, d!(1125899906842624), "pebibit per second", "pebibits per second"),
ExbibitsPerSecond: (DataTransferRate, d!(1152921504606846976), "exbibit per second", "exbibits per second"),
ZebibitsPerSecond: (DataTransferRate, d!(1180591620717411303424), "zebibit per second", "zebibits per second"),
YobibitsPerSecond: (DataTransferRate, d!(1208925819614629174706176), "yobibit per second", "yobibits per second"),
BytesPerSecond: (DataTransferRate, d!(8), "byte per second", "bytes per second"),
KilobytesPerSecond: (DataTransferRate, d!(8000), "kilobyte per second", "kilobytes per second"),
MegabytesPerSecond: (DataTransferRate, d!(8000000), "megabyte per second", "megabytes per second"),
GigabytesPerSecond: (DataTransferRate, d!(8000000000), "gigabyte per second", "gigabytes per second"),
TerabytesPerSecond: (DataTransferRate, d!(8000000000000), "terabyte per second", "terabytes per second"),
PetabytesPerSecond: (DataTransferRate, d!(8000000000000000), "petabyte per second", "petabytes per second"),
ExabytesPerSecond: (DataTransferRate, d!(8000000000000000000), "exabyte per second", "exabytes per second"),
ZettabytesPerSecond: (DataTransferRate, d!(8000000000000000000000), "zettabyte per second", "zettabytes per second"),
YottabytesPerSecond: (DataTransferRate, d!(8000000000000000000000000), "yottabyte per second", "yottabytes per second"),
KibibytesPerSecond: (DataTransferRate, d!(8192), "kibibyte per second", "kibibytes per second"),
MebibytesPerSecond: (DataTransferRate, d!(8388608), "mebibyte per second", "mebibytes per second"),
GibibytesPerSecond: (DataTransferRate, d!(8589934592), "gibibyte per second", "gibibytes per second"),
TebibytesPerSecond: (DataTransferRate, d!(8796093022208), "tebibyte per second", "tebibytes per second"),
PebibytesPerSecond: (DataTransferRate, d!(9007199254740992), "pebibyte per second", "pebibytes per second"),
ExbibytesPerSecond: (DataTransferRate, d!(9223372036854775808), "exbibyte per second", "exbibytes per second"),
ZebibytesPerSecond: (DataTransferRate, d!(9444732965739290427392), "zebibyte per second", "zebibytes per second"),
YobibytesPerSecond: (DataTransferRate, d!(9671406556917033397649408), "yobibyte per second", "yobibytes per second"),
Flop: (FlopCount, d!(1), "FLOP", "FLOP"),
KiloFlop: (FlopCount, d!(1000), "kiloFLOP", "kiloFLOP"),
MegaFlop: (FlopCount, d!(1000000), "megaFLOP", "megaFLOP"),
GigaFlop: (FlopCount, d!(1000000000), "gigaFLOP", "gigaFLOP"),
TeraFlop: (FlopCount, d!(1000000000000), "teraFLOP", "teraFLOP"),
PetaFlop: (FlopCount, d!(1000000000000000), "petaFLOP", "petaFLOP"),
ExaFlop: (FlopCount, d!(1000000000000000000), "exaFLOP", "exaFLOP"),
ZettaFlop: (FlopCount, d!(1000000000000000000000), "zettaFLOP", "zettaFLOP"),
YottaFlop: (FlopCount, d!(1000000000000000000000000), "yottaFLOP", "yottaFLOP"),
RonnaFlop: (FlopCount, d!(1000000000000000000000000000), "ronnaFLOP", "ronnaFLOP"),
QuettaFlop: (FlopCount, d!(1000000000000000000000000000000), "quettaFLOP", "quettaFLOP"),
FlopPerSecond: (FlopRate, d!(1), "FLOP per second", "FLOP per second"),
KiloFlopPerSecond: (FlopRate, d!(1000), "kiloFLOP per second", "kiloFLOP per second"),
MegaFlopPerSecond: (FlopRate, d!(1000000), "megaFLOP per second", "megaFLOP per second"),
GigaFlopPerSecond: (FlopRate, d!(1000000000), "gigaFLOP per second", "gigaFLOP per second"),
TeraFlopPerSecond: (FlopRate, d!(1000000000000), "teraFLOP per second", "teraFLOP per second"),
PetaFlopPerSecond: (FlopRate, d!(1000000000000000), "petaFLOP per second", "petaFLOP per second"),
ExaFlopPerSecond: (FlopRate, d!(1000000000000000000), "exaFLOP per second", "exaFLOP per second"),
ZettaFlopPerSecond: (FlopRate, d!(1000000000000000000000), "zettaFLOP per second", "zettaFLOP per second"),
YottaFlopPerSecond: (FlopRate, d!(1000000000000000000000000), "yottaFLOP per second", "yottaFLOP per second"),
RonnaFlopPerSecond: (FlopRate, d!(1000000000000000000000000000), "ronnaFLOP per second", "ronnaFLOP per second"),
QuettaFlopPerSecond: (FlopRate, d!(1000000000000000000000000000000), "quettaFLOP per second", "quettaFLOP per second"),
Millijoule: (Energy, d!(0.001), "millijoule", "millijoules"),
Joule: (Energy, d!(1), "joule", "joules"),
NewtonMeter: (Energy, d!(1), "newton meter", "newton meters"),
Kilojoule: (Energy, d!(1000), "kilojoule", "kilojoules"),
Megajoule: (Energy, d!(1000000), "megajoule", "megajoules"),
Gigajoule: (Energy, d!(1000000000), "gigajoule", "gigajoules"),
Terajoule: (Energy, d!(1000000000000), "terajoule", "terajoules"),
Calorie: (Energy, d!(4.1868), "calorie", "calories"),
KiloCalorie: (Energy, d!(4186.8), "kilocalorie", "kilocalories"),
BritishThermalUnit: (Energy, d!(1055.05585262), "British thermal unit", "British thermal units"),
WattHour: (Energy, d!(3600), "watt-hour", "watt-hours"),
KilowattHour: (Energy, d!(3600000), "kilowatt-hour", "kilowatt-hours"),
MegawattHour: (Energy, d!(3600000000), "megawatt-hour", "megawatt-hours"),
GigawattHour: (Energy, d!(3600000000000), "gigawatt-hour", "gigawatt-hours"),
TerawattHour: (Energy, d!(3600000000000000), "terawatt-hour", "terawatt-hours"),
PetawattHour: (Energy, d!(3600000000000000000), "petawatt-hour", "petawatt-hours"),
Milliwatt: (Power, d!(0.001), "milliwatt", "milliwatts"),
Watt: (Power, d!(1), "watt", "watts"),
Kilowatt: (Power, d!(1000), "kilowatt", "kilowatts"),
Megawatt: (Power, d!(1000000), "megawatt", "megawatts"),
Gigawatt: (Power, d!(1000000000), "gigawatt", "gigawatts"),
Terawatt: (Power, d!(1000000000000), "terawatt", "terawatts"),
Petawatt: (Power, d!(1000000000000000), "petawatt", "petawatts"),
BritishThermalUnitsPerMinute: (Power, inexact!(1055.05585262 / 60), "british thermal unit per minute", "british thermal units per minute"),
BritishThermalUnitsPerHour: (Power, inexact!(1055.05585262 / 3600), "british thermal unit per hour", "british thermal units per hour"),
Horsepower: (Power, d!(745.69987158227022), "horsepower", "horsepower"),
MetricHorsepower: (Power, d!(735.49875), "metric horsepower", "metric horsepower"),
Milliampere: (ElectricCurrent, d!(0.001), "milliampere", "milliamperes"),
Ampere: (ElectricCurrent, d!(1), "ampere", "amperes"),
Kiloampere: (ElectricCurrent, d!(1000), "kiloampere", "kiloamperes"),
Abampere: (ElectricCurrent, d!(10), "abampere", "abamperes"),
Milliohm: (Resistance, d!(0.001), "milliohm", "milliohms"),
Ohm: (Resistance, d!(1), "ohm", "ohms"),
Kiloohm: (Resistance, d!(1000), "kiloohm", "kiloohms"),
Millivolt: (Voltage, d!(0.001), "millivolt", "millivolts"),
Volt: (Voltage, d!(1), "volt", "volts"),
Kilovolt: (Voltage, d!(1000), "kilovolt", "kilovolts"),
Pascal: (Pressure, d!(1), "pascal", "pascals"),
Kilopascal: (Pressure, d!(1000), "kilopascal", "kilopascals"),
Atmosphere: (Pressure, d!(101325), "atmosphere", "atmospheres"),
Millibar: (Pressure, d!(100), "millibar", "millibars"),
Bar: (Pressure, d!(100000), "bar", "bars"),
InchOfMercury: (Pressure, d!(3386.389), "inch of mercury", "inches of mercury"),
PoundsPerSquareInch: (Pressure, inexact!(8896443230521/1290320000), "pound per square inch", "pounds per square inch"),
Torr: (Pressure, inexact!(4053000 / 30400), "torr", "torr"),
Hertz: (Frequency, d!(1), "hertz", "hertz"),
Kilohertz: (Frequency, d!(1000), "kilohertz", "kilohertz"),
Megahertz: (Frequency, d!(1000000), "megahertz", "megahertz"),
Gigahertz: (Frequency, d!(1000000000), "gigahertz", "gigahertz"),
Terahertz: (Frequency, d!(1000000000000), "terahertz", "terahertz"),
Petahertz: (Frequency, d!(1000000000000000), "petahertz", "petahertz"),
RevolutionsPerMinute: (Frequency, d!(60), "revolution per minute", "revolutions per minute"),
KilometersPerHour: (Speed, inexact!(1 / 3.6), "kilometer per hour", "kilometers per hour"),
MetersPerSecond: (Speed, d!(1), "meter per second", "meters per second"),
MilesPerHour: (Speed, d!(0.44704), "mile per hour", "miles per hour"),
FeetPerSecond: (Speed, d!(0.3048), "foot per second", "feet per second"),
Knot: (Speed, inexact!(463 / 900), "knot", "knots"),
Kelvin: (Temperature, d!(0), "kelvin", "kelvin"),
Celsius: (Temperature, d!(0), "celsius", "celsius"),
Fahrenheit: (Temperature, d!(0), "fahrenheit", "fahrenheit"),
);
fn combined_weight(unit: &[(Unit, isize)]) -> D128 {
unit.iter().fold(D128::from(1), |acc, (u, exp)| {
acc * integer_power(u.weight(), *exp)
})
}
fn integer_power(base: D128, exp: isize) -> D128 {
let positive = (0..exp.unsigned_abs()).fold(D128::from(1), |acc, _| acc * base);
if exp >= 0 {
positive
} else {
D128::from(1) / positive
}
}
pub fn convert(number: Number, to_unit: Vec<(Unit, isize)>) -> Result<Number, String> {
if number.primitive_unit() != primitive_unit(&to_unit) {
return Err(format!(
"Cannot convert {} to {}",
number,
Number::with_unit(d!(0), to_unit).plural()
));
}
let value = number.value;
if number.primitive_unit() == Temperature.primitive() {
if number.unit.len() != 1
|| to_unit.len() != 1
|| number.unit[0].1 != 1
|| to_unit[0].1 != 1
{
return Err(format!(
"Cannot convert {} to {}",
number,
Number::with_unit(d!(0), to_unit).plural()
));
}
let ok = |new_value| Ok(Number::with_unit(new_value, to_unit.clone()));
match (number.unit[0].0, to_unit[0].0) {
(Kelvin, Kelvin) => ok(value),
(Kelvin, Celsius) => ok(value - d!(273.15)),
(Kelvin, Fahrenheit) => ok(value * d!(1.8) - d!(459.67)),
(Celsius, Celsius) => ok(value),
(Celsius, Kelvin) => ok(value + d!(273.15)),
(Celsius, Fahrenheit) => ok(value * d!(1.8) + d!(32)),
(Fahrenheit, Fahrenheit) => ok(value),
(Fahrenheit, Kelvin) => ok((value + d!(459.67)) * d!(5) / d!(9)),
(Fahrenheit, Celsius) => ok((value - d!(32)) / d!(1.8)),
_ => Err(format!(
"Error converting temperature {} to {}",
number,
Number::with_unit(d!(0), to_unit).plural()
)),
}
} else {
let source_weight = combined_weight(&number.unit);
let target_weight = combined_weight(&to_unit);
Ok(Number {
value: number.value * source_weight / target_weight,
unit: to_unit.to_vec(),
})
}
}
pub fn convert_to_lowest(left: Number, right: Number) -> Result<(Number, Number), String> {
assert!(left.primitive_unit() == right.primitive_unit());
if combined_weight(&left.unit) == combined_weight(&right.unit) {
Ok((left, right))
} else if combined_weight(&left.unit) > combined_weight(&right.unit) {
let left_converted = convert(left, right.unit.clone())?;
Ok((left_converted, right))
} else {
let right_converted = convert(right, left.unit.clone())?;
Ok((left, right_converted))
}
}
pub fn add(left: Number, right: Number) -> Result<Number, String> {
if left.unit == right.unit {
Ok(Number::with_unit(left.value + right.value, left.unit))
} else if left.primitive_unit() == right.primitive_unit()
&& !left.contains_primitive(Temperature)
{
let (left, right) = convert_to_lowest(left, right)?;
Ok(Number::with_unit(left.value + right.value, left.unit))
} else {
Err(format!("Cannot add {} and {}", left, right))
}
}
pub fn subtract(left: Number, right: Number) -> Result<Number, String> {
if left.unit == right.unit {
Ok(Number::with_unit(left.value - right.value, left.unit))
} else if left.primitive_unit() == right.primitive_unit()
&& !left.contains_primitive(Temperature)
{
let (left, right) = convert_to_lowest(left, right)?;
Ok(Number::with_unit(left.value - right.value, left.unit))
} else {
Err(format!("Cannot subtract {} by {}", left, right))
}
}
pub fn to_ideal_unit(number: Number) -> Number {
let value = number.value * combined_weight(&number.unit);
let primitive = number.primitive_unit();
if primitive == Length.primitive() {
if value >= 0.1 * LightYear.weight() {
return Number::with_basic_unit(value / LightYear.weight(), LightYear);
} else if value >= Kilometer.weight() {
return Number::with_basic_unit(value / Kilometer.weight(), Kilometer);
} else if value >= Meter.weight() {
return Number::with_basic_unit(value / Meter.weight(), Meter);
} else if value >= Centimeter.weight() {
return Number::with_basic_unit(value / Centimeter.weight(), Centimeter);
} else {
return Number::with_basic_unit(value / Millimeter.weight(), Millimeter);
}
} else if primitive == Time.primitive() {
if value >= Year.weight() {
return Number::with_basic_unit(value / Year.weight(), Year);
} else if value >= Day.weight() {
return Number::with_basic_unit(value / Day.weight(), Day);
} else if value >= Hour.weight() {
return Number::with_basic_unit(value / Hour.weight(), Hour);
} else if value >= Minute.weight() {
return Number::with_basic_unit(value / Minute.weight(), Minute);
} else if value >= Second.weight() {
return Number::with_basic_unit(value / Second.weight(), Second);
} else if value >= Millisecond.weight() {
return Number::with_basic_unit(value / Millisecond.weight(), Millisecond);
} else if value >= Microsecond.weight() {
return Number::with_basic_unit(value / Microsecond.weight(), Microsecond);
} else {
return Number::with_basic_unit(value / Nanosecond.weight(), Nanosecond);
}
} else if primitive == Area.primitive() {
if value >= SquareKilometer.weight() {
return Number::with_basic_unit(value / SquareKilometer.weight(), SquareKilometer);
} else if value >= Hectare.weight() {
return Number::with_basic_unit(value / Hectare.weight(), Hectare);
} else if value >= SquareMeter.weight() {
return Number::with_basic_unit(value / SquareMeter.weight(), SquareMeter);
} else if value >= SquareCentimeter.weight() {
return Number::with_basic_unit(value / SquareCentimeter.weight(), SquareCentimeter);
} else {
return Number::with_basic_unit(value / SquareMillimeter.weight(), SquareMillimeter);
}
} else if primitive == Volume.primitive() {
if value >= CubicKilometer.weight() {
return Number::with_basic_unit(value / CubicKilometer.weight(), CubicKilometer);
} else if value >= CubicMeter.weight() {
return Number::with_basic_unit(value / CubicMeter.weight(), CubicMeter);
} else if value >= Liter.weight() {
return Number::with_basic_unit(value / Liter.weight(), Liter);
} else if value >= Milliliter.weight() {
return Number::with_basic_unit(value / Milliliter.weight(), Milliliter);
} else {
return Number::with_basic_unit(value / CubicMillimeter.weight(), CubicMillimeter);
}
} else if primitive == Energy.primitive() {
let has_second = number.unit.iter().find(|unit| unit.0 == Second).is_some();
if has_second {
if value >= Terajoule.weight() {
return Number::with_basic_unit(value / Terajoule.weight(), Terajoule);
} else if value >= Gigajoule.weight() {
return Number::with_basic_unit(value / Gigajoule.weight(), Gigajoule);
} else if value >= Megajoule.weight() {
return Number::with_basic_unit(value / Megajoule.weight(), Megajoule);
} else if value >= Kilojoule.weight() {
return Number::with_basic_unit(value / Kilojoule.weight(), Kilojoule);
} else if value >= Joule.weight() {
return Number::with_basic_unit(value / Joule.weight(), Joule);
} else {
return Number::with_basic_unit(value / Millijoule.weight(), Millijoule);
}
} else {
if value >= PetawattHour.weight() {
return Number::with_basic_unit(value / PetawattHour.weight(), PetawattHour);
} else if value >= TerawattHour.weight() {
return Number::with_basic_unit(value / TerawattHour.weight(), TerawattHour);
} else if value >= GigawattHour.weight() {
return Number::with_basic_unit(value / GigawattHour.weight(), GigawattHour);
} else if value >= MegawattHour.weight() {
return Number::with_basic_unit(value / MegawattHour.weight(), MegawattHour);
} else if value >= KilowattHour.weight() {
return Number::with_basic_unit(value / KilowattHour.weight(), KilowattHour);
} else if value >= WattHour.weight() {
return Number::with_basic_unit(value / WattHour.weight(), WattHour);
} else if value >= Joule.weight() {
return Number::with_basic_unit(value / Joule.weight(), Joule);
} else {
return Number::with_basic_unit(value / Millijoule.weight(), Millijoule);
}
}
} else if primitive == Power.primitive() {
if value >= Petawatt.weight() {
return Number::with_basic_unit(value / Petawatt.weight(), Petawatt);
} else if value >= Terawatt.weight() {
return Number::with_basic_unit(value / Terawatt.weight(), Terawatt);
} else if value >= Gigawatt.weight() {
return Number::with_basic_unit(value / Gigawatt.weight(), Gigawatt);
} else if value >= Megawatt.weight() {
return Number::with_basic_unit(value / Megawatt.weight(), Megawatt);
} else if value >= Kilowatt.weight() {
return Number::with_basic_unit(value / Kilowatt.weight(), Kilowatt);
} else if value >= Watt.weight() {
return Number::with_basic_unit(value / Watt.weight(), Watt);
} else {
return Number::with_basic_unit(value / Milliwatt.weight(), Milliwatt);
}
} else if primitive == ElectricCurrent.primitive() {
if value >= Kiloampere.weight() {
return Number::with_basic_unit(value / Kiloampere.weight(), Kiloampere);
} else if value >= Ampere.weight() {
return Number::with_basic_unit(value / Ampere.weight(), Ampere);
} else {
return Number::with_basic_unit(value / Milliampere.weight(), Milliampere);
}
} else if primitive == Resistance.primitive() {
if value >= Kiloohm.weight() {
return Number::with_basic_unit(value / Kiloohm.weight(), Kiloohm);
} else if value >= Ohm.weight() {
return Number::with_basic_unit(value / Ohm.weight(), Ohm);
} else {
return Number::with_basic_unit(value / Milliohm.weight(), Milliohm);
}
} else if primitive == Voltage.primitive() {
if value >= Kilovolt.weight() {
return Number::with_basic_unit(value / Kilovolt.weight(), Kilovolt);
} else if value >= Volt.weight() {
return Number::with_basic_unit(value / Volt.weight(), Volt);
} else {
return Number::with_basic_unit(value / Millivolt.weight(), Millivolt);
}
} else if primitive == DigitalStorage.primitive() {
let bits = &[
Bit, Kilobit, Megabit, Gigabit, Terabit, Petabit, Exabit, Zettabit, Yottabit,
];
let bibits = &[
Bit, Kibibit, Mebibit, Gibibit, Tebibit, Pebibit, Exbibit, Zebibit, Yobibit,
];
let bytes = &[
Byte, Kilobyte, Megabyte, Gigabyte, Terabyte, Petabyte, Exabyte, Zettabyte, Yottabyte,
];
let bibytes = &[
Byte, Kibibyte, Mebibyte, Gibibyte, Tebibyte, Pebibyte, Exbibyte, Zebibyte, Yobibyte,
];
for unit in &number.unit {
if unit.0.category() == DigitalStorage || unit.0.category() == DataTransferRate {
let weight = unit.0.weight();
let candidates = if weight % 8192 == d!(0) {
bibytes
} else if weight % 8000 == d!(0) {
bytes
} else if weight % 1024 == d!(0) {
bibits
} else {
bits
};
let unit = candidates
.iter()
.rev()
.find(|&&u| value >= u.weight())
.copied()
.unwrap_or(candidates[0]);
return Number::with_basic_unit(value / unit.weight(), unit);
}
}
} else if primitive == FlopCount.primitive() {
let candidates = [
Flop, KiloFlop, MegaFlop, GigaFlop, TeraFlop, PetaFlop, ExaFlop, ZettaFlop, YottaFlop,
RonnaFlop, QuettaFlop,
];
let unit = candidates
.iter()
.rev()
.find(|&&u| value >= u.weight())
.copied()
.unwrap_or(candidates[0]);
return Number::with_basic_unit(value / unit.weight(), unit);
}
number
}
pub fn multiply(left: Number, right: Number) -> Result<Number, String> {
if left.contains_primitive(Temperature) || right.contains_primitive(Temperature) {
Err(format!("Cannot multiply {} and {}", left, right))
} else {
multiply_any(left, right)
}
}
pub(crate) fn multiply_any(left: Number, right: Number) -> Result<Number, String> {
let mut new_number = left;
new_number.value *= right.value;
for (r_unit, r_exp) in right.unit {
let existing = new_number.unit.iter_mut().find(|(u, _)| u == &r_unit);
match existing {
Some(existing) => existing.1 += r_exp,
None => new_number.unit.push((r_unit, r_exp)),
}
}
Ok(new_number)
}
pub fn divide(left: Number, right: Number) -> Result<Number, String> {
if left.contains_primitive(Temperature) || right.contains_primitive(Temperature) {
Err(format!("Cannot divide {} by {}", left, right))
} else {
divide_any(left, right)
}
}
pub fn divide_any(left: Number, right: Number) -> Result<Number, String> {
let mut new_number = left;
new_number.value = new_number.value / right.value;
for (r_unit, r_exp) in right.unit {
let existing = new_number.unit.iter_mut().find(|(u, _)| u == &r_unit);
match existing {
Some(existing) => existing.1 -= r_exp,
None => new_number.unit.push((r_unit, -r_exp)),
}
}
Ok(new_number)
}
pub fn modulo(left: Number, right: Number) -> Result<Number, String> {
if left.contains_primitive(Temperature) || right.contains_primitive(Temperature) {
Err(format!("Cannot modulo {} by {}", left, right))
} else if left.primitive_unit() == right.primitive_unit() {
let (left, right) = convert_to_lowest(left, right)?;
Ok(Number::with_unit(left.value % right.value, left.unit))
} else {
Err(format!("Cannot modulo {} by {}", left, right))
}
}
pub fn pow(left: Number, right: Number) -> Result<Number, String> {
if left.contains_primitive(Temperature) || right.has_unit() {
Err(format!("Cannot raise {} to the power of {}", left, right))
} else if left.is_unitless() {
let result = left.value.pow(right.value);
let new_number = Number::new_unitless(result);
Ok(new_number)
} else {
let exp: isize = match (right.value.try_into(), right.value.is_integral()) {
(Ok(exp), true) => exp,
_ => {
return Err(format!(
"Cannot raise {} to the power of {}. Numbers with units can only be raised to integer powers",
left, right
));
}
};
let result = left.value.pow(right.value);
let mut new_number = Number::with_unit(result, left.unit);
for (_, unit_exp) in new_number.unit.iter_mut() {
*unit_exp *= exp;
}
let new_number = to_ideal_unit(new_number);
Ok(new_number)
}
}
#[cfg(test)]
mod tests {
use fastnum::decimal::Context;
use super::*;
macro_rules! assert_float_eq {
( $actual:expr, $expected:literal ) => {
assert!(
($actual - $expected).abs() < f64::EPSILON,
"assertion `left == right` failed\n left: {:?}\n right: {:?}",
$actual,
$expected
);
};
}
#[test]
fn test_convert() {
pub fn convert_test(value: f64, unit: Unit, to_unit: Unit) -> f64 {
use std::str::FromStr;
let value_string = &value.to_string();
let value_d128 = D128::from_str(value_string, Context::default()).unwrap();
let number = Number::with_basic_unit(value_d128, unit);
let result = convert(number, vec![(to_unit, 1)]);
let string_result = &result.unwrap().value.to_string();
let float_result = f64::from_str(string_result).unwrap();
return float_result;
}
assert_float_eq!(convert_test(1000.0, Nanosecond, Microsecond), 1.0);
assert_float_eq!(convert_test(1000.0, Nanosecond, Microsecond), 1.0);
assert_float_eq!(convert_test(1000.0, Microsecond, Millisecond), 1.0);
assert_float_eq!(convert_test(1000.0, Millisecond, Second), 1.0);
assert_float_eq!(convert_test(60.0, Second, Minute), 1.0);
assert_float_eq!(convert_test(60.0, Minute, Hour), 1.0);
assert_float_eq!(convert_test(24.0, Hour, Day), 1.0);
assert_float_eq!(convert_test(7.0, Day, Week), 1.0);
assert_float_eq!(convert_test(30.436875, Day, Month), 1.0);
assert_float_eq!(convert_test(3.0, Month, Quarter), 1.0);
assert_float_eq!(convert_test(4.0, Quarter, Year), 1.0);
assert_float_eq!(convert_test(10.0, Year, Decade), 1.0);
assert_float_eq!(convert_test(10.0, Decade, Century), 1.0);
assert_float_eq!(convert_test(10.0, Century, Millennium), 1.0);
assert_float_eq!(convert_test(10.0, Millimeter, Centimeter), 1.0);
assert_float_eq!(convert_test(10.0, Centimeter, Decimeter), 1.0);
assert_float_eq!(convert_test(10.0, Decimeter, Meter), 1.0);
assert_float_eq!(convert_test(1000.0, Meter, Kilometer), 1.0);
assert_float_eq!(convert_test(2.54, Centimeter, Inch), 1.0);
assert_float_eq!(convert_test(12.0, Inch, Foot), 1.0);
assert_float_eq!(convert_test(3.0, Foot, Yard), 1.0);
assert_float_eq!(convert_test(1760.0, Yard, Mile), 1.0);
assert_float_eq!(convert_test(42195.0, Meter, Marathon), 1.0);
assert_float_eq!(convert_test(1852.0, Meter, NauticalMile), 1.0);
assert_float_eq!(convert_test(9460730472580800.0, Meter, LightYear), 1.0);
assert_float_eq!(convert_test(299792458.0, Meter, LightSecond), 1.0);
assert_float_eq!(convert_test(100.0, SquareMillimeter, SquareCentimeter), 1.0);
assert_float_eq!(convert_test(100.0, SquareCentimeter, SquareDecimeter), 1.0);
assert_float_eq!(convert_test(100.0, SquareDecimeter, SquareMeter), 1.0);
assert_float_eq!(convert_test(1000000.0, SquareMeter, SquareKilometer), 1.0);
assert_float_eq!(convert_test(645.16, SquareMillimeter, SquareInch), 1.0);
assert_float_eq!(convert_test(144.0, SquareInch, SquareFoot), 1.0);
assert_float_eq!(convert_test(9.0, SquareFoot, SquareYard), 1.0);
assert_float_eq!(convert_test(3097600.0, SquareYard, SquareMile), 1.0);
assert_float_eq!(convert_test(100.0, SquareMeter, Are), 1.0);
assert_float_eq!(convert_test(10.0, Are, Decare), 1.0);
assert_float_eq!(convert_test(10.0, Decare, Hectare), 1.0);
assert_float_eq!(convert_test(640.0, Acre, SquareMile), 1.0);
assert_float_eq!(convert_test(1000.0, CubicMillimeter, CubicCentimeter), 1.0);
assert_float_eq!(convert_test(1000.0, CubicCentimeter, CubicDecimeter), 1.0);
assert_float_eq!(convert_test(1000.0, CubicDecimeter, CubicMeter), 1.0);
assert_float_eq!(convert_test(1000000000.0, CubicMeter, CubicKilometer), 1.0);
assert_float_eq!(convert_test(1728.0, CubicInch, CubicFoot), 1.0);
assert_float_eq!(convert_test(27.0, CubicFoot, CubicYard), 1.0);
assert_float_eq!(convert_test(5451776000.0, CubicYard, CubicMile), 1.0);
assert_float_eq!(convert_test(1.0, Milliliter, CubicCentimeter), 1.0);
assert_float_eq!(convert_test(10.0, Milliliter, Centiliter), 1.0);
assert_float_eq!(convert_test(10.0, Centiliter, Deciliter), 1.0);
assert_float_eq!(convert_test(10.0, Deciliter, Liter), 1.0);
assert_float_eq!(convert_test(4.92892159375, Milliliter, Teaspoon), 1.0);
assert_float_eq!(convert_test(3.0, Teaspoon, Tablespoon), 1.0);
assert_float_eq!(convert_test(2.0, Tablespoon, FluidOunce), 1.0);
assert_float_eq!(convert_test(8.0, FluidOunce, Cup), 1.0);
assert_float_eq!(convert_test(2.0, Cup, Pint), 1.0);
assert_float_eq!(convert_test(2.0, Pint, Quart), 1.0);
assert_float_eq!(convert_test(4.0, Quart, Gallon), 1.0);
assert_float_eq!(convert_test(42.0, Gallon, OilBarrel), 1.0);
assert_float_eq!(convert_test(1000.0, Milligram, Gram), 1.0);
assert_float_eq!(convert_test(100.0, Gram, Hectogram), 1.0);
assert_float_eq!(convert_test(1000.0, Gram, Kilogram), 1.0);
assert_float_eq!(convert_test(1000.0, Kilogram, MetricTon), 1.0);
assert_float_eq!(convert_test(0.45359237, Kilogram, Pound), 1.0);
assert_float_eq!(convert_test(16.0, Ounce, Pound), 1.0);
assert_float_eq!(convert_test(14.0, Pound, Stone), 1.0);
assert_float_eq!(convert_test(2000.0, Pound, ShortTon), 1.0);
assert_float_eq!(convert_test(2240.0, Pound, LongTon), 1.0);
assert_float_eq!(convert_test(1000.0, Bit, Kilobit), 1.0);
assert_float_eq!(convert_test(1000.0, Kilobit, Megabit), 1.0);
assert_float_eq!(convert_test(1000.0, Megabit, Gigabit), 1.0);
assert_float_eq!(convert_test(1000.0, Gigabit, Terabit), 1.0);
assert_float_eq!(convert_test(1000.0, Terabit, Petabit), 1.0);
assert_float_eq!(convert_test(1000.0, Petabit, Exabit), 1.0);
assert_float_eq!(convert_test(1000.0, Exabit, Zettabit), 1.0);
assert_float_eq!(convert_test(1000.0, Zettabit, Yottabit), 1.0);
assert_float_eq!(convert_test(1024.0, Bit, Kibibit), 1.0);
assert_float_eq!(convert_test(1024.0, Kibibit, Mebibit), 1.0);
assert_float_eq!(convert_test(1024.0, Mebibit, Gibibit), 1.0);
assert_float_eq!(convert_test(1024.0, Gibibit, Tebibit), 1.0);
assert_float_eq!(convert_test(1024.0, Tebibit, Pebibit), 1.0);
assert_float_eq!(convert_test(1024.0, Pebibit, Exbibit), 1.0);
assert_float_eq!(convert_test(1024.0, Exbibit, Zebibit), 1.0);
assert_float_eq!(convert_test(1024.0, Zebibit, Yobibit), 1.0);
assert_float_eq!(convert_test(8.0, Bit, Byte), 1.0);
assert_float_eq!(convert_test(1000.0, Byte, Kilobyte), 1.0);
assert_float_eq!(convert_test(1000.0, Kilobyte, Megabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Megabyte, Gigabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Gigabyte, Terabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Terabyte, Petabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Petabyte, Exabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Exabyte, Zettabyte), 1.0);
assert_float_eq!(convert_test(1000.0, Zettabyte, Yottabyte), 1.0);
assert_float_eq!(convert_test(1024.0, Kibibyte, Mebibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Mebibyte, Gibibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Gibibyte, Tebibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Tebibyte, Pebibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Pebibyte, Exbibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Exbibyte, Zebibyte), 1.0);
assert_float_eq!(convert_test(1024.0, Zebibyte, Yobibyte), 1.0);
assert_float_eq!(convert_test(1000.0, BitsPerSecond, KilobitsPerSecond), 1.0);
assert_float_eq!(
convert_test(1000.0, KilobitsPerSecond, MegabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, MegabitsPerSecond, GigabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, GigabitsPerSecond, TerabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, TerabitsPerSecond, PetabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, PetabitsPerSecond, ExabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ExabitsPerSecond, ZettabitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ZettabitsPerSecond, YottabitsPerSecond),
1.0
);
assert_float_eq!(convert_test(1024.0, BitsPerSecond, KibibitsPerSecond), 1.0);
assert_float_eq!(
convert_test(1024.0, KibibitsPerSecond, MebibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, MebibitsPerSecond, GibibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, GibibitsPerSecond, TebibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, TebibitsPerSecond, PebibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, PebibitsPerSecond, ExbibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, ExbibitsPerSecond, ZebibitsPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, ZebibitsPerSecond, YobibitsPerSecond),
1.0
);
assert_float_eq!(convert_test(8.0, BitsPerSecond, BytesPerSecond), 1.0);
assert_float_eq!(
convert_test(1000.0, BytesPerSecond, KilobytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, KilobytesPerSecond, MegabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, MegabytesPerSecond, GigabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, GigabytesPerSecond, TerabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, TerabytesPerSecond, PetabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, PetabytesPerSecond, ExabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ExabytesPerSecond, ZettabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ZettabytesPerSecond, YottabytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, KibibytesPerSecond, MebibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, MebibytesPerSecond, GibibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, GibibytesPerSecond, TebibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, TebibytesPerSecond, PebibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, PebibytesPerSecond, ExbibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, ExbibytesPerSecond, ZebibytesPerSecond),
1.0
);
assert_float_eq!(
convert_test(1024.0, ZebibytesPerSecond, YobibytesPerSecond),
1.0
);
assert_float_eq!(convert_test(1000.0, Flop, KiloFlop), 1.0);
assert_float_eq!(convert_test(1000.0, KiloFlop, MegaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, MegaFlop, GigaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, GigaFlop, TeraFlop), 1.0);
assert_float_eq!(convert_test(1000.0, TeraFlop, PetaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, PetaFlop, ExaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, ExaFlop, ZettaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, ZettaFlop, YottaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, YottaFlop, RonnaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, RonnaFlop, QuettaFlop), 1.0);
assert_float_eq!(convert_test(1000.0, FlopPerSecond, KiloFlopPerSecond), 1.0);
assert_float_eq!(
convert_test(1000.0, KiloFlopPerSecond, MegaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, MegaFlopPerSecond, GigaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, GigaFlopPerSecond, TeraFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, TeraFlopPerSecond, PetaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, PetaFlopPerSecond, ExaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ExaFlopPerSecond, ZettaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, ZettaFlopPerSecond, YottaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, YottaFlopPerSecond, RonnaFlopPerSecond),
1.0
);
assert_float_eq!(
convert_test(1000.0, RonnaFlopPerSecond, QuettaFlopPerSecond),
1.0
);
assert_float_eq!(convert_test(1000.0, Millijoule, Joule), 1.0);
assert_float_eq!(convert_test(1000.0, Joule, Kilojoule), 1.0);
assert_float_eq!(convert_test(1.0, NewtonMeter, Joule), 1.0);
assert_float_eq!(convert_test(1000.0, Kilojoule, Megajoule), 1.0);
assert_float_eq!(convert_test(1000.0, Megajoule, Gigajoule), 1.0);
assert_float_eq!(convert_test(1000.0, Gigajoule, Terajoule), 1.0);
assert_float_eq!(convert_test(4.1868, Joule, Calorie), 1.0);
assert_float_eq!(convert_test(1000.0, Calorie, KiloCalorie), 1.0);
assert_float_eq!(convert_test(1055.05585262, Joule, BritishThermalUnit), 1.0);
assert_float_eq!(convert_test(3600.0, Joule, WattHour), 1.0);
assert_float_eq!(convert_test(1000.0, WattHour, KilowattHour), 1.0);
assert_float_eq!(convert_test(1000.0, KilowattHour, MegawattHour), 1.0);
assert_float_eq!(convert_test(1000.0, MegawattHour, GigawattHour), 1.0);
assert_float_eq!(convert_test(1000.0, GigawattHour, TerawattHour), 1.0);
assert_float_eq!(convert_test(1000.0, TerawattHour, PetawattHour), 1.0);
assert_float_eq!(convert_test(1000.0, Milliwatt, Watt), 1.0);
assert_float_eq!(convert_test(1000.0, Watt, Kilowatt), 1.0);
assert_float_eq!(convert_test(1000.0, Kilowatt, Megawatt), 1.0);
assert_float_eq!(convert_test(1000.0, Megawatt, Gigawatt), 1.0);
assert_float_eq!(convert_test(1000.0, Gigawatt, Terawatt), 1.0);
assert_float_eq!(convert_test(1000.0, Terawatt, Petawatt), 1.0);
assert_float_eq!(
convert_test(
17.5842642103333333333333333333333333334,
Watt,
BritishThermalUnitsPerMinute
),
1.0
);
assert_float_eq!(
convert_test(
60.0,
BritishThermalUnitsPerHour,
BritishThermalUnitsPerMinute
),
1.0
);
assert_float_eq!(convert_test(745.6998715822702, Watt, Horsepower), 1.0);
assert_float_eq!(convert_test(735.49875, Watt, MetricHorsepower), 1.0);
assert_float_eq!(convert_test(1000.0, Milliampere, Ampere), 1.0);
assert_float_eq!(convert_test(1000.0, Ampere, Kiloampere), 1.0);
assert_float_eq!(convert_test(10.0, Ampere, Abampere), 1.0);
assert_float_eq!(convert_test(1000.0, Milliohm, Ohm), 1.0);
assert_float_eq!(convert_test(1000.0, Ohm, Kiloohm), 1.0);
assert_float_eq!(convert_test(1000.0, Millivolt, Volt), 1.0);
assert_float_eq!(convert_test(1000.0, Volt, Kilovolt), 1.0);
assert_float_eq!(convert_test(1000.0, Pascal, Kilopascal), 1.0);
assert_float_eq!(convert_test(101325.0, Pascal, Atmosphere), 1.0);
assert_float_eq!(convert_test(100.0, Pascal, Millibar), 1.0);
assert_float_eq!(convert_test(1000.0, Millibar, Bar), 1.0);
assert_float_eq!(convert_test(3386.389, Pascal, InchOfMercury), 1.0);
assert_float_eq!(
convert_test(6894.757293168361, Pascal, PoundsPerSquareInch),
1.0
);
assert_float_eq!(
convert_test(133.322368421052631578947368421052631579, Pascal, Torr),
1.0
);
assert_float_eq!(convert_test(1000.0, Hertz, Kilohertz), 1.0);
assert_float_eq!(convert_test(1000.0, Kilohertz, Megahertz), 1.0);
assert_float_eq!(convert_test(1000.0, Megahertz, Gigahertz), 1.0);
assert_float_eq!(convert_test(1000.0, Gigahertz, Terahertz), 1.0);
assert_float_eq!(convert_test(1000.0, Terahertz, Petahertz), 1.0);
assert_float_eq!(convert_test(60.0, Hertz, RevolutionsPerMinute), 1.0);
assert_float_eq!(convert_test(0.3048, MetersPerSecond, FeetPerSecond), 1.0);
assert_float_eq!(convert_test(274.15, Kelvin, Celsius), 1.0);
assert_float_eq!(convert_test(300.0, Kelvin, Fahrenheit), 80.33);
assert_float_eq!(convert_test(-272.15, Celsius, Kelvin), 1.0);
assert_float_eq!(convert_test(-15.0, Celsius, Fahrenheit), 5.0);
assert_float_eq!(convert_test(80.33, Fahrenheit, Kelvin), 300.0);
assert_float_eq!(convert_test(5.0, Fahrenheit, Celsius), -15.0);
}
#[track_caller]
fn eval_test(a: &str, b: &str) {
let result_a = crate::eval(a, true, false).unwrap();
let result_b = crate::eval(b, true, false).unwrap();
assert_eq!(result_a, result_b, "{a} != {b}");
}
#[track_caller]
fn eval_approx_test(a: &str, b: &str) {
let result_a = crate::eval(a, true, false).unwrap();
let result_b = crate::eval(b, true, false).unwrap();
let diff_pct: D128 = result_a.value / result_b.value - 1;
let diff_pct = diff_pct.abs();
assert!(diff_pct <= d!(0.00000001), "{a} !≈ {b}")
}
#[test]
fn test_unit_evals() {
eval_test("100kg*sqm / 2s^2", "50j");
eval_test("3.6km/1h", "3.6 kph");
eval_test("0.3048 m/s to ft/s", "1 ft/s");
eval_test("1.609344 km/1h to mph", "1 mph");
eval_approx_test("1.852 kph to knots", "1 knots");
eval_test("120 seconds to minutes", "2 minutes");
eval_test("100 cm to m", "1 m");
eval_test("1 km2 to m2", "1000000 m2");
eval_test("1 liter to ml", "1000 ml");
eval_test("1 kg to g", "1000 g");
eval_test("1 KB to bytes", "1000 bytes");
eval_test("1 MBps to KBps", "1000 KBps");
eval_test("1 KFLOP to FLOP", "1000 FLOP");
eval_test("1 KFLOPs to FLOPs", "1000 FLOPs");
eval_test("1 kWh to Wh", "1000 Wh");
eval_test("1 kW to W", "1000 W");
eval_test("1000 mA to A", "1 A");
eval_test("1000 mΩ to Ω", "1 Ω");
eval_test("1000 mV to V", "1 V");
eval_test("1 bar to Pa", "100000 Pa");
eval_test("1 kHz to Hz", "1000 Hz");
eval_approx_test("1 km/h to m/s", "0.27777777777777777777 m/s");
eval_test("0 C to K", "273.15 K");
eval_test("8 megabytes per second * 1 minute", "480mb");
eval_test("8 megaFLOP per second * 1 minute", "480megaFLOP");
}
}