All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.math.RomanNumeral Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.math;


import java.util.ArrayList;
import java.util.List;

import com.hfg.exception.InvalidValueException;


//------------------------------------------------------------------------------
/**
 Roman numeral representation. The current implementation works for numbers up to 4000.
 
Ex: given RomanNumeral num = new RomanNumeral(1944);

num.toString() will be equal to "MCMXLIV".

See http://en.wikipedia.org/wiki/Roman_numerals
@author J. Alex Taylor, hairyfatguy.com */ //------------------------------------------------------------------------------ // com.hfg XML/HTML Coding Library // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ // TODO: Add support for values 4000+ that need a macron (bar above the letter) for display. // The macron indicates that the number is multiplied by 1000. public class RomanNumeral extends Number implements Comparable { private String mString; private int mValue; private static List sHiToLowSymbols = new ArrayList(7); static { sHiToLowSymbols.add(RomanNumeralSymbol.M); sHiToLowSymbols.add(RomanNumeralSymbol.D); sHiToLowSymbols.add(RomanNumeralSymbol.C); sHiToLowSymbols.add(RomanNumeralSymbol.L); sHiToLowSymbols.add(RomanNumeralSymbol.X); sHiToLowSymbols.add(RomanNumeralSymbol.V); sHiToLowSymbols.add(RomanNumeralSymbol.I); } //************************************************************************** // CONSTRUCTORS //************************************************************************** //-------------------------------------------------------------------------- /** Create a RomanNumeral object from an integer value. */ public RomanNumeral(int inValue) { super(); calculateString(inValue); } //-------------------------------------------------------------------------- /** Create a RomanNumeral object from a Roman numeral string such as "MCMXLIV". */ public RomanNumeral(String inValue) { super(); parseString(inValue); } //************************************************************************** // PUBLIC METHODS //************************************************************************** //-------------------------------------------------------------------------- /** Returns the Roman numeral string value of the Roman numeral. */ @Override public String toString() { return mString; } //-------------------------------------------------------------------------- /** Returns the integer value of the Roman numeral. @deprecated use intValue() */ public int getIntValue() { return mValue; } //-------------------------------------------------------------------------- @Override public int intValue() { return mValue; } //-------------------------------------------------------------------------- @Override public long longValue() { return mValue; } //-------------------------------------------------------------------------- @Override public float floatValue() { return mValue; } //-------------------------------------------------------------------------- @Override public double doubleValue() { return mValue; } //-------------------------------------------------------------------------- /** Numerically compares two RomanNumeral objects. */ public int compareTo(RomanNumeral inObj) { return NumUtil.compare(intValue(), inObj.intValue()); } //************************************************************************** // PRIVATE METHODS //************************************************************************** //-------------------------------------------------------------------------- /* From wikipedia:

Subtractive principle

Generally, Roman numerals are written in descending order from left to right, and are added sequentially, for example MMVI (2006) is interpreted as 1000 + 1000 + 5 + 1.

Certain combinations employ a subtractive principle, which specifies that where a symbol of smaller value precedes a symbol of larger value, the smaller value is subtracted from the larger value, and the result is added to the total. For example, in MCMXLIV (1944), t he symbols C, X and I each precede a symbol of higher value, and the result is interpreted as 1000 plus (1000 minus 100) plus (50 minus 10) plus (5 minus 1).

A numeral for 10n (I, X, or C) may not precede a numeral larger than 10n+1, where n is an integer. That is, I may precede V and X, but not L or C; X may precede L or C, but not D or M. The numerals 5?10n (V, L, or D) may not be followed by a numeral of greater or equal value. Any symbol that appears more than once consecutively may not be followed by a symbol of larger value. */ private void calculateString(int inValue) { if (inValue < 1) { throw new InvalidValueException("Roman numerals cannot be less than or equal to zero!"); } else if (inValue > 3999) { throw new InvalidValueException("Roman numerals greater than 3999 are not currently supported!"); } StringBuilder buffer = new StringBuilder(); int remainingValue = inValue; while (remainingValue > 0) { for (int i = 0; i < sHiToLowSymbols.size(); i++) { RomanNumeralSymbol symbol = sHiToLowSymbols.get(i); if (symbol.getIntValue() > remainingValue) { // Is a subtractive combination appropriate? for (int j = sHiToLowSymbols.size() - 1; j > i; j--) { RomanNumeralSymbol lowerSymbol = sHiToLowSymbols.get(j); if (lowerSymbol != null && symbol.getIntValue() - lowerSymbol.getIntValue() <= remainingValue && lowerSymbol.canPrecede(symbol)) { buffer.append(lowerSymbol.getLetter()); buffer.append(symbol.getLetter()); remainingValue -= (symbol.getIntValue() - lowerSymbol.getIntValue()); break; } } } else { while (remainingValue >= symbol.getIntValue()) { buffer.append(symbol.getLetter()); remainingValue -= symbol.getIntValue(); } if (remainingValue > 0) i--; } } } mString = buffer.toString(); mValue = inValue; } //-------------------------------------------------------------------------- private void parseString(String inString) { RomanNumeralSymbol previousSymbol = null; mValue = 0; for (char theChar : inString.toCharArray()) { RomanNumeralSymbol symbol = RomanNumeralSymbol.valueOf(theChar); if (null == symbol) { throw new InvalidValueException("'" + theChar + "' is not a recognized Roman numeral symbol!"); } mValue += symbol.getIntValue(); if (previousSymbol != null && symbol.getIntValue() > previousSymbol.getIntValue()) { mValue -= 2 * previousSymbol.getIntValue(); } previousSymbol = symbol; } mString = inString; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy