Uniquely Special
with Raku

by Arne Sommer

Uniquely Special with Raku

[272] Published 20. January 2024.

This is my response to The Weekly Challenge #252.

Challenge #252.1: Special Numbers

You are given an array of integers, @ints.

Write a script to find the sum of the squares of all special elements of the given array.

An element $int[i] of @ints is called special if i divides n, i.e. n % i == 0. Where n is the length of the given array. Also the array is 1-indexed for the task.

Example 1:
Input: @ints = (1, 2, 3, 4)
Output: 21

There are exactly 3 special elements in the given array:
$ints[1] since 1 divides 4,
$ints[2] since 2 divides 4, and
$ints[4] since 4 divides 4.

Hence, the sum of the squares of all special elements of given array:
1 * 1 + 2 * 2 + 4 * 4 = 21.
Example 2:
Input: @ints = (2, 7, 1, 19, 18, 3)
Output: 63

There are exactly 4 special elements in the given array:
$ints[1] since 1 divides 6,
$ints[2] since 2 divides 6,
$ints[3] since 3 divides 6, and
$ints[6] since 6 divides 6.

Hence, the sum of the squares of all special elements of given array:
2 * 2 + 7 * 7 + 1 * 1 + 3 * 3 = 63
File: special-numbers
#! /usr/bin/env raku

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

my $sum = 0;                                                       # [2]
my $n   = @ints.elems;                                             # [3]

for 1 .. $n -> $i                                                  # [4]
{
  if $n %% $i                                                      # [5]
  {
    my $val     = @ints[$i -1];                                    # [6]
    my $squared = $val ** 2;                                       # [7]

    $sum += $squared;                                              # [8]

    say ": Index $i is special -> value:$val -> squared:$squared -> sum:$sum"
      if $verbose;
  }
}

say $sum;                                                          # [9]

[1] At least one value, and they must all be integers.

[2] The sum will be added up here.

[3] The number of elements (and the highest 1-based index).

[4] Iterate over the 1-based indices.

[5] Is the number of items divisable (the %% operator) by the index.

See docs.raku.org/routine/%% for more information about the Divisibility Operator %%.

[6] Get the value at the current index.

[7] Square the value.

[8] Add that square to the sum.

[9] Print the result.

Running it:

$ ./special-numbers 1 2 3 4
21

$ ./special-numbers 2 7 1 19 18 3
63

Looking good.

With verbose mode:

$ ./special-numbers -v 1 2 3 4
: Index 1 is special -> value:1 -> squared:1 -> sum:1
: Index 2 is special -> value:2 -> squared:4 -> sum:5
: Index 4 is special -> value:4 -> squared:16 -> sum:21
21

$ ./special-numbers -v 2 7 1 19 18 3
: Index 1 is special -> value:2 -> squared:4 -> sum:4
: Index 2 is special -> value:7 -> squared:49 -> sum:53
: Index 3 is special -> value:1 -> squared:1 -> sum:54
: Index 6 is special -> value:3 -> squared:9 -> sum:63
63

Challenge #252.2: Unique Sum Zero

You are given an integer, $n.

Write a script to find an array containing $n unique integers such that they add up to zero.

Example 1:
Input: $n = 5
Output: (-7, -1, 1, 3, 4)

Two other possible solutions could be as below:
(-5, -1, 1, 2, 3) and (-3, -1, 2, -2, 4).
Example 2:
Input: $n = 3
Output: (-1, 0, 1)
Example 3:
Input: $n = 1
Output: (0)

Random numbers, as used in the examples, only add complexity (and entropy - which makes testing complicated). So let us not go that way.

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

unit sub MAIN (UInt $n is copy, :v(:$verbose));  # [1]

my @array;                                       # [2]

if $n % 2                                        # [3]
{
  @array = (0,);                                 # [4]
  say ": Odd number, added middle zero" if $verbose;
}

for 1 .. $n div 2 -> $i                          # [5]
{
  @array.push:     $i;                           # [6]
  @array.unshift: -$i;                           # [7]

  say ": Added { -$i } at front and $i at end" if $verbose;
}

say "({ @array.join(", ") })";                   # [8]

[1] Ensure that the input is of the UInt (Unsigned Int) type.

See docs.raku.org/type/UInt for more information about the UInt type.

[2] The result will end up here, presorted.

[3] Do we have an odd number (and thus an odd number of resulting values)?

[4] Add a zero in the middle.

[5] Iterate over half the number of values to add (integer divison with div does the trick), as values 1, 2 and so on.

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

[6] Add the positive version to the end of the result.

[7] Add the negative version to the beginning of the result.

[8] Pretty print the result.

Running it:

$ ./usz 5
(-2, -1, 0, 1, 2)

$ ./usz 3
(-1, 0, 1)

$ ./usz 1
(0)

Looking good.

With verbose mode:

$ ./usz -v 5
: Odd number, added middle zero
: Added -1 at front and 1 at end
: Added -2 at front and 2 at end
(-2, -1, 0, 1, 2)

$ ./usz -v 3
: Odd number, added middle zero
: Added -1 at front and 1 at end
(-1, 0, 1)

$ ./usz -v 1
: Odd number, added middle zero
(0)

Let us try an even number, giving us an array without a zero in the middle:

$ ./usz -v 6
: Added -1 at front and 1 at end
: Added -2 at front and 2 at end
: Added -3 at front and 3 at end
(-3, -2, -1, 1, 2, 3)

And that's it.