# ETOOBUSY ðŸš€ minimal blogging for the impatient

# PWC187 - Magical Triplets

**TL;DR**

On with TASK #2 from The Weekly Challenge #187. Enjoy!

# The challenge

You are given a list of positive numbers,

`@n`

, having at least 3 numbers.Write a script to find the triplets

`(a, b, c)`

from the given list that satisfies the following rules.`1. a + b > c 2. b + c > a 3. a + c > b 4. a + b + c is maximum.`

In case, you end up with more than one triplets having the maximum then pick the triplet where a >= b >= c.

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

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

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

Example 4`Input: @n = (2, 4, 3); Output: (4, 3, 2)`

# The questions

From a very *generic* point of view, Iâ€™d argue that the disambiguation
about the triple to select *might* leave out corner cases where there
might be two triangles with the same perimeter but different side
lenghts. On the other hand, I had a hard time coming up with an example
where this perimeter is *maximal*, so I guess this condition might not
appear practically. Anywayâ€¦ at least a hat tip would have helped!

# The solution

I guess thereâ€™s a ton to optimize but weâ€™ll assume a small bunch of numbers here and no up-front optimization..

The basic approach will be the following:

- find out all possible triplets, drawing three elements at any time;
- verify each triple is indeed a triangle and calculate its perimeter;
- select the triple with the highest sum

Letâ€™s start with Perl first, leveraging the Combinations iterator:

```
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
my @result = magical_triplets(@ARGV);
say '(', join(', ', @result), ')';
sub magical_triplets (@n) {
my $it = combinations_iterator(3, @n);
my ($best, $best_score);
while (my ($combination, $complement) = $it->()) {
my $score = is_triangle($combination->@*) or next;
($best, $best_score) = ($combination, $score)
if (! defined $best) || ($best_score < $score);
}
return reverse sort {$a <=> $b} ($best // [])->@*;
}
sub is_triangle ($A, $B, $C) {
return 0 if
$A >= $B + $C
|| $B >= $C + $A
|| $C >= $A + $B;
return $A + $B + $C;
}
sub combinations_iterator ($k, @items) {
my @indexes = (0 .. ($k - 1));
my $n = @items;
return sub {
return unless @indexes;
my (@combination, @remaining);
my $j = 0;
for my $i (0 .. ($n - 1)) {
if ($j < $k && $i == $indexes[$j]) {
push @combination, $items[$i];
++$j;
}
else {
push @remaining, $items[$i];
}
}
for my $incc (reverse(-1, 0 .. ($k - 1))) {
if ($incc < 0) {
@indexes = (); # finished!
}
elsif ((my $v = $indexes[$incc]) < $incc - $k + $n) {
$indexes[$_] = ++$v for $incc .. ($k - 1);
last;
}
}
return (\@combination, \@remaining);
}
}
```

The translation into Raku is pretty much literal, with a couple exceptions:

- finding out combinations is part of the language, yay!
- I couldnâ€™t figure out how to pass an arrayâ€™s content as three individual variables to a sub, so I had to unpack the input array in a very old-Perl5-style.

```
#!/usr/bin/env raku
use v6;
sub MAIN (*@args) { put magical-triplets(@args) }
sub magical-triplets (@n) {
my ($best, $best-score);
for @n.combinations(3) -> $comb {
my $score = is-triangle($comb) or next;
($best, $best-score) = ($comb, $score)
if (! defined $best) || ($best-score < $score);
}
$best //= [];
return $best.sort({$^a <=> $^b}).reverse;
}
sub is-triangle ($x) {
my ($A, $B, $C) = @$x;
return 0 if
$A >= $B + $C
|| $B >= $C + $A
|| $C >= $A + $B;
return $A + $B + $C;
}
```

I like that the solution comes out so compact anyway.

Stay safe!

*Comments? Octodon, Twitter, GitHub, Reddit, or drop me a line!*