diff options
| author | PerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com> | 2022-10-22 18:57:43 +1000 |
|---|---|---|
| committer | PerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com> | 2022-10-22 18:57:43 +1000 |
| commit | 1bd973e615094fbd9b7ba0f7f6d84e8acf7df918 (patch) | |
| tree | 08b851521e297d25c39df728f51bdfd10229e1dd | |
| parent | 4c15f1ecad57c0abc9f6ac69a08d3c0c960b55cd (diff) | |
| download | perlweeklychallenge-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.pl | 256 | ||||
| -rw-r--r-- | challenge-187/athanasius/perl/ch-2.pl | 218 | ||||
| -rw-r--r-- | challenge-187/athanasius/raku/ch-1.raku | 252 | ||||
| -rw-r--r-- | challenge-187/athanasius/raku/ch-2.raku | 199 |
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; +} + +############################################################################### |
