aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2022-10-22 18:57:43 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2022-10-22 18:57:43 +1000
commit1bd973e615094fbd9b7ba0f7f6d84e8acf7df918 (patch)
tree08b851521e297d25c39df728f51bdfd10229e1dd
parent4c15f1ecad57c0abc9f6ac69a08d3c0c960b55cd (diff)
downloadperlweeklychallenge-club-1bd973e615094fbd9b7ba0f7f6d84e8acf7df918.tar.gz
perlweeklychallenge-club-1bd973e615094fbd9b7ba0f7f6d84e8acf7df918.tar.bz2
perlweeklychallenge-club-1bd973e615094fbd9b7ba0f7f6d84e8acf7df918.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 187
-rw-r--r--challenge-187/athanasius/perl/ch-1.pl256
-rw-r--r--challenge-187/athanasius/perl/ch-2.pl218
-rw-r--r--challenge-187/athanasius/raku/ch-1.raku252
-rw-r--r--challenge-187/athanasius/raku/ch-2.raku199
4 files changed, 925 insertions, 0 deletions
diff --git a/challenge-187/athanasius/perl/ch-1.pl b/challenge-187/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..90fe1e612e
--- /dev/null
+++ b/challenge-187/athanasius/perl/ch-1.pl
@@ -0,0 +1,256 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 187
+=========================
+
+TASK #1
+-------
+*Days Together*
+
+Submitted by: Mohammad S Anwar
+
+Two friends, Foo and Bar gone on holidays seperately to the same city. You are
+given their schedule i.e. start date and end date.
+
+To keep the task simple, the date is in the form DD-MM and all dates belong to
+the same calendar year i.e. between 01-01 and 31-12. Also the year is non-leap
+year and both dates are inclusive.
+
+Write a script to find out for the given schedule, how many days they spent
+together in the city, if at all.
+
+Example 1
+
+ Input: Foo => SD: '12-01' ED: '20-01'
+ Bar => SD: '15-01' ED: '18-01'
+
+ Output: 4 days
+
+Example 2
+
+ Input: Foo => SD: '02-03' ED: '12-03'
+ Bar => SD: '13-03' ED: '14-03'
+
+ Output: 0 day
+
+Example 3
+
+ Input: Foo => SD: '02-03' ED: '12-03'
+ Bar => SD: '11-03' ED: '15-03'
+
+ Output: 2 days
+
+Example 4
+
+ Input: Foo => SD: '30-03' ED: '05-04'
+ Bar => SD: '28-03' ED: '02-04'
+
+ Output: 4 days
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Interface
+---------
+If two arguments are given on the command-line, they are parsed as strings for-
+matted according to the Examples in the Task description.
+
+If eight arguments are given on the command-line, they are processed as four
+pairs of integers, each pair representing a day-of-month together with its
+associated month.
+
+If no arguments are given on the command-line, the test suite is run.
+
+Algorithm
+---------
+After some sanity checking by sub validate_date(), each of the four day/month
+pairs is converted to a single day-of-the-year. For example, 04-03 (1st March)
+is converted to Day 63 (= 31 + 28 + 4). This facilitates further sanity check-
+ing followed by date comparison in sub count_days_together() to produce the
+desired solution.
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Regexp::Common;
+use Test::More;
+
+const my @DAY_OF_YEAR =>
+ ( 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 );
+const my @DAYS_PER_MONTH =>
+ ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
+const my @MONTH_NAMES =>
+ qw( January February March April May June
+ July August September October November December );
+const my $TEST_FIELDS => 3;
+const my $USAGE =>
+qq{Usage:
+ perl $0 <foo> <bar>
+ perl $0 [<data> ...]
+ perl $0
+
+ <foo> String of the form "Foo => SD: '12-01' ED: '20-01'"
+ <bar> String of the form "Bar => SD: '15-01' ED: '18-01'"
+ [<data> ...] 8 integers (4 day/month pairs)\n};
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 187, Task #1: Days Together (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $args = scalar @ARGV;
+
+ if ($args == 0)
+ {
+ run_tests();
+ }
+ elsif ($args == 2)
+ {
+ my ($foo, $bar) = @ARGV;
+ my @data;
+
+ $foo =~ / ^ Foo \s => \s SD: \s ' (\d\d) - (\d\d) ' \s
+ ED: \s ' (\d\d) - (\d\d) ' $ /x
+ or error( 'Invalid input format for $foo' );
+
+ push @data, @{ ^CAPTURE };
+
+ $bar =~ / ^ Bar \s => \s SD: \s ' (\d\d) - (\d\d) ' \s
+ ED: \s ' (\d\d) - (\d\d) ' $ /x
+ or error( 'Invalid input format for $bar' );
+
+ push @data, @{ ^CAPTURE };
+
+ solve( \@data );
+ }
+ elsif ($args == 8)
+ {
+ solve( \@ARGV );
+ }
+ else
+ {
+ error( "Expected 0, 2, or 8 command-line arguments, found $args" );
+ }
+}
+
+#------------------------------------------------------------------------------
+sub solve
+#------------------------------------------------------------------------------
+{
+ my ($data) = @_;
+
+ for my $i (0, 2, 4, 6)
+ {
+ validate_date( $data->[ $i ], $data->[ $i + 1 ] );
+ }
+
+ printf "Input: Foo => SD: '%02d-%02d' ED: '%02d-%02d'\n" .
+ " Bar => SD: '%02d-%02d' ED: '%02d-%02d'\n\n", @$data;
+
+ my $days = count_days_together( $data );
+
+ printf "Output: $days day%s\n", $days == 1 ? '' : 's';
+}
+
+#------------------------------------------------------------------------------
+sub validate_date
+#------------------------------------------------------------------------------
+{
+ my ($day, $month) = @_;
+
+ $month =~ / ^ $RE{num}{int} $ /x
+ or error( qq[Month "$month" is not a valid integer]);
+
+ 1 <= $month <= 12
+ or error( qq{Invalid month "$month"} );
+
+ $day =~ / ^ $RE{num}{int} $ /x
+ or error( qq[Day "$day" is not a valid integer]);
+
+ 1 <= $day <= $DAYS_PER_MONTH[ $month - 1 ]
+ or error( sprintf qq{$day is not a valid day for %s},
+ $MONTH_NAMES[ $month - 1 ] );
+}
+
+#------------------------------------------------------------------------------
+sub count_days_together
+#------------------------------------------------------------------------------
+{
+ my ($data) = @_;
+ my $foo_sd = $DAY_OF_YEAR[ $data->[ 1 ] - 1 ] + $data->[ 0 ];
+ my $foo_ed = $DAY_OF_YEAR[ $data->[ 3 ] - 1 ] + $data->[ 2 ];
+ my $bar_sd = $DAY_OF_YEAR[ $data->[ 5 ] - 1 ] + $data->[ 4 ];
+ my $bar_ed = $DAY_OF_YEAR[ $data->[ 7 ] - 1 ] + $data->[ 6 ];
+
+ $foo_sd <= $foo_ed
+ or error( 'Foo\'s holiday cannot end before it starts' );
+
+ $bar_sd <= $bar_ed
+ or error( 'Bar\'s holiday cannot end before it starts' );
+
+ my $start = ($foo_sd >= $bar_sd) ? $foo_sd : $bar_sd; # Maximum
+ my $end = ($foo_ed <= $bar_ed) ? $foo_ed : $bar_ed; # Minimum
+ my $diff = $end - $start;
+
+ return $diff >= 0 ? $diff + 1 : 0;
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+#------------------------------------------------------------------------------
+sub run_tests
+#------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $input, $expected) =
+ split / , \s* /x, $line, $TEST_FIELDS;
+
+ my @data = split /\s+/, $input;
+
+ is count_days_together( \@data ), $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+###############################################################################
+
+__DATA__
+Example 1, 12 01 20 01 15 01 18 01, 4
+Example 2, 02 03 12 03 13 03 14 03, 0
+Example 3, 02 03 12 03 11 03 15 03, 2
+Example 4, 30 03 05 04 28 03 02 04, 4
+Whole year, 01 01 31 12 01 01 31 12, 365
+Smallest overlap, 15 01 22 02 22 02 17 03, 1
diff --git a/challenge-187/athanasius/perl/ch-2.pl b/challenge-187/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..5299fb085c
--- /dev/null
+++ b/challenge-187/athanasius/perl/ch-2.pl
@@ -0,0 +1,218 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 187
+=========================
+
+TASK #2
+-------
+*Magical Triplets*
+
+Submitted by: Mohammad S Anwar
+
+You are given a list of positive numbers, @n, having at least 3 numbers.
+
+Write a script to find the triplets (a, b, c) from the given list that satis-
+fies the following rules.
+
+ 1. a + b > c
+ 2. b + c > a
+ 3. a + c > b
+ 4. a + b + c is maximum.
+
+In case, you end up with more than one triplets having the maximum then pick
+the triplet where a >= b >= c.
+
+Example 1
+
+ Input: @n = (1, 2, 3, 2);
+ Output: (3, 2, 2)
+
+Example 2
+
+ Input: @n = (1, 3, 2);
+ Output: ()
+
+Example 3
+
+ Input: @n = (1, 1, 2, 3);
+ Output: ()
+
+Example 4
+
+ Input: @n = (2, 4, 3);
+ Output: (4, 3, 2)
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Interface
+---------
+If no arguments are given on the command-line, the test suite is run.
+
+Assumption
+----------
+A "positive number" is an integer greater than zero.
+
+Algorithm
+---------
+1) Sort the input list in descending numerical order
+2) WHILE there are at least 3 elements remaining in the list
+ Select the first 3 elements in the list as a candidate triple
+ T = <a, b, c>. Note that a, b, c ∈ N and a >= b >= c
+ a) The requirement that a + b + c is a maximum is satisfied by the fact
+ that a, b, c are drawn from the head of a list sorted in descending
+ numerical order
+ b) The requirement a + b > c is also guaranteed to be already satisfied:
+ b >= c and a >= 1, so (a + b) must be at least 1 greater than c
+ c) The requirement a + c > b is likewise guaranteed to be satisfied:
+ a >= b and c >= 1, so (a + c) must be at least 1 greater than b
+ d) It is therefore only necessary to test whether b + c > a:
+ IF b + c > a THEN
+ RETURN T as the solution
+ ELSE
+ It is pointless to test any additional triples containing
+ element a, since all further candidates for the 2nd and 3rd
+ positions in the triple are <= c, and we already know that
+ b + c is too small; therefore, remove element a from the head
+ of the list
+ ENDIF
+ ENDWHILE
+3) If this point is reached, no solution has been found so RETURN an empty list
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Regexp::Common;
+use Test::More;
+
+const my $TEST_FIELDS => 3;
+const my $TUPLE_SIZE => 3;
+const my $USAGE =>
+"Usage:
+ perl $0 [<n> ...]
+ perl $0
+
+ [<n> ...] A list of $TUPLE_SIZE or more positive integers\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 187, Task #2: Magical Triplets (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $args = scalar @ARGV;
+
+ if ($args == 0)
+ {
+ run_tests();
+ }
+ elsif ($args >= $TUPLE_SIZE)
+ {
+ my @n = parse_command_line();
+
+ printf "Input: \@n = (%s)\n", join ', ', @n;
+ printf "Output: (%s)\n", join ', ', @{ find_triplet( \@n ) };
+ }
+ else
+ {
+ error( "Expected 0 or $TUPLE_SIZE+ arguments, found $args" );
+ }
+}
+
+#------------------------------------------------------------------------------
+sub find_triplet
+#------------------------------------------------------------------------------
+{
+ my ($n_ref) = @_;
+ my @sorted = sort { $b <=> $a } @$n_ref; # Sort in descending order
+
+ while (scalar @sorted >= $TUPLE_SIZE)
+ {
+ my ($a, $b, $c) = @sorted[ 0 .. $TUPLE_SIZE - 1 ];
+
+ return [ $a, $b, $c ] if $a < $b + $c;
+
+ shift @sorted;
+ }
+
+ return [];
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my @n = @ARGV;
+
+ for my $n (@n)
+ {
+ $n =~ / ^ $RE{num}{int} $ /x
+ or error( qq["$n" is not a valid integer] );
+
+ $n > 0 or error( qq["$n" is not positive] );
+ }
+
+ return @n;
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+#------------------------------------------------------------------------------
+sub run_tests
+#------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $input, $expected) =
+ split / , \s* /x, $line, $TEST_FIELDS;
+
+ $input =~ / ^ \( (.*) \) $ /x
+ or die qq[Error matching \$input = "$input"];
+
+ my @n = split /\s+/, $1;
+ my $out = '(' . join( ' ', @{ find_triplet( \@n ) } ) . ')';
+
+ is $out, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+###############################################################################
+
+__DATA__
+Example 1, (1 2 3 2), (3 2 2)
+Example 2, (1 3 2), ()
+Example 3, (1 1 2 3), ()
+Example 4, (2 4 3), (4 3 2)
+All ones, (1 1 10 1), (1 1 1)
diff --git a/challenge-187/athanasius/raku/ch-1.raku b/challenge-187/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..8ceb313dfc
--- /dev/null
+++ b/challenge-187/athanasius/raku/ch-1.raku
@@ -0,0 +1,252 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 187
+=========================
+
+TASK #1
+-------
+*Days Together*
+
+Submitted by: Mohammad S Anwar
+
+Two friends, Foo and Bar gone on holidays seperately to the same city. You are
+given their schedule i.e. start date and end date.
+
+To keep the task simple, the date is in the form DD-MM and all dates belong to
+the same calendar year i.e. between 01-01 and 31-12. Also the year is non-leap
+year and both dates are inclusive.
+
+Write a script to find out for the given schedule, how many days they spent
+together in the city, if at all.
+
+Example 1
+
+ Input: Foo => SD: '12-01' ED: '20-01'
+ Bar => SD: '15-01' ED: '18-01'
+
+ Output: 4 days
+
+Example 2
+
+ Input: Foo => SD: '02-03' ED: '12-03'
+ Bar => SD: '13-03' ED: '14-03'
+
+ Output: 0 day
+
+Example 3
+
+ Input: Foo => SD: '02-03' ED: '12-03'
+ Bar => SD: '11-03' ED: '15-03'
+
+ Output: 2 days
+
+Example 4
+
+ Input: Foo => SD: '30-03' ED: '05-04'
+ Bar => SD: '28-03' ED: '02-04'
+
+ Output: 4 days
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Interface
+---------
+If two arguments are given on the command-line, they are parsed as strings for-
+matted according to the Examples in the Task description.
+
+If eight arguments are given on the command-line, they are processed as four
+pairs of integers, each pair representing a day-of-month together with its
+associated month.
+
+If no arguments are given on the command-line, the test suite is run.
+
+Algorithm
+---------
+After some sanity checking by sub validate-date(), each of the four day/month
+pairs is converted to a single day-of-the-year. For example, 04-03 (1st March)
+is converted to Day 63 (= 31 + 28 + 4). This facilitates further sanity check-
+ing followed by date comparison in sub count-days-together() to produce the
+desired solution.
+
+=end comment
+#==============================================================================
+
+use Test;
+
+my constant @DAY-OF-YEAR = 0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334;
+my constant @DAYS-PER-MONTH = 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31;
+my constant @MONTH-NAMES = < January February March April
+ May June July August
+ September October November December >;
+my UInt constant $TEST-FIELDS = 3;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 187, Task #1: Days Together (Raku)\n".put;
+}
+
+#==============================================================================
+multi sub MAIN # Input in canonical form
+(
+ Str:D $foo, #= String of the form "Foo => SD: '12-01' ED: '20-01'"
+ Str:D $bar #= String of the form "Bar => SD: '15-01' ED: '18-01'"
+)
+#==============================================================================
+{
+ my UInt @data;
+
+ $foo ~~ / ^ Foo \s \=\> \s SD\: \s \' (\d\d) \- (\d\d) \' \s
+ ED\: \s \' (\d\d) \- (\d\d) \' $ /
+ or error( 'Invalid input format for $foo' );
+
+ @data.push: | $/.map: { .Int };
+
+ $bar ~~ / ^ Bar \s \=\> \s SD\: \s \' (\d\d) \- (\d\d) \' \s
+ ED\: \s \' (\d\d) \- (\d\d) \' $ /
+ or error( 'Invalid input format for $bar' );
+
+ @data.push: | $/.map: { .Int };
+
+ solve( @data );
+}
+
+#==============================================================================
+multi sub MAIN # Input as a simple list of numbers
+(
+ #| 8 integers (4 day/month pairs)
+
+ *@data where { .elems == 8 && .all ~~ UInt:D }
+)
+#==============================================================================
+{
+ solve( @data );
+}
+
+#==============================================================================
+multi sub MAIN() # No input: run the test suite
+#==============================================================================
+{
+ run-tests();
+}
+
+#------------------------------------------------------------------------------
+sub solve( List:D[UInt:D] $data )
+#------------------------------------------------------------------------------
+{
+ for 0, 2, 4, 6 -> UInt $i
+ {
+ validate-date( $data[ $i ], $data[ $i + 1 ] );
+ }
+
+ ("Input: Foo => SD: '%02d-%02d' ED: '%02d-%02d'\n" ~
+ " Bar => SD: '%02d-%02d' ED: '%02d-%02d'\n\n").printf: @$data;
+
+ my UInt $days = count-days-together( @$data );
+
+ "Output: $days day%s\n".printf: $days == 1 ?? '' !! 's';
+}
+
+#------------------------------------------------------------------------------
+sub validate-date( UInt:D $day, UInt:D $month )
+#------------------------------------------------------------------------------
+{
+ 1 <= $month <= 12
+ or error( qq{Invalid month "$month"} );
+
+ 1 <= $day <= @DAYS-PER-MONTH[ $month - 1 ]
+ or error( qq{$day is not a valid day for @MONTH-NAMES[ $month - 1 ]} );
+}
+
+#------------------------------------------------------------------------------
+sub count-days-together( List:D[UInt:D] $data --> UInt:D )
+#------------------------------------------------------------------------------
+{
+ my UInt $foo-sd = @DAY-OF-YEAR[ $data[ 1 ] - 1 ] + $data[ 0 ];
+ my UInt $foo-ed = @DAY-OF-YEAR[ $data[ 3 ] - 1 ] + $data[ 2 ];
+ my UInt $bar-sd = @DAY-OF-YEAR[ $data[ 5 ] - 1 ] + $data[ 4 ];
+ my UInt $bar-ed = @DAY-OF-YEAR[ $data[ 7 ] - 1 ] + $data[ 6 ];
+
+ $foo-sd <= $foo-ed
+ or error( 'Foo\'s holiday cannot end before it starts' );
+
+ $bar-sd <= $bar-ed
+ or error( 'Bar\'s holiday cannot end before it starts' );
+
+ my UInt $start = ($foo-sd, $bar-sd).max;
+ my UInt $end = ($foo-ed, $bar-ed).min;
+ my Int $diff = $end - $start;
+
+ return $diff >= 0 ?? $diff + 1 !! 0;
+}
+
+#------------------------------------------------------------------------------
+sub run-tests()
+#------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $input, $expected) =
+ $line.split: / \, \s* /, $TEST-FIELDS, :skip-empty;
+
+ my UInt @data = $input.split( / \s+ /, :skip-empty ).map: { .Int };
+
+ is count-days-together( @data ), $expected, $test-name;
+ }
+
+ done-testing;
+}
+
+#------------------------------------------------------------------------------
+sub test-data()
+#------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example 1, 12 01 20 01 15 01 18 01, 4
+ Example 2, 02 03 12 03 13 03 14 03, 0
+ Example 3, 02 03 12 03 11 03 15 03, 2
+ Example 4, 30 03 05 04 28 03 02 04, 4
+ Whole year, 01 01 31 12 01 01 31 12, 365
+ Smallest overlap, 15 01 22 02 22 02 17 03, 1
+ END
+}
+
+#------------------------------------------------------------------------------
+sub error( Str:D $message )
+#------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+###############################################################################
diff --git a/challenge-187/athanasius/raku/ch-2.raku b/challenge-187/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..67d065ea78
--- /dev/null
+++ b/challenge-187/athanasius/raku/ch-2.raku
@@ -0,0 +1,199 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 187
+=========================
+
+TASK #2
+-------
+*Magical Triplets*
+
+Submitted by: Mohammad S Anwar
+
+You are given a list of positive numbers, @n, having at least 3 numbers.
+
+Write a script to find the triplets (a, b, c) from the given list that satis-
+fies the following rules.
+
+ 1. a + b > c
+ 2. b + c > a
+ 3. a + c > b
+ 4. a + b + c is maximum.
+
+In case, you end up with more than one triplets having the maximum then pick
+the triplet where a >= b >= c.
+
+Example 1
+
+ Input: @n = (1, 2, 3, 2);
+ Output: (3, 2, 2)
+
+Example 2
+
+ Input: @n = (1, 3, 2);
+ Output: ()
+
+Example 3
+
+ Input: @n = (1, 1, 2, 3);
+ Output: ()
+
+Example 4
+
+ Input: @n = (2, 4, 3);
+ Output: (4, 3, 2)
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Interface
+---------
+If no arguments are given on the command-line, the test suite is run.
+
+Assumption
+----------
+A "positive number" is an integer greater than zero.
+
+Algorithm
+---------
+1) Sort the input list in descending numerical order
+2) WHILE there are at least 3 elements remaining in the list
+ Select the first 3 elements in the list as a candidate triple
+ T = <a, b, c>. Note that a, b, c ∈ N and a >= b >= c
+ a) The requirement that a + b + c is a maximum is satisfied by the fact
+ that a, b, c are drawn from the head of a list sorted in descending
+ numerical order
+ b) The requirement a + b > c is also guaranteed to be already satisfied:
+ b >= c and a >= 1, so (a + b) must be at least 1 greater than c
+ c) The requirement a + c > b is likewise guaranteed to be satisfied:
+ a >= b and c >= 1, so (a + c) must be at least 1 greater than b
+ d) It is therefore only necessary to test whether b + c > a:
+ IF b + c > a THEN
+ RETURN T as the solution
+ ELSE
+ It is pointless to test any additional triples containing
+ element a, since all further candidates for the 2nd and 3rd
+ positions in the triple are <= c, and we already know that
+ b + c is too small; therefore, remove element a from the head
+ of the list
+ ENDIF
+ ENDWHILE
+3) If this point is reached, no solution has been found so RETURN an empty list
+
+=end comment
+#==============================================================================
+
+use Test;
+
+subset Pos of Int where * > 0;
+
+my UInt constant $TEST-FIELDS = 3;
+my UInt constant $TUPLE-SIZE = 3;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 187, Task #2: Magical Triplets (Raku)\n".put;
+}
+
+#==============================================================================
+multi sub MAIN
+(
+ #| A list of 3 or more positive integers
+
+ *@n where { .elems >= $TUPLE-SIZE && .all ~~ Pos:D }
+)
+#==============================================================================
+{
+ "Input: @n = (%s)\n".printf: @n.join: ', ';
+
+ "Output: (%s)\n".printf: find-triplet( @n ).join: ', ';
+}
+
+#==============================================================================
+multi sub MAIN() # Run the test suite
+#==============================================================================
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $input, $expected) =
+ $line.split: / \, \s* /, $TEST-FIELDS, :skip-empty;
+
+ $input ~~ / ^ \( (.*) \) $ /
+ or die qq[Error matching \$input = "$input"];
+
+ my Pos @n = $0.Str.split( / \s+ /, :skip-empty ).map: { .Int };
+ my Str $out = '(' ~ find-triplet( @n ).join( ' ' ) ~ ')';
+
+ is $out, $expected, $test-name;
+ }
+
+ done-testing;
+}
+
+#------------------------------------------------------------------------------
+sub find-triplet( List:D[Pos:D] $n --> List:D[Pos:D] )
+#------------------------------------------------------------------------------
+{
+ my Pos @sorted = $n.sort: { $^b <=> $^a }; # Sort in descending order
+
+ while @sorted.elems >= $TUPLE-SIZE
+ {
+ my Pos ($a, $b, $c) = @sorted[ 0 .. $TUPLE-SIZE - 1 ];
+
+ return [$a, $b, $c] if $a < $b + $c;
+
+ @sorted.shift;
+ }
+
+ return [];
+}
+
+#------------------------------------------------------------------------------
+sub test-data()
+#------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example 1, (1 2 3 2), (3 2 2)
+ Example 2, (1 3 2), ()
+ Example 3, (1 1 2 3), ()
+ Example 4, (2 4 3), (4 3 2)
+ All ones, (1 1 10 1), (1 1 1)
+ END
+}
+
+#------------------------------------------------------------------------------
+sub error( Str:D $message )
+#------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+###############################################################################