Perl Weekly Challenge 66 solution

I'm back with an article after one week break. Last week I felt I don't have anything interesting to say about the solutions I've done.

This week I've attentended the Perl and Raku Conference in Cloud. Even remotely, it's a much different experience to participate live.

The perl 7 announcement made at the conference opening is certainly unexpected, but welcome. People surely love bigger numbers, even if nothing but the defaults change (for now). This may be the start of a real Perl renaissance.

Now, for this week challenges: https://perlweeklychallenge.org/blog/perl-weekly-challenge-066

Challenge #1: Divide Integers

A funky integer division task without using division, multiplication and modulo. I'm assuming here that -$number is not considered a multiplication by -1, and therefore allowed.

I initially started coding a solution that would use regular expressions to match something like /(?: ( f{$divisor} ) )*/gx in a string "f" x $dividend, but quickly realized how obscure that will actually be. Additionally, repeated captured matches don't work as one might expect, so I won't get a list of matches even if I assign that in a list context. If I can't skip the loop then it's a wrong approach.

What I ended up doing is a lookup table for the division results. For a single operation this is just a waste of memory, but for anything that may be reused this can greatly help reducing the number of iterations. What's interesting here is how I handle the result sign - it's basically a xor of abs values:

my ($am, $an) = map abs, my ($m, $n) = @_;
my $is_negative = (($m != $am) xor ($n != $an));

A little trap here - xor has very low precedence, just like and and or. Parentheses are absolutely necessary here, and seems there's no higher precedence xor operator in Perl, other than ^ that's operating on bits.

The actual lookup table is here:

my @mul_map = (0); # zero is always zero
while ($mul_map[-1] <= $am) {
    push @mul_map, $mul_map[-1] + $an;
}

my $result = first { $mul_map[$_] <= $am }
    reverse keys @mul_map;

return $is_negative ? -$result : $result;

The results are stored as the array keys.

Challenge #2: Power Integers

I was struggling for some time to find any reasonable solution to this that isn't brute forcing everything. In the end, I've settled for a simple checking if a number is a divisor and checking every power of it until it's lesser than the number in question.

Since I'm using Perl here, it would be a shame not to use unicode. I'll be constructing superscript power using an array that stores all superscript digits. The full code is shown below:

my @superscripts = qw(
    ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹
);

sub power_integer
{
    my ($int) = @_;

    my @solutions;
    for my $base (2 .. sqrt($int)) {
        if ($int % $base == 0) {
            my $power = 2;
            while ($base ** $power < $int) {
                $power += 1;
            }

            if ($base ** $power == $int) {
                my $unicode_power = join "", map {
                    $superscripts[$_]
                } split //, $power;

                push @solutions, "$base$unicode_power";
            }
        }
    }

    return \@solutions;
}

Bonus solution - Quantum::Simplified early testing

That second solution can be solved a bit differently with my own Quantum::Simplified module I've been developing for a while. With the latest additions to the module it's possible to do some operations on superpositions and get the complete set of values that made up these values. This is somewhat complicated, but it allows to do some operations on two sets on values and then see which results match the thing we want to achieve. This allows us to flip the solution around and just write this:

use Quantum::Simplified qw(fetch_matches with_sources);

sub power_sources
{
    my ($number) = @_;

    # produce all the possible bases end exponents
    my $possible_bases = superpos(2 .. sqrt $number);
    my $possible_exponents = superpos(2 .. sqrt $number);

    # produce all possible powers
    my $possible_powers = $possible_bases ** $possible_exponents;

    # for every state, get those that match a condition
    # (any possible power that equals the number)
    return fetch_matches { with_sources { $possible_powers == $number } };
}

The return value will contain a single state (or no states at all if there's no solution), which in turn contains a source property with the final result. Keep in mind the module is awfully slow, and it is by definition. It is currently only available on my github page, as I'm preparing it for a CPAN launch somewhere in the next weeks / months.

Weekly challenges and future plans

After checking out the weekly challenges for a month I'm able to voice some personal opinions:

For starters, they are hella time consuming. The problem may be that there are two of them each week. Not sure why it is, but for me it makes it harder and harder to make them in a reasonable time, since I always want to create a solution that is somewhat special. Would be much easier if it was just one per week.

Secondly, I feel that my will to do real work has gone down a lot, and I guess it's because it's just so absorbing to do these challenges. This is something I'm not happy about, as I have much work to do - both CPAN modules, personal projects as well as improving my working environment. There's also my day job as well.

Last but not least, writing these blog posts is increasing the amount of time I'm spending doing the challenges, while I could be writing real, useful articles in that time.

That being said, I see a couple of ways I can take:

  • stop doing the challenges altogether and focus entirely on constructive work
  • do both challenges every week, but don't put much thought into them and stop blogging about them
  • do only the challenges I view as interesting and blog about them. Skip weeks if necessary

I think I'll go with the last option for now, I think it's the best of both worlds. I also may start doing some of those in Raku.