#!/usr/bin/perl
#
# Tests on Roman numbers modules
# Test on Unicode characters and the W3C specification
use strict;
use warnings;
use Math::Roman;
use Convert::Number::Roman;
# U+2160 = o20540 = 8544, U+2170 = 0o20560 = 8560
Math::Roman::tokens( "\x{2160}", 1, "\x{2161}", 2, "\x{2162}", 3, "\x{2163}", 4,
"\x{2164}", 5, "\x{2165}", 6, "\x{2166}", 7, "\x{2167}", 8, "\x{2168}", 9,
"\x{2169}", 10, "\x{216A}", 11, "\x{216B}", 12, "\x{2169}\x{2162}", 13,
"\x{2169}\x{2163}", 14, "\x{2169}\x{2164}", 15, "\x{2169}\x{2165}", 16,
"\x{2169}\x{2166}", 17, "\x{2169}\x{2167}", 18, "\x{2169}\x{2168}", 19,
"\x{2169}\x{2169}", 20, "\x{2169}\x{216A}", 21, "\x{2169}\x{216B}", 22,
"\x{2169}\x{2169}\x{2162}", 23,
"\x{2169}\x{2169}\x{2163}", 24,
"\x{2169}\x{2169}\x{2164}", 25,
"\x{2169}\x{2169}\x{2165}", 26,
"\x{2169}\x{2169}\x{2166}", 27,
"\x{2169}\x{2169}\x{2167}", 28,
"\x{2169}\x{2169}\x{2168}", 29,
"\x{2169}\x{2169}\x{2169}", 30,
"\x{2169}\x{2169}\x{216A}", 31,
"\x{2169}\x{2169}\x{216B}", 32,
"\x{2169}\x{2169}\x{2169}\x{2162}", 33,
"\x{2169}\x{2169}\x{2169}\x{2163}", 34,
"\x{2169}\x{2169}\x{2169}\x{2164}", 35,
"\x{2169}\x{2169}\x{2169}\x{2165}", 36,
"\x{2169}\x{2169}\x{2169}\x{2166}", 37,
"\x{2169}\x{2169}\x{2169}\x{2167}", 38,
"\x{2169}\x{2169}\x{2169}\x{2168}", 39,
"\x{2169}\x{216C}", 40, "\x{216C}", 50, "\x{2169}\x{216D}", 90, "\x{216D}", 100,
"\x{216D}\x{216E}", 400, "\x{216E}", 500, "\x{216D}\x{216F}", 900, "\x{216F}", 1000);
for my $n (1..3999) {
my $mod = $n % 50;
my $cnr = Convert::Number::Roman->new($n);
my $mr = Math::Roman->new($n);
if ($cnr->convert ne $mr) {
print "Error 1 for $n\n";
}
if ($mr->as_number != $n) {
print "Error 2 for $n\n";
}
}
For the argument to tokens, the initial idea was to enumerate all Unicode values from "I" to "XII", with their 1 to 12 values. It did not work, because 13 was converted to \x{216B}\x{2160}, that is "XII" + "I" instead of \x{2169}\x{2162}, that is, "X" + "III". So I had to add this pair of values. Then the same problem arose with 14, and then 15, and so on, until 40. Starting with 40, I could at last leave some holes.
For the sake of comparison, here is a script in which I decided not to use \x{216A} for "XI" and \x{216B} for "XII". Like the script above, we compare the output of Math::Roman with the output of Convert::Number::Roman, after having replaced "XI" by "X" + "I" and "XII" by "X" + "II". As you can see, it is much simpler and much more concise.
#!/usr/bin/perl
#
# Tests on Roman numbers modules
# Test on Unicode characters and the W3C specification, while ignoring the characters for XI and XII
use strict;
use warnings;
use Math::Roman;
use Convert::Number::Roman;
# U+2160 = o20540 = 8544, U+2170 = 0o20560 = 8560
Math::Roman::tokens( "\x{2160}", 1, "\x{2161}", 2, "\x{2162}", 3, "\x{2163}", 4,
"\x{2164}", 5, "\x{2165}", 6, "\x{2166}", 7, "\x{2167}", 8, "\x{2168}", 9,
"\x{2169}", 10,
"\x{2169}\x{216C}", 40, "\x{216C}", 50, "\x{2169}\x{216D}", 90, "\x{216D}", 100,
"\x{216D}\x{216E}", 400, "\x{216E}", 500, "\x{216D}\x{216F}", 900, "\x{216F}", 1000);
for my $n (1..3999) {
my $mod = $n % 50;
my $cnr = Convert::Number::Roman->new($n);
my $cnr_str = $cnr->convert;
$cnr_str =~ s/\x{216A}/\x{2169}\x{2160}/g;
$cnr_str =~ s/\x{216B}/\x{2169}\x{2161}/g;
my $mr = Math::Roman->new($n);
if ($cnr_str ne $mr) {
print "Error 1 for $n\n";
}
if ($mr->as_number != $n) {
print "Error 2 for $n\n";
}
}
Note that these two scripts can read and produce all valid numbers. But they do not catch invalid numbers. This is left as a (painful) exercise to the reader.
© 2011 Jean Forget and Les Mongueurs de Perl. The code in this file is free software; you can redistribute it or modify it under the same terms as Perl 5.10.0. The text is under Creative Commons license with paternity, no modification.