public
Created

Bergen coding dojo - Roman numerals

  • Download Gist
fromRoman.coffee
CoffeeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 
_ = require 'underscore'
 
class FromRomanNumerals
 
baseValues: {
"I": 1
"V": 5
"X": 10
"L": 50
"C": 100
"D": 500
"M": 1000
}
 
parse: (roman, prev = 1) ->
head = _.head roman
tail = _.tail roman
sign = if prev > head then -1 else 1
 
if roman.length is 1 then sign * head else sign * head + (@parse tail, head)
 
 
parseRoman: (roman) ->
# Convert to numbers, reverse and parse
@parse (_.map roman, (x) => @baseValues[x]).reverse()
 
 
module.exports = FromRomanNumerals
toRoman.coffee
CoffeeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
 
_ = require 'underscore'
 
class RomanNumerals
 
baseValues: {
1: "I"
5: "V"
10: "X"
50: "L"
100: "C"
500: "D"
1000: "M"
}
 
# Stupid fudge factor since romans wrote VIII instead of IIX
# Going for the closest one also selects the shortest (IIX over VIII)
fudgeFactor: (num) ->
strNum = String(num)
return (-1 * (Math.pow 10, strNum.length - 1)) if strNum[0] in ['3', '8']
return 0
 
getClosestNumeral: (num) ->
parseInt (_.min (Object.keys @baseValues), (x) =>
Math.abs(num - x + @fudgeFactor num))
 
isBaseValue: (num) ->
@baseValues[num]?
 
getBaseValue: (num) ->
@baseValues[num]
 
getRomanNumeral: (num) ->
return @getBaseValue num if @isBaseValue num
 
closest = @getClosestNumeral num
diff = num - closest
 
return "#{@getRomanNumeral (Math.abs diff)}#{@getBaseValue closest}" if diff < 0
return "#{@getBaseValue closest}#{@getRomanNumeral (Math.abs diff)}"
 
parseNumberToRoman: (num) ->
input = String(num)
position = input.length - 1
result = ""
 
for i in input
result += @getRomanNumeral(i * Math.pow 10, position) if i > 0
position--
 
result
 
module.exports = RomanNumerals

Mocha tester:

FromRomanNumerals = (require "../fromRoman")

describe "FromRomanNumerals", ->

    r = new FromRomanNumerals

    describe "parseRoman()", ->
        it "handles basic value", ->
            r.parseRoman("V").should.equal 5
        it "handles multiple basic values", ->
            r.parseRoman("II").should.equal 2
            r.parseRoman("VI").should.equal 6
            r.parseRoman("VII").should.equal 7
            r.parseRoman("VIII").should.equal 8
            r.parseRoman("IX").should.equal 9
            r.parseRoman("MCMXC").should.equal 1990
            r.parseRoman("LXXXVIII").should.equal 88
RomanNumerals = (require "../toRoman")

describe "RomanNumerals", ->

    r = new RomanNumerals

    describe "Helper functions", ->
        describe "getBaseValue()", ->
            it "given basic value should return roman numeral", ->
                r.getBaseValue(1).should.equal "I"
                r.getBaseValue(5).should.equal "V"
                r.getBaseValue(10).should.equal "X"
                r.getBaseValue(50).should.equal "L"
                r.getBaseValue(100).should.equal "C"
                r.getBaseValue(500).should.equal "D"
                r.getBaseValue(1000).should.equal "M"

        describe "isBaseValue()", ->
            it "should return true if base value exist", ->
                r.isBaseValue("10").should.be.true
            it "should return false if base value does not exist", ->
                r.isBaseValue("12").should.be.false

        describe "getClosestNumeral()", ->
            it "9 should return 10", ->
                r.getClosestNumeral(9).should.equal 10

    describe "getRomanNumeral()", ->
        describe "given basic number", ->
            it "return basic numerals", ->
                r.getRomanNumeral(5).should.equal "V"

        describe "given complex number", ->
            it "return composite numeral", ->
                r.getRomanNumeral(90).should.equal "XC"
                r.getRomanNumeral(3).should.equal "III"


    describe "parseNumberToRoman()", ->
        describe "given basic composite number", ->
            it "1551 returns roman numeral MDLI", ->
                r.parseNumberToRoman(1551).should.equal "MDLI"

            it "1001 returns roman numeral MI", ->
                r.parseNumberToRoman(1001).should.equal "MI"

        describe "given composite number", ->
            it "1990 returns roman numeral MCMXC", ->
                r.parseNumberToRoman(1990).should.equal "MCMXC"
            it "2000 returns roman numeral MM", ->
                r.parseNumberToRoman(2000).should.equal "MM"
            it "8 returns roman numeral VIII", ->
                r.parseNumberToRoman(8).should.equal "VIII"
            it "2008 returns roman numeral MMVIII", ->
                r.parseNumberToRoman(2008).should.equal "MMVIII"
            it "890 returns roman numeral DCCCXC", ->
                r.parseNumberToRoman(890).should.equal "DCCCXC"
            it "88 returns roman numeral LXXXVIII", ->
                r.parseNumberToRoman(88).should.equal "LXXXVIII"
            it "296 returns roman numeral CCXCVI", ->
                r.parseNumberToRoman(296).should.equal "CCXCVI"

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.