Wednesday, September 10, 2014

When in Rome

Blog Entry #334

Over and above the New Array Text Pages scripts, Jenson's Easy to Use JavaScripts directory features a Roman Numeral (RomanNumerals.html) Script that I thought we would look at in today's post. The RomanNumerals.html script

(1) converts Arabic numerals to Roman numerals and also

(2) creates a date string whose day and year numbers are expressed in Roman numerals.

On the whole the RomanNumerals.html script is well-designed but it does have one serious flaw, namely,

*it's not interactive*: a corrective demo will be provided at the end of the post.

**Numeric inputs**

Specifically, the RomanNumerals.html script converts Arabic integers in the range 1-3999 inclusive to their Roman numeral equivalents.

• For a 0 input the script outputs an empty string.

• Floating-point inputs are Math.floor( )ed, e.g., 3.14159 gives III.

• Oddly, the script accommodates negative integer inputs in the range -1 to -3999 inclusive - -25 gives -XXV, e.g. - even though Roman numerals are not really meant for negative numbers.

• For integers larger than 3999 (or smaller than -3999) the script outputs an "Out of Range" string.

Wikipedia's "Roman numerals" entry includes a

**Special values**section that addresses the Roman approaches to zero, fractions, and large numbers. Meanwhile, wikiHow reports that

the Romans themselves just wrote MMMM for 4000.

**The cast of characters**

In his script commentary, Jenson correctly notes:

Roman numerals are based on only two basic digits: 1 and 5.

However, they use different characters depending on the power of 10 being represented.

Roman Numeral | Arabic Equivalent | Power of 10 |
---|---|---|

I | 1 | 1 × 10^{0} |

X | 10 | 1 × 10^{1} |

C | 100 | 1 × 10^{2} |

M | 1000 | 1 × 10^{3} |

Roman Numeral | Arabic Equivalent | Power of 10 |
---|---|---|

V | 5 | 5 × 10^{0} |

L | 50 | 5 × 10^{1} |

D | 500 | 5 × 10^{2} |

The I, V, X, L, C, D, and M Roman numerals are arrayed in ascending order via a romanDigits Object object

`romanDigits[0] = "I";`

romanDigits[1] = "V";

romanDigits[2] = "X";

romanDigits[3] = "L";

romanDigits[4] = "C";

romanDigits[5] = "D";

romanDigits[6] = "M";

romanDigits.length = 7;

that instantiates the buildArray( ) constructor.

`function buildArray( ) { var a = arguments; for (i = 0; i < a.length; i++) this[i] = a[i]; this.length = a.length; }`

var romanDigits = new buildArray("I", "V", "X", "L", "C", "D", "M");

Could romanDigits be formulated as a bona fide Array object? That it could.

`var romanDigits = ["I", "V", "X", "L", "C", "D", "M"];`

**Fragment it**

For this and the following sections we will work with a 374 → CCCLXXIV example.

The business end of the RomanNumerals.html script comprises two functions:

(1) an arabicToRoman( ) function effectively divides an inputted Arabic integer into its expanded notation terms;

(2) an arabicToRomanDigit( ) function 'Romanizes' those terms.

A document.write( ) command calls the arabicToRoman( ) function; the 374 argument is given a number identifier.

`function arabicToRoman(number) { ... }`

document.write("374 in Roman Numerals is ", arabicToRoman(374));

The arabicToRoman( ) body initially declares j, string, and sign variables.

`var j, string = "", sign = "";`

If number is negative, then sign is set to a stringified minus sign and number is converted to its additive inverse.

`if (number < 0) { sign = "-"; number = -number; }`

Subsequently number is floor( )ed whether it needs it or not.

`number = Math.floor(number);`

If number is 4000 or greater, then Out of Range is returned to the calling document.write( ) command and the function exits.

`if (number >= 4000) return "Out of Range";`

With these preliminaries out of the way, we're ready to get to work on our number=374. A for loop splits 374 into its constituent digits and passes

(a) those digits and

(b) the powers of 10 associated with them

to the arabicToRomanDigit( ) function.

```
for (j = 0; number > 0; j++) {
digit = number % 10;
number = (number - digit) / 10;
string = arabicToRomanDigit(digit, j) + string; }
```

The loop runs for three iterations; more generally, it runs as many times as there are digits in number. Noting that

374 = 3 × 10

^{2}+ 7 × 10

^{1}+ 4 × 10

^{0}

here's a rundown of the number processing in those iterations:

j = 0

374 is moduloed by 10 to give 4, which is assigned to a digit variable.

(374 - 4) / 10 gives 37, which is assigned to number.

4 and 0 are passed to the arabicToRomanDigit( ) function.

j = 1

37 is moduloed by 10 to give 7, which is assigned to digit.

(37 - 7) / 10 gives 3, which is assigned to number.

7 and 1 are passed to arabicToRomanDigit( ).

j = 2

3 is moduloed by 10 to give 3, which is assigned to digit.

(3 - 3) / 10 gives 0, which is assigned to number.

3 and 2 are passed to arabicToRomanDigit( ).

With number now 0, the loop stops at the beginning of (what would be) its fourth iteration because the

`number > 0`

condition returns false.This is not the only way to split number: alternatively, we could stringify number and then go after its characters via, say, the substring( ) method. But given that number is in fact a number (and that we'll be doing some simple arithmetic on the 7 in the arabicToRomanDigit( ) function), the modulo/division approach is the more appropriate way to go.

**Romanize it**

The arabicToRomanDigit( ) function's digit and j arguments are given the identifiers digit and power, respectively. The arabicToRomanDigit( ) body initially declares i and string variables.

```
function arabicToRomanDigit(digit, power) {
var i, string = ""; ... }
```

The declarations are followed by a series of if statements that converts Arabic digits to corresponding Roman strings. Following modern practice, 4s and 9s are handled subtractively, e.g., 4 becomes IV and not IIII. A set of

`romanDigits[n + power * 2]`

expressions relates the power values to the romanDigits series of numerals.```
if (digit >= 5 && digit <= 8) { // Adds a V|L|D Roman numeral to string as needed
string = romanDigits[1 + power * 2];
digit = digit - 5; }
if (digit >=1 && digit < 4) { // Adds one, two, or three I|X|C|M Roman numerals to string as needed
for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; }
if (digit == 4 || digit == 9) {
string = romanDigits[0 + power * 2];
if (digit == 4)
string = string + romanDigits[1 + power * 2];
else
string = string + romanDigits[2 + power * 2]; }
```

Returning to our 374 → CCCLXXIV example and recalling that

`romanDigits = ["I", "V", "X", "L", "C", "D", "M"]`

:(4) If digit is 4 and power is 0:

`if (digit == 4 || digit == 9) { string = romanDigits[0 + power * 2];`

sets string to I.`if (digit == 4) string = string + romanDigits[1 + power * 2];`

appends a V to the I to give string = IV.(70) If digit is 7 and power is 1:

`if (digit >= 5 && digit <= 8) { string = romanDigits[1 + power * 2];`

sets string to L.`digit = digit - 5;`

drops digit to 2.`if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2];`

appends two Xs to the L to give string = LXX.(300) If digit is 3 and power is 2:

`if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2];`

sets string to CCC.The arabicToRomanDigit( ) function concludes with a

`return string;`

statement that returns string to the arabicToRoman( ) function.**Assembly and output**

Back at the arabicToRoman( ) function, string (i.e., the arabicToRoman( ) string - note that the arabicToRoman( ) string and the arabicToRomanDigit( ) string are both local variables) is appended to the arabicToRomanDigit( ) return and the resulting string is assigned to string.

`string = arabicToRomanDigit(digit, j) + string;`

For the three

`for (j = 0; number > 0; j++)`

loop iterations, string goes from an empty string to IV to LXXIV to CCCLXXIV. The order of concatenation operands is important: a `string += arabicToRomanDigit(digit, j);`

assignment would give a 'reversed' string = IVLXXCCC.When the loop has finished executing, a

`return sign + string;`

statement appends string to sign and returns the resulting string to the document.write( ) command, which then writes`374 in Roman Numerals is CCCLXXIV`

to the page. If it were up to me we would send string to an empty

`<div id="romanDiv"><div>`

via a`document.getElementById("romanDiv").textContent = "374 in Roman numerals is: " + string;`

command, but to each his own.

**The date**

The script presents the following code for

writing a date in Roman numerals:

`var month = new buildArray("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");`

var today = new Date( );

var yr = today.getYear( );

if (yr < 100) yr = yr + 1900;

document.write("<br>Today is ", arabicToRoman(today.getDate( )), " ", month[today.getMonth( )], " ", arabicToRoman(yr));

I see you rolling your eyes. "

*getYear( )??*" Yeah, that's the reason why the year in the date demo at the javascript/RomanNumerals.html page is off. Setting yr to

`today.getFullYear( )`

sorts out the situation.Sample output:

`Today is VII September MMXIV`

**Demo**

As promised, we end with a demo.

Input an Arabic integer in the range 1-3999 inclusive:

A

`!/^\d+$/.test(number)`

gate weeds out negative numbers, floating-point numbers, and nonnumeric inputs.Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)