Reverse Floor
with Raku

by Arne Sommer

Reverse Floor with Raku

[263] Published 16. November 2023.

This is my response to The Weekly Challenge #243.

Challenge #243.1: Reverse Pairs

You are given an array of integers.

Write a script to return the number of reverse pairs in the given array.

A reverse pair is a pair (i, j) where: a) 0 <= i < j < nums.length and b) nums[i] > 2 * nums[j].

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

(1, 4) => nums[1] = 3, nums[4] = 1, 3 > 2 * 1
(3, 4) => nums[3] = 3, nums[4] = 1, 3 > 2 * 1
Example 2:
Input: @nums = (2, 4, 3, 5, 1)
Output: 3

(1, 4) => nums[1] = 4, nums[4] = 1, 4 > 2 * 1
(2, 4) => nums[2] = 3, nums[4] = 1, 3 > 2 * 1
(3, 4) => nums[3] = 5, nums[4] = 1, 5 > 2 * 1
File: reverse-pairs
#! /usr/bin/env raku

unit sub MAIN (*@nums where @nums.elems > 1 && all(@nums) ~~ Int,  # [1]
               :v(:$verbose));

my $count = 0;                                                     # [2]

for 0 .. @nums.end -1 -> $i                                        # [3]
{
  for $i + 1 .. @nums.end -> $j                                    # [4]
  {
    if @nums[$i] > 2 *@nums[$j]                                    # [5]
    {
      say ": ($i, $j) => nums[$i] = @nums[$i], nums[$j] = @nums[$j], \
        @nums[$i] > 2 * @nums[$j]" if $verbose;
      $count++;                                                    # [5a]
    }
  }
}

say $count;                                                        # [6]

[1] At least 2 elements (because of the «a» rule), all of which must be integers.

[2] The number of reverse pairs will end up here.

[3] Iterate over the i indices, respecting the «a» rule. Note the use of end to get the index of the last item.

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

[4] Iterate over the j indices, respecting the «a» rule.

[5] Does the «b» rule hold? If so, add it the count [5b].

[6] Print the result.

Running it:

$ ./reverse-pairs 1 3 2 3 1
2

$ ./reverse-pairs 1 3 2 3 1
2

$ ./reverse-pairs 2 4 3 5 1
3

Looking good.

With verbose mode:

$ ./reverse-pairs -v 1 3 2 3 1
: (0, 1) => nums[0] = 1, nums[1] = 3, 1 > 2 * 3
: (0, 2) => nums[0] = 1, nums[2] = 2, 1 > 2 * 2
: (0, 3) => nums[0] = 1, nums[3] = 3, 1 > 2 * 3
: (0, 4) => nums[0] = 1, nums[4] = 1, 1 > 2 * 1
: (1, 2) => nums[1] = 3, nums[2] = 2, 3 > 2 * 2
: (1, 3) => nums[1] = 3, nums[3] = 3, 3 > 2 * 3
: (1, 4) => nums[1] = 3, nums[4] = 1, 3 > 2 * 1
: (1, 4) => nums[1] = 3, nums[4] = 1, 3 > 2 * 1
: (2, 3) => nums[2] = 2, nums[3] = 3, 2 > 2 * 3
: (2, 4) => nums[2] = 2, nums[4] = 1, 2 > 2 * 1
: (3, 4) => nums[3] = 3, nums[4] = 1, 3 > 2 * 1
: (3, 4) => nums[3] = 3, nums[4] = 1, 3 > 2 * 1
2

$ ./reverse-pairs -v 1 3 2 3 1
: (1, 4) => nums[1] = 3, nums[4] = 1, 3 > 2 * 1
: (3, 4) => nums[3] = 3, nums[4] = 1, 3 > 2 * 1
2

$ ./reverse-pairs -v 2 4 3 5 1
: (1, 4) => nums[1] = 4, nums[4] = 1, 4 > 2 * 1
: (2, 4) => nums[2] = 3, nums[4] = 1, 3 > 2 * 1
: (3, 4) => nums[3] = 5, nums[4] = 1, 5 > 2 * 1
3

Challenge #243.2: Floor Sum

You are given an array of positive integers (>=1).

Write a script to return the sum of floor(nums[i] / nums[j]) where 0 <= i,j < nums.length. The floor() function returns the integer part of the division.

Example 1:
Input: @nums = (2, 5, 9)
Output: 10

floor(2 / 5) = 0
floor(2 / 9) = 0
floor(5 / 9) = 0
floor(2 / 2) = 1
floor(5 / 5) = 1
floor(9 / 9) = 1
floor(5 / 2) = 2
floor(9 / 2) = 4
floor(9 / 5) = 1
Example 2:
Input: @nums = (7, 7, 7, 7, 7, 7, 7)
Output: 49
File: floor-sum
#! /usr/bin/env raku

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

unit sub MAIN (*@nums where @nums.elems > 0 && all(@nums) ~~ PosInt,  # [2]
               :v(:$verbose));

my $sum = 0;                                                          # [3]

for ^@nums.elems -> $i                                                # [4]
{
  for ^@nums.elems -> $j                                              # [4]
  {
    my $floor = @nums[$i] div @nums[$j];                              # [5]
    say ": floor(@nums[$i]/@nums[$j]) = $floor" if $verbose;
    $sum += $floor;                                                   # [6]
  }
}

say $sum;                                                             # [7]

[1] Set up a custom type (with subset) that allows for positive integers only.

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

[2] At least one element, and they must all be positive integers.

[3] The result will end up here.

[4] Iterate over the indices, for both variables. Note the use of ^ (the upto operator), giving the values from 0 to one less than the value given to it (i.e. «up to« but not including).

See docs.raku.org/routine/^ for more information about the Upto Operator ^.

[5] Rauku has a floor function, but it is more efficient to use integer division (with div instead of /).

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

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

[6] Add the current floor value to the sum.

[7] Print the result.

Running it:

$ ./floor-sum 2 5 9
10

$ ./floor-sum 7 7 7 7 7 7 7
49

Looking good.

With verbose mode:

$ ./floor-sum -v 2 5 9
: floor(2/2) = 1
: floor(2/5) = 0
: floor(2/9) = 0
: floor(5/2) = 2
: floor(5/5) = 1
: floor(5/9) = 0
: floor(9/2) = 4
: floor(9/5) = 1
: floor(9/9) = 1
10

$ ./floor-sum -v 7 7 7 7 7 7 7
: floor(7/7) = 1
<47 identical rows removed>
: floor(7/7) = 1
49

And that's it.