mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-08 02:04:31 +08:00
feat: add temperature unit conversions (#5315)
Co-authored-by: Bama Charan Chhandogi <b.c.chhandogi@gmail.com>
This commit is contained in:
@ -0,0 +1,23 @@
|
|||||||
|
package com.thealgorithms.conversions;
|
||||||
|
|
||||||
|
public final class AffineConverter {
|
||||||
|
private final double slope;
|
||||||
|
private final double intercept;
|
||||||
|
public AffineConverter(final double inSlope, final double inIntercept) {
|
||||||
|
slope = inSlope;
|
||||||
|
intercept = inIntercept;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double convert(final double inValue) {
|
||||||
|
return slope * inValue + intercept;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AffineConverter invert() {
|
||||||
|
assert slope != 0.0;
|
||||||
|
return new AffineConverter(1.0 / slope, -intercept / slope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AffineConverter compose(final AffineConverter other) {
|
||||||
|
return new AffineConverter(slope * other.slope, slope * other.intercept + intercept);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.thealgorithms.conversions;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
public final class UnitConversions {
|
||||||
|
private UnitConversions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final UnitsConverter TEMPERATURE = new UnitsConverter(Map.ofEntries(entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15)), entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)),
|
||||||
|
entry(Pair.of("Réaumur", "Celsius"), new AffineConverter(5.0 / 4.0, 0.0)), entry(Pair.of("Delisle", "Celsius"), new AffineConverter(-2.0 / 3.0, 100.0)), entry(Pair.of("Rankine", "Kelvin"), new AffineConverter(5.0 / 9.0, 0.0))));
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.thealgorithms.conversions;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
public final class UnitsConverter {
|
||||||
|
private final Map<Pair<String, String>, AffineConverter> conversions;
|
||||||
|
private final Set<String> units;
|
||||||
|
|
||||||
|
private static void putIfNeeded(Map<Pair<String, String>, AffineConverter> conversions, final String inputUnit, final String outputUnit, final AffineConverter converter) {
|
||||||
|
if (!inputUnit.equals(outputUnit)) {
|
||||||
|
final var key = Pair.of(inputUnit, outputUnit);
|
||||||
|
conversions.putIfAbsent(key, converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Pair<String, String>, AffineConverter> addInversions(final Map<Pair<String, String>, AffineConverter> knownConversions) {
|
||||||
|
Map<Pair<String, String>, AffineConverter> res = new HashMap<Pair<String, String>, AffineConverter>();
|
||||||
|
for (final var curConversion : knownConversions.entrySet()) {
|
||||||
|
final var inputUnit = curConversion.getKey().getKey();
|
||||||
|
final var outputUnit = curConversion.getKey().getValue();
|
||||||
|
putIfNeeded(res, inputUnit, outputUnit, curConversion.getValue());
|
||||||
|
putIfNeeded(res, outputUnit, inputUnit, curConversion.getValue().invert());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Pair<String, String>, AffineConverter> addCompositions(final Map<Pair<String, String>, AffineConverter> knownConversions) {
|
||||||
|
Map<Pair<String, String>, AffineConverter> res = new HashMap<Pair<String, String>, AffineConverter>();
|
||||||
|
for (final var first : knownConversions.entrySet()) {
|
||||||
|
final var firstKey = first.getKey();
|
||||||
|
putIfNeeded(res, firstKey.getKey(), firstKey.getValue(), first.getValue());
|
||||||
|
for (final var second : knownConversions.entrySet()) {
|
||||||
|
final var secondKey = second.getKey();
|
||||||
|
if (firstKey.getValue().equals(secondKey.getKey())) {
|
||||||
|
final var newConversion = second.getValue().compose(first.getValue());
|
||||||
|
putIfNeeded(res, firstKey.getKey(), secondKey.getValue(), newConversion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Pair<String, String>, AffineConverter> addAll(final Map<Pair<String, String>, AffineConverter> knownConversions) {
|
||||||
|
final var res = addInversions(knownConversions);
|
||||||
|
return addCompositions(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Pair<String, String>, AffineConverter> computeAllConversions(final Map<Pair<String, String>, AffineConverter> basicConversions) {
|
||||||
|
var tmp = basicConversions;
|
||||||
|
var res = addAll(tmp);
|
||||||
|
while (res.size() != tmp.size()) {
|
||||||
|
tmp = res;
|
||||||
|
res = addAll(tmp);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> extractUnits(final Map<Pair<String, String>, AffineConverter> conversions) {
|
||||||
|
Set<String> res = new HashSet<>();
|
||||||
|
for (final var conversion : conversions.entrySet()) {
|
||||||
|
res.add(conversion.getKey().getKey());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnitsConverter(final Map<Pair<String, String>, AffineConverter> basicConversions) {
|
||||||
|
conversions = computeAllConversions(basicConversions);
|
||||||
|
units = extractUnits(conversions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double convert(final String inputUnit, final String outputUnit, final double value) {
|
||||||
|
if (inputUnit.equals(outputUnit)) {
|
||||||
|
throw new IllegalArgumentException("inputUnit must be different from outputUnit.");
|
||||||
|
}
|
||||||
|
final var conversionKey = Pair.of(inputUnit, outputUnit);
|
||||||
|
return conversions.get(conversionKey).convert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> availableUnits() {
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.thealgorithms.conversions;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
public class UnitConversionsTest {
|
||||||
|
private static void addData(Stream.Builder<Arguments> builder, Map<String, Double> values) {
|
||||||
|
for (final var first : values.entrySet()) {
|
||||||
|
for (final var second : values.entrySet()) {
|
||||||
|
if (!first.getKey().equals(second.getKey())) {
|
||||||
|
builder.add(Arguments.of(first.getKey(), second.getKey(), first.getValue(), second.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> temperatureData() {
|
||||||
|
final Map<String, Double> boilingPointOfWater = Map.ofEntries(entry("Celsius", 99.9839), entry("Fahrenheit", 211.97102), entry("Kelvin", 373.1339), entry("Réaumur", 79.98712), entry("Delisle", 0.02415), entry("Rankine", 671.64102));
|
||||||
|
|
||||||
|
final Map<String, Double> freezingPointOfWater = Map.ofEntries(entry("Celsius", 0.0), entry("Fahrenheit", 32.0), entry("Kelvin", 273.15), entry("Réaumur", 0.0), entry("Delisle", 150.0), entry("Rankine", 491.67));
|
||||||
|
|
||||||
|
Stream.Builder<Arguments> builder = Stream.builder();
|
||||||
|
addData(builder, boilingPointOfWater);
|
||||||
|
addData(builder, freezingPointOfWater);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("temperatureData")
|
||||||
|
void testTemperature(String inputUnit, String outputUnit, double value, double expected) {
|
||||||
|
final double result = UnitConversions.TEMPERATURE.convert(inputUnit, outputUnit, value);
|
||||||
|
assertEquals(expected, result, 0.00001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTemperatureUnits() {
|
||||||
|
final Set<String> expectedUnits = Set.of("Celsius", "Fahrenheit", "Kelvin", "Réaumur", "Rankine", "Delisle");
|
||||||
|
assertEquals(expectedUnits, UnitConversions.TEMPERATURE.availableUnits());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.thealgorithms.conversions;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class UnitsConverterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConvertThrowsForSameUnits() {
|
||||||
|
final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("A", "B"), new AffineConverter(10.0, -20.0))));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> someConverter.convert("A", "A", 20.0));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> someConverter.convert("B", "B", 20.0));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user