Sum and Sum Again
with Raku and Perl

by Arne Sommer
[182] Published 8. May 2022.

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

Challenge #163.1: Sum Bitwise Operator

You are given a list of positive numbers, @n.

Write script to calculate the sum of bitwise & operator for all unique pairs.

Example 1:
Input: @n = (1, 2, 3)
Output: 3

Since (1 & 2) + (2 & 3) + (1 & 3) => 0 + 2 + 1 =>  3.
Example 2:
Input: @n = (2, 3, 4)
Output: 2

Since (2 & 3) + (2 & 4) + (3 & 4) => 2 + 0 + 0 =>  2.

I do believe that «Positive numbers» should be read as «Postive integers».

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

unit sub MAIN (*@values where @values.elems > 0
                          && all(@values) ~~ /^<[1..9]><[0..9]>*$/,  # [1]
               :v(:$verbose));

if $verbose
{
  say ": Combinations: { @values.combinations(2)
                              .map({ "(" ~ $_[0] ~ "," ~ $_[1] ~ ")" } ) }";
  say ": Mapped to:    { @values.combinations(2).map({ $_[0] ~& $_[1]} ) }";
}

say @values.combinations(2).map({ $_[0] ~& $_[1]} ).sum;             # [2]

[1] Note the regex to ensure a postivie integer, without a leading zero.

[2] We use combinations(2) to get all the unique combinations of length 2. Then we use map to bitwise AND (the ~& operator) the two values together, and finaly we use .sum to add the values together.

See docs.raku.org/routine/combinations for more information about combinations.

See docs.raku.org/language/operators#index-entry-Numeric_bitwise_AND_operator for more information about the numeric bitwise AND operator ~&.

Running it:

$ ./sbo 1 2 3
3

$ ./sbo 2 3 4
2

With verbose mode:

$ ./sbo -v 1 2 3
: Combinations: (1,2) (1,3) (2,3)
: Mapped to:    0 1 2
3

$ ./sbo -v 2 3 4
: Combinations: (2,3) (2,4) (3,4)
: Mapped to:    2 0 0
2

A Perl Version

This is straight forward translation of the Raku version, using modules to get all, sum and combinations.

File: sbo-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';

use Getopt::Long;
use Perl6::Junction 'all';
use List::Util qw(sum);
use Algorithm::Combinatorics qw(combinations);

my $verbose = 0;

GetOptions("verbose" => \$verbose);

my @values = @ARGV;

die "Please specify a list of positve integers" unless @values;
die "Integers only" unless qr/^[1-9][0-9]*$/ == all(@values);

my $sum = 0;

for my $pair (combinations(\@values, 2))
{
  my $and = $pair->[0] & $pair->[1];
  $sum += $and;
  say ": Pair: $pair->[0], $pair->[1] (-> $and)" if $verbose;
}

say $sum;

Running it gives the same result as the Raku version, except for a slightly different verbose output:

$ ./sbo-perl -v 1 2 3
: Pair: 1, 2 (-> 0)
: Pair: 1, 3 (-> 1)
: Pair: 2, 3 (-> 2)
3

$ ./sbo-perl -v 2 3 4
: Pair: 2, 3 (-> 2)
: Pair: 2, 4 (-> 0)
: Pair: 3, 4 (-> 0)
2

Challenge #163.2: Summations

You are given a list of positive numbers, @n.

Write a script to find out the summations as described below.

Example 1:
Input: @n = (1, 2, 3, 4, 5)
Output: 42

    1 2 3  4  5
      2 5  9 14
        5 14 28
          14 42
             42

The nth Row starts with the second element of the (n-1)th row. The
following element is sum of all elements except first element of
previous row. You stop once you have just one element in the row.
Example 2:
Input: @n = (1, 3, 5, 7, 9)
Output: 70

    1 3  5  7  9
      3  8 15 24
         8 23 47
           23 70
              70

I do believe that «Positive numbers» should be read as «Postive integers», as in the first part of the challenge.

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

unit sub MAIN (*@values where @values.elems > 0
                          && all(@values) ~~ /^<[1..9]><[0..9]>*$/,
               :v(:$verbose));

while @values.elems > 1                # [1]
{
  say ": @values[]" if $verbose;

  @values = next-row(@values);         # [2]
}

say @values[0];                        # [3]

sub next-row (@values)
{
  my @new;
  
  for (1 .. @values.elems -1) -> $index   # [4]
  {
    @new.push: @values[1..$index].sum;    # [5]
  }
  return @new;                            # [6]
}

[1] As long as there are more than 1 element in the array,

[2] Transform the array to the next version (with one element less).

[3] Print the remaining element.

[4] For each index in the incoming array (except zero),

[5] Get the sum of all the elements from index 1 to the current index, and add that to the new list.

[6] Return the new list.

Running it:

$ ./summations 1 2 3 4 5
42

$ ./summations 1 3 5 7 9
70

With verbose mode:

$ ./summations -v 1 2 3 4 5
: 1 2 3 4 5
: 2 5 9 14
: 5 14 28
: 14 42
42

$ ./summations -v 1 3 5 7 9
: 1 3 5 7 9
: 3 8 15 24
: 8 23 47
: 23 70
70

Perl

This is a straight forward translation of the Raku version.

File: summations-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';

use Getopt::Long;
use Perl6::Junction 'all';
use List::Util qw(sum);
use feature 'signatures';

no warnings qw(experimental::signatures);

my $verbose = 0;

GetOptions("verbose" => \$verbose);

my @values = @ARGV;

die "Please specify a list of positve integers" unless @values;
die "Integers only" unless qr/^[1-9][0-9]*$/ == all(@values);

while (@values > 1)
{
  say ": @values" if $verbose;

  @values = next_row(@values);
}

say $values[0];

sub next_row (@values)
{
  my @new;
  
  for my $index (1 .. @values -1)
  {
    push(@new, sum(@values[1..$index]));
  }
  return @new;
}

Running it gives the same result as the Raku version:

$ ./summations-perl 1 2 3 4 5
42

$ ./summations-perl -v 1 3 5 7 9
70

With verbose mode:

$ ./summations-perl -v 1 2 3 4 5
: 1 2 3 4 5
: 2 5 9 14
: 5 14 28
: 14 42
42

$ ./summations-perl -v 1 3 5 7 9
: 1 3 5 7 9
: 3 8 15 24
: 8 23 47
: 23 70
70

And that's it.