Missing Mad
with Raku

by Arne Sommer

Missing Mad with Raku

[349] Published 27. June 2025.

This is my response to The Weekly Challenge #327.

Challenge #327.1: Missing Integers

You are given an array of n integers.

Write a script to find all the missing integers in the range 1..n in the given array.

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

The given array has 6 elements.
So we are looking for integers in the range 1..6 in the given array.
The missing integers: (4, 6)
Example 2:
Input: @ints = (1, 1, 1)
Output: (2, 3)
Example 3:
Input: @ints = (2, 2, 1)
Output: (3)
File: missing-integers
#! /usr/bin/env raku

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

my $max = @ints.elems;                                             # [2]
my $set = @ints>>.Int.grep( 0 < * <= $max).Set;                    # [3]

if $verbose
{
  say ": Range: 1 .. $max";
  say ": Matches: { $set.keys.sort.join(", ") }";
}

my @missing = (1..$max).grep({ ! so $set{$_} });                   # [4]

say "({ @missing.join(", ") })";                                   # [5]

[1] The challenge does not say that negative inters are illegal, even if the examples do not have any, so I have chosen to allow them. At least one integer.

[2] The number of elements, i.e. the upper limit in the lookup range.

[3] Coerce the input values to Integers to get rid of the printing unfriendly IntStr type they have, courtesy of the command line. Then use grep to only keep values in the lookup range, and finally Set to turn that into a hash like structure.

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

[4] Start with the range values, apply grep to only keep the values that do not occur in the Set. We use the Boolean coercer so and negate that with !.

[5] Pretty print the result.

Running it:

$ ./missing-integers 1 2 1 3 2 5
(4, 6)

$ ./missing-integers 1 1 1
(2, 3)

$ ./missing-integers 2 2 1
(3)

Looking good.

With verbose mode:

$ ./missing-integers -v 1 2 1 3 2 5
: Range: 1 .. 6
: Matches: 1, 2, 3, 5
(4, 6)

$ ./missing-integers -v 1 1 1
: Range: 1 .. 3
: Matches: 1
(2, 3)

$ ./missing-integers -v 2 2 1
: Range: 1 .. 3
: Matches: 1, 2
(3)

Challenge #327.2: MAD

You are given an array of distinct integers.

Write a script to find all pairs of elements with minimum absolute difference (MAD) of any two elements.

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

The minimum absolute difference is 1.
Pairs with MAD: [1,2], [2,3], [3,4]
Example 2:
Input: @ints = (1, 3, 7, 11, 15)
Output: [1,3]
Example 3:
Input: @ints = (1, 5, 3, 8)
Output: [1,3], [3,5]
File: mad
#! /usr/bin/env raku

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

my @sort = @ints.sort;                                            # [2]
my @diff = (1 .. @sort.end).map({ @sort[$_] - @sort[$_ -1] });    # [3]
my $mad  = @diff.min;                                             # [4]
my $set  = @ints>>.Int.Set;                                       # [5]
my @result;                                                       # [6]

if $verbose
{
  say ": Sorted: @sort[]";
  say ": Diff: @diff[]";
}

for sort $set.keys -> $k                                          # [7]
{
  my $is-mad = so $set{$k + $mad};                                # [8]

  @result.push: ($k, $k + $mad) if $is-mad;                       # [9]
  
  say ": $k, { $k+1 } { $is-mad ?? "is MAD" !! "does not exist" }"
    if $verbose;
}

say @result.raku;                                                 # [10]

[1] Integers only, and at least one of them. Ensure that they are unique by getting rid of duplcates and check that this does not alter the number of elements [1a].

[2] Sort the values, so that we can compare two and two neighbours in search of the MAD.

[3] For each index (except the very first one), use map to get the difference between the value at the current index and the one to the left. This is by design a positive value, so can be used as is.

[4] Get the MAD value.

[5] Get rid of the IntStr type, as in the first part of the challenge, and turn the input into a Set.

[6] The result will end up here.

[7] Iterate over the set keys, in sorted order. Note that this will be numeric sorting order, courtesy of [5].

[8] Do we have the upper half of the pair?

[9] If so, add it to the result.

[10] Not-so-pretty print the result.

Running it:

$ ./mad 4 1 2 3
[(1, 2), (2, 3), (3, 4)]

$ ./mad 1 3 7 11 15
[(1, 3),]

$ ./mad 1 5 3 8
[(1, 3), (3, 5)]

Looking good. I.e. correct. Pretty it ain't...

With verbose mode:

$ ./mad -v 4 1 2 3
: Sorted: 1 2 3 4
: Diff: 1 1 1
: 1, 2 is MAD
: 2, 3 is MAD
: 3, 4 is MAD
: 4, 5 does not exist
[(1, 2), (2, 3), (3, 4)]

$ ./mad -v 1 3 7 11 15
: Sorted: 1 3 7 11 15
: Diff: 2 4 4 4
: 1, 3 is MAD
: 3, 5 does not exist
: 7, 9 does not exist
: 11, 13 does not exist
: 15, 17 does not exist
[(1, 3),]

$ ./mad -v 1 5 3 8
: Sorted: 1 3 5 8
: Diff: 2 2 3
: 1, 3 is MAD
: 3, 5 is MAD
: 5, 7 does not exist
: 8, 10 does not exist
[(1, 3), (3, 5)]

And that's it.