aboutsummaryrefslogtreecommitdiff
path: root/challenge-096/athanasius/raku/Matrix.rakumod
blob: 4f79fbf7c4a82d8aa01f4f85b94573dc750ed5a7 (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
use v6d;

###############################################################################
=begin comment

Perl Weekly Challenge 096, Task #2: Edit Distance

Matrix class for use in implementing the Wagner–Fischer algorithm for finding
the Levenshtein distance between two strings.

=end comment
###############################################################################

#--------------------------------------#
# Copyright © 2021 PerlMonk Athanasius #
#--------------------------------------#

#==============================================================================
unit class Matrix;
#==============================================================================

#------------------------------------------------------------------------------
# Matrix attributes
#------------------------------------------------------------------------------

has UInt       $.height;
has UInt       $.width;
has Array[Str] @!matrix;

#------------------------------------------------------------------------------
submethod BUILD( Str:D :$S1, Str:D :$S2 )
#------------------------------------------------------------------------------
{
    $!width  = $S1.chars + 1;
    $!height = $S2.chars + 1;

    for 0 .. $!height -> UInt $row
    {
        @!matrix[$row    ] = Array[Str].new;
        @!matrix[$row; $_] = Nil for 0 .. $!width;
    }

    my Str $word2 = "#$S2";
    my $i = 0;

    for 1 .. $!height -> UInt $row
    {
        @!matrix[$row; 0] = $word2.substr: $i, 1;
        @!matrix[$row; 1] = "$i";
        ++$i;
    }

    my Str $word1 = "#$S1";

    $i = 0;

    for 1 .. $!width -> UInt $col
    {
        @!matrix[0; $col] = $word1.substr: $i, 1;
        @!matrix[1; $col] = "$i";
        ++$i;
    }
}

#------------------------------------------------------------------------------
method element( UInt:D $row, UInt:D $col, UInt $value? --> UInt:D )  # Accessor
#------------------------------------------------------------------------------
{
    if $value.defined
    {
        @!matrix[$row + 1; $col + 1] = "$value";            # Set
    }

    return @!matrix[$row + 1; $col + 1].UInt;               # Get
}

#------------------------------------------------------------------------------
method display
#------------------------------------------------------------------------------
{
    # Pre-compute the maximum widths of individual columns

    my UInt @widths = 1;

    for 1 .. $!width -> UInt $col
    {
        my UInt $max = 0;

        for 1 .. $!height -> UInt $row
        {
            my Str  $current = @!matrix[$row; $col];
            my UInt $cur-len = $current.chars;
                    $max     = $cur-len if $cur-len > $max;
        }

        @widths[ $col ] = $max;
    }

    # Draw a vertical separator

    my Str $line  = '+';
           $line ~= '-%s-+'.sprintf: '-'  x @widths[ $_ ] for 0 .. $!width;
           $line ~= "\n";

    # Draw the matrix

    my Str $display = $line;

    for 0 .. $!height -> UInt $row
    {
        $display ~= '|';
        $display ~= ' %*s |'.sprintf: @widths[ $_ ], @!matrix[$row; $_] // ' '
                        for 0 .. $!width;
        $display ~= "\n" ~ $line;
    }

    return $display;
}

##############################################################################