aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2025-11-02 13:47:46 +0000
committerGitHub <noreply@github.com>2025-11-02 13:47:46 +0000
commitaebcea5d241033175603fd39a3250d86146b5536 (patch)
tree50062eeb8ae0abce416a1f111ed8ba6186316007
parent9490291b8cf7bf2c950c7ffe532f220cc55a111c (diff)
parentaa728e77bec9b3cedf43273a8904c9b1bd38d1fa (diff)
downloadperlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.tar.gz
perlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.tar.bz2
perlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.zip
Merge pull request #12959 from PerlMonk-Athanasius/branch-for-challenge-345
Perl & Raku solutions to Tasks 1 & 2 for Week 345
-rw-r--r--challenge-345/athanasius/perl/ch-1.pl193
-rw-r--r--challenge-345/athanasius/perl/ch-2.pl213
-rw-r--r--challenge-345/athanasius/raku/ch-1.raku183
-rw-r--r--challenge-345/athanasius/raku/ch-2.raku207
4 files changed, 796 insertions, 0 deletions
diff --git a/challenge-345/athanasius/perl/ch-1.pl b/challenge-345/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..50c8526059
--- /dev/null
+++ b/challenge-345/athanasius/perl/ch-1.pl
@@ -0,0 +1,193 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 345
+=========================
+
+TASK #1
+-------
+*Peak Positions*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers, @ints.
+
+Find all the peaks in the array, a peak is an element that is strictly greater
+than its left and right neighbours. Return the indices of all such peak posi-
+tions.
+
+Example 1
+
+ Input: @ints = (1, 3, 2)
+ Output: (1)
+
+Example 2
+
+ Input: @ints = (2, 4, 6, 5, 3)
+ Output: (2)
+
+Example 3
+
+ Input: @ints = (1, 2, 3, 2, 4, 1)
+ Output: (2, 4)
+
+Example 4
+
+ Input: @ints = (5, 3, 1)
+ Output: (0)
+
+Example 5
+
+ Input: @ints = (1, 5, 1, 5, 1, 5, 1)
+ Output: (1, 3, 5)
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Assumption
+----------
+From Example 4, it appears that an integer in either end position is considered
+to be "strictly greater than" its non-existent neighbour.
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line.
+
+=cut
+#===============================================================================
+
+use v5.38.2; # 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 integers
+END
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 345, Task #1: Peak Positions (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my @ints = @ARGV;
+
+ / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] )
+ for @ints;
+
+ printf "Input: \@ints = (%s)\n", join ', ', @ints;
+
+ my $peaks = find_peaks( \@ints );
+
+ printf "Output: (%s)\n", join ', ', @$peaks;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_peaks
+#-------------------------------------------------------------------------------
+{
+ my ($ints) = @_;
+ my $size = scalar @$ints;
+ my @peaks;
+
+ if ($size == 0)
+ {
+ die 'Empty integer list';
+ }
+ elsif ($size == 1)
+ {
+ push @peaks, 0;
+ }
+ else
+ {
+ my $e = $#$ints;
+
+ push @peaks, 0 if $ints->[0] > $ints->[1];
+
+ for my $i (1 .. $e - 1)
+ {
+ push @peaks, $i if $ints->[$i] > $ints->[$i - 1] &&
+ $ints->[$i] > $ints->[$i + 1]
+ }
+
+ push @peaks, $e if $ints->[$e] > $ints->[$e - 1];
+ }
+
+ return \@peaks;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $ints_str, $expd_str) = split / \| /x, $line;
+
+ for ($test_name, $ints_str, $expd_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @ints = split / \s+ /x, $ints_str;
+ my $peaks = find_peaks( \@ints );
+ my @expd = split / \s+ /x, $expd_str;
+
+ is_deeply $peaks, \@expd, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1|1 3 2 |1
+Example 2|2 4 6 5 3 |2
+Example 3|1 2 3 2 4 1 |2 4
+Example 4|5 3 1 |0
+Example 5|1 5 1 5 1 5 1|1 3 5
+Singleton|4 |0
+Negatives|0 -1 1 -2 0 |0 2 4
diff --git a/challenge-345/athanasius/perl/ch-2.pl b/challenge-345/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..03f48be9ab
--- /dev/null
+++ b/challenge-345/athanasius/perl/ch-2.pl
@@ -0,0 +1,213 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 345
+=========================
+
+TASK #2
+-------
+*Last Visitor*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an integer array @ints where each element is either a positive
+integer or -1.
+
+We process the array from left to right while maintaining two lists:
+
+ @seen: stores previously seen positive integers (newest at the front)
+ @ans: stores the answers for each -1
+
+Rules:
+
+ If $ints[i] is a positive number -> insert it at the front of @seen
+ If $ints[i] is -1:
+
+Let $x be how many -1s in a row we've seen before this one.
+
+If $x < len(@seen) -> append seen[x] to @ans
+
+Else -> append -1 to @ans
+
+At the end, return @ans.
+
+Example 1
+
+ Input: @ints = (5, -1, -1)
+ Output: (5, -1)
+
+ @seen = (5)
+ First -1: @ans = (5)
+ Second -1: @ans = (5, -1)
+
+Example 2
+
+ Input: @ints = (3, 7, -1, -1, -1)
+ Output: (7, 3, -1)
+
+ @seen = (3, 7)
+ First -1: @ans = (7)
+ Second -1: @ans = (7, 3)
+ Third -1: @ans = (7, 3, -1)
+
+Example 3
+
+ Input: @ints = (2, -1, 4, -1, -1)
+ Output: (2, 4, 2)
+
+Example 4
+
+ Input: @ints = (10, 20, -1, 30, -1, -1)
+ Output: (20, 30, 20)
+
+Example 5
+
+ Input: @ints = (-1, -1, 5, -1)
+ Output: (-1, -1, 5)
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Assumptions
+-----------
+1. A "positive integer" is any non-negative integer (i.e., including zero).
+ Hence, the allowed values in @ints are integers in the range: -1, 0, 1, ...
+2. "Let $x be how many -1s in a row we've seen before this one." should read:
+ "Let $x be how many -1s in a row we've seen *immediately* before this one."
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line; each integer is
+ greater than or equal to -1.
+
+=cut
+#===============================================================================
+
+use v5.38.2; # 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 integers, each -1 or greater
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 345, Task #2: Last Visitor (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] );
+ $_ >= -1 or error( qq["$_" is too small]);
+ }
+
+ printf "Input: \@ints = (%s)\n", join ', ', @ints;
+
+ my $ans = find_answers( \@ints );
+
+ printf "Output: (%s)\n", join ', ', @$ans;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_answers
+#-------------------------------------------------------------------------------
+{
+ my ($ints) = @_;
+ my (@seen, @ans);
+ my $x = 0;
+
+ for my $n (@$ints)
+ {
+ if ($n >= 0)
+ {
+ unshift @seen, $n;
+ $x = 0;
+ }
+ else
+ {
+ push @ans, (scalar @seen > $x) ? $seen[$x] : -1;
+ ++$x;
+ }
+ }
+
+ return \@ans;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $ints_str, $expd_str) = split / \| /x, $line;
+
+ for ($test_name, $ints_str, $expd_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @ints = split / \s+ /x, $ints_str;
+ my $ans = find_answers( \@ints );
+ my @expd = split / \s+ /x, $expd_str;
+
+ is_deeply $ans, \@expd, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 5 -1 -1 | 5 -1
+Example 2| 3 7 -1 -1 -1 | 7 3 -1
+Example 3| 2 -1 4 -1 -1 | 2 4 2
+Example 4|10 20 -1 30 -1 -1|20 30 20
+Example 5|-1 -1 5 -1 |-1 -1 5
diff --git a/challenge-345/athanasius/raku/ch-1.raku b/challenge-345/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..9668f9c78a
--- /dev/null
+++ b/challenge-345/athanasius/raku/ch-1.raku
@@ -0,0 +1,183 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 345
+=========================
+
+TASK #1
+-------
+*Peak Positions*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers, @ints.
+
+Find all the peaks in the array, a peak is an element that is strictly greater
+than its left and right neighbours. Return the indices of all such peak posi-
+tions.
+
+Example 1
+
+ Input: @ints = (1, 3, 2)
+ Output: (1)
+
+Example 2
+
+ Input: @ints = (2, 4, 6, 5, 3)
+ Output: (2)
+
+Example 3
+
+ Input: @ints = (1, 2, 3, 2, 4, 1)
+ Output: (2, 4)
+
+Example 4
+
+ Input: @ints = (5, 3, 1)
+ Output: (0)
+
+Example 5
+
+ Input: @ints = (1, 5, 1, 5, 1, 5, 1)
+ Output: (1, 3, 5)
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Assumption
+----------
+From Example 4, it appears that an integer in either end position is considered
+to be "strictly greater than" its non-existent neighbour.
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line.
+3. If the first integer is negative, it must be preceded by "--" to signal that
+ it is not a command-line flag.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 345, Task #1: Peak Positions (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty list of integers
+
+ *@ints where { .elems > 0 && .all ~~ Int:D }
+)
+#===============================================================================
+{
+ "Input: \@ints = (%s)\n".printf: @ints.join: ', ';
+
+ my UInt @peaks = find-peaks( @ints );
+
+ "Output: (%s)\n".printf: @peaks.join: ', ';
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-peaks( List:D[Int:D] $ints where { .elems > 0 } --> List:D[UInt:D] )
+#-------------------------------------------------------------------------------
+{
+ my UInt @peaks;
+
+ if $ints.elems == 1
+ {
+ @peaks.push: 0;
+ }
+ else
+ {
+ my UInt $e = $ints.end;
+
+ @peaks.push: 0 if $ints[0] > $ints[1];
+
+ for 1 .. $e - 1 -> UInt $i
+ {
+ @peaks.push: $i if $ints[$i] > $ints[$i - 1] &&
+ $ints[$i] > $ints[$i + 1]
+ }
+
+ @peaks.push: $e if $ints[$e] > $ints[$e - 1];
+ }
+
+ return @peaks;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $ints-str, $expd-str) = $line.split: / \| /;
+
+ for $test-name, $ints-str, $expd-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Int @ints = $ints-str.split( / \s+ /, :skip-empty ).map: { .Int };
+ my UInt @peaks = find-peaks( @ints );
+ my UInt @expd = $expd-str.split( / \s+ /, :skip-empty ).map: { .Int };
+
+ is-deeply @peaks, @expd, $test-name;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+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|1 3 2 |1
+ Example 2|2 4 6 5 3 |2
+ Example 3|1 2 3 2 4 1 |2 4
+ Example 4|5 3 1 |0
+ Example 5|1 5 1 5 1 5 1|1 3 5
+ Singleton|4 |0
+ Negatives|0 -1 1 -2 0 |0 2 4
+ END
+}
+
+################################################################################
diff --git a/challenge-345/athanasius/raku/ch-2.raku b/challenge-345/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..e1ad9a7797
--- /dev/null
+++ b/challenge-345/athanasius/raku/ch-2.raku
@@ -0,0 +1,207 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 345
+=========================
+
+TASK #2
+-------
+*Last Visitor*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an integer array @ints where each element is either a positive
+integer or -1.
+
+We process the array from left to right while maintaining two lists:
+
+ @seen: stores previously seen positive integers (newest at the front)
+ @ans: stores the answers for each -1
+
+Rules:
+
+ If $ints[i] is a positive number -> insert it at the front of @seen
+ If $ints[i] is -1:
+
+Let $x be how many -1s in a row we've seen before this one.
+
+If $x < len(@seen) -> append seen[x] to @ans
+
+Else -> append -1 to @ans
+
+At the end, return @ans.
+
+Example 1
+
+ Input: @ints = (5, -1, -1)
+ Output: (5, -1)
+
+ @seen = (5)
+ First -1: @ans = (5)
+ Second -1: @ans = (5, -1)
+
+Example 2
+
+ Input: @ints = (3, 7, -1, -1, -1)
+ Output: (7, 3, -1)
+
+ @seen = (3, 7)
+ First -1: @ans = (7)
+ Second -1: @ans = (7, 3)
+ Third -1: @ans = (7, 3, -1)
+
+Example 3
+
+ Input: @ints = (2, -1, 4, -1, -1)
+ Output: (2, 4, 2)
+
+Example 4
+
+ Input: @ints = (10, 20, -1, 30, -1, -1)
+ Output: (20, 30, 20)
+
+Example 5
+
+ Input: @ints = (-1, -1, 5, -1)
+ Output: (-1, -1, 5)
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Assumptions
+-----------
+1. A "positive integer" is any non-negative integer (i.e., including zero).
+ Hence, the allowed values in @ints are integers in the range: -1, 0, 1, ...
+2. "Let $x be how many -1s in a row we've seen before this one." should read:
+ "Let $x be how many -1s in a row we've seen *immediately* before this one."
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line; each integer is
+ greater than or equal to -1.
+3. If the first such integer is -1, it must be preceded by "--" to signal that
+ it is not a command-line flag.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+subset Type of Int where * >= -1;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 345, Task #2: Last Visitor (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty list of integers, each -1 or greater
+
+ *@ints where { .elems > 0 && .all ~~ Type:D }
+)
+#===============================================================================
+{
+ "Input: \@ints = (%s)\n".printf: @ints.join: ', ';
+
+ my Type @ans = find-answers( @ints );
+
+ "Output: (%s)\n".printf: @ans.join: ', ';
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-answers( List:D[Type:D] $ints --> List:D[Type:D] )
+#-------------------------------------------------------------------------------
+{
+ my UInt @seen;
+ my Type @ans;
+ my UInt $x = 0;
+
+ for @$ints -> Type $n
+ {
+ if $n >= 0
+ {
+ @seen.unshift: $n;
+ $x = 0;
+ }
+ else
+ {
+ @ans.push: (@seen.elems > $x) ?? @seen[$x] !! -1;
+ ++$x;
+ }
+ }
+
+ return @ans;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $ints-str, $expd-str) = $line.split: / \| /;
+
+ for $test-name, $ints-str, $expd-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Type @ints = $ints-str.split( / \s+ /, :skip-empty ).map: { .Int };
+ my Type @ans = find-answers( @ints );
+ my Type @expd = $expd-str.split( / \s+ /, :skip-empty ).map: { .Int };
+
+ is-deeply @ans, @expd, $test-name;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+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 -1 -1 | 5 -1
+ Example 2| 3 7 -1 -1 -1 | 7 3 -1
+ Example 3| 2 -1 4 -1 -1 | 2 4 2
+ Example 4|10 20 -1 30 -1 -1|20 30 20
+ Example 5|-1 -1 5 -1 |-1 -1 5
+ END
+}
+
+################################################################################