aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2024-02-21 09:09:21 +0000
committerGitHub <noreply@github.com>2024-02-21 09:09:21 +0000
commitca0b0155464c85ed3a2a2f3a35d885fc2db6480d (patch)
tree429b642941c84dc992b38aa77d0fd977795dd1d9
parent1036bfd73a5af401f2fb49d91cb22d62f9a782eb (diff)
parent0bc762c27d1e12169f8c2ce41e5feb8902cdc8af (diff)
downloadperlweeklychallenge-club-ca0b0155464c85ed3a2a2f3a35d885fc2db6480d.tar.gz
perlweeklychallenge-club-ca0b0155464c85ed3a2a2f3a35d885fc2db6480d.tar.bz2
perlweeklychallenge-club-ca0b0155464c85ed3a2a2f3a35d885fc2db6480d.zip
Merge pull request #9620 from PerlMonk-Athanasius/branch-for-challenge-257
Perl & Raku solutions to Tasks 1 & 2 for Week 257
-rw-r--r--challenge-257/athanasius/perl/ch-1.pl177
-rw-r--r--challenge-257/athanasius/perl/ch-2.pl410
-rw-r--r--challenge-257/athanasius/raku/ch-1.raku178
-rw-r--r--challenge-257/athanasius/raku/ch-2.raku410
4 files changed, 1175 insertions, 0 deletions
diff --git a/challenge-257/athanasius/perl/ch-1.pl b/challenge-257/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..9be329e6ae
--- /dev/null
+++ b/challenge-257/athanasius/perl/ch-1.pl
@@ -0,0 +1,177 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 257
+=========================
+
+TASK #1
+-------
+*Smaller than Current*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers, @ints.
+
+Write a script to find out how many integers are smaller than current i.e.
+foreach ints[i], count ints[j] < ints[i] where i != j.
+
+Example 1
+
+ Input: @ints = (5, 2, 1, 6)
+ Output: (2, 1, 0, 3)
+
+ For $ints[0] = 5, there are two integers (2,1) smaller than 5.
+ For $ints[1] = 2, there is one integer (1) smaller than 2.
+ For $ints[2] = 1, there is none integer smaller than 1.
+ For $ints[3] = 6, there are three integers (5,2,1) smaller than 6.
+
+Example 2
+
+ Input: @ints = (1, 2, 0, 3)
+ Output: (1, 2, 0, 3)
+
+Example 3
+
+ Input: @ints = (0, 1)
+ Output: (0, 1)
+
+Example 4
+
+ Input: @ints = (9, 4, 9, 2)
+ Output: (2, 1, 2, 0)
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+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 array of integers
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 257, Task #1: Smaller than Current (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] );
+ }
+
+ printf "Input: \@ints = (%s)\n", join ', ', @ints;
+
+ my $counts = count_smaller_than_current( \@ints );
+
+ printf "Output: (%s)\n", join ', ', @$counts;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub count_smaller_than_current
+#-------------------------------------------------------------------------------
+{
+ my ($ints) = @_;
+ my @counts;
+
+ for my $i (0 .. $#$ints)
+ {
+ my $count = 0;
+
+ for my $j (0 .. $#$ints)
+ {
+ next if $i == $j;
+
+ ++$count if $ints->[ $j ] < $ints->[ $i ];
+ }
+
+ push @counts, $count;
+ }
+
+ return \@counts;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $int_str, $exp_str) = split / \| /x, $line;
+
+ for ($test_name, $int_str, $exp_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @ints = split / \s+ /x, $int_str;
+ my @exp = split / \s+ /x, $exp_str;
+ my $counts = count_smaller_than_current( \@ints );
+
+ is_deeply $counts, \@exp, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 5 2 1 6|2 1 0 3
+Example 2| 1 2 0 3|1 2 0 3
+Example 3| 0 1 |0 1
+Example 4| 9 4 9 2|2 1 2 0
+Negatives|-2 -2 -1 |0 0 2
diff --git a/challenge-257/athanasius/perl/ch-2.pl b/challenge-257/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..bb16d5aaf9
--- /dev/null
+++ b/challenge-257/athanasius/perl/ch-2.pl
@@ -0,0 +1,410 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 257
+=========================
+
+TASK #2
+-------
+*Reduced Row Echelon*
+
+Submitted by: Ali Moradi
+
+Given a matrix M, check whether the matrix is in reduced row echelon form.
+
+A matrix must have the following properties to be in reduced row echelon form:
+
+ 1. If a row does not consist entirely of zeros, then the first
+ nonzero number in the row is a 1. We call this the leading 1.
+ 2. If there are any rows that consist entirely of zeros, then
+ they are grouped together at the bottom of the matrix.
+ 3. In any two successive rows that do not consist entirely of zeros,
+ the leading 1 in the lower row occurs farther to the right than
+ the leading 1 in the higher row.
+ 4. Each column that contains a leading 1 has zeros everywhere else
+ in that column.
+
+For example:
+
+ [
+ [1,0,0,1],
+ [0,1,0,2],
+ [0,0,1,3]
+ ]
+
+The above matrix is in reduced row echelon form since the first nonzero number
+in each row is a 1, leading 1s in each successive row are farther to the right,
+and above and below each leading 1 there are only zeros.
+
+For more information check out this wikipedia
+[https://en.wikipedia.org/wiki/Row_echelon_form|article].
+
+Example 1
+
+ Input: $M = [
+ [1, 1, 0],
+ [0, 1, 0],
+ [0, 0, 0]
+ ]
+ Output: 0
+
+Example 2
+
+ Input: $M = [
+ [0, 1,-2, 0, 1],
+ [0, 0, 0, 1, 3],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0]
+ ]
+ Output: 1
+
+Example 3
+
+ Input: $M = [
+ [1, 0, 0, 4],
+ [0, 1, 0, 7],
+ [0, 0, 1,-1]
+ ]
+ Output: 1
+
+Example 4
+
+ Input: $M = [
+ [0, 1,-2, 0, 1],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 3],
+ [0, 0, 0, 0, 0]
+ ]
+ Output: 0
+
+Example 5
+
+ Input: $M = [
+ [0, 1, 0],
+ [1, 0, 0],
+ [0, 0, 0]
+ ]
+ Output: 0
+
+Example 6
+
+ Input: $M = [
+ [4, 0, 0, 0],
+ [0, 1, 0, 7],
+ [0, 0, 1,-1]
+ ]
+ Output: 0
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. The input matrix M is entered on the command-line as a non-empty list of
+ strings (the matrix rows) containing elements separated by whitespace. For
+ example, the matrix:
+
+ [ 1 1 0 ]
+ [ 0 1 0 ]
+ [ 0 0 0 ]
+
+ is entered as: >raku ch-2.raku "1 1 0" "0 1 0" "0 0 1"
+
+Assumptions
+-----------
+1. The input matrix M is an integer matrix.
+2. M is not an empty matrix.
+
+Note
+----
+Matrix-handling code is adapted from the solution to Task 2 for Week 248.
+
+=cut
+#===============================================================================
+
+use v5.32.1; # Enables strictures
+use warnings;
+use Const::Fast;
+use Regexp::Common qw( number );
+use Test::More;
+use enum qw( AllZeros LeadingOne Other );
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<M> ...]
+ perl $0
+
+ [<M> ...] A non-empty integer matrix
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 257, Task #2: Reduced Row Echelon (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my $matrix = parse_matrix( \@ARGV );
+
+ print_matrix( 'Input: $M = ', $matrix );
+
+ printf "Output: %d\n", is_reduced_row_echelon( $matrix );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub is_reduced_row_echelon
+#-------------------------------------------------------------------------------
+{
+ my ($matrix) = @_;
+
+ # Test matrix properties 1 and 2:
+ # 1. If a row does not consist entirely of zeros, then the first nonzero
+ # number in the row is a 1. We call this the leading 1.
+ # 2. If there are any rows that consist entirely of zeros, then they are
+ # grouped together at the bottom of the matrix.
+
+ my $row_types = classify_rows( $matrix );
+ my $all_zeros = 0;
+
+ for my $type (@$row_types)
+ {
+ if ($type == AllZeros)
+ {
+ $all_zeros = 1;
+ }
+ elsif ($type == LeadingOne)
+ {
+ return 0 if $all_zeros; # Requirement 2
+ }
+ else
+ {
+ return 0; # Requirement 1
+ }
+ }
+
+ # Test matrix properties 3 and 4:
+ # 3. In any two successive rows that do not consist entirely of zeros, the
+ # leading 1 in the lower row occurs farther to the right than the leading
+ # 1 in the higher row.
+ # 4. Each column that contains a leading 1 has zeros everywhere else in that
+ # column.
+
+ return check_leading_ones( $matrix, $row_types );
+}
+
+#-------------------------------------------------------------------------------
+sub classify_rows
+#-------------------------------------------------------------------------------
+{
+ my ($matrix) = @_;
+ my $width = scalar @{ $matrix->[ 0 ] };
+ my @row_types;
+
+ L_OUTER:
+ for my $r (0 .. $#$matrix)
+ {
+ my $row = $matrix->[ $r ];
+
+ for my $c (0 .. $width - 1)
+ {
+ my $element = $row->[ $c ];
+
+ if ($element == 1)
+ {
+ push @row_types, LeadingOne;
+ next L_OUTER;
+ }
+ elsif ($element != 0)
+ {
+ push @row_types, Other;
+ next L_OUTER;
+ }
+ }
+
+ push @row_types, AllZeros;
+ }
+
+ return \@row_types;
+}
+
+#-------------------------------------------------------------------------------
+sub check_leading_ones
+#-------------------------------------------------------------------------------
+{
+ my ($matrix, $row_t) = @_;
+ my $width = scalar @{ $matrix->[ 0 ] };
+ my $last_one = -1;
+
+ for my $r (0 .. $#$matrix)
+ {
+ last if $row_t->[ $r ] == AllZeros;
+
+ my $row = $matrix->[ $r ];
+
+ for my $c (0 .. $width - 1)
+ {
+ my $element = $row->[ $c ];
+
+ if ($element == 1)
+ {
+ return 0 unless $c > $last_one; # Test Requirement 3
+
+ $last_one = $c;
+
+ for my $rr (0 .. $#$matrix) # Test Requirement 4
+ {
+ return 0 unless $rr == $r || $matrix->[ $rr ][ $c ] == 0;
+ }
+
+ last;
+ }
+ }
+ }
+
+ return 1;
+}
+
+#-------------------------------------------------------------------------------
+sub parse_matrix
+#-------------------------------------------------------------------------------
+{
+ my ($M) = @_;
+ my (@matrix, $num_cols);
+
+ for my $row_str (@$M)
+ {
+ my @row;
+
+ for my $elem (grep { / \S /x } split / \s+ /x, $row_str)
+ {
+ if ($elem =~ / ^ $RE{num}{int} $ /x)
+ {
+ push @row, $elem;
+ }
+ else
+ {
+ error( qq[Element "$elem" is not a valid integer] );
+ }
+ }
+
+ push @matrix, \@row;
+
+ if (defined $num_cols)
+ {
+ scalar @row == $num_cols
+ or error( 'The input matrix is not rectangular' );
+ }
+ else
+ {
+ $num_cols = scalar @row;
+ $num_cols > 0 or error( 'Empty row' );
+ }
+ }
+
+ return \@matrix;
+}
+
+#-------------------------------------------------------------------------------
+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\[\n";
+
+ for my $row (@$matrix)
+ {
+ my @row_str;
+
+ for my $i (0 .. $#$row)
+ {
+ push @row_str, sprintf '%*d', $width[ $i ], $row->[ $i ];
+ }
+
+ printf "%s [%s]\n", $tab, join ', ', @row_str;
+ }
+
+ print "$tab]\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 @M = split / \; /x, $matrix_str;
+ my $matrix = parse_matrix( \@M );
+ my $is_rre = is_reduced_row_echelon( $matrix );
+
+ is $is_rre, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 0|1 0 0 1 ; 0 1 0 2 ; 0 0 1 3 |1
+Example 1|1 1 0 ; 0 1 0 ; 0 0 0 |0
+Example 2|0 1 -2 0 1; 0 0 0 1 3; 0 0 0 0 0; 0 0 0 0 0|1
+Example 3|1 0 0 4 ; 0 1 0 7 ; 0 0 1 -1 |1
+Example 4|0 1 -2 0 1; 0 0 0 0 0; 0 0 0 1 3; 0 0 0 0 0|0
+Example 5|0 1 0 ; 1 0 0 ; 0 0 0 |0
+Example 6|4 0 0 0 ; 0 1 0 7 ; 0 0 1 -1 |0
diff --git a/challenge-257/athanasius/raku/ch-1.raku b/challenge-257/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..21c615c9d8
--- /dev/null
+++ b/challenge-257/athanasius/raku/ch-1.raku
@@ -0,0 +1,178 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 257
+=========================
+
+TASK #1
+-------
+*Smaller than Current*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers, @ints.
+
+Write a script to find out how many integers are smaller than current i.e.
+foreach ints[i], count ints[j] < ints[i] where i != j.
+
+Example 1
+
+ Input: @ints = (5, 2, 1, 6)
+ Output: (2, 1, 0, 3)
+
+ For $ints[0] = 5, there are two integers (2,1) smaller than 5.
+ For $ints[1] = 2, there is one integer (1) smaller than 2.
+ For $ints[2] = 1, there is none integer smaller than 1.
+ For $ints[3] = 6, there are three integers (5,2,1) smaller than 6.
+
+Example 2
+
+ Input: @ints = (1, 2, 0, 3)
+ Output: (1, 2, 0, 3)
+
+Example 3
+
+ Input: @ints = (0, 1)
+ Output: (0, 1)
+
+Example 4
+
+ Input: @ints = (9, 4, 9, 2)
+ Output: (2, 1, 2, 0)
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+If no command-line arguments are given, the test suite is run.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 257, Task #1: Smaller than Current (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty array of integers
+
+ *@ints where { .elems > 0 && .all ~~ Int:D }
+)
+#===============================================================================
+{
+ "Input: \@ints = (%s)\n".printf: @ints.join: ', ';
+
+ my UInt @counts = count-smaller-than-current( @ints );
+
+ "Output: (%s)\n".printf: @counts.join: ', ';
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub count-smaller-than-current( List:D[Int:D] $ints --> List:D[UInts:D] )
+#-------------------------------------------------------------------------------
+{
+ my UInt @counts;
+
+ for 0 .. $ints.end -> UInt $i
+ {
+ my UInt $count = 0;
+
+ for 0 .. $ints.end -> UInt $j
+ {
+ next if $i == $j;
+
+ ++$count if $ints[ $j ] < $ints[ $i ];
+ }
+
+ @counts.push: $count;
+ }
+
+ return @counts;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $int-str, $exp-str) = $line.split: / \| /;
+
+ for $test-name, $int-str, $exp-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Int @ints = ($int-str.split: / \s+ /, :skip-empty).map: { .Int };
+ my UInt @exp = ($exp-str.split: / \s+ /, :skip-empty).map: { .Int };
+ my UInt @counts = count-smaller-than-current( @ints );
+
+ is-deeply @counts, @exp, $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| 5 2 1 6|2 1 0 3
+ Example 2| 1 2 0 3|1 2 0 3
+ Example 3| 0 1 |0 1
+ Example 4| 9 4 9 2|2 1 2 0
+ Negatives|-2 -2 -1 |0 0 2
+ END
+}
+
+################################################################################
diff --git a/challenge-257/athanasius/raku/ch-2.raku b/challenge-257/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..4bb6a71228
--- /dev/null
+++ b/challenge-257/athanasius/raku/ch-2.raku
@@ -0,0 +1,410 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 257
+=========================
+
+TASK #2
+-------
+*Reduced Row Echelon*
+
+Submitted by: Ali Moradi
+
+Given a matrix M, check whether the matrix is in reduced row echelon form.
+
+A matrix must have the following properties to be in reduced row echelon form:
+
+ 1. If a row does not consist entirely of zeros, then the first
+ nonzero number in the row is a 1. We call this the leading 1.
+ 2. If there are any rows that consist entirely of zeros, then
+ they are grouped together at the bottom of the matrix.
+ 3. In any two successive rows that do not consist entirely of zeros,
+ the leading 1 in the lower row occurs farther to the right than
+ the leading 1 in the higher row.
+ 4. Each column that contains a leading 1 has zeros everywhere else
+ in that column.
+
+For example:
+
+ [
+ [1,0,0,1],
+ [0,1,0,2],
+ [0,0,1,3]
+ ]
+
+The above matrix is in reduced row echelon form since the first nonzero number
+in each row is a 1, leading 1s in each successive row are farther to the right,
+and above and below each leading 1 there are only zeros.
+
+For more information check out this wikipedia
+[https://en.wikipedia.org/wiki/Row_echelon_form|article].
+
+Example 1
+
+ Input: $M = [
+ [1, 1, 0],
+ [0, 1, 0],
+ [0, 0, 0]
+ ]
+ Output: 0
+
+Example 2
+
+ Input: $M = [
+ [0, 1,-2, 0, 1],
+ [0, 0, 0, 1, 3],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0]
+ ]
+ Output: 1
+
+Example 3
+
+ Input: $M = [
+ [1, 0, 0, 4],
+ [0, 1, 0, 7],
+ [0, 0, 1,-1]
+ ]
+ Output: 1
+
+Example 4
+
+ Input: $M = [
+ [0, 1,-2, 0, 1],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 3],
+ [0, 0, 0, 0, 0]
+ ]
+ Output: 0
+
+Example 5
+
+ Input: $M = [
+ [0, 1, 0],
+ [1, 0, 0],
+ [0, 0, 0]
+ ]
+ Output: 0
+
+Example 6
+
+ Input: $M = [
+ [4, 0, 0, 0],
+ [0, 1, 0, 7],
+ [0, 0, 1,-1]
+ ]
+ Output: 0
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. The input matrix M is entered on the command-line as a non-empty list of
+ strings (the matrix rows) containing elements separated by whitespace. For
+ example, the matrix:
+
+ [ 1 1 0 ]
+ [ 0 1 0 ]
+ [ 0 0 0 ]
+
+ is entered as: >raku ch-2.raku "1 1 0" "0 1 0" "0 0 1"
+
+Assumptions
+-----------
+1. The input matrix M is an integer matrix.
+2. M is not an empty matrix.
+
+Note
+----
+Matrix-handling code is adapted from the solution to Task 2 for Week 248.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+enum RowType < AllZeros LeadingOne Other >;
+subset Matrix of Array where * ~~ Array[Array[Int]];
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 257, Task #2: Reduced Row Echelon (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ *@M where { .all ~~ Str:D && .elems > 0 } #= A non-empty integer matrix
+)
+#===============================================================================
+{
+ my Matrix $matrix = parse-matrix( @M );
+
+ print-matrix( 'Input: $M = ', $matrix );
+
+ my Bool $is-rre = is-reduced-row-echelon( $matrix );
+
+ "Output: %d\n".printf: $is-rre ?? 1 !! 0;
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub is-reduced-row-echelon( Matrix:D $matrix --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ # Test matrix properties 1 and 2:
+ # 1. If a row does not consist entirely of zeros, then the first nonzero
+ # number in the row is a 1. We call this the leading 1.
+ # 2. If there are any rows that consist entirely of zeros, then they are
+ # grouped together at the bottom of the matrix.
+
+ my RowType @row-types = classify-rows( $matrix );
+ my Bool $all-zeros = False;
+
+ for @row-types -> RowType $type
+ {
+ given $type
+ {
+ when AllZeros { $all-zeros = True; }
+ when LeadingOne { return False if $all-zeros; } # Requirement 2
+ when Other { return False; } # Requirement 1
+ }
+ }
+
+ # Test matrix properties 3 and 4:
+ # 3. In any two successive rows that do not consist entirely of zeros, the
+ # leading 1 in the lower row occurs farther to the right than the leading
+ # 1 in the higher row.
+ # 4. Each column that contains a leading 1 has zeros everywhere else in that
+ # column.
+
+ return check-leading-ones( $matrix, @row-types );
+}
+
+#-------------------------------------------------------------------------------
+sub classify-rows( Matrix:D $matrix --> List:D[RowType:D] )
+#-------------------------------------------------------------------------------
+{
+ my RowType @row-types;
+ my UInt $width = $matrix[ 0 ].elems;
+
+ L-OUTER:
+ for 0 .. $matrix.end -> UInt $r
+ {
+ my Array[Int] $row = $matrix[ $r ];
+
+ for 0 .. $width - 1 -> UInt $c
+ {
+ my Int $element = $row[ $c ];
+
+ if $element == 1
+ {
+ @row-types.push: LeadingOne;
+ next L-OUTER;
+ }
+ elsif $element != 0
+ {
+ @row-types.push: Other;
+ next L-OUTER;
+ }
+ }
+
+ @row-types.push: AllZeros;
+ }
+
+ return @row-types;
+}
+
+#-------------------------------------------------------------------------------
+sub check-leading-ones( Matrix:D $matrix, List:D[RowType:D] $row-t --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt $width = $matrix[ 0 ].elems;
+ my Int $last-one = -1;
+
+ for 0 .. $matrix.end -> UInt $r
+ {
+ last if $row-t[ $r ] == AllZeros;
+
+ my Array[Int] $row = $matrix[ $r ];
+
+ for 0 .. $width - 1 -> UInt $c
+ {
+ my Int $element = $row[ $c ];
+
+ if $element == 1
+ {
+ return False unless $c > $last-one; # Test Requirement 3
+
+ $last-one = $c;
+
+ for 0 .. $matrix.end -> UInt $rr # Test Requirement 4
+ {
+ return False unless $rr == $r || $matrix[ $rr; $c ] == 0;
+ }
+
+ last;
+ }
+ }
+ }
+
+ return True;
+}
+
+#-------------------------------------------------------------------------------
+sub parse-matrix( List:D[Str:D] $M --> Matrix:D )
+#-------------------------------------------------------------------------------
+{
+ my Matrix $matrix = Array[Array[Int]].new;
+ my UInt $num-cols;
+
+ for @$M -> Str $row-str
+ {
+ my Int @row;
+
+ for $row-str.split( / \s+ /, :skip-empty ) -> Str $elem
+ {
+ if +$elem ~~ Int:D
+ {
+ @row.push: +$elem;
+ }
+ else
+ {
+ error( qq[Element "$elem" is not a valid integer] );
+ }
+ }
+
+ $matrix.push: @row;
+
+ if $num-cols.defined
+ {
+ @row.elems == $num-cols
+ or error( 'The input matrix is not rectangular' );
+ }
+ else
+ {
+ ($num-cols = @row.elems) > 0 or error( 'Empty row' );
+ }
+ }
+
+ return $matrix;
+}
+
+#-------------------------------------------------------------------------------
+sub print-matrix( Str:D $prefix, Matrix: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\[".put;
+
+ for @$matrix -> Int @row
+ {
+ my Str @row-str;
+
+ for 0 .. @row.end -> UInt $i
+ {
+ @row-str.push: '%*d'.sprintf: @width[ $i ], @row[ $i ];
+ }
+
+ "%s [%s]\n".printf: $tab, @row-str.join: ', ';
+ }
+
+ "$tab]".put;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $matrix-str, $exp-str) = $line.split: / \| /;
+
+ for $test-name, $matrix-str, $exp-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Str @M = $matrix-str.split: / \; /, :skip-empty;
+ my Matrix $matrix = parse-matrix( @M );
<