diff options
| -rw-r--r-- | challenge-347/athanasius/perl/ch-1.pl | 249 | ||||
| -rw-r--r-- | challenge-347/athanasius/perl/ch-2.pl | 192 | ||||
| -rw-r--r-- | challenge-347/athanasius/raku/ch-1.raku | 257 | ||||
| -rw-r--r-- | challenge-347/athanasius/raku/ch-2.raku | 185 |
4 files changed, 883 insertions, 0 deletions
diff --git a/challenge-347/athanasius/perl/ch-1.pl b/challenge-347/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..9f4c04a94c --- /dev/null +++ b/challenge-347/athanasius/perl/ch-1.pl @@ -0,0 +1,249 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 347 +========================= + +TASK #1 +------- +*Format Date* + +Submitted by: Mohammad Sajid Anwar + +You are given a date in the form: 10th Nov 2025. + +Write a script to format the given date in the form: 2025-11-10 using the set +below. + + @DAYS = ("1st", "2nd", "3rd", ....., "30th", "31st") + @MONTHS = ("Jan", "Feb", "Mar", ....., "Nov", "Dec") + @YEARS = (1900..2100) + +Example 1 + + Input: $str = "1st Jan 2025" + Output: "2025-01-01" + +Example 2 + + Input: $str = "22nd Feb 2025" + Output: "2025-02-22" + +Example 3 + + Input: $str = "15th Apr 2025" + Output: "2025-04-15" + +Example 4 + + Input: $str = "23rd Oct 2025" + Output: "2025-10-23" + +Example 5 + + Input: $str = "31st Dec 2025" + Output: "2025-12-31" + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A date in the form "10th Nov 2025" is entered on the command-line, either: + i. as a single string; or + ii. as three separate strings. + +Date Validity +------------- +Valid dates are defined by the set (@DAYS, @MONTHS, @YEARS). Dates falling out- +side this set (e.g., "21th Jan 2025", "1st XYZ 2025", "1st Jan 1899") are +rejected. Dates falling within the set are accepted and formatted; but, if they +are in fact invalid (e.g., "31st Apr 2025", "29th Feb 2025"), they are flagged +as such in an appended message. + +Leap Years +---------- +From https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar +"Every year that is exactly divisible by four is a leap year, except for years + that are exactly divisible by 100, but these centurial years are leap years if + they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 + are not leap years, but the years 1600 and 2000 are." + +=cut +#=============================================================================== + +use v5.38.2; # Enables strictures +use warnings; +use Const::Fast; +use List::MoreUtils qw( any firstidx firstval ); +use Test::More; +use enum qw( VALID OUT_OF_RANGE NOT_A_LEAP_YEAR ); + +const my @DAYS => qw( 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th + 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th + 21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th 31st ); +const my @MONTHS => qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); +const my @YEARS => (1900 .. 2100); + +const my $USAGE => <<END; +Usage: + perl $0 <str> + perl $0 <date> <month> <year> + perl $0 + + <str> A date with the format "10th Nov 2025" + <date> A date such as "1st", "22nd", or "30th" + <month> A month such as "Jan" or "Feb" + <year> A year in the range 1900 to 2100 inclusive +END +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 347, Task #1: Format Date (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $argc = scalar @ARGV; + + if ($argc == 0) + { + run_tests(); + } + elsif ($argc == 1) + { + main( $ARGV[0] ); + } + elsif ($argc == 3) + { + main( join ' ', @ARGV ); + } + else + { + error( "Expected 0, 1, or 3 command-line arguments, found $argc" ); + } +} + +#------------------------------------------------------------------------------- +sub main +#------------------------------------------------------------------------------- +{ + my ($str) = @_; + + print qq[Input: \$str = "$str"\n]; + + my ($status, $date) = format_date( $str ); + + print qq[Output: "$date"\n]; + + if ($status == OUT_OF_RANGE) + { + print "\nNote: The date is invalid: out of range\n"; + } + elsif ($status == NOT_A_LEAP_YEAR) + { + print "\nNote: The date is invalid: not a leap year\n"; + } +} + +#------------------------------------------------------------------------------- +sub format_date +#------------------------------------------------------------------------------- +{ + my ($str) = @_; + my $status = VALID; + + my ($day, $month, $year) = $str =~ / ^ (\w{3,4}) \s (\w{3}) \s (\d{4}) $ /x + or error( 'The input date is incorrectly formatted' ); + + firstval { $_ eq $day } @DAYS + or error( 'The day "' . $day . '" is invalid' ); + firstval { $_ eq $month } @MONTHS + or error( 'The month "' . $month . '" is invalid' ); + firstval { $_ eq $year } @YEARS + or error( 'The year "' . $year . '" is invalid' ); + + my ($date) = $day =~ / ^ (\d{1,2}) /x; + + if ($month eq 'Feb') + { + if ($date == 29) + { + $status = NOT_A_LEAP_YEAR if ($year % 4) || + (!($year % 100) && ($year % 400)); + } + elsif ($date >= 30) + { + $status = OUT_OF_RANGE; + } + } + elsif (any { $_ eq $month } qw( Apr Jun Sep Nov )) + { + $status = OUT_OF_RANGE if $date == 31; + } + + my $formatted_date = sprintf '%4d-%02d-%02d', + $year, (firstidx { $_ eq $month } @MONTHS) + 1, $date; + + return ($status, $formatted_date); +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $str, $expected) = split / \| /x, $line; + + for ($test_name, $str, $expected) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my ($status, $date) = format_date( $str ); + + $status == VALID or die "Invalid date: $str"; + + is $date, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "\nERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1| 1st Jan 2025|2025-01-01 +Example 2|22nd Feb 2025|2025-02-22 +Example 3|15th Apr 2025|2025-04-15 +Example 4|23rd Oct 2025|2025-10-23 +Example 5|31st Dec 2025|2025-12-31 diff --git a/challenge-347/athanasius/perl/ch-2.pl b/challenge-347/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..0106732f6f --- /dev/null +++ b/challenge-347/athanasius/perl/ch-2.pl @@ -0,0 +1,192 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 347 +========================= + +TASK #2 +------- +*Format Phone Number* + +Submitted by: Mohammad Sajid Anwar + +You are given a phone number as a string containing digits, space and dash only. + +Write a script to format the given phone number using the below rules: + + 1. Removing all spaces and dashes + 2. Grouping digits into blocks of length 3 from left to right + 3. Handling the final digits (4 or fewer) specially: + - 2 digits: one block of length 2 + - 3 digits: one block of length 3 + - 4 digits: two blocks of length 2 + 4. Joining all blocks with dashes + +Example 1 + + Input: $phone = "1-23-45-6" + Output: "123-456" + +Example 2 + + Input: $phone = "1234" + Output: "12-34" + +Example 3 + + Input: $phone = "12 345-6789" + Output: "123-456-789" + +Example 4 + + Input: $phone = "123 4567" + Output: "123-45-67" + +Example 5 + + Input: $phone = "123 456-78" + Output: "123-456-78" + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A phone number is entered on the command-line as a single string containing + digits, spaces, and dashes (hyphens) only. + +Assumptions +----------- +1. A phone number contains at least 2 digits. +2. Spaces and dashes in a phone number are separators, and so do not occur in + either initial or final position. + +=cut +#=============================================================================== + +use v5.38.2; # Enables strictures +use warnings; +use Const::Fast; +use Test::More; + +const my $USAGE => <<END; +Usage: + perl $0 <phone> + perl $0 + + <phone> A string containing digits, spaces, and dashes only +END + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 347, Task #2: Format Phone Number (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $argc = scalar @ARGV; + + if ($argc == 0) + { + run_tests(); + } + elsif ($argc == 1) + { + my ($phone) = @ARGV; + $phone =~ / ^ \d [\d\s-]* \d $ /x + or error( 'Invalid phone number' ); + + print qq[Input: \$phone = "$phone"\n]; + + my $formatted_num = format_phone_number( $phone ); + + print qq[Output: "$formatted_num"\n]; + } + else + { + error( "Expected 1 or 0 command-line arguments, found $argc" ); + } +} + +#------------------------------------------------------------------------------- +sub format_phone_number +#------------------------------------------------------------------------------- +{ + my ($phone) = @_; + $phone =~ / ^ \d [\d\s-]* \d $ /x + or die 'Invalid phone number'; + + $phone =~ s/ \s //gx; + $phone =~ s/ \- //gx; + + my @blocks = $phone =~ / (.{1,3}) /gx; + + if (length $blocks[-1] == 1) + { + my $ult = \$blocks[-1]; + my $pen = \$blocks[-2]; + my $c = substr $$pen, -1, 1, ''; + $$ult = $c . $$ult; + } + + return join '-', @blocks; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $phone, $expected) = split / \| /x, $line; + + for ($test_name, $phone, $expected) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my $formatted_num = format_phone_number( $phone ); + + is $formatted_num, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1|1-23-45-6 |123-456 +Example 2|1234 |12-34 +Example 3|12 345-6789|123-456-789 +Example 4|123 4567 |123-45-67 +Example 5|123 456-78 |123-456-78 diff --git a/challenge-347/athanasius/raku/ch-1.raku b/challenge-347/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..fb07fecc7c --- /dev/null +++ b/challenge-347/athanasius/raku/ch-1.raku @@ -0,0 +1,257 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 347 +========================= + +TASK #1 +------- +*Format Date* + +Submitted by: Mohammad Sajid Anwar + +You are given a date in the form: 10th Nov 2025. + +Write a script to format the given date in the form: 2025-11-10 using the set +below. + + @DAYS = ("1st", "2nd", "3rd", ....., "30th", "31st") + @MONTHS = ("Jan", "Feb", "Mar", ....., "Nov", "Dec") + @YEARS = (1900..2100) + +Example 1 + + Input: $str = "1st Jan 2025" + Output: "2025-01-01" + +Example 2 + + Input: $str = "22nd Feb 2025" + Output: "2025-02-22" + +Example 3 + + Input: $str = "15th Apr 2025" + Output: "2025-04-15" + +Example 4 + + Input: $str = "23rd Oct 2025" + Output: "2025-10-23" + +Example 5 + + Input: $str = "31st Dec 2025" + Output: "2025-12-31" + +=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 date in the form "10th Nov 2025" is entered on the command-line, either: + i. as a single string; or + ii. as three separate strings. + +Date Validity +------------- +Valid dates are defined by the set (@DAYS, @MONTHS, @YEARS). Dates falling out- +side this set (e.g., "21th Jan 2025", "1st XYZ 2025", "1st Jan 1899") are +rejected. Dates falling within the set are accepted and formatted; but, if they +are in fact invalid (e.g., "31st Apr 2025", "29th Feb 2025"), they are flagged +as such in an appended message. + +Leap Years +---------- +From https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar +"Every year that is exactly divisible by four is a leap year, except for years + that are exactly divisible by 100, but these centurial years are leap years if + they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 + are not leap years, but the years 1600 and 2000 are." + +=end comment +#=============================================================================== + +use Test; + +enum Status < Valid Out-of-range Not-a-leap-year >; +subset Result of List where (Status, Str); + +my constant @DAYS = Array[Str].new: + < 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th + 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th + 21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th 31st >; +my constant @MONTHS = Array[Str].new: + < Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec >; +my constant @YEARS = Array[UInt].new: 1900 .. 2100; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 347, Task #1: Format Date (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + Str:D $str #= A date with the format "10th Nov 2025" +) +#=============================================================================== +{ + main( $str ); +} + +#=============================================================================== +multi sub MAIN +( + Str:D $date, #= A date such as "1st", "22nd", or "30th" + Str:D $month, #= A month such as "Jan" or "Feb" + UInt:D $year #= A year in the range 1900 to 2100 inclusive +) +#=============================================================================== +{ + main( "$date $month $year" ); +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub main( Str:D $str ) +#------------------------------------------------------------------------------- +{ + qq[Input: \$str = "$str"].put; + + my (Status $status, Str $date) = format-date( $str ); + + qq[Output: "$date"].put; + + if $status == Out-of-range + { + "\nNote: The date is invalid: out of range".put; + } + elsif $status == Not-a-leap-year + { + "\nNote: The date is invalid: not a leap year".put; + } +} + +#------------------------------------------------------------------------------- +sub format-date( Str:D $str --> Result:D ) +#------------------------------------------------------------------------------- +{ + my Status $status = Valid; + + $str ~~ / ^ (\w ** 3..4) \s (\w ** 3) \s (\d ** 4) $ / + or error( 'The input date is incorrectly formatted' ); + + my Str $day = ~$0; + my Str $month = ~$1; + my UInt $year = +$2; + + @DAYS\ .first: $day or error( 'The day "' ~ $day ~ '" is invalid' ); + @MONTHS.first: $month or error( 'The month "' ~ $month ~ '" is invalid' ); + @YEARS\.first: $year or error( 'The year "' ~ $year ~ '" is invalid' ); + + my UInt $date = (S/ \D ** 2 $ // with $day).Int; + + if $month eq 'Feb' + { + if $date == 29 + { + $status = Not-a-leap-year if !($year %% 4) || + (($year %% 100) && !($year %% 400)); + } + elsif $date >= 30 + { + $status = Out-of-range; + } + } + elsif $month eq < Apr Jun Sep Nov >.any + { + $status = Out-of-range if $date == 31; + } + + my Str $formatted-date = '%4d-%02d-%02d'.sprintf: + $year, (@MONTHS.first: $month, :k) + 1, $date; + + return ($status, $formatted-date); +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $str, $expected) = $line.split: / \| /; + + for $test-name, $str, $expected + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my (Status $status, Str $date) = format-date( $str ); + + $status == Valid or die "Invalid date: $str"; + + is $date, $expected, $test-name; + } + + done-testing; +} + +#------------------------------------------------------------------------------- +sub error( Str:D $message ) +#------------------------------------------------------------------------------- +{ + "\nERROR: $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| 1st Jan 2025|2025-01-01 + Example 2|22nd Feb 2025|2025-02-22 + Example 3|15th Apr 2025|2025-04-15 + Example 4|23rd Oct 2025|2025-10-23 + Example 5|31st Dec 2025|2025-12-31 + END +} + +################################################################################ diff --git a/challenge-347/athanasius/raku/ch-2.raku b/challenge-347/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..b9cf150b9f --- /dev/null +++ b/challenge-347/athanasius/raku/ch-2.raku @@ -0,0 +1,185 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 347 +========================= + +TASK #2 +------- +*Format Phone Number* + +Submitted by: Mohammad Sajid Anwar + +You are given a phone number as a string containing digits, space and dash only. + +Write a script to format the given phone number using the below rules: + + 1. Removing all spaces and dashes + 2. Grouping digits into blocks of length 3 from left to right + 3. Handling the final digits (4 or fewer) specially: + - 2 digits: one block of length 2 + - 3 digits: one block of length 3 + - 4 digits: two blocks of length 2 + 4. Joining all blocks with dashes + +Example 1 + + Input: $phone = "1-23-45-6" + Output: "123-456" + +Example 2 + + Input: $phone = "1234" + Output: "12-34" + +Example 3 + + Input: $phone = "12 345-6789" + Output: "123-456-789" + +Example 4 + + Input: $phone = "123 4567" + Output: "123-45-67" + +Example 5 + + Input: $phone = "123 456-78" + Output: "123-456-78" + +=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 phone number is entered on the command-line as a single string containing + digits, spaces, and dashes (hyphens) only. + +Assumptions +----------- +1. A phone number contains at least 2 digits. +2. Spaces and dashes in a phone number are separators, and so do not occur in + either initial or final position. + +=end comment +#=============================================================================== + +use Test; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 347, Task #2: Format Phone Number (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A string containing digits, spaces, and dashes only + + Str:D $phone where { / ^ \d <[ \d \s - ]>* \d $ / } +) +#=============================================================================== +{ + qq[Input: \$phone = "$phone"].put; + + my Str $formatted-num = format-phone-number( $phone ); + + qq[Output: "$formatted-num"].put; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub format-phone-number +( + Str:D $phone where { / ^ \d <[ \d \s - ]>* \d $ / } +--> Str:D +) +#------------------------------------------------------------------------------- +{ + my $str = $phone; + $str ~~ s:g/ \s //; + $str ~~ s:g/ \- //; + + my Match @matches = m:g/ (. ** 1..3) / given $str; + my Str @blocks = @matches.map: { ~$_ }; + + if @blocks[*-1].chars == 1 + { + my $ult := @blocks[*-1]; + my $pen := @blocks[*-2]; + + my Str $c = $pen.substr: *-1, 1; + $pen.substr-rw( *-1, 1 ) = ''; + $ult = $c ~ $ult; + } + + return @blocks.join: '-'; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $phone, $expected) = $line.split: / \| /; + + for $test-name, $phone, $expected + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Str $formatted-num = format-phone-number( $phone ); + + is $formatted-num, $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-23-45-6 |123-456 + Example 2|1234 |12-34 + Example 3|12 345-6789|123-456-789 + Example 4|123 4567 |123-45-67 + Example 5|123 456-78 |123-456-78 + END +} + +################################################################################ |
