Add the Table (or not)
with Raku

by Arne Sommer

Add the Table (or not) with Raku

[156] Published 28. November 2021.

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

Challenge #140.1: Add Binary

You are given two decimal-coded binary numbers, $a and $b.

Write a script to simulate the addition of the given binary numbers.

The script should simulate something like $a + $b. (operator overloading)

Example 1:
Input: $a = 11; $b = 1;
Output: 100
Example 2:
Input: $a = 101; $b = 1;
Output: 110
Example 3:
Input: $a = 100; $b = 11;
Output: 111

A simple version, without operator overloading.

File: add-binary-unloaded
#! /usr/bin/env raku

subset DCB of Int where $_ ~~ /^1<[01]>*$/;     # [1]

unit sub MAIN (DCB $a, DCB $b, :v(:$verbose));  # [1a]

my $length = max($a.chars, $b.chars);           # [5a]

say ": Max lenght: $length" if $verbose;

my @a = $a.comb.reverse;                        # [2]
my @b = $b.comb.reverse;                        # [2]

my $carry = 0;                                  # [3]

my @result;                                     # [4]

for ^$length -> $index                          # [5]
{
  my $aa  = @a.shift // 0;                      # [6]
  my $bb  = @b.shift // 0;                      # [6]
  my $sum = $carry + $aa + $bb;                 # [7]

  say ": a:$aa b:$bb c:$carry -> $sum";

  if    $sum == 3 { $sum = 1; $carry = 1; }     # [8]
  elsif $sum == 2 { $sum = 0; $carry = 1; }     # [9]
  elsif $sum == 1 {           $carry = 0; }     # [10]
  elsif $sum == 0 {           $carry = 0; }     # [11]

  @result.push: $sum;                           # [12]
}

@result.push: $carry if $carry;                 # [13]

say @result.join.flip;                          # [14]

[1] Set up a custom type (with subset) for the input (allowing «0» and «1» only). Note that the first digit must be «1», thus disallowing leading zeroes. (And also disallowing «0» as a value. Oh well.)

See docs.raku.org/language/typesystem#index-entry-subset-subset for more information about subset.

[2] Reverse the values, as arrays, as we want to work from the right hand side (because of carrying).

[3] Carrying.

[4] The result goes here, digit by digit.

[5] Iterate over the longest number (as computed in [5a]).

[6] Get the next digit, for both numbers. If none use a zero (to compensate for a situation where they do not have the same lnegth.)

[7] Add them all togeter, incluing the carry(over from the previous digit calculation).

[8] 3 x «1» gives «11» (i.e. a «1» and a carry).

[9] 2 x «1» gives «10» (i.e. a «0» and a carry).

[10] A single «1» gives «1» (and no carry).

[11] «0» gives «0» (and no carry)..

[12] Add the digit to the list.

[13] Do we have a leftover carry? If so, add it.

[14] We reversed the numbers initially (in [2]), so now we have to de-reverisfy the result.

Running it:

$ ./add-binary-unloaded 11 1
100

$ ./add-binary-unloaded 101 1
110

$ ./add-binary-unloaded 100 11
111

Challenge #140.2: Multiplication Table

You are given 3 positive integers, $i, $j and $k.

Write a script to print the $kth element in the sorted multiplication table of $i and $j.

Example 1:
Input: $i = 2; $j = 3; $k = 4
Output: 3

Since the multiplication of 2 x 3 is as below:

    1 2 3
    2 4 6

The sorted multiplication table:

    1 2 2 3 4 6

Now the 4th element in the table is "3".
Example 2:
Input: $i = 3; $j = 3; $k = 6
Output: 4

Since the multiplication of 3 x 3 is as below:

    1 2 3
    2 4 6
    3 6 9

The sorted multiplication table:

    1 2 2 3 3 4 6 6 9

Now the 6th element in the table is "4".
File: multab
#! /usr/bin/env raku

subset PosInt of Int where $_ > 0;                               # [1]

unit sub MAIN (PosInt $i, PosInt $j, PosInt $k, :v(:$verbose));  # [1a]

my @result;                                                      # [2]

for 1 .. $i -> $ii                                               # [3]
{
  for 1 .. $j -> $jj                                             # [3]
  {
    @result.push: $ii * $jj;                                     # [4]
  }
}

my @sorted = @result.sort;                                       # [5]

say ": Sorted: @sorted[]" if $verbose;

say @sorted[$k -1] // "";                                        # [6]

[1] Set up a custom type ensuring positive integers, and apply it on the arguments (1a).

[2] We are only interested in the result, not the matrix, so store everything in a simple array.

[3] Iterate over the two dimensions.

[4] • Add the product to the array.

[5] Sort the array.

[6] Print the required element. Note the -1 to compensate for arrays starting at index 0. The challenge does not say how to handle an out of bounds situation (i.e. $k > $i * $j), so we print an empty string in that case.

Running it:

$ ./multab 2 3 4
3

$ ./multab 3 3 6
: Sorted: 1 2 2 3 3 4 6 6 9
4

Looking good.

With verbose mode:

$ ./multab -v 2 3 4
: Sorted: 1 2 2 3 4 6
3

$ ./multab -v 3 3 6
: Sorted: 1 2 2 3 3 4 6 6 9
4

The error situation handled by [6] abvove can be done in [1a] instead:

File: multitab2
#! /usr/bin/env raku

subset PosInt of Int where $_ > 0;

unit sub MAIN (PosInt $i, PosInt $j, PosInt $k where 1 <= $k <= $i * $j, # [1]
               :v(:$verbose));

my @result;

for 1 .. $i -> $ii
{
  for 1 .. $j -> $jj
  {
    @result.push: $ii * $jj;
  }
}

my @sorted = @result.sort;

say ": Sorted: @sorted[]" if $verbose;

say @sorted[$k -1];                                                      # [2]

[1] Note the where clause, that can be used like this and not only in combination with subset as above.

[2] The // error handling has gone now.

Running the programs with an out of bounds $k value:

$ ./multab 3 3 10


$ ./multab2 3 3 10
Usage:
  ./multab2 [-v|--verbose[=Any]] <i> <j> <k>

And that's it.