with Raku

This is my response to The Weekly Challenge #241.

You are given an array (3 or more members) of integers in increasing order and
a positive integer.

Write a script to find out the number of unique

File: arithmetic-triplets
Write a script to find out the number of unique

`Arithmetic Triplets`

satisfying the following rules:
a) i < j < k
b) nums[j] - nums[i] == diff
c) nums[k] - nums[j] == diff

Example 1:
Input: @nums = (0, 1, 4, 6, 7, 10)
$diff = 3
Output: 2
Index (1, 2, 4) is an arithmetic triplet because both 7 - 4 == 3
and 4 - 1 == 3.
Index (2, 4, 5) is an arithmetic triplet because both 10 - 7 == 3
and 7 - 4 == 3.

Example 2:
Input: @nums = (4, 5, 6, 7, 8, 9)
$diff = 2
Output: 2
(0, 2, 4) is an arithmetic triplet because both 8 - 6 == 2
and 6 - 4 == 2.
(1, 3, 5) is an arithmetic triplet because both 9 - 7 == 2
and 7 - 5 == 2.

#! /usr/bin/env raku
unit sub MAIN ($diff where $diff ~~ UInt && $diff > 0, # [1]
*@nums where @nums.elems > 2 # [2]
&& all(@nums) ~~ Int # [2a]
&& ( [<] @nums ), # [2b]
:v(:$verbose));
my $end = @nums.end; # [3]
my $triplets = 0; # [4]
for 0 .. $end -2 -> $i # [5]
{
for $i+1 .. $end -1 -> $j # [5a]
{
for $j+1 .. $end -> $k # [5b]
{
if $diff == @nums[$j] - @nums[$i] == @nums[$k] - @nums[$j] # [6]
{
$triplets++; # [7]
say ":Indices: $i,$j,$k -> values: @nums[$i],@nums[$j],@nums[$k] \
[triplet]" if $verbose;
}
elsif $verbose
{
say ":Indices: $i,$j,$k -> values: @nums[$i],@nums[$j],@nums[$k]";
}
}
}
}
say $triplets; # [8]

[1] The difference, as a positive intger. Note the use of
the `UInt`

(Unsigned Int) type, instead of the more permissive
`Int`

. In this case it does not really matter which one we use (as
long it enforces integers), as the `where`

clause handles the
restriction.

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

type.

[2] An array with three or more
elements, all of which are integers [2a], and they come in increasing
order [2b] courtesy of the. Reduction Metaoperator `[]`

in
combination with `<`

(the less than operator.)

See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator `[]`

.

[3] The index of the last element in the array. Note that
`.end`

is a shortcut for `.elems -1`

.

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

.

[4] The number of found triplets will end up here.

[5] Iterate over all the possible **i**, **j** and **k** indices,
satisfying the «a» rule: `i < j < k`

.

[6] Do the triplet satisfy the «b» and «c» rules?

[7] If so, increase the triplet count.

[8] Print the result.

Running it:

$ ./arithmetic-triplets 3 0 1 4 6 7 10
2
$ ./arithmetic-triplets 2 4 5 6 7 8 9
2

Looking good.

With verbose mode:

$ ./arithmetic-triplets -v 3 0 1 4 6 7 10
:Indices: 0,1,2 -> values: 0,1,4
:Indices: 0,1,3 -> values: 0,1,6
:Indices: 0,1,4 -> values: 0,1,7
:Indices: 0,1,5 -> values: 0,1,10
:Indices: 0,2,3 -> values: 0,4,6
:Indices: 0,2,4 -> values: 0,4,7
:Indices: 0,2,5 -> values: 0,4,10
:Indices: 0,3,4 -> values: 0,6,7
:Indices: 0,3,5 -> values: 0,6,10
:Indices: 0,4,5 -> values: 0,7,10
:Indices: 1,2,3 -> values: 1,4,6
:Indices: 1,2,4 -> values: 1,4,7 [triplet]
:Indices: 1,2,5 -> values: 1,4,10
:Indices: 1,3,4 -> values: 1,6,7
:Indices: 1,3,5 -> values: 1,6,10
:Indices: 1,4,5 -> values: 1,7,10
:Indices: 2,3,4 -> values: 4,6,7
:Indices: 2,3,5 -> values: 4,6,10
:Indices: 2,4,5 -> values: 4,7,10 [triplet]
:Indices: 3,4,5 -> values: 6,7,10
2
$ ./arithmetic-triplets -v 2 4 5 6 7 8 9
:Indices: 0,1,2 -> values: 4,5,6
:Indices: 0,1,3 -> values: 4,5,7
:Indices: 0,1,4 -> values: 4,5,8
:Indices: 0,1,5 -> values: 4,5,9
:Indices: 0,2,3 -> values: 4,6,7
:Indices: 0,2,4 -> values: 4,6,8 [triplet]
:Indices: 0,2,5 -> values: 4,6,9
:Indices: 0,3,4 -> values: 4,7,8
:Indices: 0,3,5 -> values: 4,7,9
:Indices: 0,4,5 -> values: 4,8,9
:Indices: 1,2,3 -> values: 5,6,7
:Indices: 1,2,4 -> values: 5,6,8
:Indices: 1,2,5 -> values: 5,6,9
:Indices: 1,3,4 -> values: 5,7,8
:Indices: 1,3,5 -> values: 5,7,9 [triplet]
:Indices: 1,4,5 -> values: 5,8,9
:Indices: 2,3,4 -> values: 6,7,8
:Indices: 2,3,5 -> values: 6,7,9
:Indices: 2,4,5 -> values: 6,8,9
:Indices: 3,4,5 -> values: 7,8,9
2

You are given an array of unique positive integers greater than 2.

Write a script to sort them in ascending order of the count of their prime factors, tie-breaking by ascending value.

Example:

Write a script to sort them in ascending order of the count of their prime factors, tie-breaking by ascending value.

Example:

Input: @int = (11, 8, 27, 4)
Output: (11, 4, 8, 27))
Prime factors of 11 => 11
Prime factors of 4 => 2, 2
Prime factors of 8 => 2, 2, 2
Prime factors of 27 => 3, 3, 3

We need the prime factors. Happily Part 5 of my «Centenary Sequences with Raku» article comes to the rescue... (Scroll down to the «Factors» section for the «factors» program.)

File: prime-order#! /usr/bin/env raku
unit sub MAIN (*@int where all(@int) ~~ UInt # [1]
&& all(@int) > 0 # [1]
&& @int.elems == @int.unique.elems > 2, # [1a]
:v(:$verbose));
my %factor-count; # [2]
for @int -> $int # [3]
{
my @factors = factors($int); # [4]
%factor-count{$int} = @factors.elems; # [5]
say ":Int $int -> factors: { @factors.join(",") } -> count: \
{ @factors.elems }" if $verbose;
}
say @int.sort({ %factor-count{$^a} <=> %factor-count{$^b} || $^b <=> $^a });
# [6]
sub factors ($number is copy) # [7]
{
return (1) if $number == 1; # [8]
return ($number) if $number.is-prime; # [9]
my @factors; # [10]
for (2 .. $number div 2).grep( *.is-prime) -> $candidate # [11]
{
while $number %% $candidate # [12]
{
@factors.push: $candidate; # [13]
$number /= $candidate; # [14]
}
}
return @factors; # [15]
}

[1] Zero or more positive integers. They must be unique, i.e. have the same number of elements if we try to remove duplicates [1a]

[2] We are going to sort the values on the number of factors, and it is smart to cache these values so that the sort function can look them up (instead of computing them) all the time.

[3] Iterate over the integers.

[4] Get the factors (so that verbose mode can print them, if we so desire).

[5] Save the count for later.

[6] Sort the numbers, first on the number of factors (increasing order), and secondly on the number itself (decreasing number) if the counts are equal, and print the result.

[7] Procedure arguments are read only by default, so we use `is copy`

so that we can change the value itself (in [14]).

[8] The number one is not a prime, but we support it anyway.

[9]

See
docs.raku.org/routine/is-prime
for more information about `is-prime`

.

[10] The prime factors will end up here.

[11] Iterate over the primes from 2 up to the number itself divided by 2. (As the largest prime cannot be larger than the number divided by 2, unless the number itself is a prime - and that was handled by [8]).

[12] As long as the candidate is a (prime) divisor,

[13] Add the candidate to the divisor list.

[14] Divide away the the divisor. (`/=`

is the divide and assign
operator.)

[15] Return the divisors.

Running it:

$ ./prime-order 11 8 27 4
(11 4 27 8)

Looking good.

With verbose mode:

$ ./prime-order -v 11 8 27 4
:Int 11 -> factors: 11 -> count: 1
:Int 8 -> factors: 2,2,2 -> count: 3
:Int 27 -> factors: 3,3,3 -> count: 3
:Int 4 -> factors: 2,2 -> count: 2
(11 4 27 8)

And that's it.