# Perl Weekly Challenge 47: Roman Number Calculator and Gapful Numbers

One way to let me improve my knowledge about Raku (aka Perl 6) is to implement programs in it. Unluckily, I don’t have any production code to implement in Raku yet (sob!). So, why not try solving the Perl Weekly Challenge tasks?In the following, the assigned tasks for Challenge 47.

## PWC 47 - Task 1

The first task required to write a Roman Number calculator, so something that can accept an expression on the command line using Roman Numbers. The problem had to be split into two main functions: one to decode a roman number into arabic values, and one to do the viceversa. Let’s see how to convert a roman number into an arabic one:```
my %roman-to-arabic = :I(1), :V(5), :X(10), :L(50), :C(100), :D(500), :M(1000);
sub convert-roman-to-arabic( Str:D $roman ) {
# convert to uppercase, reverse the string
# and translates into numbers. For example,
# IX => [ 10 1 ]
# then map so that the current value is multiplied by -1 if the
# previous one is higher
my @arabic-digits = $roman.uc.comb.reverse.map: {
state $last = 0;
my $value = %roman-to-arabic{ $_ };
$value *= -1 if $value < $last;
$last = $value;
$value;
};
return [+] @arabic-digits;
}
```

The trick I used is quite simple:
1) I convert the string into upper case so that each letter can be found as a key into the `%roman-to-arabic`

hash of values;
2) I reverse the list of letters, so that `IX`

becomes `XI`

;
3) I walk the list of letters and, if the last seen value is greater than the current one it means that I have to subtract (that is the value must be modified to a negative number);
4) I then perform the sum of all the values and I obtain the value.
To better explain, imagine I’ve got

`VII`

: I reverse it to `IIV`

and then compose an array of number `[1 1 5]`

and finally I sum to get `7`

. In the case of `IX`

I reverse it to `XI`

and to the array `[10 1 ]`

but since the last seen value when I encounter `1`

is `10`

, the number has to be negated, so `[ 10 -1 ]`

that points me to the final value `9`

.
More complicated is the function to do the opposite:

```
sub convert-arabic-to-roman( Int $arabic ) {
# special cases: what to subtract from every value
my %subtractors = 1_000 => 100,
500 => 100,
100 => 10,
50 => 10,
10 => 1,
5 => 1,
1 => 0;
# reverse the map of the values so that each arabic value
# corresponds to a letter
my %translator = %roman-to-arabic.map( { $_.value => $_.key } );
return '' if ! $arabic;
for %subtractors.pairs.sort: { $^b.key.Int <=> $^a.key.Int } -> $pair {
my $subtractor = $pair.key.Int;
my $removing = $pair.value.Int;
return %translator{ $subtractor } ~ convert-arabic-to-roman( $arabic - $subtractor.Int ) if $arabic >= $subtractor;
return %translator{ $removing } ~ convert-arabic-to-roman( $arabic + $removing.Int ) if $arabic >= $subtractor - $removing;
}
}
```

First of all, I need another hash that contains special *subtractions*, like

`IX`

that means that I can subtract `1`

from `10`

and therefore the number `10`

has a subtracting value of `1`

, here I build the map `10 => 1`

.
I also need to reverse the `%roman-to-arabic`

hash from *letter-to-value*into a

*value-to-letter*hash, so I re

`map`

it to a different hash.
Then I sort the subtracting set of values from the greatest to the lowest and see if the current number that I have is greater then current value, if so I need to add the corresponding letter and do the same with the remaining part. If not, I have to consider the subtraction part (`$removing`

) and append such letter to the letter found for the greater part.
Last, the script:

```
my $operand-a = convert-roman-to-arabic( @*ARGS[0] );
my $operand-b = convert-roman-to-arabic( @*ARGS[2] );
my $result = do given @*ARGS[1].trim {
when '+' { $operand-a + $operand-b; }
when '-' { $operand-a - $operand-b; }
when '/' { $operand-a / $operand-b; }
when '*' { $operand-a * $operand-b; }
};
say convert-arabic-to-roman( $result );
```

The first and last arguments are converted from romant to arabic, then a `given`

switch decides which computation to perform and place it into `$result`

, that is lastly converted to roman and printed.
I admit this is not the most robust calculator, but seems to work to different tests. There might be edge cases that I’ve not thought about.
## PWC 47 - Task 2

In this task I need to print the first 20 Gapful numbers, that are described as:```
# Gapful numbers >= 100: numbers that are divisible by the number
# formed by their first and last digit.
# Numbers up to 100 trivially have this property and are excluded.
```

`100`

to infinity, pushing a number into an array if it is a Gapful number, and stopping the loop when the array has more than 20 elements.
```
for 100 .. Inf {
$_ ~~ / ^ $<first>=\d \d+ $<last>=\d $ /;
my $divisor = ( $/<first> ~ $/<last> ).Int;
@found.push: $divisor if $_ %% $divisor && ! @found.grep: { $_ == $divisor };
last if @found.elems == $limit;
}
```

With a regular expression I got the first and last digit, and then I see with `%%`

if the number is divisible by such number, and if so I add it to the `@found`

array but only if that number is not already present. Here `$limit`

is a `MAIN** argument set to 20 by default.
## Arghhhh!!! Typo in the program I submitted!

**updated on 2020-02-18**I discovered that my program was not printing out numbers greater than

`100`

, that was what the task asked me.
Why? Well, I mistakenly **pushed the**

`$divisor`

instead of the number itself!The right piece of code, therefore, has the following line:

```
@found.push: $_ if $_ %% $divisor && ! @found.grep: { $_ == $divisor };
```

*What a shame!*

The solution is available here, where I’ve also added an extra check to avoid division by zero (that cannot happen with the input from the task).