Skip to content

Commit

Permalink
Fix parsing of Roman numerals in issue number
Browse files Browse the repository at this point in the history
  • Loading branch information
proninyaroslav committed Jan 17, 2024
1 parent 79419fd commit 4378ec3
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 15 deletions.
59 changes: 49 additions & 10 deletions app/src/main/java/org/proninyaroslav/opencomicvine/model/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,20 @@ fun issuesCount(firstIssueNumber: String?, lastIssueNumber: String?): Int? {
return 1
}

val firstIssue = if (isSupplement(firstIssueNumber)) {
firstIssueNumber?.replace(numberExtractRegex, "")?.toInt()?.plus(1)
} else {
firstIssueNumber?.toInt()
val toInteger = { str: String? ->
if (str == null) {
null
} else if (isSupplement(str)) {
str.replace(numberExtractRegex, "").toInt() + 1
} else if (str.isRomanNumeral()) {
str.romanToArabic()
} else {
str.toInt()
}
}

val lastIssue = if (isSupplement(lastIssueNumber)) {
lastIssueNumber?.replace(numberExtractRegex, "")?.toInt()?.plus(1)
} else {
lastIssueNumber?.toInt()
}
val firstIssue = toInteger(firstIssueNumber?.trim())
val lastIssue = toInteger(lastIssueNumber?.trim())

return if (firstIssue != null && lastIssue != null) {
lastIssue - firstIssue + 1
Expand All @@ -96,4 +99,40 @@ fun issuesCount(firstIssueNumber: String?, lastIssueNumber: String?): Int? {
// At least there are one non-null issue
1
}
}
}

private val romanCharToValue = mapOf(
'I' to 1,
'V' to 5,
'X' to 10,
'L' to 50,
'C' to 100,
'D' to 500,
'M' to 1000,
)
private const val romanNumeralRegex = "^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"

fun String.romanToArabic(): Int {
var arabicValue = 0
var lastValue = 0

uppercase().forEach { char ->
val value =
romanCharToValue[char] ?: throw IllegalArgumentException("Invalid Roman numeral: $char")

// If last value is less than current, that means we need to subtract the last value
// as it was added previously, then we add the current value minus last value.
// For example: IV = 5 - 2*1 = 4 (since I was added previously, we subtract it twice).
arabicValue += if (lastValue < value) {
value - 2 * lastValue
} else {
value
}
lastValue = value
}

return arabicValue
}

fun String.isRomanNumeral(): Boolean =
isNotEmpty() && uppercase().matches(romanNumeralRegex.toRegex())
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package org.proninyaroslav.opencomicvine.types
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.proninyaroslav.opencomicvine.model.issuesCount
import java.util.*
import java.util.Date

sealed interface SearchInfo {
val id: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import androidx.compose.runtime.Immutable
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.proninyaroslav.opencomicvine.model.issuesCount
import java.util.*
import java.util.Date

@JsonClass(generateAdapter = true)
@Immutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import androidx.room.Ignore
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.proninyaroslav.opencomicvine.model.issuesCount
import java.util.*
import java.util.Date

@JsonClass(generateAdapter = true)
@Immutable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.proninyaroslav.opencomicvine.model

import org.junit.Assert.*

import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test

class UtilsTest {
Expand Down Expand Up @@ -49,5 +52,36 @@ class UtilsTest {
}

assertEquals(6, issuesCount("1", "5 Suppl."))

assertEquals(3, issuesCount("1 ", "3 \n"))

assertEquals(5, issuesCount("I", "V"))
}

@Test
fun isRomanNumeral() {
val validRoman = "MCMXCIV"
val invalidRoman = "MCMZ"

assertTrue(validRoman.isRomanNumeral())

assertFalse(invalidRoman.isRomanNumeral())

assertFalse("".isRomanNumeral())

assertFalse("ABC".isRomanNumeral())

assertFalse("123".isRomanNumeral())
}

@Test
fun romanToArabic() {
assertEquals(1994, "MCMXCIV".romanToArabic())

assertThrows(IllegalArgumentException::class.java) { "".romanToArabic() }

assertThrows(IllegalArgumentException::class.java) { "ABC".romanToArabic() }

assertThrows(IllegalArgumentException::class.java) { "123".romanToArabic() }
}
}

0 comments on commit 4378ec3

Please sign in to comment.