diff options
| -rw-r--r-- | challenge-212/athanasius/perl/ch-1.pl | 193 | ||||
| -rw-r--r-- | challenge-212/athanasius/perl/ch-2.pl | 252 | ||||
| -rw-r--r-- | challenge-212/athanasius/raku/ch-1.raku | 167 | ||||
| -rw-r--r-- | challenge-212/athanasius/raku/ch-2.raku | 224 |
4 files changed, 836 insertions, 0 deletions
diff --git a/challenge-212/athanasius/perl/ch-1.pl b/challenge-212/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..96ffb1cf78 --- /dev/null +++ b/challenge-212/athanasius/perl/ch-1.pl @@ -0,0 +1,193 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 212 +========================= + +TASK #1 +------- +*Jumping Letters* + +Submitted by: Mohammad S Anwar + +You are given a word having alphabetic characters only, and a list of positive +integers of the same length + +Write a script to print the new word generated after jumping forward each letter +in the given word by the integer in the list. The given list would have exactly +the number as the total alphabets in the given word. + +Example 1 + + Input: $word = 'Perl' and @jump = (2,22,19,9) + Output: Raku + + 'P' jumps 2 place forward and becomes 'R'. + 'e' jumps 22 place forward and becomes 'a'. + (jump is cyclic i.e. after 'z' you go back to 'a') + 'r' jumps 19 place forward and becomes 'k'. + 'l' jumps 9 place forward and becomes 'u'. + +Example 2 + + Input: $word = 'Raku' and @jump = (24,4,7,17) + Output: 'Perl' + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +Note +---- +Case is preserved in each jump. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Devel::Assert qw( on ); +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => +"Usage: + perl $0 <word> [<jump> ...] + perl $0 + + <word> A non-empty word containing alphabetic characters only + [<jump> ...] A list of positive integers of the same length as \$word\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 212, Task #1: Jumping Letters (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $args = scalar @ARGV; + + if ($args == 0) + { + run_tests(); + } + elsif ($args == 1) + { + error( 'Expected 0 or 2+ command line arguments, found 1' ); + } + else + { + my ($word, $jump) = parse_command_line(); + + printf "Input: \$word = '%s' and \@jump = (%s)\n", + $word, join ', ', @$jump; + + printf "Output: '%s'\n", jump_letters( $word, $jump ); + } +} + +#------------------------------------------------------------------------------- +sub jump_letters +#------------------------------------------------------------------------------- +{ + my ($word, $jump) = @_; + + assert $word =~ / ^ [A-Za-z]+ $ /x; + assert scalar @$jump == length $word; + + my $new_word = ''; + + for my $i (0 .. $#$jump) + { + my $letter = substr $word, $i, 1; + my $is_uc = ord( 'A' ) <= ord( $letter ) <= ord( 'Z' ); + my $offset = ord( $is_uc ? 'A' : 'a' ); + my $value = ord( $letter ) - $offset; + my $new_value = ($value + $jump->[ $i ]) % 26; + $new_word .= chr( $new_value + $offset ); + } + + return $new_word; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my $word = shift @ARGV; + my $chars = length $word; + my $ints = scalar @ARGV; + + $chars > 0 or error( 'Empty word' ); + $ints == $chars or error( "Expected $chars integers, found $ints" ); + + for (@ARGV) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + $_ > 0 or error( qq["$_" is not a positive integer] ); + } + + return ($word, [ @ARGV ]); +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $word, $jump_str, $expected) = split / \| /x, $line; + + for ($test_name, $word, $jump_str) # Trim whitespace + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @jump = split / , \s* /x, $jump_str; + my $got = jump_letters( $word, \@jump ); + + is $got, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 |Perl | 2,22,19, 9 |Raku +Example 2 |Raku |24, 4, 7,17 |Perl +One letter|B |25 |A +Me |Imperlmonk|18, 7,18,22,22,15,6,20,7,8|Athanasius diff --git a/challenge-212/athanasius/perl/ch-2.pl b/challenge-212/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..335fe1edda --- /dev/null +++ b/challenge-212/athanasius/perl/ch-2.pl @@ -0,0 +1,252 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 212 +========================= + +TASK #2 +------- +*Rearrange Groups* + +Submitted by: Mohammad S Anwar + +You are given a list of integers and group size greater than zero. + +Write a script to split the list into equal groups of the given size where +integers are in sequential order. If it can’t be done then print -1. + +Example 1: + + Input: @list = (1,2,3,5,1,2,7,6,3) and $size = 3 + Output: (1,2,3), (1,2,3), (5,6,7) + +Example 2: + + Input: @list = (1,2,3) and $size = 2 + Output: -1 + +Example 3: + + Input: @list = (1,2,4,3,5,3) and $size = 3 + Output: (1,2,3), (3,4,5) + +Example 4: + + Input: @list = (1,5,2,6,4,7) and $size = 3 + Output: -1 + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. If negative numbers are included in @list, they must be preceded by '--'. + +Algorithm +--------- +Given: M, a multiset of integers of size m = |M|; and + s, an integer > 0. + +Task: Partition M into (m/s) groups, of equal size s, such that the elements of + each group can be arranged in sequential order. + +Solution: +(1) If s > m or s does not evenly divide m, output -1 as there is no solution. + Otherwise: +(2) Remove the smallest element e from M and place it in the first group. Then + remove an element f=e+1 from M and add it to group 1; and so on (element g= + e+2, etc.) until the first group is filled (i.e., of size s). +(3) Repeat this process for the remaining groups, until M is empty. +(4) If at any point the required element is not found in M, immediately output + -1 as there is no solution. Otherwise: +(5) Output all the groups constructed in (2) and (3). Since the order of the + groups does not matter, this is the unique solution. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Getopt::Long; +use List::Util qw( min ); +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => +"Usage: + perl $0 [--size[=Pos]] [<list> ...] + perl $0 + + --size[=Pos] Size of each group + [<list> ...] A list of integers + + where Pos is an integer > 0\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 212, Task #2: Rearrange Groups (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my ($size, $list) = parse_command_line(); + + printf "Input: \@list = (%s) and \$size = %d\n", + join( ',', @$list ), $size; + + my $groups = rearrange_groups( $list, $size ); + + printf "Output: %s\n", scalar @$groups == 0 ? '-1' : + join( ', ', map { '(' . join( ',', @$_ ) . ')' } @$groups ); + } +} + +#------------------------------------------------------------------------------- +sub rearrange_groups +#------------------------------------------------------------------------------- +{ + my ($list, $size) = @_; + my $elements = scalar @$list; + my @groups; + + if ($size <= $elements && $elements % $size == 0) + { + my %ints; + ++$ints{ $_ } for @$list; + + while (%ints) + { + my $smallest = min keys %ints; + my @partition = $smallest; + + delete $ints{ $smallest } if --$ints{ $smallest } == 0; + + for my $inc (1 .. $size - 1) + { + my $next = $smallest + $inc; + + return [] unless exists $ints{ $next }; + + push @partition, $next; + + delete $ints{ $next } if --$ints{ $next } == 0; + } + + push @groups, \@partition; + } + } + + return \@groups; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my $size; + + GetOptions( 'size=i' => \$size ) + or error( 'Invalid command line arguments' ); + + defined $size or error( 'Missing size' ); + + $size =~ / ^ $RE{num}{int} $ /x + or error( qq["$size" is not a valid integer] ); + + $size > 0 or error( qq["$size" is not positive] ); + + my @list = @ARGV; + + scalar @list > 0 or error( 'Empty list' ); + + for (@list) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + } + + return ($size, \@list); +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $list, $size, $output) = split / \| /x, $line; + + for ($test_name, $list, $size) # Trim whitespace + { + s/ ^ \s+ //x; + s/ \s+ \s* $ //x; + } + + my @ints = split / , \s* /x, $list; + my $groups = rearrange_groups( \@ints, $size ); + my @expected; + + if ($output ne '-1') + { + my @group_strs = ($output =~ / \,? \( .+? \) /gx); + + for (@group_strs) # Trim whitespace + { + s/ ^ \,? \( //x; + s/ \) $ //x; + + push @expected, [ split / , /x ]; + } + } + + is_deeply $groups, \@expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 | 1, 2, 3, 5, 1, 2,7,6,3|3|(1,2,3),(1,2,3),(5,6,7) +Example 2 | 1, 2, 3 |2|-1 +Example 3 | 1, 2, 4, 3, 5, 3 |3|(1,2,3),(3,4,5) +Example 4 | 1, 5, 2, 6, 4, 7 |3|-1 +Negatives |-7,-5,-6,-2,-3,-1 |3|(-7,-6,-5),(-3,-2,-1) +Mixed | 1,-8, 0,-9 |2|(-9,-8),(0,1) +All in one| 6, 3, 5, 1, 4, 2 |6|(1,2,3,4,5,6) +Singles |-2, 3,-1,-1, 2, 7 |1|(-2),(-1),(-1),(2),(3),(7) diff --git a/challenge-212/athanasius/raku/ch-1.raku b/challenge-212/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..eeed3c3379 --- /dev/null +++ b/challenge-212/athanasius/raku/ch-1.raku @@ -0,0 +1,167 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 212 +========================= + +TASK #1 +------- +*Jumping Letters* + +Submitted by: Mohammad S Anwar + +You are given a word having alphabetic characters only, and a list of positive +integers of the same length + +Write a script to print the new word generated after jumping forward each letter +in the given word by the integer in the list. The given list would have exactly +the number as the total alphabets in the given word. + +Example 1 + + Input: $word = 'Perl' and @jump = (2,22,19,9) + Output: Raku + + 'P' jumps 2 place forward and becomes 'R'. + 'e' jumps 22 place forward and becomes 'a'. + (jump is cyclic i.e. after 'z' you go back to 'a') + 'r' jumps 19 place forward and becomes 'k'. + 'l' jumps 9 place forward and becomes 'u'. + +Example 2 + + Input: $word = 'Raku' and @jump = (24,4,7,17) + Output: 'Perl' + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +Note +---- +Case is preserved in each jump. + +=end comment +#=============================================================================== + +use Test; + +subset Pos of Int where * > 0; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 212, Task #1: Jumping Letters (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A non-empty word containing alphabetic characters only + + Str:D $word where { / ^ <[ A .. Z a .. z ]>+ $ / }, + + #| A list of positive integers of the same length as $word + + *@jump where { .all ~~ Pos:D && .elems == $word.chars } +) +#=============================================================================== +{ + "Input: \$word = '%s' and \@jump = (%s)\n".printf: $word, @jump.join: ', '; + + "Output: '%s'\n".printf: jump-letters( $word, @jump ); +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub jump-letters +( + Str:D $word where { / ^ <[ A .. Z a .. z ]>+ $ / }, + List:D[Pos:D] $jump where { .elems == $word.chars } +--> Str:D +) +#------------------------------------------------------------------------------- +{ + my Str $new-word = ''; + + for 0 .. $jump.end -> UInt $i + { + my Str $letter = $word.substr: $i, 1; + my Bool $is-uc = 'A'.ord <= $letter.ord <= 'Z'.ord; + my UInt $offset = ( $is-uc ?? 'A' !! 'a' ).ord; + my UInt $value = $letter.ord - $offset; + my UInt $new-value = ( $value + $jump[ $i ] ) % 26; + $new-word ~= ( $new-value + $offset ).chr; + } + + return $new-word; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $word, $jump-str, $expected) = $line.split: / \| /; + + for $test-name, $word, $jump-str # Trim whitespace + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Pos @jump = $jump-str.split( / \, \s* / ).map: { .Int }; + my Str $got = jump-letters( $word, @jump ); + + is $got, $expected, $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 |Perl | 2,22,19, 9 |Raku + Example 2 |Raku |24, 4, 7,17 |Perl + One letter|B |25 |A + Me |Imperlmonk|18, 7,18,22,22,15,6,20,7,8|Athanasius + END +} + +################################################################################ diff --git a/challenge-212/athanasius/raku/ch-2.raku b/challenge-212/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..a907c827e4 --- /dev/null +++ b/challenge-212/athanasius/raku/ch-2.raku @@ -0,0 +1,224 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 212 +========================= + +TASK #2 +------- +*Rearrange Groups* + +Submitted by: Mohammad S Anwar + +You are given a list of integers and group size greater than zero. + +Write a script to split the list into equal groups of the given size where +integers are in sequential order. If it can’t be done then print -1. + +Example 1: + + Input: @list = (1,2,3,5,1,2,7,6,3) and $size = 3 + Output: (1,2,3), (1,2,3), (5,6,7) + +Example 2: + + Input: @list = (1,2,3) and $size = 2 + Output: -1 + +Example 3: + + Input: @list = (1,2,4,3,5,3) and $size = 3 + Output: (1,2,3), (3,4,5) + +Example 4: + + Input: @list = (1,5,2,6,4,7) and $size = 3 + Output: -1 + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. If the first element of @list is negative, it must be preceded by '--'. + +Algorithm +--------- +Given: M, a multiset of integers of size m = |M|; and + s, an integer > 0. + +Task: Partition M into (m/s) groups, of equal size s, such that the elements of + each group can be arranged in sequential order. + +Solution: +(1) If s > m or s does not evenly divide m, output -1 as there is no solution. + Otherwise: +(2) Remove the smallest element e from M and place it in the first group. Then + remove an element f=e+1 from M and add it to group 1; and so on (element g= + e+2, etc.) until the first group is filled (i.e., of size s). +(3) Repeat this process for the remaining groups, until M is empty. +(4) If at any point the required element is not found in M, immediately output + -1 as there is no solution. Otherwise: +(5) Output all the groups constructed in (2) and (3). Since the order of the + groups does not matter, this is the unique solution. + +=end comment +#=============================================================================== + +use Test; + +subset Pos of Int where * > 0; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 211, Task #2: Rearrange Groups (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + Pos:D :$size, #= Size of each group + *@list where { .elems > 0 && .all ~~ Int:D } #= A list of integers +) +#=============================================================================== +{ + my Int @int-list = @list.map: { .Int }; # Change IntStr's to Int's + + "Input: \@list = (%s) and \$size = %d\n".printf: + @int-list.join( ',' ), $size; + + my Array[Int] @groups = rearrange-groups( @int-list, $size ); + + "Output: %s\n".printf: + @groups ?? @groups.map( { '(' ~ .join( ',' ) ~ ')' } ).join( ', ' ) + !! '-1'; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub rearrange-groups +( + List:D[Int:D] $list where { .elems > 0 }, + Pos:D $size, +--> List:D[List:D[Int:D]] +) +#------------------------------------------------------------------------------- +{ + my Array[Int] @groups; + my Pos $elements = $list.elems; + + if $size <= $elements && $elements %% $size + { + my UInt %ints{Int}; + ++%ints{ $_ } for @$list; + + while %ints # OR: for 1 .. ($elements / $size).Int + { + my Int @partition = my Int $smallest = %ints.keys.min; + + %ints{ $smallest }:delete if --%ints{ $smallest } == 0; + + for 1 .. $size - 1 -> UInt $inc + { + my Int $next = $smallest + $inc; + + return () unless %ints{ $next }:exists; + + @partition.push: $next; + + %ints{ $next }:delete if --%ints{ $next } == 0; + } + + @groups.push: @partition; + } + } + + return @groups; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $list, $size, $output) = $line.split: / \| /; + + for $test-name, $list, $size # Trim whitespace + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Int @ints = $list.split( / \, \s* / ).map: { .Int }; + my Array[Int] @groups = rearrange-groups( @ints, $size.Int ); + my Array[Int] @expected; + + if $output ne '-1' + { + my Str @group-strs = + ($output ~~ m:g/ \,? \( .+? \) /).map: { .Str }; + + for @group-strs # Trim whitespace + { + s/ ^ \,? \( //; + s/ \) $ //; + + @expected.push: Array[Int].new: .split( ',' ).map: { .Int }; + } + } + + is-deeply @groups, @expected, $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, 2, 3, 5, 1, 2,7,6,3|3|(1,2,3),(1,2,3),(5,6,7) + Example 2 | 1, 2, 3 |2|-1 + Example 3 | 1, 2, 4, 3, 5, 3 |3|(1,2,3),(3,4,5) + Example 4 | 1, 5, 2, 6, 4, 7 |3|-1 + Negatives |-7,-5,-6,-2,-3,-1 |3|(-7,-6,-5),(-3,-2,-1) + Mixed | 1,-8, 0,-9 |2|(-9,-8),(0,1) + All in one| 6, 3, 5, 1, 4, 2 |6|(1,2,3,4,5,6) + Singles |-2, 3,-1,-1, 2, 7 |1|(-2),(-1),(-1),(2),(3),(7) + END +} + +################################################################################ |
