diff options
| -rw-r--r-- | challenge-333/athanasius/perl/ch-1.pl | 279 | ||||
| -rw-r--r-- | challenge-333/athanasius/perl/ch-2.pl | 176 | ||||
| -rw-r--r-- | challenge-333/athanasius/raku/ch-1.raku | 268 | ||||
| -rw-r--r-- | challenge-333/athanasius/raku/ch-2.raku | 168 |
4 files changed, 891 insertions, 0 deletions
diff --git a/challenge-333/athanasius/perl/ch-1.pl b/challenge-333/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..0a5bfb3679 --- /dev/null +++ b/challenge-333/athanasius/perl/ch-1.pl @@ -0,0 +1,279 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 333 +========================= + +TASK #1 +------- +*Straight Line* + +Submitted by: Mohammad Sajid Anwar + +You are given a list of co-ordinates. + +Write a script to find out if the given points make a straight line. + +Example 1 + + Input: @list = ([2, 1], [2, 3], [2, 5]) + Output: true + +Example 2 + + Input: @list = ([1, 4], [3, 4], [10, 4]) + Output: true + +Example 3 + + Input: @list = ([0, 0], [1, 1], [2, 3]) + Output: false + +Example 4 + + Input: @list = ([1, 1], [1, 1], [1, 1]) + Output: true + +Example 5 + + Input: @list = ([1000000, 1000000], [2000000, 2000000], [3000000, 3000000]) + Output: true + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of coordinate strings is entered on the command-line. +3. If the first coordinate string begins with a hyphen (because the x-coordin- + ate is negative), it must be preceded by "--" to indicate that it is not a + command-line flag. + +Assumptions +----------- +1. The coordinates represent points on the Cartesian plane; therefore, coordin- + ate values are real numbers. +2. Degenerate cases: if there are only 1 or 2 (distinct) coordinates, they are + deemed to be collinear. + +Note +---- +This script handles real number co-ordinate values using Perl's built-in float- +ing-point arithmetic. For this reason, equality comparison cannot be performed +reliably using the "==" operator; instead, an equal_values() subroutine is pro- +vided. If the absolute difference between the values to be compared is less than +a given $EPSILON, then the values are considered equal. + +=cut +#=============================================================================== + +use v5.32; # Enables strictures +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $EPSILON => 1e-10; +const my $USAGE => <<END; +Usage: + perl $0 [<list> ...] + perl $0 + + [<list> ...] A non-empty list of co-ordinate strings (e.g., "1 2" "3 4") +END +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 333, Task #1: Straight Line (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my @list = @ARGV; + my $coords = get_coords( \@list ); + + printf "Input: \@list = (%s)\n", + join ', ', map { '[' . join( ', ', @$_ ) . ']' } @$coords; + + my $collinear = are_collinear( $coords ); + + printf "Output: %s\n", $collinear ? 'true' : 'false'; + } +} + +#------------------------------------------------------------------------------- +sub are_collinear +#------------------------------------------------------------------------------- +{ + my ($list) = @_; + my $coords = remove_duplicates( $list ); + + if (scalar @$coords > 2) + { + my $x0 = $coords->[ 0 ][ 0 ]; + my $y0 = $coords->[ 0 ][ 1 ]; + my $x1 = $coords->[ 1 ][ 0 ]; + my $y1 = $coords->[ 1 ][ 1 ]; + my $m0 = equal_values( $x0, $x1 ) ? undef : ($y1 - $y0) / ($x1 - $x0); + + for my $i (2 .. $#$coords) + { + my $xi = $coords->[ $i ][ 0 ]; + my $yi = $coords->[ $i ][ 1 ]; + my $lhs = $xi; # Assume the line is vertical + my $rhs = $x0; + + if (defined $m0) # It's not + { + $lhs = ($yi - $y0) / ($xi - $x0); + $rhs = $m0; + } + + return '' unless equal_values( $lhs, $rhs ); + } + } + + return 1; +} + +#------------------------------------------------------------------------------- +sub remove_duplicates +#------------------------------------------------------------------------------- +{ + my ($list) = @_; + my @unique; + + for my $coord (@$list) + { + my $found = ''; + + for my $uniq (@unique) + { + if (equal_coords( $uniq, $coord )) + { + $found = 1; + last; + } + } + + push @unique, $coord unless $found; + } + + return \@unique; +} + +#------------------------------------------------------------------------------- +sub equal_coords +#------------------------------------------------------------------------------- +{ + my ($p, $q) = @_; + + return equal_values( $p->[ 0 ], $q->[ 0 ] ) && + equal_values( $p->[ 1 ], $q->[ 1 ] ); +} + +#------------------------------------------------------------------------------- +sub equal_values +#------------------------------------------------------------------------------- +{ + my ($x, $y) = @_; + + return abs( $x - $y ) < $EPSILON; +} + +#------------------------------------------------------------------------------- +sub get_coords +#------------------------------------------------------------------------------- +{ + my ($list) = @_; + my @coords; + + for my $str (@$list) + { + my @pair = grep { / \S /x } split / \s+ /x, $str; + + scalar @pair == 2 or error( qq[Invalid coordinate string "$str"] ); + + for (@pair) + { + / ^ $RE{num}{real} $ /x + or error( qq["$_" is not a valid coordinate value] ); + } + + push @coords, [ map { $_ + 0 } @pair ]; # Normalize the coord values + } + + return \@coords; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $coord_str, $expected) = split / \| /x, $line; + + for ($test_name, $coord_str, $expected) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @list = split / ; /x, $coord_str; + my $coords = get_coords( \@list ); + my $collinear = are_collinear( $coords ) ? 'true' : 'false'; + + is $collinear, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 | 2 1; 2 3; 2 5 |true +Example 2 | 1 4; 3 4; 10 4 |true +Example 3 | 0 0; 1 1; 2 3 |false +Example 4 | 1 1; 1 1; 1 1 |true +Example 5 |1000000 1000000; 2000000 2000000; 3000000 3000000|true +Duplicate 1| 1 2; 1 2; 2 3; 3 4 |true +Duplicate 2| 1 2; 1 2; 2 3; 3 5 |false +Duplicate 3| 1 2; 1 2; 2 3; 1 2 |true +Singleton | 4 9 |true +Pair | 4 9; 7 6 |true +Reals |-1.5 0.3; -2.5 -0.7; -3.5 -1.7 |true diff --git a/challenge-333/athanasius/perl/ch-2.pl b/challenge-333/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..553630be28 --- /dev/null +++ b/challenge-333/athanasius/perl/ch-2.pl @@ -0,0 +1,176 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 333 +========================= + +TASK #2 +------- +*Duplicate Zeros* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers. + +Write a script to duplicate each occurrence of zero, shifting the remaining +elements to the right. The elements beyond the length of the original array are +not written. + +Example 1 + + Input: @ints = (1, 0, 2, 3, 0, 4, 5, 0) + Output: (1, 0, 0, 2, 3, 0, 0, 4) + + Each zero is duplicated. + Elements beyond the original length (like 5 and last 0) are discarded. + +Example 2 + + Input: @ints = (1, 2, 3) + Output: (1, 2, 3) + + No zeros exist, so the array remains unchanged. + +Example 3 + + Input: @ints = (1, 2, 3, 0) + Output: (1, 2, 3, 0) + +Example 4 + + Input: @ints = (0, 0, 1, 2) + Output: (0, 0, 0, 0) + +Example 5 + + Input: @ints = (1, 2, 0, 3, 4) + Output: (1, 2, 0, 0, 3) + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is entered on the command-line. + +=cut +#=============================================================================== + +use v5.32; # Enables strictures +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => <<END; +Usage: + perl $0 [<ints> ...] + perl $0 + + [<ints> ...] A non-empty list of integers +END + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 333, Task #2: Duplicate Zeros (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my @ints = @ARGV; + + for (@ints) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + } + + printf "Input: \@ints = (%s)\n", join ', ', @ints; + + my $dups = duplicate_zeros( \@ints ); + + printf "Output: (%s)\n", join ', ', @$dups; + } +} + +#------------------------------------------------------------------------------- +sub duplicate_zeros +#------------------------------------------------------------------------------- +{ + my ($ints) = @_; + my @dups; + + for my $int (@$ints) + { + push @dups, $int; + push @dups, 0 if $int == 0; + } + + return [ @dups[ 0 .. $#$ints ] ]; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $int_str, $exp_str) = split / \| /x, $line; + + for ($test_name, $int_str, $exp_str) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @ints = split / \s+ /x, $int_str; + my @exps = split / \s+ /x, $exp_str; + my $dups = duplicate_zeros( \@ints ); + + is_deeply $dups, \@exps, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1|1 0 2 3 0 4 5 0|1 0 0 2 3 0 0 4 +Example 2|1 2 3 |1 2 3 +Example 3|1 2 3 0 |1 2 3 0 +Example 4|0 0 1 2 |0 0 0 0 +Example 5|1 2 0 3 4 |1 2 0 0 3 diff --git a/challenge-333/athanasius/raku/ch-1.raku b/challenge-333/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..c20475cdae --- /dev/null +++ b/challenge-333/athanasius/raku/ch-1.raku @@ -0,0 +1,268 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 333 +========================= + +TASK #1 +------- +*Straight Line* + +Submitted by: Mohammad Sajid Anwar + +You are given a list of co-ordinates. + +Write a script to find out if the given points make a straight line. + +Example 1 + + Input: @list = ([2, 1], [2, 3], [2, 5]) + Output: true + +Example 2 + + Input: @list = ([1, 4], [3, 4], [10, 4]) + Output: true + +Example 3 + + Input: @list = ([0, 0], [1, 1], [2, 3]) + Output: false + +Example 4 + + Input: @list = ([1, 1], [1, 1], [1, 1]) + Output: true + +Example 5 + + Input: @list = ([1000000, 1000000], [2000000, 2000000], [3000000, 3000000]) + Output: true + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of coordinate strings is entered on the command-line. +3. If the first coordinate string begins with a hyphen (because the x-coordin- + ate is negative), it must be preceded by "--" to indicate that it is not a + command-line flag. + +Assumptions +----------- +1. The coordinates represent points on the Cartesian plane; therefore, coordin- + ate values are real numbers. +2. Degenerate cases: if there are only 1 or 2 (distinct) coordinates, they are + deemed to be collinear. + +Note +---- +The difficulties of equality comparison using floating-point arithmetic (see the +Perl solution to this Task) are here avoided by representing real number co- +ordinate values as instances of the built-in Raku class Rat. + +=end comment +#=============================================================================== + +use Test; + +subset Coord of List where (Rat, Rat); + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 333, Task #1: Straight Line (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + *@list #= A non-empty list of co-ordinate strings (e.g., "1 2" "3 4") +) +#=============================================================================== +{ + my Coord @coords = get-coords( @list ); + + "Input: \@list = (%s)\n".printf: + @coords.map( { '[' ~ .join( ', ' ) ~ ']' } ).join: ', '; + + my Bool $collinear = are-collinear( @coords ); + + "Output: %s\n".printf: $collinear ?? 'true' !! 'false'; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub are-collinear( List:D[Coord:D] $list --> Bool:D ) +#------------------------------------------------------------------------------- +{ + my Coord @coords = remove-duplicates( $list ); + + if @coords.elems > 2 + { + my Rat $x0 = @coords[ 0; 0 ]; + my Rat $y0 = @coords[ 0; 1 ]; + my Rat $x1 = @coords[ 1; 0 ]; + my Rat $y1 = @coords[ 1; 1 ]; + my Rat $m0 = ($x0 == $x1) ?? Rat !! ($y1 - $y0) / ($x1 - $x0); + + for 2 .. @coords.end -> UInt $i + { + my Rat $xi = @coords[ $i; 0 ]; + my Rat $yi = @coords[ $i; 1 ]; + my Rat $lhs = $xi; # Assume the line is vertical + my Rat $rhs = $x0; + + if $m0.defined # It's not + { + $lhs = ($yi - $y0) / ($xi - $x0); + $rhs = $m0; + } + + return False unless $lhs == $rhs; + } + } + + return True; +} + +#------------------------------------------------------------------------------- +sub remove-duplicates( List:D[Coord:D] $list --> List:D[Coord:D] ) +#------------------------------------------------------------------------------- +{ + my Coord @unique; + + for @$list -> Coord $coord + { + my Bool $found = False; + + for @unique -> Coord $uniq + { + if equal( $uniq, $coord ) + { + $found = True; + last; + } + } + + @unique.push: $coord unless $found; + } + + return @unique; +} + +#------------------------------------------------------------------------------- +sub equal( Coord:D $p, Coord:D $q --> Bool:D ) +#------------------------------------------------------------------------------- +{ + return $p[ 0 ] == $q[ 0 ] && $p[ 1 ] == $q[ 1 ]; +} + +#------------------------------------------------------------------------------- +sub get-coords( List:D[Str:D] $list --> List:D[Coord:D] ) +#------------------------------------------------------------------------------- +{ + my Coord @coords; + + for @$list -> Str $str + { + my Str @pair = $str.split: / \s+ /, :skip-empty; + + @pair.elems == 2 or error( qq[Invalid coordinate string "$str"] ); + + for @pair + { + .Rat // error( qq["$_" is not a valid coordinate value] ); + } + + @coords.push: [ @pair.map: { .Rat } ]; + } + + return @coords; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $coord-str, $expected) = $line.split: / \| /; + + for $test-name, $coord-str, $expected + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Coord @coords = get-coords( [ $coord-str.split: ';' ] ); + my Str $collinear = are-collinear( @coords ) ?? 'true' !! 'false'; + + is $collinear, $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 | 2 1; 2 3; 2 5 |true + Example 2 | 1 4; 3 4; 10 4 |true + Example 3 | 0 0; 1 1; 2 3 |false + Example 4 | 1 1; 1 1; 1 1 |true + Example 5 |1000000 1000000; 2000000 2000000; 3000000 3000000|true + Duplicate 1| 1 2; 1 2; 2 3; 3 4 |true + Duplicate 2| 1 2; 1 2; 2 3; 3 5 |false + Duplicate 3| 1 2; 1 2; 2 3; 1 2 |true + Singleton | 4 9 |true + Pair | 4 9; 7 6 |true + Reals |-1.5 0.3; -2.5 -0.7; -3.5 -1.7 |true + END +} + +################################################################################ diff --git a/challenge-333/athanasius/raku/ch-2.raku b/challenge-333/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..04c0410f74 --- /dev/null +++ b/challenge-333/athanasius/raku/ch-2.raku @@ -0,0 +1,168 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 333 +========================= + +TASK #2 +------- +*Duplicate Zeros* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers. + +Write a script to duplicate each occurrence of zero, shifting the remaining +elements to the right. The elements beyond the length of the original array are +not written. + +Example 1 + + Input: @ints = (1, 0, 2, 3, 0, 4, 5, 0) + Output: (1, 0, 0, 2, 3, 0, 0, 4) + + Each zero is duplicated. + Elements beyond the original length (like 5 and last 0) are discarded. + +Example 2 + + Input: @ints = (1, 2, 3) + Output: (1, 2, 3) + + No zeros exist, so the array remains unchanged. + +Example 3 + + Input: @ints = (1, 2, 3, 0) + Output: (1, 2, 3, 0) + +Example 4 + + Input: @ints = (0, 0, 1, 2) + Output: (0, 0, 0, 0) + +Example 5 + + Input: @ints = (1, 2, 0, 3, 4) + Output: (1, 2, 0, 0, 3) + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is 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 333, Task #2: Duplicate Zeros (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A non-empty list of integers + + *@ints where { .elems > 0 && .all ~~ Int:D } +) +#=============================================================================== +{ + "Input: \@ints = (%s)\n".printf: @ints.join: ', '; + + my Int @dups = duplicate-zeros( @ints ); + + "Output: (%s)\n"\.printf: @dups.join: ', '; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub duplicate-zeros( List:D[Int:D] $ints --> List:D[Int:D] ) +#------------------------------------------------------------------------------- +{ + my Int @dups; + + for @$ints -> Int $int + { + @dups.push: $int; + @dups.push: 0 if $int == 0; + } + + return @dups[ ^$ints.elems ]; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $int-str, $exp-str) = $line.split: / \| /; + + for $test-name, $int-str, $exp-str + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Int @ints = $int-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my Int @exps = $exp-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my Int @dups = duplicate-zeros( @ints ); + + is-deeply @dups, @exps, $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 0 2 3 0 4 5 0|1 0 0 2 3 0 0 4 + Example 2|1 2 3 |1 2 3 + Example 3|1 2 3 0 |1 2 3 0 + Example 4|0 0 1 2 |0 0 0 0 + Example 5|1 2 0 3 4 |1 2 0 0 3 + END +} + +################################################################################ |
