qiprofile
Version:
Quantitative Imaging Profile ( QiPr) web application
97 lines (81 loc) • 3.39 kB
text/coffeescript
# Functions to convert between an integer and a roman numeral.
# This module is freely adapted from the suggestions offered at
# http://stackoverflow.com/questions/9083037/convert-a-number-into-a-roman-numeral-in-javascript.
define ['lodash'], (_) ->
# @param s the string to repeat
# @param n the number of repititions
# @returns the string repeated n times
repeat = (s, n) ->
new Array(n + 1).join(s)
# @param one the one character
# @param five the five character
# @param ten the ten character
# @param max the number of roman numerals to generate
# (0 to 10, default 10)
# @returns an array of the roman numerals
# @throws Error if one of the characters or the max is invalid
genRomans = (one, five, ten, max=10) ->
# @param n the input integer
# @returns the corresponding roman numeral
# in one, five and ten units
genRoman = (n) ->
ones = n % 5
if n < 4 or not five
repeat(one, n)
else if n < 6
repeat(one, 5 - n) + five
else if n < 9
five + repeat(one, n - 5)
else
repeat(one, 10 - n) + ten
if max <= 0 or max > 10
throw new RangeError("The roman numeral generator count must be in" +
" the range 1 to 10: #{ max }")
if not one
throw new Error("The roman numeral generator is missing a one character")
if five and not ten
throw new Error("The roman numeral generator is missing a ten character")
if ten and not five
throw new Error("The roman numeral generator is missing a five character")
[0..max].map(genRoman)
ONES = genRomans('I', 'V', 'X')
TENS = genRomans('X', 'L', 'C')
HUNDREDS = genRomans('C', 'D', 'M')
THOUSANDS = genRomans('M', '', '')
ROMANS = [ONES, TENS, HUNDREDS, THOUSANDS]
# The maximum supported value.
incrementMax = (sum, place) ->
(sum * 10) + ROMANS[place].length - 1
MAX = _.reduceRight([0...ROMANS.length], incrementMax, 0)
# @param s the input string or integer
# @returns the roman numeral string
# @throws TypeError if the input value is not a string consisting of
# positive digits
romanize: (s) ->
s = s.toString()
n = parseInt(s)
if not n or n < 0
throw new RangeError("The romanize input value cannot be converted" +
" to a positive integer: #{ s }")
if n > MAX
throw new RangeError("The romanize input value exceeds the maximum" +
" supported value of #{ MAX }: #{ s }")
# The string digits.
# Note: cannot use parseInt directly as the map argument, since the
# second parseInt argument is incorrectly interpreted as a radix
# (cf. http://stackoverflow.com/questions/262427/javascript-arraymap-and-parseint)
digits = s.split('').map((n) -> parseInt(n))
# The place indexes.
places = [0...digits.length]
# Prepends the roman numeral conversion of the right-most digit to the
# given roman numeral. This function removes the right-most digit from
# the digits array.
#
# @param roman the roman numeral to augment
# @param place the ROMANS place index
# @returns the augmented roman numeral
prepend = (roman, place) ->
digit = digits.pop()
ROMANS[place][digit] + roman
# Make the roman numeral by converting each digit from right to left.
_.reduce(places, prepend, '')