by Arne Sommer

# The 13th Roman with Raku

 Published 26. July 2023.

This is my response to The Weekly Challenge #227.

## Challenge #227.1: Friday 13th

You are given a year number in the range 1753 to 9999.

Write a script to find out how many dates in the year are Friday 13th, assume that the current Gregorian calendar applies.

Example: ```Input: \$year = 2023 Output: 2 Since there are only 2 Friday 13th in the given year 2023 i.e. 13th Jan and 13th Oct. ```
File: friday-13th ```#! /usr/bin/env raku unit sub MAIN (Int \$year where 1753 <= \$year <= 9999 = Date.today.year, #  :v(:\$verbose)); my \$fridays = 0; #  for 1 .. 12 -> \$month #  { my \$date = Date.new(year => \$year, month => \$month, day => 13); #  my \$is-friday = \$date.day-of-week == 5; #  \$fridays++ if \$is-friday; #  say ": \$date { \$is-friday ?? " Friday" !! ""}" if \$verbose; } say \$fridays; #  ```

 Ensure that the user specified year is within the limits, if given. If not, default to the current year (with `Date.today.year`).

See docs.raku.org/type/Date for information about the `Date` class.

 The result will end up here.

 Iterate over the months.

 Create a `Date` object for the 13th of each month.

 Check if it is indeed a Friday,

 and increase the counter if it is.

 Print the result (i.e. number of Friday 13ths in the given year).

Running it:

```\$ ./friday-13th 2 ```

Looking good.

With verbose mode:

```\$ ./friday-13th -v : 2023-01-13 Friday : 2023-02-13 : 2023-03-13 : 2023-04-13 : 2023-05-13 : 2023-06-13 : 2023-07-13 : 2023-08-13 : 2023-09-13 : 2023-10-13 Friday : 2023-11-13 : 2023-12-13 2 ```

We can make it shorter, with `grep` instead of the explicit `for` loop:

File: friday-13th-grep ```#! /usr/bin/env raku unit sub MAIN (Int \$year where 1753 <= \$year <= 9999 = Date.today.year); say (1 .. 12).grep({Date.new(year => \$year, month => \$_, day => 13).day-of-week == 5 }).elems; ```

Running it gives the expected result:

```\$ ./friday-13th-grep 2 ```

## Challenge #227.2: Roman Maths

Write a script to handle a 2-term arithmetic operation expressed in Roman numeral.

Example: ```IV + V => IX M - I => CMXCIX X / II => V XI * VI => LXVI VII ** III => CCCXLIII V - V => nulla (they knew about zero but didn't have a symbol) V / II => non potest (they didn't do fractions) MMM + M => non potest (they only went up to 3999) V - X => non potest (they didn't do negative numbers) ```

This is very similar to the first part of Challenge 47: «Roman Calculator»; see Roman Gap with Raku for my take on it. The following code is a minor adaptation of code presented there, and I will only comment the changes.

We start off with a module with procedures converting to and from Roman numerals.

File: lib/Number/Roman.rakumod ```unit module Number::Roman; our sub to-roman (Numeric \$number is copy) is export(:to) #  { return "nulla" if \$number == 0; #  return "non potest" unless 0 < \$number < 3999; #  return "non potest" unless \$number.Int == \$number; #  my \$string = ""; while \$number >= 1000 { \$string ~= "M"; \$number -= 1000; } if \$number >= 900 { \$string ~= "CM"; \$number -= 900; } if \$number >= 500 { \$string ~= "D"; \$number -= 500; } if \$number >= 400 { \$string ~= "CD"; \$number -= 400; } while \$number >= 100 { \$string ~= "C"; \$number -= 100; } if \$number >= 90 { \$string ~= "XC"; \$number -= 90; } if \$number >= 50 { \$string ~= "L"; \$number -= 50; } if \$number >= 40 { \$string ~= "XL"; \$number -= 40; } while \$number >= 10 { \$string ~= "X"; \$number -= 10; } if \$number >= 9 { \$string ~= "IX"; \$number -= 9; } if \$number >= 5 { \$string ~= "V"; \$number -= 5; } if \$number >= 4 { \$string ~= "IV"; \$number -= 4; } while \$number >= 1 { \$string ~= "I"; \$number -= 1; } return \$string; } my %value = (I => 1, V => 5, X => 10, L => 50, C => 100, D => 500, M => 1000); my Set \$valid-roman = %value.keys.Set; my \$current-value = Inf; our sub from-roman (Str \$roman) is export(:from) { my @digits = \$roman.comb; die "Non-Roman digit \$_ detected." unless \$valid-roman{\$_} for @digits; my \$number = 0; while @digits { my \$current = @digits.shift; if @digits.elems { if %value{@digits} > %value{\$current} { \$number += %value{@digits.shift} - %value{\$current}; next; } } \$number += %value{\$current}; } return to-roman(\$number) eq \$roman ?? \$number !! die "Not a valid Roman Number: \$roman"; } ```

 The type is now `Numeric` instead of `Int`. That makes it possible to pass on rational numbers (as e.g. 2.5 in the `V / II` example).

 Zeroes are illegal, return an error message.

 Negative vales, and values larger than 3999 are also illegal.

 Detect non-integer values.

The main program:

File: roman-maths ```#! /usr/bin/env raku use lib "lib"; #  use Number::Roman :to, :from; unit sub MAIN (Str \$first, Str \$operator, Str \$second); my \$f = from-roman(\$first); my \$s = from-roman(\$second); given \$operator { when '+' { say to-roman(\$f + \$s) }; when '-' { say to-roman(\$f - \$s) }; when 'x' { say to-roman(\$f * \$s) }; #  when '*' { say to-roman(\$f * \$s) }; when 'xx' { say to-roman(\$f ** \$s) }; #  when '**' { say to-roman(\$f ** \$s) }; #  when '/' { say to-roman(\$f / \$s) }; default { die "unknown operator"; } #  } ```

 The module is located in this directory.

 The `*` operation has this alias, so that we can drop the quotes (as the shell interferes with unquoted `*`).

 This operation in new in this version of the program.

 Error catching with `default` on the `given`/`when` structure was missing in the original program. My bad.

See docs.raku.org/language/control#index-entry-switch_(given) for more information about `given`/`when`.

See docs.raku.org/language/control#default_and_when for more information about `default`.

Running it:

```\$ ./roman-maths IV + V IX ./roman-maths M - I CMXCIX \$ ./roman-maths X / II V \$ ./roman-maths XI x VI LXVI \$ ./roman-maths VII xx III CCCXLIII \$ ./roman-maths V - V nulla \$ ./roman-maths V / II non potest \$ ./roman-maths MMM + M non potest \$ ./roman-maths V - X non potest ```

Looking good.

And that's it.