by Arne Sommer

# The Workdays are Numbered with Raku (and Perl)

[154] Published 14. November 2021.

This is my response to the Perl Weekly Challenge #138.

## Challenge #138.1: Workdays

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 ```

The calender used is the Gregorian calendar, which replaced the Julian calendar from as early as 1582 - depending on country. Dates before the adoption are left as they are. This leads to a fine mess, which I have ignored. The program assumes a Gregorian calender all the way back to year 0 (or 0000, as you would have to specify it).

File: workdays ```#! /usr/bin/env raku unit sub MAIN (Int \$year where \$year.chars == 4); # [1] my \$d = Date.new("\$year-01-01"); # [2] my \$first-day = \$d.day-of-week; # [3] my @workdaysinyear = # [4] ( 0, # dummy 261, # Monday - Monday 261, # Tuesday - Tuesday 261, # Wednesday - wednesday 261, # Thursday - Thursday 261, # Friday - Friday 260, # Saturday - Saturday 259, # Sunday - Sunday ); my @add-leap = # [5] ( 0, # Dummy 1, # Mo -> Tu 1, # Tu -> We 1, # We -> Th 1, # Th -> Fr 0, # Fr -> Sa 0, # Sa -> Su 1, # Su -> Ma ); my \$count = @workdaysinyear[\$first-day] # [6] + (\$d.is-leap-year ?? @add-leap[\$first-day] !! 0); say \$count; ```

[1] Ensure a four digit number as input. Leading zeroes are permitted.

[3] and get which day of the week it is. (Monday is 1, up to Sunday which is 7.)

[4] The number of workdays in a given year, based on 365 days/year (i.e. ignoring leap years). 7 days * 52 weeks = 364 days. So we have to add on a single day to get to 365. The numer og working days is 5 * 52 = 260 + the extra day, if they are all workingdays. They are not. The table has the values. The first day in the comment is the first day of the year, and the second is the last.

[5] If the year is a leap year, does the extra day (number 365) land on a working day?

[6] Get the number of days + any leap year working day.

Running it:

```\$ ./workdays 2021 261 \$ ./workdays 2020 262 ```

### A Perl Version

This is straight forward translation of the Raku version.

File: workdays-perl ```#! /usr/bin/env perl use strict; use warnings; use feature 'say'; use DateTime; my \$year = shift(@ARGV) // ""; die "Please specify a 4 digit year" unless \$year =~ /^\d\d\d\d\$/; my \$dt = DateTime->new(year => \$year, month => 1, day => 1); my \$first_day = \$dt->day_of_week; my @workdaysinyear = ( 0, # dummy 261, # Monday - Monday 261, # Tuesday - Tuesday 261, # Wednesday - wednesday 261, # Thursday - Thursday 261, # Friday - Friday 260, # Saturday - Saturday 259, # Sunday - Sunday ); my @add_leap = ( 0, # Dummy 1, # Mo -> Tu 1, # Tu -> We 1, # We -> Th 1, # Th -> Fr 0, # Fr -> Sa 0, # Sa -> Su 1, # Su -> Ma ); my \$count = \$workdaysinyear[\$first_day] + (\$dt->is_leap_year ? \$add_leap[\$first_day] : 0); say \$count; ```

Running it gives the same result as the Raku version:

```\$ ./workdays-perl 2021 261 \$ ./workdays-perl 2020 262 ```

## Challenge #138.2: Split Number

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 ```

A perfect square is the result of squaring another integer.

File: split-number ```#! /usr/bin/env raku unit sub MAIN (Int \$psq where \$psq.sqrt.Int == \$psq.sqrt && \$psq > 9, # [1] :v(:\$verbose)); my \$sqrt = \$psq.sqrt; # [2] my \$size = \$psq.chars; # [3] say ": Square root of \$psq = \$sqrt" if \$verbose; my \$found = False; # [4] my \$splits := gather # [5] { recurse( \$psq, () ); # [5a] } for \$splits -> \$candidate # [6] { my @list = @\$candidate; # [6a] if @list.elems == 1 # [7] { say ": Candidate list: { @list.join(", ") } (ignored size)" if \$verbose; next; } my \$sum = @list.sum; # [8] if \$sqrt == \$sum # [8a] { say ": Candidate list: { @list.join(", ") } - with correct sum \$sqrt" if \$verbose; say 1 unless \$found; # [8b] \$verbose ?? ( \$found = True ) !! exit; # [8c] } else { say ": Candidate list: { @list.join(", ") } (wrong sum \$sum vs \ expected \$sqrt)" if \$verbose; } } say 0 unless \$found; # [9] sub recurse (\$remainder, @done) # [10] { for 1 .. \$remainder.chars -> \$count # [11] { my \$partial = \$remainder.substr(0, \$count); # [12] my \$new = \$remainder.substr(\$count); # [13] my @so-far = @done.clone; # [14] @so-far.push: \$partial; # [15] \$new eq "" # [16] ?? take @so-far # [16a] !! recurse(\$new, @so-far); # [16b] } } ```

[1] Ensure that the given integer is a perfect square. Also, that it has at least to digits (because of «2 or more splits of the given number», which requires at least 2 digits).

[2] The quare root.

[3] The number of digits (length of the string).

[4] In case of verbose mode, where we go through all the candidates even after finding a match.

[5] Setting up the sequence with `gather`/`take` is ideal here, with a recurisve procedure. The actual `take` is hidden away in the procedure (see [16a]).

See my Raku Gather, I Take article or docs.raku.org/syntax/gather take for more information about `gather`/`take`.

[6] For each candidate, which is a list of values (substrings).

[7] Skip lists with 1 element (i.e. no split at all).

[8] Get the sum. Do we have a match [8a]? If so, print «1» [8b] and exit [8c].

[9] No match? Then say so.

[10] The recursive procedure. The first argument is the remainder of the original number that has not been processes yet. The second is the list of splits so far in this iteration.

[11] We can have from 1 to the entire string characters in each split. (We do not need the whole string, but it felt right to make a general procedure).

[12] Get the new partial,

[13] and the new remainder.

[14] The list of splits, which we clone so that we do not clobber up the values for the other iterations.

See docs.raku.org/routine/clone for more information about the `clone` method.

[15] Add the new partial to the list.

[16] No remainder? If so return the value (with `take`).

Running it:

```\$ ./split-number 81 1 \$ ./split-number 9801 1 \$ ./split-number 36 0 ```

Looking good.

With verbose mode:

```\$ ./split-number -v 81 : Square root of 81 = 9 : Candidate list: 8, 1 - with correct sum 9 1 : Candidate list: 81 (ignored size) \$ ./split-number -v 9801 : Square root of 9801 = 99 : Candidate list: 9, 8, 0, 1 (wrong sum 18 vs expected 99) : Candidate list: 9, 8, 01 (wrong sum 18 vs expected 99) : Candidate list: 9, 80, 1 (wrong sum 90 vs expected 99) : Candidate list: 9, 801 (wrong sum 810 vs expected 99) : Candidate list: 98, 0, 1 - with correct sum 99 1 : Candidate list: 98, 01 - with correct sum 99 : Candidate list: 980, 1 (wrong sum 981 vs expected 99) : Candidate list: 9801 (ignored size) \$ ./split-number -v 36 : Square root of 36 = 6 : Candidate list: 3, 6 (wrong sum 9 vs expected 6) : Candidate list: 36 (ignored size) 0 ```

### Perl

No perl version of this program this week.

And that's it.