001package com.hfg.math; 002 003import java.util.Map; 004 005import com.hfg.util.collection.OrderedMap; 006 007 008//------------------------------------------------------------------------------ 009/** 010 Individual Roman numeral symbol representation. Used by the RomanNumeral class, 011 it encapsulates the logic of which symbols can precede a given symbol. 012 <p> 013 See <a href='http://en.wikipedia.org/wiki/Roman_numerals'>http://en.wikipedia.org/wiki/Roman_numerals</a> 014 </p> 015 @author J. Alex Taylor, hairyfatguy.com 016 */ 017//------------------------------------------------------------------------------ 018// com.hfg XML/HTML Coding Library 019// 020// This library is free software; you can redistribute it and/or 021// modify it under the terms of the GNU Lesser General Public 022// License as published by the Free Software Foundation; either 023// version 2.1 of the License, or (at your option) any later version. 024// 025// This library is distributed in the hope that it will be useful, 026// but WITHOUT ANY WARRANTY; without even the implied warranty of 027// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 028// Lesser General Public License for more details. 029// 030// You should have received a copy of the GNU Lesser General Public 031// License along with this library; if not, write to the Free Software 032// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 033// 034// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 035// jataylor@hairyfatguy.com 036//------------------------------------------------------------------------------ 037 038public class RomanNumeralSymbol implements Comparable<RomanNumeralSymbol> 039{ 040 //************************************************************************** 041 // PRIVATE FIELDS 042 //************************************************************************** 043 044 char mLetter; 045 int mValue; 046 047 048 private static Map<Character, RomanNumeralSymbol> sValueMap = new OrderedMap<Character, RomanNumeralSymbol>(10); 049 050 //************************************************************************** 051 // PUBLIC FIELDS 052 //************************************************************************** 053 054 /** The Roman numeral for the integer 1. */ 055 public static RomanNumeralSymbol I = new RomanNumeralSymbol('I', 1); 056 /** The Roman numeral for the integer 5. */ 057 public static RomanNumeralSymbol V = new RomanNumeralSymbol('V', 5); 058 /** The Roman numeral for the integer 10. */ 059 public static RomanNumeralSymbol X = new RomanNumeralSymbol('X', 10); 060 /** The Roman numeral for the integer 50. */ 061 public static RomanNumeralSymbol L = new RomanNumeralSymbol('L', 50); 062 /** The Roman numeral for the integer 100. */ 063 public static RomanNumeralSymbol C = new RomanNumeralSymbol('C', 100); 064 /** The Roman numeral for the integer 500. */ 065 public static RomanNumeralSymbol D = new RomanNumeralSymbol('D', 500); 066 /** The Roman numeral for the integer 1000. */ 067 public static RomanNumeralSymbol M = new RomanNumeralSymbol('M', 1000); 068 069 //************************************************************************** 070 // CONSTRUCTORS 071 //************************************************************************** 072 073 //-------------------------------------------------------------------------- 074 private RomanNumeralSymbol(char inLetter, int inValue) 075 { 076 mLetter = inLetter; 077 mValue = inValue; 078 079 sValueMap.put(mLetter, this); 080 } 081 082 //************************************************************************** 083 // PUBLIC METHODS 084 //************************************************************************** 085 086 //-------------------------------------------------------------------------- 087 /** 088 Returns the RomanNumeralSymbol value for the specified character. 089 */ 090 public static RomanNumeralSymbol valueOf(char inLetter) 091 { 092 return sValueMap.get(Character.toUpperCase(inLetter)); 093 } 094 095 //-------------------------------------------------------------------------- 096 /** 097 Returns an array of all the RomanNumeralSymbol values. 098 */ 099 public static RomanNumeralSymbol[] values() 100 { 101 return sValueMap.values().toArray(new RomanNumeralSymbol[1]); 102 } 103 104 //-------------------------------------------------------------------------- 105 /** 106 Returns the character value of the Roman numeral symbol. 107 */ 108 public char getLetter() 109 { 110 return mLetter; 111 } 112 113 //-------------------------------------------------------------------------- 114 /** 115 Returns the integer value of the Roman numeral symbol. 116 */ 117 public int getIntValue() 118 { 119 return mValue; 120 } 121 122 //-------------------------------------------------------------------------- 123 /** 124 Returns the Roman numeral string value of the Roman numeral symbol. 125 */ 126 @Override 127 public String toString() 128 { 129 return mLetter + ""; 130 } 131 132 //-------------------------------------------------------------------------- 133 /** 134 Numerically compares two RomanNumeralSymbol objects. 135 */ 136 public int compareTo(RomanNumeralSymbol inObj) 137 { 138 return NumUtil.compare(mValue, inObj.mValue); 139 } 140 141 //-------------------------------------------------------------------------- 142 /** 143 Returns whether this symbol can legally precede the specified symbol given 144 the subtractive principle. 145 */ 146 /* 147 From wikipedia: 148 149 Subtractive principle 150 151 Generally, Roman numerals are written in descending order from left to right, 152 and are added sequentially, for example MMVI (2006) is interpreted as 1000 + 1000 + 5 + 1. 153 154 Certain combinations employ a subtractive principle, which specifies that where a symbol 155 of smaller value precedes a symbol of larger value, the smaller value is subtracted from 156 the larger value, and the result is added to the total. For example, in MCMXLIV (1944), t 157 he symbols C, X and I each precede a symbol of higher value, and the result is interpreted as 158 1000 plus (1000 minus 100) plus (50 minus 10) plus (5 minus 1). 159 160 A numeral for 10n (I, X, or C) may not precede a numeral larger than 10n+1, where n is an integer. 161 That is, I may precede V and X, but not L or C; X may precede L or C, but not D or M. 162 The numerals 5?10n (V, L, or D) may not be followed by a numeral of greater or equal value. 163 Any symbol that appears more than once consecutively may not be followed by a symbol of larger value. 164 */ 165 public boolean canPrecede(RomanNumeralSymbol inSymbol) 166 { 167 boolean result; 168 if (inSymbol.getIntValue() < getIntValue()) 169 { 170 result = true; 171 } 172 else 173 { 174 double predecessorLog10 = Math.log10((double)getIntValue()); 175 176 result = (predecessorLog10%1 == 0 177 && (inSymbol.getIntValue() <= Math.pow(10, (predecessorLog10 + 1)))); 178 } 179 180 return result; 181 } 182}