aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xchallenge-084/alexander-pankoff/perl/ch-2.pl116
-rw-r--r--challenge-084/alexander-pankoff/perl/lib/MatrixParser.pm109
-rw-r--r--challenge-084/alexander-pankoff/perl/lib/MatrixParser/Lexer.pm67
-rw-r--r--challenge-084/alexander-pankoff/perl/lib/MatrixParser/Token.pm26
4 files changed, 318 insertions, 0 deletions
diff --git a/challenge-084/alexander-pankoff/perl/ch-2.pl b/challenge-084/alexander-pankoff/perl/ch-2.pl
new file mode 100755
index 0000000000..779685a979
--- /dev/null
+++ b/challenge-084/alexander-pankoff/perl/ch-2.pl
@@ -0,0 +1,116 @@
+#!/usr/bin/env perl
+use v5.20;
+use utf8;
+use strict;
+use warnings;
+use autodie;
+use feature qw(say signatures);
+no warnings 'experimental::signatures';
+
+use List::Util qw(any sum0);
+
+use Getopt::Long;
+use Pod::Usage;
+
+use FindBin;
+use lib File::Spec->join( $FindBin::RealBin, 'lib' );
+use lib File::Spec->join( $FindBin::RealBin, '..', '..', '..', 'challenge-083',
+ 'alexander-pankoff', 'perl', 'lib' );
+
+use Combinations qw(combinations);
+use MatrixParser;
+
+{
+ my $file;
+ GetOptions( "file=s" => \$file, ),
+ or pod2usage( -exitval => 1, );
+
+ my $input = read_input($file);
+
+ my $parser = MatrixParser->new($input);
+
+ # read the corners from the input matrix.
+ my $corners = $parser->corners();
+
+ say find_squares($corners);
+}
+
+# we process the corners row by row. For each row we build pairs from the
+# possible corners and check wether we can find the same pair in the row that is
+# as far away from the current row as the corners in the pair are apart from each
+# other
+sub find_squares ( $corners, $count = 0 ) {
+ ## base case. no more squares possible
+ return $count if keys @$corners < 2;
+
+ my $row = $corners->[0];
+ my @rest = @{$corners}[ 1 .. $#{$corners} ];
+
+ # build corner pairs from the current row, if there are less than 2
+ # elements no pais will be build
+ my @corner_pairs = combinations( 2, keys %{$row} );
+
+ my $squares = sum0(
+ map {
+ my ( $a, $b ) = @{$_};
+
+ # calculate the distance between the 2 corners.
+ my $dist = abs( $a - $b );
+ my $check_row = $rest[ $dist - 1 ];
+
+ # if both corners are set in check_row aswell we found a square
+ $check_row && $check_row->{$a} && $check_row->{$b}
+ ? 1
+ : 0
+ } @corner_pairs
+ );
+
+ return find_squares( \@rest, $count + $squares );
+}
+
+sub read_input($file) {
+ my $fh;
+ if ($file) {
+ open( $fh, '<', $file );
+ }
+ else {
+ $fh = *STDIN;
+ }
+ local $/ = undef;
+ my $input = <$fh>;
+ return $input;
+}
+
+=pod
+
+=head1 NAME
+
+wk-085 ch-2 - Find Squares
+
+=head1 SYNOPSIS
+
+Given a matrix of size M X N with only 1 and 0, this script will count the
+squares with all corners set to 1.
+
+The Matrix can be provided via STDIN or read from a file.
+
+Example:
+
+Input: [ 0 1 0 1 ]
+ [ 0 0 1 0 ]
+ [ 1 1 0 1 ]
+ [ 1 0 0 1 ]
+
+Output: 1
+
+ch-2.pl [--matrix=INPUTFILE]
+
+=head1 ARGUMENTS
+
+=over 8
+
+=item B<matrix> - the file to read the matrix from
+
+=back
+
+=cut
diff --git a/challenge-084/alexander-pankoff/perl/lib/MatrixParser.pm b/challenge-084/alexander-pankoff/perl/lib/MatrixParser.pm
new file mode 100644
index 0000000000..f2a8e91717
--- /dev/null
+++ b/challenge-084/alexander-pankoff/perl/lib/MatrixParser.pm
@@ -0,0 +1,109 @@
+package MatrixParser;
+use strict;
+use warnings;
+use feature qw(say signatures);
+no warnings 'experimental::signatures';
+
+use List::Util qw(none);
+
+use MatrixParser::Lexer;
+
+sub new ( $class, $input ) {
+ my $self = {
+ lexer => MatrixParser::Lexer->new($input),
+ corners => [],
+ row => 0,
+ col => 0,
+ element_count => undef,
+ };
+
+ $self->{lookahead} = $self->{lexer}->next_token;
+
+ return bless $self, $class;
+}
+
+# returns an array of hashes with the corner positions (1s) from the input
+# matrix. each hash contains the corner positions for the corresponding row in
+# the input matrix
+sub corners($self) {
+ while ( $self->{lookahead} ) {
+ $self->_parse_row;
+ }
+ return $self->{corners};
+}
+
+sub _parse_row($self) {
+ my $start = $self->_expect('[');
+
+ my $count = 0;
+ while ( !$self->_match(']') ) {
+ $self->_parse_element;
+ $count++;
+ }
+
+ $self->_expect(']');
+ if ( defined $self->{element_count} && $self->{element_count} != $count ) {
+ $self->_unexpected_element_count( $start, $count );
+ }
+ $self->{element_count} = $count;
+ $self->{col} = 0;
+ $self->{row}++;
+
+ return;
+}
+
+sub _parse_element($self) {
+ if ( $self->_match('1') ) {
+ $self->{corners}[ $self->{row} ]{ $self->{col} } = 1;
+ }
+ elsif ( !$self->_match('0') ) {
+ $self->_unexpected_token( $self->{lookahead}, '1|0' );
+ }
+
+ $self->{col}++;
+ return $self->_next_token()->lexeme;
+}
+
+sub _match ( $self, $expect ) {
+ $self->_unexpected_eof()
+ if !$self->{lookahead};
+ return $self->{lookahead}->lexeme eq $expect;
+}
+
+sub _expect ( $self, $expect ) {
+ my $token = $self->_next_token();
+ return $token if $token->lexeme eq $expect;
+
+ $self->_unexpected_token( $token, $expect );
+}
+
+sub _next_token($self) {
+ my $token = $self->{lookahead};
+ $self->{lookahead} = $self->{lexer}->next_token;
+ return $token;
+}
+
+sub _unexpected_token ( $self, $got, $expect ) {
+ die sprintf(
+"unexpected character in line %d at position %d. Got '%s', expect: '%s'\n",
+ $got->line, $got->pos, $got->lexeme, $expect );
+}
+
+sub _unexpected_element_count ( $self, $start, $count ) {
+ die sprintf(
+"unexpected element count in row %d starting at line %d at position %d. Got %d, expect: %d \n",
+ $self->{row} + 1,
+ $start->line, $start->pos, $count, $self->{element_count}
+ );
+
+}
+
+sub _unexpected_eof ( $self ) {
+ die sprintf(
+ "unexpected end of input at line %d at position %d.\n",
+ $self->{lexer}{line},
+ $self->{lexer}{pos}
+ );
+}
+
+1;
diff --git a/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Lexer.pm b/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Lexer.pm
new file mode 100644
index 0000000000..9bc51e4e09
--- /dev/null
+++ b/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Lexer.pm
@@ -0,0 +1,67 @@
+package MatrixParser::Lexer;
+use strict;
+use warnings;
+use feature qw(say signatures);
+no warnings 'experimental::signatures';
+
+use List::Util qw(none);
+
+use MatrixParser::Token;
+
+sub new ( $class, $input ) {
+ my $self = {
+ input => [ split( //, $input ) ],
+ line => 1,
+ pos => 0
+ };
+
+ return bless $self, $class;
+}
+
+sub next_token($self) {
+ $self->_skip_whitespace;
+ return if $self->_eof;
+
+ my $char = $self->_advance();
+
+ $self->_unexpected_input($char)
+ if none { $_ eq $char } qw(1 0 [ ]);
+
+ return MatrixParser::Token->new( $char, $self->{line}, $self->{pos} );
+}
+
+sub _skip_whitespace($self) {
+ return if $self->_eof;
+ while (1) {
+ my $char = $self->_peek;
+ last if !defined $char or $char !~ /\s/;
+
+ $self->_advance;
+ if ( $char eq "\n" ) {
+ $self->{line} += 1;
+ $self->{pos} = 0;
+ }
+ }
+}
+
+sub _eof($self) {
+ return !( scalar @{ $self->{input} } );
+
+}
+
+sub _peek($self) {
+ return $self->{input}[0];
+}
+
+sub _advance($self) {
+ return if $self->_eof;
+ $self->{pos} += 1;
+ return shift @{ $self->{input} };
+}
+
+sub _unexpected_input ( $self, $char ) {
+ die sprintf( "unexpected input '%s' in line %d at position %d\n",
+ $char, $self->{line}, $self->{pos} );
+}
+
+1;
diff --git a/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Token.pm b/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Token.pm
new file mode 100644
index 0000000000..8fe2b25255
--- /dev/null
+++ b/challenge-084/alexander-pankoff/perl/lib/MatrixParser/Token.pm
@@ -0,0 +1,26 @@
+package MatrixParser::Token;
+use strict;
+use warnings;
+use feature qw(say signatures);
+no warnings 'experimental::signatures';
+
+sub new ( $class, $lexeme, $line, $pos ) {
+
+ my $self = [ $lexeme, $line, $pos ];
+
+ return bless $self, $class;
+}
+
+sub lexeme($self) {
+ $self->[0];
+}
+
+sub line($self) {
+ $self->[1];
+}
+
+sub pos($self) {
+ $self->[2];
+}
+
+1;