Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/main/java/kyu4/RomanNumerals.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,22 @@ public static String toRoman(int n) {
}

public static int fromRoman(String romanNumeral) {
if (romanNumeral == null) {
throw new IllegalArgumentException("Roman numeral must not be null");
}

// Traverse from right to left: add when symbol value is >= previous,
// subtract otherwise. This naturally applies the subtractive rule.
StringBuilder stringBuilderReverse = new StringBuilder(romanNumeral).reverse();
char[] chars = stringBuilderReverse.toString().toCharArray();
int result = 0;
long result = 0;
int previous = 0;
for (char c : chars
) {
Integer integer = MAP_FROM.get(c);
if (integer == null) {
throw new IllegalArgumentException("Unsupported Roman numeral character: " + c);
}
Comment thread
krotname marked this conversation as resolved.
if (integer >= previous) {
result += integer;
} else {
Expand All @@ -101,7 +108,7 @@ public static int fromRoman(String romanNumeral) {
previous = integer;
}

return result;
return Math.toIntExact(result);
}


Expand Down
19 changes: 19 additions & 0 deletions src/test/java/kyu4/RomanNumeralsTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package kyu4;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.stream.Stream;
import org.junit.jupiter.api.Tag;
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;
Expand All @@ -30,6 +32,19 @@ void shouldRoundTripCanonicalRomanNumerals(int value) {
assertEquals(value, RomanNumerals.fromRoman(RomanNumerals.toRoman(value)));
}

@ParameterizedTest
@MethodSource("invalidRomanNumerals")
void shouldRejectInvalidRomanNumerals(String romanNumeral) {
assertThrows(IllegalArgumentException.class, () -> RomanNumerals.fromRoman(romanNumeral));
}

@Test
void shouldRejectResultsOutsideIntegerRange() {
String romanNumeral = "M".repeat(2_147_484);

assertThrows(ArithmeticException.class, () -> RomanNumerals.fromRoman(romanNumeral));
}

private static Stream<Arguments> romanCases() {
return Stream.of(
Arguments.of(1, "I"),
Expand All @@ -45,4 +60,8 @@ private static Stream<Arguments> romanCases() {
Arguments.of(3_999, "MMMCMXCIX")
);
}

private static Stream<String> invalidRomanNumerals() {
return Stream.of((String) null, "A", "MCMX?");
}
}