#!/usr/bin/perl -w
# This program is based on Ozawa Sakuro 's program available on the
# web. He wrote it in 1995. I have added extensive comments to
# make it comprehendable.
# Search on his name to find it with "Roman Numerals Arabic".
#
use strict;
#Domain of valid Roman numerals is limited to less than 4000, since
#proper Roman digits for the rest are not available in ASCII.
# Declare Sub/Functions
sub Roman; # returns a Roman Numeral String from an Arabic Number
# String as input.
# Declare Variables
# This is the hash table for translating an arabic number to a roman
# numeral string.
my %arabic2roman = qw(1 IV 10 XL 100 CD 1000 MMMMMM);
# Following is an array which represents the sort keys of the
# hash table in reverse order, the first key is 1000.
my @figure = reverse sort keys %arabic2roman;# This will be used
# extensively for sub Roman;
# my @figure = qw(1000 100 10 1);
# is another way of getting this.
# We will now change the hash table so the value part becomes
# a value array. That is the string "IV", will become array
# ("I", "V"). Uncomment what is below to see the original hash table.
# my $key ;
# my $val ;
# print " Original Hash Table \n\n";
# while (($key,$val) = each %arabic2roman) {
# print "key is: $key\t\t value is: ($val) \n";
# # print original hash table.
# }
foreach (@figure) { # split the value string into an array of
# two elements. Note, the key for the hash
# remains the same. The split is done after
# the first byte, with the remainder being the
# second element.
$arabic2roman{$_} = [split(//, $arabic2roman{$_}, 2)];
}
# To see what happened we can simply walk the hash table again.
# by uncommenting the lines below.
#
# print " \n\n New Hash Table with an array for Value \n\n";
# while (($key,$val) = each %arabic2roman) {
# print "key is: $key\t\t array value is: (@$val) \n";
# # notice its @$val now because it is an array!
# }
# This new hash table makes the algorithm, very easy for converting
# an Arabic Number to a Roman Numeral. This is Ozawa Sakuro's genius
# not mine!
sub Roman { # Input Arabic Number, Return Roman Numeral as string.
# Note we use the "x" operator here to denote repetition
# when concatenating.
# We loop through the @figure array which will be used to
# count out the number of 1000's, 100's, 10 's and 1's
# respectively. By dividing by the arabic part of the place
# holder hash key (@figure[i])for each position we
# know how many times to repeat the Roman Numeral.
my($arg) = shift;
0 < $arg and $arg < 4000 or return undef;# leave if not
# arabic number
my($x, $roman); # define variables for concatenation
# $x is one Roman Numeral Digit (I,X,V, etc.)
# $roman is the string we will concatenate to
# build the Roman Numeral String. Note, we start
# off with a Roman Numeral String of MMMMM which we
# then subtract from to get our thousands place
# if there is any.
# The original arabic number string is passed by
# $arg. Note, if you uncommented the hash table
# walk in the beginning for %arabic2roman, all
# will be obvious here.
#
foreach (@figure) { # i.e. for 1000, 100, 10, 1
my($digit, $i, $v) = (int($arg / $_), @{$arabic2roman{$_}});
# Uncomment next line for debugging.
# print "dollar_digit: $digit, dollar_i: $i, dollar_v: $v \n";
if (1 <= $digit and $digit <= 3) {
$roman .= $i x $digit; # Repeat Numeral for "LLL", "CCC",
# "MMM", "XXX", "III", etc.
} elsif ($digit == 4) {
$roman .= "$i$v"; # yields "CD", "XL", "IV"
} elsif ($digit == 5) {
$roman .= $v; # yields "D", "L", "V"
} elsif (6 <= $digit and $digit <= 8) {
$roman .= $v . $i x ($digit - 5);
#yields "DC", "DCC", "DCCC", ... "VI", "VII", "VIII" etc.
} elsif ($digit == 9) { # this is the "9 type".
$roman .= "$i$x"; # yields "CM", "XC", "IX"
}
$arg -= $digit * $_; # subtract out what you have already
# taken care of, i.e. 3 times 1000
# for the first iteration here.
$x = $i; # Store for later if this is the "9 type"
# Roman Numeral digit.
# uncomment next line for debugging.
# print "this is dollar roman after each iteration: $roman \n";
}
$roman;
}
#Main Section
my $arabic_number = "3554";
my $roman_numeral = "";
$roman_numeral = Roman($arabic_number);
print "The arabic number: $arabic_number \n";
print "translates to: $roman_numeral \n";
# End Main Program Section
exit;