//https://www.codeproject.com/Articles/311333/A-Csharp-Temperature-Struct
namespace FoodFactory;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
///
/// Options for temperature measurement units.
///
public enum TemperatureUnit
{
///
/// The SI base unit of thermodynamic temperature,
/// equal in magnitude to the degree Celsius.
///
Kelvin,
///
/// A scale of temperature on which water freezes
/// at 0° and boils at 100° under standard conditions.
///
Celsius,
///
/// A scale of temperature on which water freezes at 32°
/// and boils at 212° under standard conditions.
///
Fahrenheit
}
///
/// A temperature value.
///
public struct Temperature : IFormattable, IComparable,
IComparable, IEquatable,
IAdditionOperators,
ISubtractionOperators,
ISubtractionOperators,
IEqualityOperators,
IComparisonOperators
{
private double _kelvin;
///
/// Creates a new temperature with the specified value in Kelvin.
///
/// The value of the temperature.
public Temperature(double kelvin) : this() { _kelvin = kelvin; }
///
/// Creates a new temperature with the specified value in the
/// specified unit of measurement.
///
/// The value of the temperature.
/// The unit of measurement that defines how
/// the value is used.
public Temperature(double temperature, TemperatureUnit unit)
: this()
{
switch (unit)
{
case TemperatureUnit.Kelvin:
_kelvin = temperature;
break;
case TemperatureUnit.Celsius:
Celsius = temperature;
break;
case TemperatureUnit.Fahrenheit:
Fahrenheit = temperature;
break;
default:
throw new ArgumentException(
"The temperature unit '" + unit.ToString() + "' is unknown.");
}
}
///
/// Gets or sets the temperature value in Kelvin.
///
public double Kelvin
{
readonly get => _kelvin;
set => _kelvin = value;
}
///
/// Gets or sets the temperature value in Celsius.
///
public double Celsius
{
readonly get => KelvinToCelsius(_kelvin); set => _kelvin = CelsiusToKelvin(value);
}
///
/// Gets or sets the temperature value in Fahrenheit.
///
public double Fahrenheit
{
readonly get => KelvinToFahrenheit(_kelvin); set => _kelvin = FahrenheitToKelvin(value);
}
///
/// Gets the temperature value in the specified unit of measurement.
///
/// The unit of measurement
/// in which the temperature should be retrieved.
/// The temperature value in the specified
/// .
public readonly double ValueIn(TemperatureUnit unit) => unit switch
{
TemperatureUnit.Kelvin => _kelvin,
TemperatureUnit.Celsius => Celsius,
TemperatureUnit.Fahrenheit => Fahrenheit,
_ => throw new ArgumentException(
"Unknown temperature unit '" + unit.ToString() + "'."),
};
///
/// Returns a string representation of the temperature value.
///
///
/// A single format specifier that indicates how to format the value of this
/// temperature. The format parameter can be "G",
/// "C", "F", or "K". If format
/// is null or the empty string (""), "G" is used.
///
///
/// An IFormatProvider reference that supplies culture-specific formatting
/// services.
///
/// A string representation of the temperature.
///
/// The value of format is not null, the empty string (""), "G", "C", "F", or
/// "K".
///
public readonly string ToString(string? format, IFormatProvider? provider)
{
if (string.IsNullOrEmpty(format))
{
format = "G";
}
provider ??= CultureInfo.CurrentCulture;
return format switch
{
"G" or "C" or "g" or "c" => Celsius.ToString("F2", provider) + " °C",
"F" or "f" => Fahrenheit.ToString("F2", provider) + " °F",
"K" or "k" => _kelvin.ToString("F2", provider) + " K",
_ => throw new FormatException(
string.Format("The {0} format string is not supported.", format)),
};
}
///
/// Returns a string representation of the temperature value.
///
///
/// A single format specifier that indicates how to format the value of this
/// temperature. The format parameter can be "G",
/// "C", "F", or "K". If format
/// is null or the empty string (""), "G" is used.
///
/// A string representation of the temperature.
///
/// The value of format is not null,
/// the empty string (""), "G", "C", "F", or
/// "K".
///
public readonly string ToString(string format) => ToString(format, null);
///
/// Returns a string representation of the temperature value.
///
/// A string representation of the temperature.
public override readonly string ToString() => ToString(null, null);
///
/// Returns a string representation of the temperature value.
///
///
/// The temperature unit as which the temperature value should be displayed.
///
///
/// An IFormatProvider reference that supplies culture-specific formatting
/// services.
///
/// A string representation of the temperature.
public readonly string ToString(TemperatureUnit unit, IFormatProvider? provider)
{
return unit switch
{
TemperatureUnit.Celsius => ToString("C", provider),
TemperatureUnit.Fahrenheit => ToString("F", provider),
TemperatureUnit.Kelvin => ToString("K", provider),
_ => throw new FormatException("The temperature unit '" +
unit.ToString() + "' is unknown."),
};
}
///
/// Returns a string representation of the temperature value.
///
///
/// The temperature unit as which the temperature value should be displayed.
///
/// A string representation of the temperature.
public readonly string ToString(TemperatureUnit unit) => ToString(unit, null);
///
/// Compares this instance to a specified Temperature object and returns an indication
/// of their relative values.
///
/// A Temperature object
/// to compare to this instance.
///
/// A signed number indicating the relative values of this instance and value.
/// Value Description A negative integer This instance is less than value. Zero
/// This instance is equal to value. A positive integer This instance is greater
/// than value.
///
public readonly int CompareTo(Temperature value) => _kelvin.CompareTo(value._kelvin);
///
/// Compares this instance to a specified object and returns an indication of
/// their relative values.
///
/// An object to compare, or null.
///
/// A signed number indicating the relative values of this instance and value.
/// Value Description A negative integer This instance is less than value. Zero
/// This instance is equal to value. A positive integer This instance is greater
/// than value, or value is null.
///
///
/// The value is not a Temperature.
///
public readonly int CompareTo(object? value)
{
if (value == null)
{
return 1;
}
if (value is not Temperature)
{
throw new ArgumentException($"Can not compare {value.GetType()} and {GetType()}");
}
return CompareTo((Temperature)value);
}
///
/// Determines whether or not the given temperature is considered equal to this instance.
///
/// The temperature to compare to this instance.
/// True if the temperature is considered equal
/// to this instance. Otherwise, false.
public readonly bool Equals(Temperature value) => _kelvin == value._kelvin;
///
/// Determines whether or not the given object is considered equal to the temperature.
///
/// The object to compare to the temperature.
/// True if the object is considered equal
/// to the temperature. Otherwise, false.
public override readonly bool Equals([NotNullWhen(true)] object? value) => value is Temperature temp && Equals(temp);
///
/// Returns the hash code for this instance.
///
/// A 32-bit signed integer hash code.
public override readonly int GetHashCode() => _kelvin.GetHashCode();
///
/// Determines the equality of two temperatures.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
/// True if the temperatures are equal. Otherwise, false.
public static bool operator ==(Temperature t1, Temperature t2) => t1.Equals(t2);
///
/// Determines the inequality of two temperatures.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
/// True if the temperatures are NOT equal. Otherwise, false.
public static bool operator !=(Temperature t1, Temperature t2) => !t1.Equals(t2);
///
/// Determines whether one temperature is considered greater than another.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
/// True if the first temperature is greater than the second.
/// Otherwise, false.
public static bool operator >(Temperature t1, Temperature t2) => t1._kelvin > t2._kelvin;
///
/// Determines whether one temperature is considered less than another.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
/// True if the first temperature is less than the second.
/// Otherwise, false.
public static bool operator <(Temperature t1, Temperature t2) => t1._kelvin < t2._kelvin;
///
/// Determines whether one temperature is considered greater to or equal to another.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
///
/// True if the first temperature is greater to or equal to the second. Otherwise, false.
///
public static bool operator >=(Temperature t1, Temperature t2) => t1._kelvin >= t2._kelvin;
///
/// Determines whether one temperature is considered less than or equal to another.
///
/// The first temperature to be compared.
/// The second temperature to be compared.
///
/// True if the first temperature is less than or equal to the second. Otherwise, false.
///
public static bool operator <=(Temperature t1, Temperature t2) => t1._kelvin <= t2._kelvin;
///
/// Adds two instances of the temperature object.
///
/// The temperature on the left-hand side of the operator.
///
/// The temperature on the right-hand side of the operator.
///
/// The sum of the two temperatures.
public static Temperature operator +(Temperature t1, TemperatureDelta t2) => new(t1._kelvin + t2.KelvinDelta);
///
/// Subtracts one instance from another.
///
/// The temperature on the left-hand side of the operator.
///
/// The temperature on the right-hand side of the operator.
///
/// The difference of the two temperatures.
public static TemperatureDelta operator -(Temperature t1, Temperature t2) => new(t1._kelvin - t2._kelvin);
public static TemperatureDelta operator -(Temperature left, TemperatureDelta right) => new(left._kelvin - right.KelvinDelta);
///
/// Converts a Kelvin temperature value to Celsius.
///
/// The Kelvin value to convert to Celsius.
///
/// The Kelvin value in Celsius.
public static double KelvinToCelsius(double kelvin) => kelvin - 273.15;
///
/// Converts a Celsius value to Kelvin.
///
/// The Celsius value to convert to Kelvin.
///
/// The Celsius value in Kelvin.
public static double CelsiusToKelvin(double celsius) => celsius + 273.15;
///
/// Converts a Kelvin value to Fahrenheit.
///
/// The Kelvin value to convert to Fahrenheit.
///
/// The Kelvin value in Fahrenheit.
public static double KelvinToFahrenheit(double kelvin) => (kelvin * 9 / 5) - 459.67;
///
/// Converts a Fahrenheit value to Kelvin.
///
/// The Fahrenheit value to convert to Kelvin.
///
/// The Fahrenheit value in Kelvin.
public static double FahrenheitToKelvin(double fahrenheit) => (fahrenheit + 459.67) * 5 / 9;
///
/// Converts a Fahrenheit value to Celsius.
///
/// The Fahrenheit value to convert to Celsius.
///
/// The Fahrenheit value in Celsius.
public static double FahrenheitToCelsius(double fahrenheit) => (fahrenheit - 32) * 5 / 9;
///
/// Converts a Celsius value to Fahrenheit.
///
/// The Celsius value to convert to Fahrenheit.
///
/// The Celsius value in Fahrenheit.
public static double CelsiusToFahrenheit(double celsius) => (celsius * 9 / 5) + 32;
}
public readonly struct TemperatureDelta : IFormattable, IComparable,
IComparable, IEquatable,
IComparisonOperators,
IEqualityOperators,
IAdditionOperators,
ISubtractionOperators,
IMultiplyOperators,
IDivisionOperators
{
private readonly double _kelvinDelta;
public TemperatureDelta(double kelvin)
{
_kelvinDelta = kelvin;
}
public readonly double KelvinDelta => _kelvinDelta;
public readonly double FahrenheitDelta => CelsiusToFahrenheitDelta(_kelvinDelta);
public readonly double CelsiusDelta => _kelvinDelta;
public TemperatureDelta(double temperature, TemperatureUnit unit)
{
_kelvinDelta = unit switch
{
TemperatureUnit.Celsius => temperature,
TemperatureUnit.Fahrenheit => FahrenheitToKelvinDelta(temperature),
TemperatureUnit.Kelvin => temperature,
_ => throw new ArgumentException($"Unit {unit} is not a valid {nameof(TemperatureUnit)}"),
};
}
public static double FahrenheitToKelvinDelta(double fahrenheit) => fahrenheit * 5 / 9;
public static double KelvinToFahrenheitDelta(double kelvin) => kelvin * 9 / 5;
public static double FahrenheitToCelsiusDelta(double fahrenheit) => FahrenheitToKelvinDelta(fahrenheit);
public static double CelsiusToFahrenheitDelta(double celsius) => KelvinToFahrenheitDelta(celsius);
public bool Equals(TemperatureDelta other) => _kelvinDelta == other._kelvinDelta;
public override bool Equals([NotNullWhen(true)] object? obj) => obj is TemperatureDelta delta && Equals(delta);
public override int GetHashCode() => _kelvinDelta.GetHashCode();
public readonly string ToString(string? format, IFormatProvider? provider)
{
if (string.IsNullOrEmpty(format))
{
format = "G";
}
provider ??= CultureInfo.CurrentCulture;
return format switch
{
"G" or "C" or "g" or "c" => CelsiusDelta.ToString("F2", provider) + " °C",
"F" or "f" => FahrenheitDelta.ToString("F2", provider) + " °F",
"K" or "k" => _kelvinDelta.ToString("F2", provider) + " K",
_ => throw new FormatException(
string.Format("The {0} format string is not supported.", format)),
};
}
public readonly string ToString(string format) => ToString(format, null);
public override string ToString() => ToString(null, null);
public readonly int CompareTo(TemperatureDelta value) => _kelvinDelta.CompareTo(value._kelvinDelta);
public readonly int CompareTo(object? value)
{
if (value == null)
{
return 1;
}
if (value is not TemperatureDelta)
{
throw new ArgumentException(
$"Object must be of type {nameof(TemperatureDelta)}",
nameof(value));
}
return CompareTo((TemperatureDelta)value);
}
public static TemperatureDelta operator +(TemperatureDelta left, TemperatureDelta right) => new(left._kelvinDelta + right._kelvinDelta);
public static TemperatureDelta operator -(TemperatureDelta left, TemperatureDelta right) => new(left._kelvinDelta - right._kelvinDelta);
public static TemperatureDelta operator *(TemperatureDelta left, double right) => new(left._kelvinDelta * right);
public static TemperatureDelta operator /(TemperatureDelta left, double right) => new(left._kelvinDelta / right);
public static bool operator >(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta > right._kelvinDelta;
public static bool operator >=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta >= right._kelvinDelta;
public static bool operator <(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta < right._kelvinDelta;
public static bool operator <=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta <= right._kelvinDelta;
public static bool operator ==(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta == right._kelvinDelta;
public static bool operator !=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta != right._kelvinDelta;
}