Skip to content

Commit 50d2d74

Browse files
committed
Merge remote-tracking branch 'origin/bloom_improve' into bloom_improve
2 parents 88adc9e + 842f3a8 commit 50d2d74

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

src/main/java/com/thealgorithms/conversions/UnitConversions.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,47 @@
55
import java.util.Map;
66
import org.apache.commons.lang3.tuple.Pair;
77

8+
/**
9+
* A utility class to perform unit conversions between different measurement systems.
10+
*
11+
* <p>Currently, the class supports temperature conversions between several scales:
12+
* Celsius, Fahrenheit, Kelvin, Réaumur, Delisle, and Rankine.
13+
*
14+
* <h2>Example Usage</h2>
15+
* <pre>
16+
* double result = UnitConversions.TEMPERATURE.convert("Celsius", "Fahrenheit", 100.0);
17+
* // Output: 212.0 (Celsius to Fahrenheit conversion of 100°C)
18+
* </pre>
19+
*
20+
* <p>This class makes use of an {@link UnitsConverter} that handles the conversion logic
21+
* based on predefined affine transformations. These transformations include scaling factors
22+
* and offsets for temperature conversions.
23+
*
24+
* <h2>Temperature Scales Supported</h2>
25+
* <ul>
26+
* <li>Celsius</li>
27+
* <li>Fahrenheit</li>
28+
* <li>Kelvin</li>
29+
* <li>Réaumur</li>
30+
* <li>Delisle</li>
31+
* <li>Rankine</li>
32+
* </ul>
33+
*/
834
public final class UnitConversions {
935
private UnitConversions() {
1036
}
1137

38+
/**
39+
* A preconfigured instance of {@link UnitsConverter} for temperature conversions.
40+
* The converter handles conversions between the following temperature units:
41+
* <ul>
42+
* <li>Kelvin to Celsius</li>
43+
* <li>Celsius to Fahrenheit</li>
44+
* <li>Réaumur to Celsius</li>
45+
* <li>Delisle to Celsius</li>
46+
* <li>Rankine to Kelvin</li>
47+
* </ul>
48+
*/
1249
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)),
1350
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))));
1451
}

src/main/java/com/thealgorithms/conversions/UnitsConverter.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@
77
import java.util.Set;
88
import org.apache.commons.lang3.tuple.Pair;
99

10+
/**
11+
* A class that handles unit conversions using affine transformations.
12+
*
13+
* <p>The {@code UnitsConverter} allows converting values between different units using
14+
* pre-defined affine conversion formulas. Each conversion is represented by an
15+
* {@link AffineConverter} that defines the scaling and offset for the conversion.
16+
*
17+
* <p>For each unit, both direct conversions (e.g., Celsius to Fahrenheit) and inverse
18+
* conversions (e.g., Fahrenheit to Celsius) are generated automatically. It also computes
19+
* transitive conversions (e.g., Celsius to Kelvin via Fahrenheit if both conversions exist).
20+
*
21+
* <p>Key features include:
22+
* <ul>
23+
* <li>Automatic handling of inverse conversions (e.g., Fahrenheit to Celsius).</li>
24+
* <li>Compositional conversions, meaning if conversions between A -> B and B -> C exist,
25+
* it can automatically generate A -> C conversion.</li>
26+
* <li>Supports multiple unit systems as long as conversions are provided in pairs.</li>
27+
* </ul>
28+
*
29+
* <h2>Example Usage</h2>
30+
* <pre>
31+
* Map&lt;Pair&lt;String, String&gt;, AffineConverter&gt; basicConversions = Map.ofEntries(
32+
* entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)),
33+
* entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15))
34+
* );
35+
*
36+
* UnitsConverter converter = new UnitsConverter(basicConversions);
37+
* double result = converter.convert("Celsius", "Fahrenheit", 100.0);
38+
* // Output: 212.0 (Celsius to Fahrenheit conversion of 100°C)
39+
* </pre>
40+
*
41+
* <h2>Exception Handling</h2>
42+
* <ul>
43+
* <li>If the input unit and output unit are the same, an {@link IllegalArgumentException} is thrown.</li>
44+
* <li>If a conversion between the requested units does not exist, a {@link NoSuchElementException} is thrown.</li>
45+
* </ul>
46+
*/
1047
public final class UnitsConverter {
1148
private final Map<Pair<String, String>, AffineConverter> conversions;
1249
private final Set<String> units;
@@ -68,11 +105,29 @@ private static Set<String> extractUnits(final Map<Pair<String, String>, AffineCo
68105
return res;
69106
}
70107

108+
/**
109+
* Constructor for {@code UnitsConverter}.
110+
*
111+
* <p>Accepts a map of basic conversions and automatically generates inverse and
112+
* transitive conversions.
113+
*
114+
* @param basicConversions the initial set of unit conversions to add.
115+
*/
71116
public UnitsConverter(final Map<Pair<String, String>, AffineConverter> basicConversions) {
72117
conversions = computeAllConversions(basicConversions);
73118
units = extractUnits(conversions);
74119
}
75120

121+
/**
122+
* Converts a value from one unit to another.
123+
*
124+
* @param inputUnit the unit of the input value.
125+
* @param outputUnit the unit to convert the value into.
126+
* @param value the value to convert.
127+
* @return the converted value in the target unit.
128+
* @throws IllegalArgumentException if inputUnit equals outputUnit.
129+
* @throws NoSuchElementException if no conversion exists between the units.
130+
*/
76131
public double convert(final String inputUnit, final String outputUnit, final double value) {
77132
if (inputUnit.equals(outputUnit)) {
78133
throw new IllegalArgumentException("inputUnit must be different from outputUnit.");
@@ -81,6 +136,11 @@ public double convert(final String inputUnit, final String outputUnit, final dou
81136
return conversions.computeIfAbsent(conversionKey, k -> { throw new NoSuchElementException("No converter for: " + k); }).convert(value);
82137
}
83138

139+
/**
140+
* Retrieves the set of all units supported by this converter.
141+
*
142+
* @return a set of available units.
143+
*/
84144
public Set<String> availableUnits() {
85145
return units;
86146
}

src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
public class UnitConversionsTest {
1515
private static void addData(Stream.Builder<Arguments> builder, Map<String, Double> values) {
16-
for (final var first : values.entrySet()) {
17-
for (final var second : values.entrySet()) {
16+
for (var first : values.entrySet()) {
17+
for (var second : values.entrySet()) {
1818
if (!first.getKey().equals(second.getKey())) {
1919
builder.add(Arguments.of(first.getKey(), second.getKey(), first.getValue(), second.getValue()));
2020
}

src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.thealgorithms.conversions;
22

33
import static java.util.Map.entry;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertThrows;
56

67
import java.util.Map;
78
import java.util.NoSuchElementException;
9+
import java.util.Set;
810
import org.apache.commons.lang3.tuple.Pair;
911
import org.junit.jupiter.api.Test;
1012

@@ -24,4 +26,19 @@ void testConvertThrowsForUnknownUnits() {
2426
assertThrows(NoSuchElementException.class, () -> someConverter.convert("X", "A", 20.0));
2527
assertThrows(NoSuchElementException.class, () -> someConverter.convert("X", "Y", 20.0));
2628
}
29+
30+
@Test
31+
void testAvailableUnits() {
32+
final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)), entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15))));
33+
assertEquals(Set.of("Celsius", "Fahrenheit", "Kelvin"), someConverter.availableUnits());
34+
}
35+
36+
@Test
37+
void testInvertConversion() {
38+
final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("A", "B"), new AffineConverter(2.0, 5.0))));
39+
// Check conversion from A -> B
40+
assertEquals(25.0, someConverter.convert("A", "B", 10.0), 0.0001);
41+
// Check inverse conversion from B -> A
42+
assertEquals(10.0, someConverter.convert("B", "A", 25.0), 0.0001);
43+
}
2744
}

0 commit comments

Comments
 (0)