aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2024-01-15 00:42:51 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2024-01-15 00:42:51 +1000
commitb37cacdeb8b2a8b97abeddc3466f1326bff0d3a5 (patch)
tree03d515c00f27d7612c879c947f91a30d04af6e22
parentdba8c691b150fd0086ac6f95e674d0302f437cac (diff)
downloadperlweeklychallenge-club-b37cacdeb8b2a8b97abeddc3466f1326bff0d3a5.tar.gz
perlweeklychallenge-club-b37cacdeb8b2a8b97abeddc3466f1326bff0d3a5.tar.bz2
perlweeklychallenge-club-b37cacdeb8b2a8b97abeddc3466f1326bff0d3a5.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 251
-rw-r--r--challenge-251/athanasius/perl/ch-1.pl205
-rw-r--r--challenge-251/athanasius/perl/ch-2.pl307
-rw-r--r--challenge-251/athanasius/raku/ch-1.raku206
-rw-r--r--challenge-251/athanasius/raku/ch-2.raku313
4 files changed, 1031 insertions, 0 deletions
diff --git a/challenge-251/athanasius/perl/ch-1.pl b/challenge-251/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..e946765634
--- /dev/null
+++ b/challenge-251/athanasius/perl/ch-1.pl
@@ -0,0 +1,205 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 251
+=========================
+
+TASK #1
+-------
+*Concatenation Value*
+
+Submitted by: Mohammad S Anwar
+
+You are given an array of integers, @ints.
+
+Write a script to find the concatenation value of the given array.
+
+The concatenation of two numbers is the number formed by concatenating their
+numerals.
+
+ For example, the concatenation of 10, 21 is 1021.
+ The concatenation value of @ints is initially equal to 0.
+ Perform this operation until @ints becomes empty:
+
+ If there exists more than one number in @ints, pick the first element
+ and last element in @ints respectively and add the value of their
+ concatenation to the concatenation value of @ints, then delete the
+ first and last element from @ints.
+
+ If one element exists, add its value to the concatenation value of
+ @ints, then delete it.
+
+Example 1
+
+ Input: @ints = (6, 12, 25, 1)
+ Output: 1286
+
+ 1st operation: concatenation of 6 and 1 is 61
+ 2nd operation: concaternation of 12 and 25 is 1225
+
+ Concatenation Value => 61 + 1225 => 1286
+
+Example 2
+
+ Input: @ints = (10, 7, 31, 5, 2, 2)
+ Output: 489
+
+ 1st operation: concatenation of 10 and 2 is 102
+ 2nd operation: concatenation of 7 and 2 is 72
+ 3rd operation: concatenation of 31 and 5 is 315
+
+ Concatenation Value => 102 + 72 + 315 => 489
+
+Example 3
+
+ Input: @ints = (1, 2, 10)
+ Output: 112
+
+ 1st operation: concatenation of 1 and 10 is 110
+ 2nd operation: only element left is 2
+
+ Concatenation Value => 110 + 2 => 112
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Assumption
+----------
+The input integers are unsigned (i.e., non-negative).
+
+Rationale: If the right hand side of a concatenation is a negative integer, then
+the result of the concatenation will not be an integer. (For example, "3" con-
+catenated with "-45" gives "3-45", which is not an integer.) The simplest way to
+avoid this possibility is to disallow negative numbers in the input.
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+=cut
+#===============================================================================
+
+use v5.32.1; # Enables strictures
+use warnings;
+use Const::Fast;
+use Regexp::Common qw( number );
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<ints> ...]
+ perl $0
+
+ [<ints> ...] A non-empty list of unsigned integers
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 251, Task #1: Concatenation Value (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my @ints = @ARGV;
+
+ for (@ints)
+ {
+ / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] );
+
+ $_ >= 0 or error( "$_ is negative" );
+ }
+
+ printf "Input: \@ints = (%s)\n", join ', ', @ints;
+
+ my $concat_value = find_concatenation_value( \@ints );
+
+ print "Output: $concat_value\n";
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_concatenation_value
+#-------------------------------------------------------------------------------
+{
+ my ($ints) = @_;
+ my $concat_value = 0;
+
+ while (@$ints)
+ {
+ # Addition of 0 normalizes an integer; for example, "+234" becomes "234"
+
+ if (scalar @$ints == 1)
+ {
+ $concat_value += 0 + shift @$ints;
+ }
+ else
+ {
+ $concat_value += (0 + shift @$ints) . (0 + pop @$ints);
+ }
+ }
+
+ return $concat_value;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $ints_str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $ints_str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @ints = split / \s+ /x, $ints_str;
+ my $concat_value = find_concatenation_value( \@ints );
+
+ is $concat_value, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 6 12 25 1 |1286
+Example 2|10 7 31 5 2 2| 489
+Example 3| 1 2 10 | 112
diff --git a/challenge-251/athanasius/perl/ch-2.pl b/challenge-251/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..1d521c3619
--- /dev/null
+++ b/challenge-251/athanasius/perl/ch-2.pl
@@ -0,0 +1,307 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 251
+=========================
+
+TASK #2
+-------
+*Lucky Numbers*
+
+Submitted by: Mohammad S Anwar
+
+You are given a m x n matrix of distinct numbers.
+
+Write a script to return the lucky number, if there is one, or -1 if not.
+
+ A lucky number is an element of the matrix such that it is
+ the minimum element in its row and maximum in its column.
+
+Example 1
+
+ Input: $matrix = [ [ 3, 7, 8],
+ [ 9, 11, 13],
+ [15, 16, 17] ];
+ Output: 15
+
+ 15 is the only lucky number since it is the minimum in its row
+ and the maximum in its column.
+
+Example 2
+
+ Input: $matrix = [ [ 1, 10, 4, 2],
+ [ 9, 3, 8, 7],
+ [15, 16, 17, 12] ];
+ Output: 12
+
+Example 3
+
+ Input: $matrix = [ [7, 8],
+ [1, 2] ];
+ Output: 7
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+Assumptions
+-----------
+Since -1 is returned on failure, elements of the input matrix are limited to
+non-negative numbers. For convenience, they are in addition limited to integers.
+
+Algorithm
+---------
+This is a simple search for a lucky number: if one is found, it is immediately
+returned, otherwise after the search has completed -1 is returned.
+
+In fact, there can only ever be one lucky number for a given matrix. Proof (by
+contradiction):
+
+1. Let element x = matrix(i, j) be a lucky number. Can there be another lucky
+ number in the same matrix?
+
+2. Assume there is a lucky number y = matrix(k, l) with k ≠ i and l ≠ j.
+
+3. Let w = matrix(i, l). Since x is the smallest element in row i, w > x.
+ And since y is the largest element in column l, y > w. So, y > w > x, and by
+ transitivity, y > x (a).
+
+4. Let z = matrix(k, j). Since x is the largest element in column j, z < x.
+ And since y is the smallest element in row k, y < z. So, y < z < x, and by
+ transitivity, y < x (b).
+
+5. Relations (a) and (b) are mutually contradictory, therefore the assumption
+ made in step 2 must be false. QED.
+
+Note
+----
+This solution incorporates or adapts code used in the solution of Task 2 for
+Challenge 248.
+
+=cut
+#===============================================================================
+
+use v5.32.1; # Enables strictures
+use warnings;
+use Const::Fast;
+use List::Util qw( min );
+use Regexp::Common qw( number );
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<matrix> ...]
+ perl $0
+
+ [<matrix> ...] A non-empty matrix of distinct unsigned integers
+ e.g., "3 7 8" "9 11 13" "15 16 17"
+ Note: the input matrix must be rectangular
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 251, Task #2: Lucky Numbers (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my $matrix = parse_matrix( \@ARGV );
+
+ print_matrix( 'Input: $matrix = ', $matrix );
+
+ printf "Output: %d\n", find_lucky_number( $matrix );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_lucky_number
+#-------------------------------------------------------------------------------
+{
+ my ($matrix) = @_;
+
+ for my $row (@$matrix)
+ {
+ my $row_min = min @$row; # Row minimum
+
+ for my $col (0 .. $#$row)
+ {
+ if ($row->[ $col ] == $row_min)
+ {
+ my $col_max = 0;
+
+ for my $row (0 .. $#$matrix) # Find column maximum
+ {
+ my $elem = $matrix->[ $row ][ $col ];
+
+ $col_max = $elem if $elem > $col_max;
+ }
+
+ return $row_min if $row_min == $col_max;
+ }
+ }
+ }
+
+ return -1;
+}
+
+#-------------------------------------------------------------------------------
+sub parse_matrix
+#-------------------------------------------------------------------------------
+{
+ my ($matrix_strs) = @_;
+ my @matrix;
+ my %dict;
+ my $num_cols;
+
+ for my $row_str (@$matrix_strs)
+ {
+ my $row = fill_row( $row_str, \%dict );
+
+ push @matrix, $row;
+
+ if (defined $num_cols)
+ {
+ scalar @$row == $num_cols
+ or error( 'The matrix is not rectangular' );
+ }
+ else
+ {
+ ($num_cols = scalar @$row) > 0
+ or error( 'The first row is empty' );
+ }
+ }
+
+ return \@matrix;
+}
+
+#-------------------------------------------------------------------------------
+sub fill_row
+#-------------------------------------------------------------------------------
+{
+ my ($row_str, $dict) = @_;
+ my @row;
+
+ for my $elem_str (split ' ', $row_str) # split ' ' removes leading ws
+ {
+ $elem_str =~ / ^ $RE{num}{int} $ /x
+ or error( qq["$elem_str" is not a valid integer] );
+
+ my $elem = $elem_str + 0; # Normalize
+
+ $elem >= 0
+ or error( qq["$elem" is negative] );
+
+ ++$dict->{ $elem } == 1
+ or error( qq[Element "$elem" is repeated] );
+
+ push @row, $elem;
+ }
+
+ return \@row;
+}
+
+#-------------------------------------------------------------------------------
+sub print_matrix
+#-------------------------------------------------------------------------------
+{
+ my ($prefix, $matrix) = @_;
+ my $tab = ' ' x length $prefix;
+ my @width = (1) x scalar @{ $matrix->[ 0 ] };
+
+ for my $row (@$matrix)
+ {
+ for my $i (0 .. $#$row)
+ {
+ my $w = length $row->[ $i ];
+
+ $width[ $i ] = $w if $w > $width[ $i ];
+ }
+ }
+
+ print "$prefix\[ ";
+
+ for my $i (0 .. $#$matrix)
+ {
+ my $row = $matrix->[ $i ];
+ my @row_str;
+
+ for my $j (0 .. $#$row)
+ {
+ push @row_str, sprintf '%*d', $width[ $j ], $row->[ $j ];
+ }
+
+ printf "%s\[%s]", $i == 0 ? '' : $tab . ' ', join ', ', @row_str;
+ print "\n" unless $i == $#$matrix;
+ }
+
+ print " ]\n";
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $matrix_str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $matrix_str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @rows = split / \; /x, $matrix_str;
+ my $matrix = parse_matrix( \@rows );
+ my $lucky_num = find_lucky_number( $matrix );
+
+ is $lucky_num, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 3 7 8; 9 11 13; 15 16 17|15
+Example 2|1 10 4 2; 9 3 8 7; 15 16 17 12|12
+Example 3| 7 8; 1 2; | 7
+Singleton| 4; | 4
+None | 1 3; 4 2; |-1
diff --git a/challenge-251/athanasius/raku/ch-1.raku b/challenge-251/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..45ac215f86
--- /dev/null
+++ b/challenge-251/athanasius/raku/ch-1.raku
@@ -0,0 +1,206 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 251
+=========================
+
+TASK #1
+-------
+*Concatenation Value*
+
+Submitted by: Mohammad S Anwar
+
+You are given an array of integers, @ints.
+
+Write a script to find the concatenation value of the given array.
+
+The concatenation of two numbers is the number formed by concatenating their
+numerals.
+
+ For example, the concatenation of 10, 21 is 1021.
+ The concatenation value of @ints is initially equal to 0.
+ Perform this operation until @ints becomes empty:
+
+ If there exists more than one number in @ints, pick the first element
+ and last element in @ints respectively and add the value of their
+ concatenation to the concatenation value of @ints, then delete the
+ first and last element from @ints.
+
+ If one element exists, add its value to the concatenation value of
+ @ints, then delete it.
+
+Example 1
+
+ Input: @ints = (6, 12, 25, 1)
+ Output: 1286
+
+ 1st operation: concatenation of 6 and 1 is 61
+ 2nd operation: concaternation of 12 and 25 is 1225
+
+ Concatenation Value => 61 + 1225 => 1286
+
+Example 2
+
+ Input: @ints = (10, 7, 31, 5, 2, 2)
+ Output: 489
+
+ 1st operation: concatenation of 10 and 2 is 102
+ 2nd operation: concatenation of 7 and 2 is 72
+ 3rd operation: concatenation of 31 and 5 is 315
+
+ Concatenation Value => 102 + 72 + 315 => 489
+
+Example 3
+
+ Input: @ints = (1, 2, 10)
+ Output: 112
+
+ 1st operation: concatenation of 1 and 10 is 110
+ 2nd operation: only element left is 2
+
+ Concatenation Value => 110 + 2 => 112
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Assumption
+----------
+The input integers are unsigned (i.e., non-negative).
+
+Rationale: If the right hand side of a concatenation is a negative integer, then
+the result of the concatenation will not be an integer. (For example, "3" con-
+catenated with "-45" gives "3-45", which is not an integer.) The simplest way to
+avoid this possibility is to disallow negative numbers in the input.
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 251, Task #1: Concatenation Value (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty list of unsigned integers
+
+ *@ints where { .elems > 0 && .all ~~ UInt:D }
+)
+#===============================================================================
+{
+ "Input: \@ints = (%s)\n".printf: @ints.join: ', ';
+
+ my UInt $concat-value = find-concatenation-value( @ints );
+
+ "Output: $concat-value".put;
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-concatenation-value( List:D[UInt:D] $ints --> UInt:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt @ints = @$ints;
+ my UInt $concat-value = 0;
+
+ while @ints
+ {
+ # The prepended "+" operators normalize the integers; for example,
+ # "+234" becomes "234"
+
+ if @ints.elems == 1
+ {
+ $concat-value += +@ints.shift;
+ }
+ else
+ {
+ $concat-value += +@ints.shift ~ +@ints.pop;
+ }
+ }
+
+ return $concat-value;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $ints-str, $expected) = $line.split: / \| /;
+
+ for $test-name, $ints-str, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my UInt @ints = $ints-str.split( / \s+ / ).map: { .Int };
+ my UInt $concat-value = find-concatenation-value( @ints );
+
+ is $concat-value, $expected.Int, $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| 6 12 25 1 |1286
+ Example 2|10 7 31 5 2 2| 489
+ Example 3| 1 2 10 | 112
+ END
+}
+
+################################################################################
diff --git a/challenge-251/athanasius/raku/ch-2.raku b/challenge-251/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..4fd942d0a1
--- /dev/null
+++ b/challenge-251/athanasius/raku/ch-2.raku
@@ -0,0 +1,313 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 251
+=========================
+
+TASK #2
+-------
+*Lucky Numbers*
+
+Submitted by: Mohammad S Anwar
+
+You are given a m x n matrix of distinct numbers.
+
+Write a script to return the lucky number, if there is one, or -1 if not.
+
+ A lucky number is an element of the matrix such that it is
+ the minimum element in its row and maximum in its column.
+
+Example 1
+
+ Input: $matrix = [ [ 3, 7, 8],
+ [ 9, 11, 13],
+ [15, 16, 17] ];
+ Output: 15
+
+ 15 is the only lucky number since it is the minimum in its row
+ and the maximum in its column.
+
+Example 2
+
+ Input: $matrix = [ [ 1, 10, 4, 2],
+ [ 9, 3, 8, 7],
+ [15, 16, 17, 12] ];
+ Output: 12
+
+Example 3
+
+ Input: $matrix = [ [7, 8],
+ [1, 2] ];
+ Output: 7
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+Assumptions
+-----------
+Since -1 is returned on failure, elements of the input matrix are limited to
+non-negative numbers. For convenience, they are in addition limited to integers.
+
+Algorithm
+---------
+This is a simple search for a lucky number: if one is found, it is immediately
+returned, otherwise after the search has completed -1 is returned.
+
+In fact, there can only ever be one lucky number for a given matrix. Proof (by
+contradiction):
+
+1. Let element x = matrix(i, j) be a lucky number. Can there be another lucky
+ number in the same matrix?
+
+2. Assume there is a lucky number y = matrix(k, l) with k ≠ i and l ≠ j.
+
+3. Let w = matrix(i, l). Since x is the smallest element in row i, w > x.
+ And since y is the largest element in column l, y > w. So, y > w > x, and by
+ transitivity, y > x (a).
+
+4. Let z = matrix(k, j). Since x is the largest element in column j, z < x.
+ And since y is the smallest element in row k, y < z. So, y < z < x, and by
+ transitivity, y < x (b).
+
+5. Relations (a) and (b) are mutually contradictory, therefore the assumption
+ made in step 2 must be false. QED.
+
+Note
+----
+This solution incorporates or adapts code used in the solution of Task 2 for
+Challenge 248.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 251, Task #2: Lucky Numbers (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty matrix of distinct unsigned integers
+ # e.g., "3 7 8" "9 11 13" "15 16 17"
+ # Note: the input matrix must be rectangular
+
+ *@matrix where { .elems > 0 && .all ~~ Str:D }
+)
+#===============================================================================
+{
+ my Array[Array[UInt]] $matrix = parse-matrix( @matrix );
+
+ print-matrix( 'Input: $matrix = ', $matrix );
+
+ "Output: %d\n".printf: find-lucky-number( $matrix );
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-lucky-number( List:D[List:D[UInt:D]] $matrix --> Int:D )
+#-------------------------------------------------------------------------------
+{
+ for @$matrix -> Array[UInt] $row
+ {
+ my UInt $row-min = $row.min; # Row minimum
+
+ for 0 .. $row.end -> UInt $col
+ {
+ if $row[ $col ] == $row-min
+ {
+ my UInt $col-max = 0;
+
+ for 0 .. $matrix.end -> UInt $row # Find column maximum
+ {
+ my UInt $elem = $matrix[ $row; $col ];
+
+ $col-max = $elem if $elem > $col-max;
+ }
+
+ return $row-min if $row-min == $col-max;
+ }
+ }
+ }
+
+ return -1;
+}
+
+#-------------------------------------------------------------------------------
+sub parse-matrix
+(
+ List:D[Str:D] $matrix-strs where { .elems > 0 }
+--> List:D[List:D[UInt:D]]
+)
+#-------------------------------------------------------------------------------
+{
+ my Array[UInt] @matrix;
+ my UInt %dict{UInt};
+ my UInt $num-cols;
+
+ for @$matrix-strs -> Str $row-str
+ {
+ my UInt @row = fill-row( $row-str, %dict );
+
+ @matrix.push: @row;
+
+ if $num-cols.defined
+ {
+ @row.elems == $num-cols
+ or error( 'The matrix is not rectangular' );
+ }
+ else
+ {
+ ($num-cols = @row.elems) > 0
+ or error( 'The first row is empty' );
+ }
+ }
+
+ return @matrix;
+}
+
+#-------------------------------------------------------------------------------
+sub fill-row( Str:D $row-str, Hash:D[UInt:D, UInt:D] $dict --> List:D[UInt:D] )
+#-------------------------------------------------------------------------------
+{
+ my UInt @row;
+
+ for $row-str.split( / \s+ /, :skip-empty ) -> Str $elem-str
+ {
+ +$elem-str ~~ UInt:D
+ or error( qq["$elem-str" is not a valid unsigned integer] );
+
+ my UInt $elem = +$elem-str; # Normalize
+
+ ++$dict{ $elem } == 1
+ or error( qq[Element "$elem" is repeated] );
+
+ @row.push: $elem;
+ }
+
+ return @row;
+}
+
+#-------------------------------------------------------------------------------
+sub print-matrix( Str:D $prefix, List:D[List:D[Int:D]] $matrix )
+#-------------------------------------------------------------------------------
+{
+ my Str $tab = ' ' x $prefix.chars;
+ my UInt @width = 1 xx $matrix[ 0 ].elems;
+
+ for @$matrix -> Int @row
+ {
+ for 0 .. @row.end -> UInt $i
+ {
+ my UInt $w = @row[ $i ].chars;
+
+ @width[ $i ] = $w if $w > @width[ $i ];
+ }
+ }
+
+ "$prefix\[ ".print;
+
+ for 0 .. $matrix.end -> UInt $i
+ {
+ my Array[UInt] $row = $matrix[ $i ];
+ my Str @row-str;
+
+ for 0 .. $row.end -> UInt $j
+ {
+ @row-str.push: '%*d'.sprintf: @width[ $j ], $row[ $j ];
+ }
+
+ "%s\[%s]".printf: $i == 0 ?? '' !! $tab ~ ' ', @row-str.join: ', ';
+
+ put() unless $i == $matrix.end;
+ }
+
+ ' ]'.put;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $matrix-str, $expected) = $line.split: / \| /;
+
+ for $test-name, $matrix-str, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Array[Array[UInt]] $matrix =
+ parse-matrix( $matrix-str.split( / \; /, :skip-empty ).list );
+
+ my Int $lucky-num = find-lucky-number( $matrix );
+
+ is $lucky-num, $expected.Int, $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| 3 7 8; 9 11 13; 15 16 17|15
+ Example 2|1 10 4 2; 9 3 8 7; 15 16 17 12|12
+ Example 3| 7 8; 1 2; | 7
+ Singleton| 4; | 4
+ None | 1 3; 4 2; |-1
+ END
+}
+
+################################################################################