# ETOOBUSY 🚀 minimal blogging for the impatient

# PWC101 - Pack a Spiral

**TL;DR**

Here we are with TASK #1 from the Perl Weekly Challenge #101. Enjoy!

# The challenge

You are given an array

`@A`

of items (integers say, but they can be anything). Your task is to pack that array into an`MxN`

matrix spirally counterclockwise, as tightly as possible.‘Tightly’ means the absolute value |M-N| of the difference has to be as small as possible.

# The questions

Folks how I hate this kind of challenges. It’s all an intricate
calculation of indexes etc. which becomes unnerving very quickly for me,
*especially* if the solution does not pop up immediately.

Well, maybe this is what *excercise* is for.

There’s an obvious thread of questions:

- Is any order is OK, as long as the array is arranged in a counter-clockwise spiral?
- What should we do when there is a prime number of elements?
- Is it OK to have a good packing, but empty slots in the matrix?

# The solution

Well, I’m a bit tired so I decided to go for minimizing the target function and stick to either square matrices, or with a maximum difference of 1 in the two dimensions.

There are a lot of cases to be considered, which… I didn’t enjoy particularly!

The bottom line, anyway, is that:

- I start from the center and go to the outside - which is pretty much the contrary of the examples, but still within the requested packing;
- I calculate where to start, as well as initializing the matrix with all empty slots.
- Then I proceed to populate the matrix, one “frame” at a time. Each frame has a side that is two items bigger than the previous frame.

Without further ado… here’s my whole solution:

```
#!/usr/bin/env perl
use 5.024;
use warnings;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;
use Data::Dumper;
sub pack_a_spiral (@A) {
my $N = scalar @A;
my $side = int sqrt $N;
--$side if $side * $side == $N;
my (@M, $x, $y);
if ($side * ($side + 1) >= $N) { # rectangle
if ($side % 2) {
$x = ($side - 1) / 2;
$y = $side - $x - 1;
}
else {
$x = $side / 2;
$y = $side - $x;
}
@M = map { [('') x ($side + 1)] } 1 .. ($side);
}
else { # square
$x = $side % 2 ? (($side - 1) / 2) : $side / 2;
$y = $side - $x;
++$side;
@M = map { [('') x $side] } 1 .. $side;
}
$M[$y][$x] = shift @A;
$side = 2;
OUTER:
while ('necessary') {
++$x; # move to next frame
++$y;
for ([0, -1], [-1, 0], [0, +1], [1, 0]) { # four sides
my ($dx, $dy) = $_->@*;
for (1 .. $side) {
last OUTER unless @A;
$x += $dx;
$y += $dy;
$M[$y][$x] = shift @A;
}
}
$side+=2;
}
return \@M;
}
sub print_matrix ($M) {
for my $row ($M->@*) {
for my $item ($row->@*) {
printf '%4s ', $item;
}
print "\n";
}
}
my @items = @ARGV ? @ARGV : (1..16);
my $s = pack_a_spiral(@items);
print_matrix($s);
```

Stay safe!!!