aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörg Sommrey <28217714+jo-37@users.noreply.github.com>2023-01-31 16:31:00 +0100
committerJörg Sommrey <28217714+jo-37@users.noreply.github.com>2023-02-03 15:43:23 +0100
commit457d80f476928f097b792bfcc4d605aaa4e5b3fd (patch)
tree940c2d1828952f07bbc906c3c3f0a6443c06dcd2
parent9d652ba823e2b8ac14bc7e6c468f735bd1245508 (diff)
downloadperlweeklychallenge-club-457d80f476928f097b792bfcc4d605aaa4e5b3fd.tar.gz
perlweeklychallenge-club-457d80f476928f097b792bfcc4d605aaa4e5b3fd.tar.bz2
perlweeklychallenge-club-457d80f476928f097b792bfcc4d605aaa4e5b3fd.zip
Solution to task 2
-rwxr-xr-xchallenge-202/jo-37/perl/ch-2.pl133
1 files changed, 133 insertions, 0 deletions
diff --git a/challenge-202/jo-37/perl/ch-2.pl b/challenge-202/jo-37/perl/ch-2.pl
new file mode 100755
index 0000000000..940afb24be
--- /dev/null
+++ b/challenge-202/jo-37/perl/ch-2.pl
@@ -0,0 +1,133 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Test2::V0;
+use experimental 'postderef';
+
+our ($tests, $examples);
+
+run_tests() if $tests || $examples; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [N1 N2...]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+N1 N2...
+ List of altitudes
+
+EOS
+
+
+### Input and Output
+
+say join ' ', widest_valley(@ARGV);
+
+
+### Implementation
+
+# Find the leftmost widest valley in a single pass through the list.
+# This can be achieved by tracking every valley, identified by its
+# starting index, length (in data points, not steps!) and current
+# direction. A valley at the end of its uphill slope is considered
+# "closed" and is marked with a zero direction.
+#
+sub widest_valley {
+ # Start with one valley consisting of a single point downhill slope.
+ # This enables a starting uphill half-valley.
+ my @valleys = ({start => 0, length => 1, dir => -1});
+ my $widest = $valleys[0];
+ for my $i (0 .. $#_ - 1) {
+ my $dir = $_[$i + 1] <=> $_[$i];
+ # Mark the potential beginning of a new valley on a
+ # non-ascending step.
+ my $new = $dir <= 0;
+ for my $valley (@valleys) {
+ # Skip "closed" valleys.
+ next unless $valley->{dir};
+
+ # Close the valley if it goes uphill and the next step goes
+ # downhill.
+ # _
+ # / \
+ #
+ # In all other cases the next step extends the valley and
+ # may lead to a new maximum.
+ #
+ # \_ \__ \_/ __ _/
+ # \ / /
+ if ($valley->{dir} > 0 && $dir < 0) {
+ $valley->{dir} = 0;
+ } else {
+ $widest = $valley
+ if ++$valley->{length} > $widest->{length};
+ }
+ # Invert the valley's direction to uphill if the next step
+ # goes uphill.
+ #
+ # \_/
+ $valley->{dir} = 1 if $valley->{dir} < 0 && $dir > 0;
+
+ # A (non-ascending) step that extends the downhill slope of
+ # a valley is not the beginning of a new valley.
+ # _ _
+ # \_ \
+ # \
+ $new = 0 if $valley->{dir} < 0;
+ }
+ # Open a new valley if a non-ascending step is not part of a
+ # known valley's downhill slope.
+ # _
+ # _/ _/\
+ #
+ # If the new valley starts with an indifferent step, it might
+ # turn out to be part of an uphill slope later, though. It would
+ # not violate the definition of a valley but cannot be maximal.
+ # Such spurious valleys do not hurt in this task.
+ #
+ # _/
+ # _/
+ push @valleys, {start => $i, length => 2, dir => -1} if $new;
+ }
+
+ # Extract the leftmost widest valley.
+ splice @_, $widest->{start}, $widest->{length};
+}
+
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ is [widest_valley(1, 5, 5, 2, 8)],
+ [5, 5, 2, 8], 'example 1';
+ is [widest_valley(2, 6, 8, 5)],
+ [2, 6, 8], 'example 2';
+ is [widest_valley(9, 8, 13, 13, 2, 2, 15, 17)],
+ [13, 13, 2, 2, 15, 17], 'example 3';
+ is [widest_valley(2, 1, 2, 1, 3)],
+ [2, 1, 2], 'example 4';
+ is [widest_valley(1, 3, 3, 2, 1, 2, 3, 3, 2)],
+ [3, 3, 2, 1, 2, 3, 3], 'example 5';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ is [widest_valley(1, 2, 3, 4, 3, 4)], [1, 2, 3, 4], 'half at start';
+ is [widest_valley(4, 3, 4, 3, 2, 1)], [4, 3, 2, 1], 'haft at end';
+ is [widest_valley(2, 1, 2, 2, 1, 2, 2, 1, 2)],
+ [2, 2, 1, 2, 2], 'embedded';
+ is [widest_valley(2, 1, 2, 2, 3, 3, 4, 4, 1)],
+ [2, 1, 2, 2, 3, 3, 4, 4], 'spurious valleys';
+ }
+
+ done_testing;
+ exit;
+}