#!/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.