Perl Weekly Challenge: Week 123

Challenge 1:

Ugly Numbers

You are given an integer $n >= 1.

Write a script to find the $nth element of Ugly Numbers.

Ugly numbers are those number whose prime factors are 2, 3 or 5. For example, the first 10 Ugly Numbers are 1, 2, 3, 4, 5, 6, 8, 9, 10, 12.

Example
Input: $n = 7
Output: 8

Input: $n = 10
Output: 12

Prime factors have come up before in the Weekly Challenge so I got to reuse some code which I wrote for week 23 (and originated even earlier in week 12 but I wasn't blogging back then.) So see the blog for that one for discussion of how I got prime factors.

Once we have the list of prime factors we can check that it only contains 2, 3, and 5. I used .grep() for this even though Raku's set support would have been more elegant because I am still having problems getting the latter to work properly. I really need to update my Raku version to something vaguely current but I just haven't had the time. Still, the present approach works and that's the main thing. If the grep results in a list of prime factors other than 2, 3, or 5, the number is not ugly and a false value is returned otherwise true is returned.

sub isUgly(Int $n) {
    my @primeFactors;
    factorize($n, @primeFactors);
    return @primeFactors.grep({ $_ != 2 && $_ != 3 &&  $_ != 5 }).elems == 0;
}

The MAIN() driver is simple. We start from 1 and count upwards in a loop. (Could have used a while loop but did it this way on a whim.) There is a count of ugly numbers found and when that number reaches $n, we print the last ugly number found and exit the loop and thus, the script.

    my $number = 1;
    my $uglies = 0;

    loop {
        if isUgly($number) {
            $uglies++;
        }

        if $uglies >= $n {
            say $number;
            last;
        }

        $number++;
    }

(Full code on Github.)

The Perl version not only needs code for prime factors but for calculating if a number is prime in the first place. (Raku has a method for that bit.) Luckily, that code was already written. Again, see week 23 for discussion.

For Perls' version of isUgly() there is no question of using anything but the grep method.

sub isUgly {
    my ($n) = @_;
    my @primeFactors;
    factorize($n, \@primeFactors);
    return (scalar grep { $_ != 2 && $_ != 3 &&  $_ != 5 } @primeFactors) == 0;
}

The main code works exactly the same as Raku. Too much the same. I actually am using while so I could have done while ($uglies < $n) but didn't.

my $number = 1;
my $uglies = 0;

while(1) {
    if (isUgly($number)) {
        $uglies++;
    }

    if ($uglies >= $n) {
        say $number;
        last;
    }

    $number++;
}

(Full code on Github.)

Challenge 2:

Square Points

You are given coordinates of four points i.e. (x1, y1), (x2, y2), (x3, y3) and (x4, y4).

Write a script to find out if the given four points form a square.

Example
Input: x1 = 10, y1 = 20
    x2 = 20, y2 = 20
    x3 = 20, y3 = 10
    x4 = 10, y4 = 10
Output: 1 as the given coordinates form a square.

Input: x1 = 12, y1 = 24
    x2 = 16, y2 = 10
    x3 = 20, y3 = 12
    x4 = 18, y4 = 16
Output: 0 as the given coordinates doesn't form a square.

This one is really easy. You just compare the x coordinates of opposing sides to see if the two sides are equal and then do the same to the y coordinates of opposing sides. If both pairs are equal you have a square. The code is almost exactly the same in Perl and Raku minus minor syntactical differences.

say (abs($x1 - $x3) == abs($x2 - $x4) && abs($y1 - $y2) == abs($y3 - $y4)) ?? 1 !! 0;

(Full code on Github.)

say 0+(abs($x1 - $x3) == abs($x2 - $x4) && abs($y1 - $y2) == abs($y3 - $y4)) ? 1 : 0;

(Full code on Github.)

Ahh maybe this was deceptively too easy! As I write this, it has just occurred to me that the code as written above actually determines if the points make a rectangle. True, a square is a type of rectangle but if I really wanted to be faithful to the spec and find squares only I should also have checked the x-sides against the y-sides to see if they are equal too.