aboutsummaryrefslogtreecommitdiff
path: root/challenge-227
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2023-07-30 23:05:55 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2023-07-30 23:05:55 +1000
commit6ee04682659df40ba2c1eb09d1fa36245ed8b5d7 (patch)
tree657dbe33ea31c948c965fd7d87c9ae3211876985 /challenge-227
parentfe6b182a3e03e5219bb444d3924536d0ec8471ea (diff)
downloadperlweeklychallenge-club-6ee04682659df40ba2c1eb09d1fa36245ed8b5d7.tar.gz
perlweeklychallenge-club-6ee04682659df40ba2c1eb09d1fa36245ed8b5d7.tar.bz2
perlweeklychallenge-club-6ee04682659df40ba2c1eb09d1fa36245ed8b5d7.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 227
Diffstat (limited to 'challenge-227')
-rw-r--r--challenge-227/athanasius/perl/ch-1.pl180
-rw-r--r--challenge-227/athanasius/perl/ch-2.pl262
-rw-r--r--challenge-227/athanasius/raku/ch-1.raku176
-rw-r--r--challenge-227/athanasius/raku/ch-2.raku271
4 files changed, 889 insertions, 0 deletions
diff --git a/challenge-227/athanasius/perl/ch-1.pl b/challenge-227/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..b11a838279
--- /dev/null
+++ b/challenge-227/athanasius/perl/ch-1.pl
@@ -0,0 +1,180 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 227
+=========================
+
+TASK #1
+-------
+*Friday 13th*
+
+Submitted by: Peter Campbell Smith
+
+You are given a year number in the range 1753 to 9999.
+
+Write a script to find out how many dates in the year are Friday 13th, assume
+that the current Gregorian calendar applies.
+
+Example
+
+ Input: $year = 2023
+ Output: 2
+
+ Since there are only 2 Friday 13th in the given year 2023 i.e. 13th Jan and
+ 13th Oct.
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. If $VERBOSE is set to a true value, the solution (the number of dates) is
+ followed by a list of the months in the given year in which the 13th is a
+ Friday.
+
+Note
+----
+"Friday the 13th .... occurs when the 13th day of the month in the Gregorian
+calendar falls on a Friday, which happens at least once every year but can occur
+up to three times in the same year." --"Friday the 13th", Wikipedia
+
+=cut
+#===============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use DateTime;
+use Regexp::Common qw( number );
+use Test::More;
+
+const my $FRIDAY => 5; # DateTime::day_of_week() outputs 1-7, Monday is 1
+const my $MIN => 1753; # First valid year
+const my $MAX => 9999; # Last valid year
+const my $VERBOSE => 1; # Output month names in addition to the count
+const my $USAGE =>
+"Usage:
+ perl $0 <year>
+ perl $0
+
+ <year> A year in the range $MIN to $MAX\n";
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 227, Task #1: Friday 13th (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $args = scalar @ARGV;
+
+ if ($args == 0)
+ {
+ run_tests();
+ }
+ elsif ($args == 1)
+ {
+ my $year = $ARGV[ 0 ];
+
+ $year =~ / ^ $RE{num}{int} $ /x
+ or error( qq["$year" is not a valid integer] );
+ $MIN <= $year <= $MAX or error( qq["$year" is out of range] );
+
+ print "Input: \$year = $year\n";
+
+ my $list = find_friday_13th( $year );
+ my $count = scalar @$list;
+
+ print "Output: $count\n";
+
+ if ($VERBOSE)
+ {
+ printf "\nMonth%s in %d in which 13th falls on a Friday: %s\n",
+ $count == 1 ? '' : 's', $year, join ', ', @$list;
+ }
+ }
+ else
+ {
+ error( "Expected 1 or 0 command-line arguments, found $args" );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_friday_13th
+#-------------------------------------------------------------------------------
+{
+ my ($year) = @_;
+ my @list;
+
+ for my $month (1 .. 12)
+ {
+ my $dt = DateTime->new( year => $year, month => $month, day => 13 );
+ my $dow = $dt->day_of_week;
+
+ push @list, $dt->month_abbr if $dow == $FRIDAY;
+ }
+
+ return \@list;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test, $year, $exp_count, $exp_list_str) = split / \| /x, $line;
+
+ for ($test, $year, $exp_count, $exp_list_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @exp_list = split / \s+ /x, $exp_list_str;
+ my $list = find_friday_13th( $year );
+ my $count = scalar @$list;
+
+ is $count, $exp_count, "$test: count";
+ is_deeply $list, \@exp_list, "$test: months";
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example |2023|2|Jan Oct
+Wikipedia 1|2015|3|Feb Mar Nov
+Wikipedia 2|1960|1|May
+Wikipedia 3|1900|2|Apr Jul
+Wikipedia 4|2099|3|Feb Mar Nov
diff --git a/challenge-227/athanasius/perl/ch-2.pl b/challenge-227/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..74ad9bbf95
--- /dev/null
+++ b/challenge-227/athanasius/perl/ch-2.pl
@@ -0,0 +1,262 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 227
+=========================
+
+TASK #2
+-------
+*Roman Maths*
+
+Submitted by: Peter Campbell Smith
+
+Write a script to handle a 2-term arithmetic operation expressed in Roman
+numeral.
+
+Example
+
+ IV + V => IX
+ M - I => CMXCIX
+ X / II => V
+ XI * VI => LXVI
+ VII ** III => CCCXLIII
+ V - V => nulla (they knew about zero but didn't have a symbol)
+ V / II => non potest (they didn't do fractions)
+ MMM + M => non potest (they only went up to 3999)
+ V - X => non potest (they didn't do negative numbers)
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+Notes
+-----
+1. Roman numerals in lower case will be converted to upper case.
+2. A non-standard form such as "IM" is (incorrectly?) interpreted as 1001, not
+ 999 as might be expected.
+
+=cut
+#===============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Switch::Plain;
+use Test::More;
+use Text::Roman qw( int2roman isroman roman2int );
+
+const my $MAX_ROM_NUM => 3999;
+const my @OPERATORS => qw( + - * / ** );
+const my $ROM_NUM_ZERO => 'nulla'; # Latin "no", Italian "nothing"
+const my $UNDEFINED => 'non potest'; # Latin "can not"
+const my $USAGE =>
+"Usage:
+ perl $0 <operand1> <operator> <operand2>
+ perl $0
+
+ <operand1> Left operand: number in Roman numerals
+ <operator> Arithmetic operator: + - * / **
+ <operand2> Right operand: number in Roman numerals\n";
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 227, Task #2: Roman Maths (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $args = scalar @ARGV;
+
+ if ($args == 0)
+ {
+ run_tests();
+ }
+ elsif ($args == 3)
+ {
+ my ($operand1, $operator, $operand2) = parse_command_line();
+
+ print "Input: $operand1 $operator $operand2\n";
+
+ my $result = calculate( $operand1, $operator, $operand2 );
+
+ print "Output: $result\n";
+ }
+ else
+ {
+ error( "Expected 0 or 3 command-line arguments, found $args" );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub calculate
+#-------------------------------------------------------------------------------
+{
+ my ($operand1, $operator, $operand2) = @_;
+ my $result;
+ my $left_op = roman2int( $operand1 );
+ my $right_op = roman2int( $operand2 );
+
+ sswitch ($operator)
+ {
+ case '+': { $result = add( $left_op, $right_op ); }
+ case '-': { $result = subtract( $left_op, $right_op ); }
+ case '*': { $result = multiply( $left_op, $right_op ); }
+ case '/': { $result = divide( $left_op, $right_op ); }
+ case '**': { $result = raise_to_power( $left_op, $right_op ); }
+ default: { die qq[Invalid operator "$operator"]; }
+ }
+
+ return $result;
+}
+
+#-------------------------------------------------------------------------------
+sub add
+#-------------------------------------------------------------------------------
+{
+ my ($left_op, $right_op) = @_;
+
+ my $sum = $left_op + $right_op;
+
+ return $sum > $MAX_ROM_NUM ? $UNDEFINED : int2roman( $sum );
+}
+
+#-------------------------------------------------------------------------------
+sub subtract
+#-------------------------------------------------------------------------------
+{
+ my ($left_op, $right_op) = @_;
+
+ my $difference = $left_op - $right_op;
+
+ return $difference == 0 ? $ROM_NUM_ZERO :
+ $difference < 0 ? $UNDEFINED : int2roman( $difference );
+}
+
+#-------------------------------------------------------------------------------
+sub multiply
+#-------------------------------------------------------------------------------
+{
+ my ($left_op, $right_op) = @_;
+
+ my $product = $left_op * $right_op;
+
+ return $product > $MAX_ROM_NUM ? $UNDEFINED : int2roman( $product );
+}
+
+#-------------------------------------------------------------------------------
+sub divide
+#-------------------------------------------------------------------------------
+{
+ my ($left_op, $right_op) = @_;
+
+ return ($left_op % $right_op) ? $UNDEFINED :
+ int2roman( int( $left_op / $right_op ) );
+}
+
+#-------------------------------------------------------------------------------
+sub raise_to_power
+#-------------------------------------------------------------------------------
+{
+ my ($left_op, $right_op) = @_;
+
+ my $power = $left_op ** $right_op;
+
+ return $power > $MAX_ROM_NUM ? $UNDEFINED : int2roman( $power );
+}
+
+#-------------------------------------------------------------------------------
+sub parse_command_line
+#-------------------------------------------------------------------------------
+{
+ my ($operand1, $operator, $operand2) = @ARGV;
+
+ $operand1 = uc $operand1;
+ $operand2 = uc $operand2;
+
+ isroman( $operand1 )
+ or error( qq["$operand1" is not a valid Roman numeral] );
+
+ my $valid_op = 0;
+
+ for my $op (@OPERATORS)
+ {
+ if ($operator eq $op)
+ {
+ $valid_op = 1;
+ last;
+ }
+ }
+
+ $valid_op or error( qq["$operator" is not a valid arithmetic operator] );
+
+ isroman( $operand2 )
+ or error( qq["$operand2" is not a valid Roman numeral] );
+
+ return ($operand1, $operator, $operand2);
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $op1, $opr, $op2, $expected) = split / \| /x, $line;
+
+ for ($test_name, $op1, $opr, $op2, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my $result = calculate( $op1, $opr, $op2 );
+
+ is $result, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1 |IV |+ |V |IX
+Example 2 |M |- |I |CMXCIX
+Example 3 |X |/ |II |V
+Example 4 |XI |* |VI |LXVI
+Example 5 |VII |**|III|CCCXLIII
+Example 6 |V |- |V |nulla
+Example 7 |V |/ |II |non potest
+Example 8 |MMM |+ |M |non potest
+Example 9 |V |- |X |non potest
+Large division|MMMCMXCVI|/ |IV |CMXCIX
diff --git a/challenge-227/athanasius/raku/ch-1.raku b/challenge-227/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..4124c630d2
--- /dev/null
+++ b/challenge-227/athanasius/raku/ch-1.raku
@@ -0,0 +1,176 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 227
+=========================
+
+TASK #1
+-------
+*Friday 13th*
+
+Submitted by: Peter Campbell Smith
+
+You are given a year number in the range 1753 to 9999.
+
+Write a script to find out how many dates in the year are Friday 13th, assume
+that the current Gregorian calendar applies.
+
+Example
+
+ Input: $year = 2023
+ Output: 2
+
+ Since there are only 2 Friday 13th in the given year 2023 i.e. 13th Jan and
+ 13th Oct.
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. If $VERBOSE is set to True, the solution (the number of dates) is followed by
+ a list of the months in the given year in which the 13th is a Friday.
+
+Note
+----
+"Friday the 13th .... occurs when the 13th day of the month in the Gregorian
+calendar falls on a Friday, which happens at least once every year but can occur
+up to three times in the same year." --"Friday the 13th", Wikipedia
+
+=end comment
+#===============================================================================
+
+use Date::Names;
+use Test;
+
+my UInt constant $FRIDAY = 5; # Date::day-of-week() outputs 1-7, Monday is 1
+my UInt constant $MIN = 1753;
+my UInt constant $MAX = 9999;
+my Bool constant $VERBOSE = True;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 227, Task #1: Friday 13th (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ UInt:D $year where $MIN <= * <= $MAX #= A year in the range 1753 to 9999
+)
+#===============================================================================
+{
+ "Input: \$year = $year".put;
+
+ my Str @list = find-friday_13th( $year );
+ my UInt $count = @list.elems;
+
+ "Output: $count".put;
+
+ if $VERBOSE
+ {
+ "\nMonth%s in %d in which 13th falls on a Friday: %s\n".printf:
+ $count == 1 ?? '' !! 's', $year, @list.join: ', ';
+ }
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-friday_13th( UInt:D $year where $MIN <= * <= $MAX --> List:D[Str:D] )
+#-------------------------------------------------------------------------------
+{
+ my Date::Names $dn = Date::Names.new: :lang< en >, :mset< mon3 >;
+ my Str @list;
+
+ for 1 .. 12 -> UInt $month
+ {
+ my Date $date = Date.new: year => $year, month => $month, day => 13;
+ my UInt $dow = $date.day-of-week;
+
+ @list.push: $dn.mon( $month ) if $dow == $FRIDAY;
+ }
+
+ return @list;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test, $year, $exp-count, $exp-list-str) = $line.split: / \| /;
+
+ for $test, $year, $exp-count, $exp-list-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Str @exp-list = $exp-list-str.split: / \s+ /;
+ my Str @list = find-friday_13th( $year.Int );
+ my UInt $count = @list.elems;
+
+ is $count, $exp-count.Int, "$test: count";
+ is-deeply @list, @exp-list, "$test: months";
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error( Str:D $message )
+#-------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#-------------------------------------------------------------------------------
+sub USAGE()
+#-------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+#-------------------------------------------------------------------------------
+sub test-data( --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example |2023|2|Jan Oct
+ Wikipedia 1|2015|3|Feb Mar Nov
+ Wikipedia 2|1960|1|May
+ Wikipedia 3|1900|2|Apr Jul
+ Wikipedia 4|2099|3|Feb Mar Nov
+ END
+}
+
+################################################################################
diff --git a/challenge-227/athanasius/raku/ch-2.raku b/challenge-227/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..a24a6b882f
--- /dev/null
+++ b/challenge-227/athanasius/raku/ch-2.raku
@@ -0,0 +1,271 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 227
+=========================
+
+TASK #2
+-------
+*Roman Maths*
+
+Submitted by: Peter Campbell Smith
+
+Write a script to handle a 2-term arithmetic operation expressed in Roman
+numeral.
+
+Example
+
+ IV + V => IX
+ M - I => CMXCIX
+ X / II => V
+ XI * VI => LXVI
+ VII ** III => CCCXLIII
+ V - V => nulla (they knew about zero but didn't have a symbol)
+ V / II => non potest (they didn't do fractions)
+ MMM + M => non potest (they only went up to 3999)
+ V - X => non potest (they didn't do negative numbers)
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+Notes
+-----
+1. Roman numerals in lower case will be converted to upper case.
+2. A non-standard form such as "IM" is (incorrectly?) interpreted as 1001, not
+ 999 as might be expected.
+
+=end comment
+#===============================================================================
+
+use Math::Roman;
+use Test;
+
+my UInt constant $MAX-ROM-NUM = 3999;
+my constant @OPERATORS = Array[Str].new: < + - * / ** >;
+my Str constant $ROM-NUM-ZERO = 'nulla'; # Latin "no", Italian "nothing"
+my Str constant $UNDEFINED = 'non potest'; # Latin "can not"
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 227, Task #2: Roman Maths (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ Str:D $operand1, #= Left operand: number in Roman numerals
+ Str:D $operator, #= Arithmetic operator: + - * / **
+ Str:D $operand2 #= Right operand: number in Roman numerals
+)
+#===============================================================================
+{
+ my Str $op1 = $operand1.uc;
+ my Str $op2 = $operand2.uc;
+
+ is-roman-num( $op1 )
+ or error( qq["$op1" is not a valid Roman numeral] );
+
+ is-operator\( $operator )
+ or error( qq["$operator" is not a valid arithmetic operator] );
+
+ is-roman-num( $op2 )
+ or error( qq["$op2" is not a valid Roman numeral] );
+
+ "Input: $op1 $operator $op2".put;
+
+ my Str $result = calculate( $op1, $operator, $op2 );
+
+ "Output: $result".put;
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub calculate( Str:D $operand1, Str:D $operator, Str:D $operand2 --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my Str $result;
+ my Math::Roman $left-op = Math::Roman.new: $operand1;
+ my Math::Roman $right-op = Math::Roman.new: $operand2;
+
+ given $operator
+ {
+ when '+' { $result = add( $left-op, $right-op ); }
+ when '-' { $result = subtract( $left-op, $right-op ); }
+ when '*' { $result = multiply( $left-op, $right-op ); }
+ when '/' { $result = divide( $left-op, $right-op ); }
+ when '**' { $result = raise-to-power( $left-op, $right-op ); }
+ default { die qq[Invalid operator "$operator"]; }
+ }
+
+ return $result;
+}
+
+#-------------------------------------------------------------------------------
+sub add( Math::Roman:D $left-op, Math::Roman:D $right-op --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt $sum = $left-op + $right-op;
+
+ return $sum > $MAX-ROM-NUM ?? $UNDEFINED !! to-roman( $sum );
+}
+
+#-------------------------------------------------------------------------------
+sub subtract( Math::Roman:D $left-op, Math::Roman:D $right-op --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my Int $difference = $left-op - $right-op;
+
+ return $difference == 0 ?? $ROM-NUM-ZERO !!
+ $difference < 0 ?? $UNDEFINED !! to-roman( $difference );
+}
+
+#-------------------------------------------------------------------------------
+sub multiply( Math::Roman:D $left-op, Math::Roman:D $right-op --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt $product = $left-op * $right-op;
+
+ return $product > $MAX-ROM-NUM ?? $UNDEFINED !! to-roman( $product );
+}
+
+#-------------------------------------------------------------------------------
+sub divide( Math::Roman:D $left-op, Math::Roman:D $right-op --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my Rat $quotient = $left-op / $right-op;
+
+ return $quotient.denominator == 1 ?? to-roman( $quotient.Int ) !!
+ $UNDEFINED;
+}
+
+#-------------------------------------------------------------------------------
+sub raise-to-power( Math::Roman:D $left-op, Math::Roman:D $right-op --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt $power = $left-op ** $right-op;
+
+ return $power > $MAX-ROM-NUM ?? $UNDEFINED !! to-roman( $power );
+}
+
+#-------------------------------------------------------------------------------
+sub is-roman-num( Str:D $str --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ my Bool $valid = True;
+ my Math::Roman $roman-num;
+
+ {
+ CATCH
+ {
+ when X::TypeCheck::Return
+ {
+ $valid = False;
+ }
+ }
+
+ $roman-num = Math::Roman.new: $str;
+ }
+
+ $valid = False if $valid && $roman-num > $MAX-ROM-NUM;
+
+ return $valid;
+}
+
+#-------------------------------------------------------------------------------
+sub is-operator( Str:D $str --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ for @OPERATORS -> Str $op
+ {
+ return True if $str eq $op;
+ }
+
+ return False;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $op1, $opr, $op2, $expected) = $line.split: / \| /;
+
+ for $test-name, $op1, $opr, $op2, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Str $result = calculate( $op1, $opr, $op2 );
+
+ is $result, $expected, $test-name;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error( Str:D $message )
+#-------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#-------------------------------------------------------------------------------
+sub USAGE()
+#-------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+#-------------------------------------------------------------------------------
+sub test-data( --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example 1 |IV |+ |V |IX
+ Example 2 |M |- |I |CMXCIX
+ Example 3 |X |/ |II |V
+ Example 4 |XI |* |VI |LXVI
+ Example 5 |VII |**|III|CCCXLIII
+ Example 6 |V |- |V |nulla
+ Example 7 |V |/ |II |non potest
+ Example 8 |MMM |+ |M |non potest
+ Example 9 |V |- |X |non potest
+ Large division|MMMCMXCVI|/ |IV |CMXCIX
+ END
+}
+
+################################################################################