From 668504ca244e4d0e47049874e72cacf5f689fdbf Mon Sep 17 00:00:00 2001 From: PerlMonk-Athanasius Date: Tue, 7 May 2024 16:45:24 +1000 Subject: Perl & Raku solutions to Tasks 1 & 2 for Week 268 --- challenge-268/athanasius/perl/ch-1.pl | 255 ++++++++++++++++++++++++++++++++ challenge-268/athanasius/perl/ch-2.pl | 168 +++++++++++++++++++++ challenge-268/athanasius/raku/ch-1.raku | 255 ++++++++++++++++++++++++++++++++ challenge-268/athanasius/raku/ch-2.raku | 157 ++++++++++++++++++++ 4 files changed, 835 insertions(+) create mode 100644 challenge-268/athanasius/perl/ch-1.pl create mode 100644 challenge-268/athanasius/perl/ch-2.pl create mode 100644 challenge-268/athanasius/raku/ch-1.raku create mode 100644 challenge-268/athanasius/raku/ch-2.raku diff --git a/challenge-268/athanasius/perl/ch-1.pl b/challenge-268/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..c6aa86f31c --- /dev/null +++ b/challenge-268/athanasius/perl/ch-1.pl @@ -0,0 +1,255 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 268 +========================= + +TASK #1 +------- +*Magic Number* + +Submitted by: Mohammad Sajid Anwar + +You are given two arrays of integers of same size, @x and @y. + +Write a script to find the magic number that when added to each elements of one +of the array gives the second array. Elements order is not important. + +Example 1 + + Input: @x = (3, 7, 5) + @y = (9, 5, 7) + Output: 2 + + The magic number is 2. + @x = (3, 7, 5) + + 2 2 2 + @y = (5, 9, 7) + +Example 2 + + Input: @x = (1, 2, 1) + @y = (5, 4, 4) + Output: 3 + + The magic number is 3. + @x = (1, 2, 1) + + 3 3 3 + @y = (5, 4, 4) + +Example 3 + + Input: @x = (2) + @y = (5) + Output: 3 + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2024 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. The input lists @x and @y are entered as two strings on the command-line: + each string is a list of whitespace-separated integers. + +Algorithm +--------- +Assume there is a magic number m that satisfies the requirement that when m is +added to each element of @x it produces @y (ignoring element order). Let there +be n elements in each of @x and @y, and let the elements of @x be sorted in monotonically-ascending order as x1 <= x2 <= x3 <= ... <= xn. Likewise, let the +elements of @y be y1 <= y2 <= y3 <= ... <= yn. + +Suppose that x1 + m = y2, and that y2 > y1. Then y1's counterpart in @x would be +x0 = y1 - m, and since y1 < y2, we have x0 < x1, which is impossible since x1 is +lowest in sort order. It follows that x1's counterpart in @y can only be y1 (or +another y equal to y1). So we can remove the pair (x1, y1) and apply the same +reasoning to the remaining elements of @x and @y recursively. We conclude that +if m exists, it must be the difference between corresponding elements of the two +ordered arrays. That is, x1 + m = y1, x2 + m = y2, ... xn + m = yn. + +This leads to a straightforward algorithm: + + SUB find-magic-number + Sort @x in increasing numerical order + Sort @y in increasing numerical order + m := y1 - x1 + fail := False + i := 1 + WHILE not fail AND i <= n + diff := yi - xi + IF diff ≠ m + fail := True + ENDIF + i := i + 1 + ENDWHILE + IF fail + m := undefined + ENDIF + RETURN m + ENDSUB + +=cut +#=============================================================================== + +use v5.32.1; # Enables strictures +use warnings; +use Const::Fast; +use Devel::Assert qw( on ); +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => < + perl $0 + + A list of integers separated by whitespace + Another integer list of the same size +END + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 268, Task #1: Magic Number (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $argc = scalar @ARGV; + + if ($argc == 0) + { + run_tests(); + } + elsif ($argc == 2) + { + my ($x, $y) = parse_command_line(); + + printf "Input: \@x = (%s)\n", join ', ', @$x; + printf " \@y = (%s)\n", join ', ', @$y; + + my $magic = find_magic_number( $x, $y ); + + printf "Output: %s\n", $magic // 'none'; + } + else + { + error( "Expected 0 or 2 command-line arguments, found $argc" ); + } +} + +#------------------------------------------------------------------------------- +sub find_magic_number +#------------------------------------------------------------------------------- +{ + my ($x, $y) = @_; + + assert scalar @$x == scalar @$y; + + my @x_sorted = sort { $a <=> $b } @$x; + my @y_sorted = sort { $a <=> $b } @$y; + my $magic = $y_sorted[ 0 ] - $x_sorted[ 0 ]; + + for my $i (1 .. $#$x) + { + return unless $y_sorted[ $i ] - $x_sorted[ $i ] == $magic; + } + + return $magic; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + assert scalar @ARGV == 2; + + for (@ARGV) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @x_strs = split / \s+ /x, $ARGV[ 0 ]; + my @y_strs = split / \s+ /x, $ARGV[ 1 ]; + + scalar @x_strs == scalar @y_strs + or error( '@x and @y are of different sizes' ); + + my (@x, @y); + + for (@x_strs) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + push @x, $_; + } + + for (@y_strs) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + push @y, $_; + } + + return \@x, \@y; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = ) + { + chomp $line; + + my ($test_name, $x_str, $y_str, $expected) = split / \| /x, $line; + + for ($test_name, $x_str, $y_str, $expected) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @x = split / \s+ /x, $x_str; + my @y = split / \s+ /x, $y_str; + my $magic = find_magic_number( \@x, \@y ); + + is $magic // 'none', $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 | 3 7 5| 9 5 7| 2 +Example 2 | 1 2 1| 5 4 4| 3 +Example 3 | 2 | 5 | 3 +None | 1 2 | 3 5 |none +Zero | 1 2 3| 2 3 1| 0 +Negative 1|-1 -3 -5|-3 1 -1| 2 +Negative 2|-1 -3 -5|-8 -4 -6|-3 diff --git a/challenge-268/athanasius/perl/ch-2.pl b/challenge-268/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..bbee2a8309 --- /dev/null +++ b/challenge-268/athanasius/perl/ch-2.pl @@ -0,0 +1,168 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 268 +========================= + +TASK #2 +------- +*Number Game* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers, @ints, with even number of elements. + +Write a script to create a new array made up of elements of the given array. +Pick the two smallest integers and add it to new array in decreasing order i.e. +high to low. Keep doing until the given array is empty. + +Example 1 + + Input: @ints = (2, 5, 3, 4) + Output: (3, 2, 5, 4) + + Round 1: we picked (2, 3) and push it to the new array (3, 2) + Round 2: we picked the remaining (4, 5) and push it to the new array (5, 4) + +Example 2 + + Input: @ints = (9, 4, 1, 3, 6, 4, 6, 1) + Output: (1, 1, 4, 3, 6, 4, 9, 6) + +Example 3 + + Input: @ints = (1, 2, 2, 3) + Output: (2, 1, 3, 2) + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2024 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. The input integers are entered on the command-line. + +=cut +#=============================================================================== + +use v5.32.1; # Enables strictures +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => < ...] + perl $0 + + [ ...] A non-empty, even-numbered list of integers +END + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 268, Task #2: Number Game (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my @ints = @ARGV; + + scalar @ints % 2 == 0 + or error( 'The input list has an odd number of elements' ); + + for (@ints) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + } + + printf "Input: \@ints = (%s)\n", join ', ', @ints; + + my $soln = solve( \@ints ); + + printf "Output: (%s)\n", join ', ', @$soln; + } +} + +#------------------------------------------------------------------------------- +sub solve +#------------------------------------------------------------------------------- +{ + my ($ints) = @_; + my @solution; + my @sorted = sort { $a <=> $b } @$ints; + + while (@sorted) + { + my $first = shift @sorted; + my $second = shift @sorted; + + push @solution, $second, $first; + } + + return \@solution; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = ) + { + 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 $soln = solve( \@ints ); + my @expd = split / \s+ /x, $expd_str; + + is_deeply $soln, \@expd, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1|2 5 3 4 |3 2 5 4 +Example 2|9 4 1 3 6 4 6 1|1 1 4 3 6 4 9 6 +Example 3|1 2 2 3 |2 1 3 2 diff --git a/challenge-268/athanasius/raku/ch-1.raku b/challenge-268/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..a63591e8e3 --- /dev/null +++ b/challenge-268/athanasius/raku/ch-1.raku @@ -0,0 +1,255 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 268 +========================= + +TASK #1 +------- +*Magic Number* + +Submitted by: Mohammad Sajid Anwar + +You are given two arrays of integers of same size, @x and @y. + +Write a script to find the magic number that when added to each elements of one +of the array gives the second array. Elements order is not important. + +Example 1 + + Input: @x = (3, 7, 5) + @y = (9, 5, 7) + Output: 2 + + The magic number is 2. + @x = (3, 7, 5) + + 2 2 2 + @y = (5, 9, 7) + +Example 2 + + Input: @x = (1, 2, 1) + @y = (5, 4, 4) + Output: 3 + + The magic number is 3. + @x = (1, 2, 1) + + 3 3 3 + @y = (5, 4, 4) + +Example 3 + + Input: @x = (2) + @y = (5) + Output: 3 + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2024 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. The input lists @x and @y are entered as two strings on the command-line: + each string is a list of whitespace-separated integers. + +Algorithm +--------- +Assume there is a magic number m that satisfies the requirement that when m is +added to each element of @x it produces @y (ignoring element order). Let there +be n elements in each of @x and @y, and let the elements of @x be sorted in monotonically-ascending order as x1 <= x2 <= x3 <= ... <= xn. Likewise, let the +elements of @y be y1 <= y2 <= y3 <= ... <= yn. + +Suppose that x1 + m = y2, and that y2 > y1. Then y1's counterpart in @x would be +x0 = y1 - m, and since y1 < y2, we have x0 < x1, which is impossible since x1 is +lowest in sort order. It follows that x1's counterpart in @y can only be y1 (or +another y equal to y1). So we can remove the pair (x1, y1) and apply the same +reasoning to the remaining elements of @x and @y recursively. We conclude that +if m exists, it must be the difference between corresponding elements of the two +ordered arrays. That is, x1 + m = y1, x2 + m = y2, ... xn + m = yn. + +This leads to a straightforward algorithm: + + SUB find-magic-number + Sort @x in increasing numerical order + Sort @y in increasing numerical order + m := y1 - x1 + fail := False + i := 1 + WHILE not fail AND i <= n + diff := yi - xi + IF diff ≠ m + fail := True + ENDIF + i := i + 1 + ENDWHILE + IF fail + m := undefined + ENDIF + RETURN m + ENDSUB + +=end comment +#=============================================================================== + +use Test; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 268, Task #1: Magic Number (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + Str:D $x, #= A list of integers separated by whitespace + Str:D $y #= Another integer list of the same size +) +#=============================================================================== +{ + my Array[Int] ($x-array, $y-array) = parse-command-line( $x, $y ); + + "Input: \@x = (%s)\n".printf: $x-array.join: ', '; + " \@y = (%s)\n".printf: $y-array.join: ', '; + + my Int $magic = find-magic-number( $x-array, $y-array ); + + "Output: %s\n".printf: $magic // 'none'; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub find-magic-number +( + List:D[Int:D] $x, + List:D[Int:D] $y where { .elems == $x.elems } +--> Int +) +#------------------------------------------------------------------------------- +{ + my Int @x-sorted = $x.sort; + my Int @y-sorted = $y.sort; + my Int $magic = @y-sorted[ 0 ] - @x-sorted[ 0 ]; + + for 1 .. $x.end -> UInt $i + { + return unless @y-sorted[ $i ] - @x-sorted[ $i ] == $magic; + } + + return $magic; +} + +#------------------------------------------------------------------------------- +sub parse-command-line( Str:D $x, Str:D $y --> List:D[List:D[Int:D]] ) +#------------------------------------------------------------------------------- +{ + my Str @x-strs = $x.split: / \s+ /, :skip-empty; + my Str @y-strs = $y.split: / \s+ /, :skip-empty; + + @x-strs.elems == @y-strs.elems + or error( '@x and @y are of different sizes' ); + + my Int (@x, @y); + + for @x-strs + { + +$_ ~~ Int or error( qq["$_" is not a valid integer] ); + @x.push: +$_; + } + + for @y-strs + { + +$_ ~~ Int or error( qq["$_" is not a valid integer] ); + @y.push: +$_; + } + + return @x, @y; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $x-str, $y-str, $exp-str) = $line.split: / \| /; + + for $test-name, $x-str, $y-str, $exp-str + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Int @x = $x-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my Int @y = $y-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my Int $magic = find-magic-number( @x, @y ); + + if $magic.defined + { + is $magic, $exp-str.Int, $test-name; + } + else + { + is 'none', $exp-str, $test-name; + } + } + + done-testing; +} + +#------------------------------------------------------------------------------- +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; +} + +#------------------------------------------------------------------------------- +sub test-data( --> Str:D ) +#------------------------------------------------------------------------------- +{ + return q:to/END/; + Example 1 | 3 7 5| 9 5 7| 2 + Example 2 | 1 2 1| 5 4 4| 3 + Example 3 | 2 | 5 | 3 + None | 1 2 | 3 5 |none + Zero | 1 2 3| 2 3 1| 0 + Negative 1|-1 -3 -5|-3 1 -1| 2 + Negative 2|-1 -3 -5|-8 -4 -6|-3 + END +} + +################################################################################ diff --git a/challenge-268/athanasius/raku/ch-2.raku b/challenge-268/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..1f61b270bb --- /dev/null +++ b/challenge-268/athanasius/raku/ch-2.raku @@ -0,0 +1,157 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 268 +========================= + +TASK #2 +------- +*Number Game* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers, @ints, with even number of elements. + +Write a script to create a new array made up of elements of the given array. +Pick the two smallest integers and add it to new array in decreasing order i.e. +high to low. Keep doing until the given array is empty. + +Example 1 + + Input: @ints = (2, 5, 3, 4) + Output: (3, 2, 5, 4) + + Round 1: we picked (2, 3) and push it to the new array (3, 2) + Round 2: we picked the remaining (4, 5) and push it to the new array (5, 4) + +Example 2 + + Input: @ints = (9, 4, 1, 3, 6, 4, 6, 1) + Output: (1, 1, 4, 3, 6, 4, 9, 6) + +Example 3 + + Input: @ints = (1, 2, 2, 3) + Output: (2, 1, 3, 2) + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2024 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. The input integers are entered on the command-line. +3. If the first integer is negative, it must be preceded by "--" to indicate + that it is not a command-line flag. + +=end comment +#=============================================================================== + +use Test; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 268, Task #2: Number Game (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A non-empty, even-numbered list of integers + + *@ints where { .elems > 0 && .elems %% 2 } +) +#=============================================================================== +{ + "Input: \@ints = (%s)\n".printf: @ints.join: ', '; + + my Int @soln = solve( @ints ); + + "Output: (%s)\n"\.printf: @soln.join: ', '; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub solve( List:D[Int:D] $ints where { .elems %% 2 } --> List:D[Int:D] ) +#------------------------------------------------------------------------------- +{ + my Int @solution; + my Int @sorted = $ints.sort; + + while @sorted + { + my Int $first = @sorted.shift; + my Int $second = @sorted.shift; + + @solution.push: $second, $first; + } + + return @solution; +} + +#------------------------------------------------------------------------------- +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 Int @soln = solve( @ints ); + my Int @expd = $expd-str.split( / \s+ /, :skip-empty ).map: { .Int }; + + is-deeply @soln, @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|2 5 3 4 |3 2 5 4 + Example 2|9 4 1 3 6 4 6 1|1 1 4 3 6 4 9 6 + Example 3|1 2 2 3 |2 1 3 2 + END +} + +################################################################################ -- cgit