diff options
| -rw-r--r-- | challenge-208/athanasius/perl/ch-1.pl | 213 | ||||
| -rw-r--r-- | challenge-208/athanasius/perl/ch-2.pl | 278 | ||||
| -rw-r--r-- | challenge-208/athanasius/raku/ch-1.raku | 220 | ||||
| -rw-r--r-- | challenge-208/athanasius/raku/ch-2.raku | 282 |
4 files changed, 993 insertions, 0 deletions
diff --git a/challenge-208/athanasius/perl/ch-1.pl b/challenge-208/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..41d55bef1b --- /dev/null +++ b/challenge-208/athanasius/perl/ch-1.pl @@ -0,0 +1,213 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 208 +========================= + +TASK #1 +------- +*Minimum Index Sum* + +Submitted by: Mohammad S Anwar + +You are given two arrays of strings. + +Write a script to find out all common strings in the given two arrays with +minimum index sum. If no common strings found returns an empty list. + +Example 1 + + Input: @list1 = ("Perl", "Raku", "Love") + @list2 = ("Raku", "Perl", "Hate") + + Output: ("Perl", "Raku") + + There are two common strings "Perl" and "Raku". + Index sum of "Perl": 0 + 1 = 1 + Index sum of "Raku": 1 + 0 = 1 + +Example 2 + + Input: @list1 = ("A", "B", "C") + @list2 = ("D", "E", "F") + + Output: () + + No common string found, so no result. + +Example 3 + + Input: @list1 = ("A", "B", "C") + @list2 = ("C", "A", "B") + + Output: ("A") + + There are three common strings "A", "B" and "C". + Index sum of "A": 0 + 1 = 1 + Index sum of "B": 1 + 2 = 3 + Index sum of "C": 2 + 0 = 2 + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use List::Util qw( min ); +use Test::More; + +const my $SEPARATOR => '-'; +const my $USAGE => +"Usage: + perl $0 [<list> ...] + perl $0 + + [<list> ...] <string list 1> - <string list 2>\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 208, Task #1: Minimum Index Sum (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $args = scalar @ARGV; + + if ($args == 0) + { + run_tests(); + } + else + { + my ($list1, $list2) = parse_command_line( @ARGV ); + + printf "Input: \@list1 = (%s)\n", format_list( $list1 ); + printf " \@list2 = (%s)\n", format_list( $list2 ); + + my $strings = find_min_idx_strings( $list1, $list2 ); + + printf "\nOutput: (%s)\n", format_list( $strings ); + } +} + +#------------------------------------------------------------------------------- +sub find_min_idx_strings +#------------------------------------------------------------------------------- +{ + my ($list1, $list2) = @_; + my %common; + + OUTER: + for my $i (0 .. $#$list1) + { + my $elem1 = $list1->[ $i ]; + + for my $j (0 .. $#$list2) + { + my $elem2 = $list2->[ $j ]; + + if ($elem1 eq $elem2) + { + push @{ $common{ $i + $j } }, $elem1; + next OUTER; + } + } + } + + my $min_idx = min keys %common; + + return defined $min_idx ? $common{ $min_idx } : []; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my @args = @_; + my $idx = -1; + + for my $i (0 .. $#args) + { + if ($args[ $i ] eq $SEPARATOR) + { + $idx = $i; + last; + } + } + + $idx >= 0 or error( qq[Missing list separator "$SEPARATOR"] ); + + return ([ @args[ 0 .. $idx - 1 ] ], + [ @args[ $idx + 1 .. $#args ] ]); +} + +#------------------------------------------------------------------------------- +sub format_list +#------------------------------------------------------------------------------- +{ + my ($list) = @_; + + return join ', ', map { qq["$_"] } @$list; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $input, $expected) = split / \| /x, $line; + + $test_name =~ s/ \s+ $ //x; + + my @args = split / \s+ /x, $input; + my ($list1, $list2) = parse_command_line( @args ); + my $strings = find_min_idx_strings( $list1, $list2 ); + my $got = join ' ', @$strings; + + is $got, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1|Perl Raku Love - Raku Perl Hate|Perl Raku +Example 2|A B C - D E F | +Example 3|A B C - C A B |A diff --git a/challenge-208/athanasius/perl/ch-2.pl b/challenge-208/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..1c685f02d7 --- /dev/null +++ b/challenge-208/athanasius/perl/ch-2.pl @@ -0,0 +1,278 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 208 +========================= + +TASK #2 +------- +*Duplicate and Missing* + +Submitted by: Mohammad S Anwar + +You are given an array of integers in sequence with one missing and one dupli- +cate. + +Write a script to find the duplicate and missing integer in the given array. +Return -1 if none found. + +For the sake of this task, let us assume the array contains no more than one +duplicate and missing. + +Example 1: + + Input: @nums = (1,2,2,4) + Output: (2,3) + + Duplicate is 2 and Missing is 3. + +Example 2: + + Input: @nums = (1,2,3,4) + Output: -1 + + No duplicate and missing found. + +Example 3: + + Input: @nums = (1,2,3,3) + Output: (3,4) + + Duplicate is 3 and Missing is 4. + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +Error Handling +-------------- +1. Input errors abort the script with a Usage message. The following are input + errors: + - an element in the input array is not a valid integer + - the input array is not sorted in monotonically increasing order. + +2. Sequence errors produce an output of "-1". If $VERBOSE is set to a true + value, an error message is also printed (immediately before the output). The + following are sequence errors: + - an element is duplicated more than once + - more than one element is duplicated + - there is a gap in the sequence of more than one integer + - there is more than one gap in the sequence. + + If no duplicates are found, this is not an error, but since the output is -1, + an explanatory note is printed if $VERBOSE is true. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $VERBOSE => 1; +const my $USAGE => +"Usage: + perl $0 <nums> + perl $0 + + <nums> An ordered list of integers\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 208, Task #2: Duplicate and Missing (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my $nums = parse_command_line(); + + printf "Input: \@nums = (%s)\n", join ',', @$nums; + + my ($dups, $elem) = find_duplicates( $nums ); + my $output = -1; + + if ($dups > 0) + { + my $missing = find_missing( $nums ); + $output = defined $missing ? "($elem,$missing)" : -1; + } + elsif ($VERBOSE) + { + warn "Note: No duplicate found\n"; + } + + print "Output: $output\n"; + } +} + +#------------------------------------------------------------------------------- +sub find_duplicates +#------------------------------------------------------------------------------- +{ + my ($nums) = @_; + my (%counts, @dups); + + ++$counts{ $_ } for @$nums; + + for my $num (sort keys %counts) + { + my $count = $counts{ $num }; + + if ($count > 2) + { + seq_error( qq["$num" is duplicated more than once] ); + return; + } + elsif ($count == 2) + { + push @dups, $num; + } + } + + my $dups = scalar @dups; + + if ($dups > 1) + { + seq_error( 'More than one integer is duplicated' ); + return; + } + + return ($dups, $dups == 0 ? '' : $dups[ 0 ]); +} + +#------------------------------------------------------------------------------- +sub find_missing +#------------------------------------------------------------------------------- +{ + my ($nums) = @_; + my $elem = $nums->[ 0 ]; + my $count = 0; + my $missing; + + for my $i (1 .. $#$nums) + { + my $next = $nums->[ $i ]; + my $diff = $next - $elem; + + if ($diff > 2) + { + seq_error( "More than one integer is missing between $elem and " . + "and $next" ); + return; + } + elsif ($diff == 2) + { + $missing = $elem + 1; + ++$count; + } + + $elem = $next; + } + + if ($count > 1) + { + seq_error( 'There is more than one gap in the sequence' ); + return; + } + + return $missing // $nums->[ -1 ] + 1; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my $elem = $ARGV[ 0 ]; + $elem =~ / ^ $RE{num}{int} $ /x + or input_error( qq["$elem" is not a valid integer] ); + + for my $i (1 .. $#ARGV) + { + my $next = $ARGV[ $i ]; + $next =~ / ^ $RE{num}{int} $ /x + or input_error( qq["$next" is not a valid integer] ); + + $elem <= $next or input_error( 'The input array is not in sequence' ); + $elem = $next; + } + + return [ @ARGV ]; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $input, $expected) = split / \| /x, $line; + + my @nums = split / \, /x, $input; + my ($dups, $elem) = find_duplicates( \@nums ); + my $got = -1; + + if ($dups) + { + my $missing = find_missing( \@nums ); + $got = "$elem,$missing"; + } + + is $got, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub input_error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +#------------------------------------------------------------------------------- +sub seq_error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + warn "Error: $message\n" if $VERBOSE; +} + +################################################################################ + +__DATA__ +Example 1|1,2,2,4|2,3 +Example 2|1,2,3,4|-1 +Example 3|1,2,3,3|3,4 diff --git a/challenge-208/athanasius/raku/ch-1.raku b/challenge-208/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..525b6d2087 --- /dev/null +++ b/challenge-208/athanasius/raku/ch-1.raku @@ -0,0 +1,220 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 208 +========================= + +TASK #1 +------- +*Minimum Index Sum* + +Submitted by: Mohammad S Anwar + +You are given two arrays of strings. + +Write a script to find out all common strings in the given two arrays with +minimum index sum. If no common strings found returns an empty list. + +Example 1 + + Input: @list1 = ("Perl", "Raku", "Love") + @list2 = ("Raku", "Perl", "Hate") + + Output: ("Perl", "Raku") + + There are two common strings "Perl" and "Raku". + Index sum of "Perl": 0 + 1 = 1 + Index sum of "Raku": 1 + 0 = 1 + +Example 2 + + Input: @list1 = ("A", "B", "C") + @list2 = ("D", "E", "F") + + Output: () + + No common string found, so no result. + +Example 3 + + Input: @list1 = ("A", "B", "C") + @list2 = ("C", "A", "B") + + Output: ("A") + + There are three common strings "A", "B" and "C". + Index sum of "A": 0 + 1 = 1 + Index sum of "B": 1 + 2 = 3 + Index sum of "C": 2 + 0 = 2 + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +=end comment +#=============================================================================== + +use Test; + +my Str constant $SEPARATOR = '-'; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 208, Task #1: Minimum Index Sum (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + *@list #= <string list 1> - <string list 2> +) +#=============================================================================== +{ + my List @lists = parse-command-line( @list ); + + "Input: \@list1 = (%s)\n".printf: format-list( @lists[ 0 ] ); + " \@list2 = (%s)\n".printf: format-list( @lists[ 1 ] ); + + my Str @strings = |find-min-idx-strings( @lists ); + + "\nOutput: (%s)\n".printf: format-list( @strings ); +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub find-min-idx-strings( List:D[List:D[Str:D]] $lists --> List:D[Str:D] ) +#------------------------------------------------------------------------------- +{ + my Array[Str] %common{ UInt }; + + L-OUTER: + for 0 .. $lists[ 0 ].end -> UInt $i + { + my Str $elem1 = $lists[ 0; $i ]; + + for 0 .. $lists[ 1 ].end -> UInt $j + { + my Str $elem2 = $lists[ 1; $j ]; + + if $elem1 eq $elem2 + { + %common{ $i + $j }.push: $elem1; + next L-OUTER; + } + } + } + + return [] if %common.elems == 0; + + my UInt $min-idx = %common.keys.min; + + return %common{ $min-idx }; +} + +#------------------------------------------------------------------------------- +sub parse-command-line +( + List:D[Str:D] $list --> List:D[ List:D[Str:D], List:D[Str:D] ] +) +#------------------------------------------------------------------------------- +{ + my Int $idx = -1; + + for 0 .. $list.end -> UInt $i + { + if $list[ $i ] eq $SEPARATOR + { + $idx = $i; + last; + } + } + + $idx >= 0 or error( qq[Missing list separator "$SEPARATOR"] ); + + return $list[ 0 .. $idx - 1 ], $list[ $idx + 1 .. $list.end ]; +} + +#------------------------------------------------------------------------------- +sub format-list( List:D[Str:D] $list --> Str:D ) +#------------------------------------------------------------------------------- +{ + return $list.map( { qq["$_"] } ).join: ', '; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $input, $expected) = $line.split: / \| /; + + $test-name ~~ s/ \s+ $ //; + + my Str @args = $input.split: / \s+ /; + my List @lists = parse-command-line( @args ); + my Str @strings = |find-min-idx-strings( @lists ); + my Str $got = @strings.join: ' '; + + is $got, $expected, $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|Perl Raku Love - Raku Perl Hate|Perl Raku + Example 2|A B C - D E F | + Example 3|A B C - C A B |A + END +} + +################################################################################ diff --git a/challenge-208/athanasius/raku/ch-2.raku b/challenge-208/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..071a0b6795 --- /dev/null +++ b/challenge-208/athanasius/raku/ch-2.raku @@ -0,0 +1,282 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 208 +========================= + +TASK #2 +------- +*Duplicate and Missing* + +Submitted by: Mohammad S Anwar + +You are given an array of integers in sequence with one missing and one dupli- +cate. + +Write a script to find the duplicate and missing integer in the given array. +Return -1 if none found. + +For the sake of this task, let us assume the array contains no more than one +duplicate and missing. + +Example 1: + + Input: @nums = (1,2,2,4) + Output: (2,3) + + Duplicate is 2 and Missing is 3. + +Example 2: + + Input: @nums = (1,2,3,4) + Output: -1 + + No duplicate and missing found. + +Example 3: + + Input: @nums = (1,2,3,3) + Output: (3,4) + + Duplicate is 3 and Missing is 4. + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2023 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +If no command-line arguments are given, the test suite is run. + +Error Handling +-------------- +1. Input errors abort the script with a Usage message. The following are input + errors: + - an element in the input array is not a valid integer + - the input array is not sorted in monotonically increasing order. + +2. Sequence errors produce an output of "-1". If $VERBOSE is set to True, an + error message is also printed (immediately before the output). The following + are sequence errors: + - an element is duplicated more than once + - more than one element is duplicated + - there is a gap in the sequence of more than one integer + - there is more than one gap in the sequence. + + If no duplicates are found, this is not an error, but since the output is -1, + an explanatory note is printed if $VERBOSE is True. + +=end comment +#=============================================================================== + +use Test; + +my Bool constant $VERBOSE = True; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 208, Task #2: Duplicate and Missing (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| An ordered list of integers + + *@nums where { .elems > 0 && .all ~~ Int:D && is-ordered( @nums ) } +) +#=============================================================================== +{ + "Input: \@nums = (%s)\n".printf: @nums.join: ','; + + my (UInt $dups, + Int $elem) = find-duplicates( @nums ); + my Str $output = '-1'; + + if $dups > 0 + { + my Int $missing = find-missing( @nums ); + $output = $missing.defined ?? "($elem,$missing)" !! '-1'; + } + elsif $VERBOSE + { + 'Note: No duplicate found'.note; + } + + "Output: $output".put; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub find-duplicates( List:D[Int:D] $nums --> List:D[ UInt:D, Int ] ) +#------------------------------------------------------------------------------- +{ + my Int %counts{ UInt }; + my Int @dups; + + ++%counts{ $_ } for @$nums; + + for %counts.keys.sort -> $num + { + my UInt $count = %counts{ $num }; + + if $count > 2 + { + seq-error( qq["$num" is duplicated more than once] ); + return; + } + elsif $count == 2 + { + @dups.push: $num; + } + } + + my UInt $dups = @dups.elems; + + if $dups > 1 + { + seq-error( 'More than one integer is duplicated' ); + return; + } + + return $dups, $dups == 0 ?? Int !! @dups[ 0 ]; +} + +#------------------------------------------------------------------------------- +sub find-missing( List:D[Int:D] $nums --> Int ) +#------------------------------------------------------------------------------- +{ + my Int $elem = $nums[ 0 ]; + my UInt $count = 0; + my Int $missing; + + for 1 .. $nums.end -> UInt $i + { + my Int $next = $nums[ $i ]; + my UInt $diff = $next - $elem; + + if $diff > 2 + { + seq-error( "More than one integer is missing between $elem and " ~ + "and $next" ); + return; + } + elsif $diff == 2 + { + $missing = $elem + 1; + ++$count; + } + + $elem = $next; + } + + if $count > 1 + { + seq-error( 'There is more than one gap in the sequence' ); + return; + } + + return $missing // $nums[ *-1 ] + 1; +} + +#------------------------------------------------------------------------------- +sub is-ordered( List:D[Int:D] $nums --> Bool:D ) +#------------------------------------------------------------------------------- +{ + my Int $elem = $nums[ 0 ]; + + for 1 .. $nums.end -> UInt $i + { + my Int $next = $nums[ $i ]; + + $elem <= $next or return False; + $elem = $next; + } + + return True; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $input, $expected) = $line.split: / \| /; + + my Int @nums = $input.split( / \, / ).map: { .Int }; + my (UInt $dups, + Int $elem) = find-duplicates( @nums ); + my Str $got = '-1'; + + if $dups + { + my Int $missing = find-missing( @nums ); + $got = "$elem,$missing"; + } + + is $got, $expected, $test-name; + } + + done-testing; +} + +#------------------------------------------------------------------------------- +sub error( Str:D $message ) +#------------------------------------------------------------------------------- +{ + "ERROR: $message".put; + + USAGE(); + + exit 0; +} + +#------------------------------------------------------------------------------- +sub seq-error( Str:D $message ) +#------------------------------------------------------------------------------- +{ + "Error: $message".note if $VERBOSE; +} + +#------------------------------------------------------------------------------- +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,2,4|2,3 + Example 2|1,2,3,4|-1 + Example 3|1,2,3,3|3,4 + END +} + +################################################################################ |
