class Integer

  #
  ROMAN_MAX = 3999 unless const_defined?(:ROMAN_MAX)

  #
  ROMAN_VALUES = [
    ["M", 1000],
    ["CM", 900],
    ["D",  500],
    ["CD", 400],
    ["C",  100],
    ["XC",  90],
    ["L",   50],
    ["XL",  40],
    ["X",   10],
    ["IX",   9],
    ["V",    5],
    ["IV",   4],
    ["I",    1]
  ] unless const_defined?(:ROMAN_VALUES)

  # Converts this integer to a roman numeral.
  #
  # NOTE: This method is not a common core extension and is not
  # loaded automatically when using <code>require 'facets'</code>.
  #
  # @uncommon
  #   require 'facets/integer/roman'
  #
  def roman
    int = self
    #return nil if integer > ROMAN_MAX
    return "-#{(-int).roman}" if int < 0
    return "" if int == 0
    ROMAN_VALUES.each do |(i, v)|
      return(i + (int-v).roman) if v <= int 
    end
  end

end


class String

  # Taken from O'Reilly's Perl Cookbook 6.23. Regular Expression Grabbag.
  ROMAN = /^M*(D?C{0,3}|C[DM])(L?X{0,3}|X[LC])(V?I{0,3}|I[VX])$/i unless const_defined?(:ROMAN)

  ROMAN_VALUES = Integer::ROMAN_VALUES.inject({}) do |h,(r,a)|
    h[r] = a; h
  end unless const_defined?(:ROMAN_VALUES)

  # Considers string a Roman numeral numeral,
  # and converts it to the corresponding integer.
  #
  # NOTE: This method is not a common core extension and is not
  # loaded automatically when using <code>require 'facets'</code>.
  #
  # @uncommon
  #   require 'facets/string/roman'
  #
  def roman
    roman = upcase
    raise unless roman?
    last = roman[-1,1]
    roman.reverse.split('').inject(0) do |result, c|
      if ROMAN_VALUES[c] < ROMAN_VALUES[last]
        result -= ROMAN_VALUES[c]
      else
        last = c
        result += ROMAN_VALUES[c]
      end
    end
  end

  # Returns true iif the subject is a valid Roman numeral.
  #
  # NOTE: This method is not a common core extension and is not
  # loaded automatically when using <code>require 'facets'</code>.
  #
  # @uncommon
  #   require 'facets/string/roman'
  #
  def roman?
    ROMAN =~ upcase
  end

end

