aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xchallenge-138/jo-37/perl/ch-1.pl91
-rwxr-xr-xchallenge-138/jo-37/perl/ch-2.pl87
2 files changed, 178 insertions, 0 deletions
diff --git a/challenge-138/jo-37/perl/ch-1.pl b/challenge-138/jo-37/perl/ch-1.pl
new file mode 100755
index 0000000000..da396433b8
--- /dev/null
+++ b/challenge-138/jo-37/perl/ch-1.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Test2::V0;
+use DateTime;
+use experimental 'signatures';
+# For testing only:
+use DateTime::Duration;
+
+our ($tests, $examples);
+
+run_tests() if $tests || $examples; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [Y]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+Y
+ Count workday in year Y.
+
+EOS
+
+
+### Input and Output
+
+# Count workdays between Jan 1 of the given year and the next.
+say workdays_int(DateTime->new(year => $ARGV[0]),
+ DateTime->new(year => $ARGV[0] + 1));
+
+
+### Implementation
+
+# Find the number of workdays in the interval from $start to (not
+# including) $end. Expecting DateTime arguments.
+sub workdays_int ($start, $end) {
+ return 0 if $start >= $end;
+ my $days = $end->delta_days($start)->in_units('days');
+ # Sat is 5, Sun is 6.
+ my $dow = $start->day_of_week_0;
+
+ # Every full week has 5 workdays. From the remaining days count
+ # working days only.
+ 5 * int($days / 7) + grep abs($dow + $_ - 5.5) > 1, 0 .. $days % 7 - 1;
+}
+
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ is workdays_int(DateTime->new(year => 2021),
+ DateTime->new(year => 2022)), 261, 'example 1';
+ is workdays_int(DateTime->new(year => 2020),
+ DateTime->new(year => 2021)), 262, 'example 2';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ my $m = DateTime->new(
+ year => 2021,
+ month => 11,
+ day => 8); # Challenge 138 published on Mon
+ my $d = DateTime::Duration->new(days => 1);
+ is workdays_int($m, $m), 0, 'same day';
+ is workdays_int($m, $m + 4*$d), 4, 'Mon to Fri';
+ is workdays_int($m, $m + 5*$d), 5, 'Mon to Sat';
+ is workdays_int($m, $m + 6*$d), 5, 'Mon to Sun';
+ is workdays_int($m + 3*$d, $m + 17*$d), 10, 'two full weeks';
+ is workdays_int($m + 3*$d, $m + 5*$d), 2, 'Thu to Sat';
+ is workdays_int($m + 3*$d, $m + 6*$d), 2, 'Thu to Sun';
+ is workdays_int($m + 3*$d, $m + 7*$d), 2, 'Thu to Mon';
+ is workdays_int($m + 4*$d, $m + 5*$d), 1, 'Fri to Sat';
+ is workdays_int($m + 4*$d, $m + 6*$d), 1, 'Fri to Sun';
+ is workdays_int($m + 4*$d, $m + 7*$d), 1, 'Fri to Mon';
+ is workdays_int($m + 5*$d, $m + 7*$d), 0, 'Sat to Mon';
+ is workdays_int($m + 6*$d, $m + 7*$d), 0, 'Sun to Mon';
+ is workdays_int($m + 6*$d, $m + 12*$d), 5, 'Sun to Sat';
+ is workdays_int($m + $d, $m), 0, 'end before start';
+ }
+
+ done_testing;
+ exit;
+}
diff --git a/challenge-138/jo-37/perl/ch-2.pl b/challenge-138/jo-37/perl/ch-2.pl
new file mode 100755
index 0000000000..bf1930ebec
--- /dev/null
+++ b/challenge-138/jo-37/perl/ch-2.pl
@@ -0,0 +1,87 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Test2::V0;
+use experimental 'signatures';
+
+our ($tests, $examples);
+
+run_tests() if $tests || $examples; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [N]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+N
+ Check if the square root of N is the sum of two or more splits of N
+
+EOS
+
+
+### Input and Output
+
+say 0 + root_sum(shift);
+
+
+### Implementation
+
+sub root_sum ($n) {
+ # Split N into at least two parts and compare the squared sum of the
+ # parts with N.
+ # There is no need to disallow leading zeros in any part (except the
+ # first), as these may be split-off as separate parts resulting in
+ # the same sum and a larger number of parts, e.g.
+ # 201: 2 + 01 or 2 + 0 + 1.
+ # The only exception are single digit numbers that cannot be split
+ # into two parts and therefore must not have leading zeros.
+ our $sum;
+ $n =~ m{
+ ^
+ (?=\d+$) # Numbers only.
+ (?!0+.$) # Forbid leading zeros for 1-digit numbers.
+ (?{local $sum = 0}) # Initialize and localize the part sum.
+ (?:
+ (.+) # capture one part
+ (?{local $sum = $sum + $^N}) # backtracking-safe part sum
+ ){2,} # two or more parts
+ $
+
+ # Check part sum for complete matches:
+ (?(?{$sum ** 2 != $n}) (*FAIL))
+ }x;
+}
+
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ is root_sum(81), T(), 'example 1';
+ is root_sum(9801), T(), 'example 2';
+ is root_sum(36), F(), 'example 3';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ is root_sum(1296), T(), 'without zeros';
+ is root_sum(18), F(), 'not a square';
+ is root_sum(0), F(), 'zero';
+ is root_sum('00'), F(), 'double zero';
+ is root_sum(1), F(), 'one';
+ is root_sum('01'), F(), 'one with leading zero';
+ is root_sum('001'), F(), 'one with leading zeros';
+ is root_sum('081'), T(), 'leading zero';
+ is root_sum(-abc), F(), 'non-numeric';
+ }
+
+ done_testing;
+ exit;
+}