Skip to content

Instantly share code, notes, and snippets.

@stianeikeland
Created April 4, 2013 10:16
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save stianeikeland/5309302 to your computer and use it in GitHub Desktop.
Bergen coding dojo - Roman numerals
_ = 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
_ = 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
@stianeikeland
Copy link
Author

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"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment