Skip to content

Instantly share code, notes, and snippets.

@edamico
Created November 2, 2011 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edamico/1333626 to your computer and use it in GitHub Desktop.
Save edamico/1333626 to your computer and use it in GitHub Desktop.
object RomanConverter {
val ENTRY_LIST = collection.immutable.List(1 -> "I", 5 -> "V", 10 -> "X", 50 -> "L", 100 -> "C", 500 -> "D", 1000 -> "M").reverse
val ENTRY_MAP = collection.immutable.Map[String, Int]("I" -> 1, "V" -> 5, "X" -> 10, "L" -> 50, "C" -> 100, "D" -> 500, "M" -> 1000)
/**
* Retrieves the number converted to Roman expression
*/
def convertToArabic(roman: String): Int = {
convertToArabic(roman.toList, 0)
}
def convertToArabic(roman: List[Char], previousValue: Int): Int = {
val currentValue = ENTRY_MAP.get(roman.head.toString()).get
roman match {
case head :: Nil =>
if (previousValue < currentValue && is10Power(previousValue)) {
return currentValue - previousValue
} else {
return currentValue + previousValue
}
case head :: tail =>
if (previousValue < currentValue && is10Power(previousValue)) {
return convertToArabic(roman.tail, currentValue) - previousValue
} else {
return convertToArabic(roman.tail, currentValue) + previousValue
}
case (_) => return -1
}
}
def convertToRoman(decimal: Int): String = {
return convertToRoman(decimal, 0)
}
/**
* Converts an arabic number to a roman representation
*/
private def convertToRoman(arabicNumber: Int, index: Int): String = {
if (index >= 0 && index > ENTRY_LIST.size - 1 || arabicNumber == 0) return ""
val entry = ENTRY_LIST(index)
val factor = arabicNumber / entry._1
if (factor < 1) {
val closestMinuend = retrieveClosestMinuend(arabicNumber)
val factorIncreased = (arabicNumber + closestMinuend) / entry._1
if (factorIncreased == 1) {
val rest = arabicNumber - (entry._1 - closestMinuend)
return convertToRoman(closestMinuend, index + 1) + entry._2 + convertToRoman(rest, index)
} else {
return convertToRoman(arabicNumber, index + 1)
}
} else {
if (factor <= 3) {
return entry._2 * (factor) + convertToRoman(arabicNumber - (factor * entry._1), index)
} else {
return entry._2 + convertToRoman(arabicNumber + entry._1, index - 1)
}
}
"ERROR"
}
/**
*
*/
def retrieveClosestMinuend(decimal: Int): Int = {
var result = 0
if (decimal > 1) {
val tenPowers = ENTRY_LIST.filter { entry => is10Power(entry._1) }
result = tenPowers.find { entry => decimal - entry._1 > 0 }.first._1
}
result
}
def is10Power(number: Double): Boolean = {
if (number == 0) return false
else
return (java.lang.Math.log10(number) - Math.floor(java.lang.Math.log10(number)) == 0)
}
}
class RomanTest extends FlatSpec with ShouldMatchers{
"1" should "be converted as I" in {
RomanConverter.convertToRoman(1) should equal("I")
}
"2" should "be converted as II" in {
RomanConverter.convertToRoman(2) should equal("II")
}
"3" should "be converted as III" in {
RomanConverter.convertToRoman(3) should equal("III")
}
"4" should "be converted as IV" in {
RomanConverter.convertToRoman(4) should equal("IV")
}
"5" should "be converted as V" in {
RomanConverter.convertToRoman(5) should equal("V")
}
"6" should "be converted as VI" in {
RomanConverter.convertToRoman(6) should equal("VI")
}
"7" should "be converted as VII" in {
RomanConverter.convertToRoman(7) should equal("VII")
}
"8" should "be converted as VIII" in {
RomanConverter.convertToRoman(8) should equal("VIII")
}
"9" should "be converted as IX" in {
RomanConverter.convertToRoman(9) should equal("IX")
}
"10" should "be converted as X" in {
RomanConverter.convertToRoman(10) should equal("X")
}
"39" should "be converted as XXXIX" in {
RomanConverter.convertToRoman(39) should equal("XXXIX")
}
"45" should "be converted as XLV" in {
RomanConverter.convertToRoman(45) should equal("XLV")
}
"50" should "be converted as L" in {
RomanConverter.convertToRoman(50) should equal("L")
}
"100" should "be converted as C" in {
RomanConverter.convertToRoman(100) should equal("C")
}
"666" should "be converted as DCLXVI" in {
RomanConverter.convertToRoman(666) should equal("DCLXVI")
}
"999" should "be converted as CMXCIX" in {
RomanConverter.convertToRoman(999) should equal("CMXCIX")
}
"1444" should "be converted as MCDXLIV" in {
RomanConverter.convertToRoman(1444) should equal("MCDXLIV")
}
"2008" should "be converted as MMVIII" in {
RomanConverter.convertToRoman(2008) should equal("MMVIII")
}
"I" should "be converted as 1" in {
RomanConverter.convertToArabic("I") should equal(1)
}
"II" should "be converted as 2" in {
RomanConverter.convertToArabic("II") should equal(2)
}
"III" should "be converted as 3" in {
RomanConverter.convertToArabic("III") should equal(3)
}
"IV" should "be converted as 4" in {
RomanConverter.convertToArabic("IV") should equal(4)
}
"V" should "be converted as 5" in {
RomanConverter.convertToArabic("V") should equal(5)
}
"VI" should "be converted as 6" in {
RomanConverter.convertToArabic("VI") should equal(6)
}
"VII" should "be converted as 7" in {
RomanConverter.convertToArabic("VII") should equal(7)
}
"VIII" should "be converted as 8" in {
RomanConverter.convertToArabic("VIII") should equal(8)
}
"IX" should "be converted as 9" in {
RomanConverter.convertToArabic("IX") should equal(9)
}
"X" should "be converted as X" in {
RomanConverter.convertToArabic("X") should equal(10)
}
"XXXIX" should "be converted as 39" in {
RomanConverter.convertToArabic("XXXIX") should equal(39)
}
"XLV" should "be converted as 45" in {
RomanConverter.convertToArabic("XLV") should equal(45)
}
"L" should "be converted as 50" in {
RomanConverter.convertToArabic("L") should equal(50)
}
"C" should "be converted as 100" in {
RomanConverter.convertToArabic("C") should equal(100)
}
"DCLXVI" should "be converted as 666" in {
RomanConverter.convertToArabic("DCLXVI") should equal(666)
}
"CMXCIX" should "be converted as 999" in {
RomanConverter.convertToArabic("CMXCIX") should equal(999)
}
"MCDXLIV" should "be converted as 1444" in {
RomanConverter.convertToArabic("MCDXLIV") should equal(1444)
}
"MMVIII" should "be converted as 2008" in {
RomanConverter.convertToArabic("MMVIII") should equal(2008)
}
"All numbers from 1 to 3000 converted to roman " should "be converted back to arabic returning the original value" in {
for (number <- 1 to 3000){
val roman = RomanConverter.convertToRoman(number)
val arabic = RomanConverter.convertToArabic(roman)
number should equal(arabic)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment