Weekly Challenge 227
Tags: perl, python, theweeklychallenge
Published: 2023-07-30 05:01:20

Each week Mohammad  S. Anwar sends out [The Weekly Challenge](https://theweeklychallenge.org/), a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. It's a great way for us all to practice some coding.

[Challenge](https://theweeklychallenge.org/blog/perl-weekly-challenge-227/), [My solutions](https://github.com/manwar/perlweeklychallenge-club/tree/master/challenge-227/sgreen)

## Task 1: Friday 13th

### Task

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.

### My solution

This is rather straight forward. Create a loop for each month of the year and see if the 13th of the month is a Friday. For Python, I use the [date](https://docs.python.org/3/library/datetime.html#date-objects) module (from datetime). In Perl, I use [Date::Calc](https://metacpan.org/pod/Date::Calc).

### Examples

```bash
$ ./ch-1.py 2023
2
```

## Task 2: Roman Maths

### Task

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

### My solution

The main part of this routine is a method `from_roman` which converts a roman number to an integer, and `to_roman` which converts a number to a roman number.

The main function converts the two provided numbers into decimals, performs the mathematical operation, and converts the result back to a roman number.

#### from_roman method

The first thing is to convert the letter to its equivalent number.

```python
mapping = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
digits = [mapping[s] for s in roman]
```

I then loop through each value. If the next value is higher, I subtract that from number (as is the case with 'I' in 'IV' or 'IX'), otherwise I add the number.

```python
for i, v in enumerate(digits):
    if i < len(digits)-1 and v < digits[i+1]:
        number -= v
    else:
        number += v
```

This is not perfect. For example `MIM` will erroneously return 1999.

#### to_roman method

The first step is to handle cases where the number is greater than 3999, less than 1 or not a whole number. In this case, I display the appropriate words.

I start by creating a mapping table. Each row represents single digits, tens, hundreds and thousands. Each value represents how to write that number. For example 40 is the 5th value in the 2nd row which is 'XL'.

```python
mapping = [
    ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', ],
    ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', ],
    ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', ],
    ['', 'M', 'MM', 'MMM', ],
]
```

and then iterate backwards to get the correct roman number.

```python
for i, d in enumerate(number[::-1]):
    roman = mapping[i][int(d)] + roman
```

### Examples

```bash
$ ./ch-2.py IV + V
IX

$ ./ch-2.py M - I
CMXCIX

$ ./ch-2.py X / II
V

$ ./ch-2.py XI '*' VI
LXVI

$ ./ch-2.py VII '**' III
CCCXLIII

$ ./ch-2.py V - V
nulla

$ ./ch-2.py V / II
non potest

$ ./ch-2.py MMM + M
non potest

$ ./ch-2.py V - X
non potest
```