aboutsummaryrefslogtreecommitdiff
path: root/challenge-257/bob-lied/perl/ch-2.pl
blob: 892f3031640d75bd011efb670d3af7c6c6a2eac2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env perl
# vim:set ts=4 sw=4 sts=4 et ai wm=0 nu:
#=============================================================================
# Copyright (c) 2024, Bob Lied
#=============================================================================
# ch-2.pl Perl Weekly Challenge 257 Task 2 Reduced Row Echelon
#=============================================================================
# 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 more information check out this wikipedia article.
# https://en.wikipedia.org/wiki/Row_echelon_form

#=============================================================================

use v5.38;
use builtin qw/true false/; no warnings "experimental::builtin";

use List::Util qw/any all/;
use List::MoreUtils qw/first_index/;

use Getopt::Long;
my $Verbose = 0;
my $DoTest  = 0;

GetOptions("test" => \$DoTest, "verbose" => \$Verbose);
exit(!runTest()) if $DoTest;

sub rre($m)
{
    my @pivotCol = map { first_index { $_ != 0 } $_->@* } $m->@*;

    # Rows without a pivot should be at the bottom of the matrix.
    my $zeroRow = first_index {$_ == -1} @pivotCol;
    if ( $zeroRow > -1 )
    {
        return false if ( any {$_ != -1} @pivotCol[ $zeroRow .. $#pivotCol ] );

        # Eliminate zero rows from further consideration
        splice(@pivotCol, $zeroRow);
    }

    # All pivots must be 1
    for my $row ( 0 .. $#pivotCol )
    {
        return false if $m->[$row][$pivotCol[$row]] != 1;
    }

    # Pivot columns must be in strictly increasing order
    for ( my ($i, $j) = (0, 1); $j <= $#pivotCol; $i++, $j++ )
    {
        return false if ( $pivotCol[$i] >= $pivotCol[$j] );
    }

    # There must be zeroes above all the pivots.
    my $maxPivot = $pivotCol[-1];
    for my $row ( 1 .. ($#pivotCol) )
    {
        my $col = $pivotCol[$row];
        return false if any { $_ != 0 }
                    map { $_->[$col] } $m->@[0 .. $row-1]
    }
    return true;
}

sub runTest
{
    use Test2::V0;
    use builtin qw/true false/; no warnings "experimental::builtin";
    my $matrix = [
                   [1,0,0,1],
                   [0,1,0,2],
                   [0,0,1,3]
                 ];
    is( rre($matrix), true, "Example 0");

    $matrix = [
                [1, 1, 0],
                [0, 1, 0],
                [0, 0, 0]
              ];
    is( rre($matrix), false, "Example 1");

    $matrix = [ [0, 1,-2, 0, 1],
                [0, 0, 0, 1, 3],
                [0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0]
              ];
    is( rre($matrix), true, "Example 2");

    $matrix = [ [1, 0, 0, 4],
                [0, 1, 0, 7],
                [0, 0, 1,-1]
              ];
    is( rre($matrix), true, "Example 3");

    $matrix = [ [0, 1,-2, 0, 1],
                [0, 0, 0, 0, 0],
                [0, 0, 0, 1, 3],
                [0, 0, 0, 0, 0]
              ];
    is( rre($matrix), false, "Example 4");

    $matrix = [ [0, 1, 0],
                [1, 0, 0],
                [0, 0, 0]
              ];
    is( rre($matrix), false, "Example 5");

    $matrix = [ [4, 0, 0, 0],
                [0, 1, 0, 7],
                [0, 0, 1,-1]
              ];
    is( rre($matrix), false, "Example 6");

    done_testing;
}