## Perl Weekly Challenge 138: Workdays and Split Number

These are some answers to the Week 138 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on November 14, 2021 at 24:00). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

*You are given a year, `\$year` in 4-digits form.

Write a script to calculate the total number of workdays in the given year.

For the task, we consider, Monday - Friday as workdays.

Example 1:

``````Input: \$year = 2021
Output: 261
``````

Example 2:

``````Input: \$year = 2020
Output: 262
``````

A year has 52 weeks, plus one or two days (when the year is leap). So, we will have `5 * 52 = 260` work days, plus 0, 1, or 2 days. So we basically start with 260 work days and add one if Dec. 31 falls on a week day, plus another work day if the year is leap and if Dec. 30 falls on a week day.

### Workdays in Raku

In Raku, we start with Jan. 1 of the input year and add 52 weeks to it. The later of the Dateish role will happily compute the date 52 weeks after Jan. 1 and accurately take care of leap years, so that it will return Dec. 30 on leap years and Dec. 31 on other years. It is then only a matter to find out is these dates fall on week days.

``````sub work-days (UInt \$y) {
my \$new-year-day = Date.new("\$y-01-01");
my \$fifty-two-weeks-later = \$new-year-day.later(:52weeks);
my \$count = 52 * 5;
\$count++ if 1 <= \$fifty-two-weeks-later.day-of-week < 6;
return \$count if \$fifty-two-weeks-later.day == 31;
\$count++ if 1 <= \$fifty-two-weeks-later.later(:1days).day-of-week < 6;
return \$count;
}
say "\$_ -> ", work-days \$_ for 2000..2021;
``````

This program displays the following output:

``````\$ raku ./working-days.raku
2000 -> 260
2001 -> 261
2002 -> 261
2003 -> 261
2004 -> 262
2005 -> 260
2006 -> 260
2007 -> 261
2008 -> 262
2009 -> 261
2010 -> 261
2011 -> 260
2012 -> 261
2013 -> 261
2014 -> 261
2015 -> 261
2016 -> 261
2017 -> 260
2018 -> 261
2019 -> 261
2020 -> 262
2021 -> 261
``````

### Workdays in Perl

In Perl, we’ll use the core Time::Piece module. We’ll use a slightly different strategy: we start with `5 * 52 = 260` work days, add one if Dec. 31 is a week day, and add one more if the year is leap and if Dec. 30 is a week day.

``````use warnings;
use feature qw/say/;
use Time::Piece;

sub work_days {
my \$y = shift;
my \$count = 52 * 5;
my \$t = Time::Piece->strptime("\$y Dec 31", '%Y %b %e');
my \$last_d = \$t->day_of_week;
\$count++ if \$last_d > 0 and \$last_d < 6;
if (\$t->is_leap_year) {
my \$thirtieth = (\$last_d - 1) % 7;
\$count++ if \$thirtieth > 0 and \$thirtieth < 6;
}
return \$count;
}
say "\$_ -> ", work_days \$_ for 2000..2021;
``````

This script displays the following output:

``````\$ perl ./working-days.pl
2000 -> 260
2001 -> 261
2002 -> 261
2003 -> 261
2004 -> 262
2005 -> 260
2006 -> 260
2007 -> 261
2008 -> 262
2009 -> 261
2010 -> 261
2011 -> 260
2012 -> 261
2013 -> 261
2014 -> 261
2015 -> 261
2016 -> 261
2017 -> 260
2018 -> 261
2019 -> 261
2020 -> 262
2021 -> 261
``````

You are given a perfect square.

Write a script to figure out if the square root the given number is same as sum of 2 or more splits of the given number.

Example 1:

``````Input: \$n = 81
Output: 1

Since, sqrt(81) = 8 + 1
``````

Example 2:

``````Input: \$n = 9801
Output: 1

Since, sqrt(9801) = 98 + 0 + 1
``````

Example 3:

``````Input: \$n = 36
Output: 0

Since, sqrt(36) != 3 + 6
``````

### Split Number in Raku

I first thought I could use some combination of the `combinations` and `permutations` methods to solve the problem, but this turned out to be more complicated than expected. So I decided to write a recursive `partition` subroutine to build the various partitions of the input square number and add the various parts. The `split-square` subroutine checks that the input number is a perfect square and calls the `partition` subroutine, passing to it a list of the input number digits. Note that we are using dynamic variables (`\$*found` and `\$*root`) to avoid passing them back and forth in the recursive subroutine calls.

``````use v6;

sub partition (Int \$sum is copy, @in) {
if @in.elems == 0 {
\$*found = 1 if \$sum == \$*root;
return;
}
for 1..@in.elems -> \$i {
my \$new-sum = \$sum + join "", @in[0..(\$i-1)];
last if \$new-sum > \$*root;
partition \$new-sum, @in[\$i..@in.end];
}
}

sub split-square (UInt \$sq) {
my \$*root = sqrt(\$sq).Int;
say "\$sq is not a perfect square\n" and return 0 if \$*root² != \$sq;
my \$*found = 0;
partition(0, \$sq.comb);
return \$*found;
}
for <1 27 81 100 225 1296 6561 9801> -> \$test {
say \$test.fmt("%5d\t") , split-square \$test;
}
``````

This program displays the following output:

raku ./split-squares.raku 1 1 27 is not a perfect square

``````   27   0
81   1
100   1
225   0
1296   1
6561   0
9801   1
``````

### Split Number in Perl

This is essentially a Perl port of the Raku solution above. We also implement a recursive `partition` subroutine to build the various partitions of the input square number and add the various parts. The `split_square` subroutine checks that the input number is a perfect square and calls the `partition` subroutine, passing to it a list of the input number digits.

``````use strict;
use warnings;
use feature "say";

my (\$found, \$root);

sub partition {
my (\$sum, @in) = @_;
if (scalar @in == 0) {
\$found = 1 if \$sum == \$root;
return;
}
for my \$i (1..(scalar @in)) {
my \$new_sum = \$sum + join "", @in[0..(\$i-1)];
last if \$new_sum > \$root;
partition (\$new_sum, @in[\$i..\$#in]);
}
}

sub split_square {
my \$sq = shift;
\$root = int sqrt(\$sq);
say "\$sq is not a perfect square\n" and return 0 if \$root ** 2 != \$sq;
\$found = 0;
partition(0, split //, \$sq);
return \$found;
}
for my \$test (qw<1 27 81 100 225 1296 6561 9801>) {
printf "%5d\t%d\n", \$test , split_square \$test;
}
``````

This program displays the following output:

``````\$ perl  ./split-squares.pl
1   1
27 is not a perfect square

27   0
81   1
100   1
225   0
1296   1
6561   0
9801   1
``````

## Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on November 21, 2021. And, please, also spread the word about the Perl Weekly Challenge if you can.