Perl Weekly Challenge 144: headache!

It is sad that, after more than two years of me doing Raku, I still don’t have any production code project to work on. Therefore, in order to keep my coding and Raku-ing (is that a term?) knowdledge, I try to solve every Perl Weekly Challenge tasks.

In the following, the assigned tasks for Challenge 144.


And this week, as for the previous PWC, I had time to quickly implement the tasks also on PostgreSQL plpgsql language:

PWC 144 - Task 1

The first task was about finding out all semiprimes numbers lower than 100. A semiprime number is a number obtained by multiplying two prime numbers.
My first attempt was by doing a nested loop, than I modified it to be a single loop:

sub MAIN( Int $limit where { $limit > 1 } = 100 ) {
    my @semi-primes;

    for 1 .. $limit -> $current-number {
        @semi-primes.push: $current-number if ( 1 .. $current-number ).grep( { $_.is-prime
                                                                               && $current-number %% $_
                                                                               && ( $current-number / $_ ).Int.is-prime } )
                            .elems > 0;
    }

    @semi-primes.join( ',' ).say;
}


Given a $limit, in this case 100, I do iterate on all integers up to such limit. For every number, I keep it as semiprime if, given all factors, I can find a couple of factors that are primes. To find out such couple, I look for all primes lower than the specific number and that, if I divide the number by such prime, give me another prime.

PWC 144 - Task 2

The second task was complicated, at least according to me: it was related t producing an Ulmar sequence. The problem was to understand how to implement such Ulamr sequence, because it was not really clear to me. However, according to the results, it seems it can be implemented in two steps:
  • provide a way to sum the Ulmar sequence built so far, finding out the unique sums (this was hard for me to understand from the description);
  • add the lowest number in the sum to the sequence.

Let’s start from the first step: the do-sum function accepts an array (the Ulmar sequence itself) and returns the unique sums of elements from left to right. I use an ugly hash vivification to find out all the unique sums.

sub do-sum( @array ) {
    my %sums;

    for 0 .. @array.end -> $index-left {
        for $index-left ^.. @array.end -> $index-right {
            next if $index-left == $index-right;

            %sums{ @array[ $index-left ] + @array[ $index-right ] }++;
        }
    }


    my @found;
    @found.push: $_.Int if %sums{ $_ } == 1 for %sums.keys;
    @found;

}



Then there is the MAIN, that iterates to get a new value for the sequence:

sub MAIN( Int $u where { $u > 0 },
          Int $v where { $v > 0 },
          Int $limit = 10 ) {

    my @ulam = $u, $v, $u + $v;

    while @ulam.elems < $limit {
        @ulam.push: do-sum( @ulam ).grep( { $_ > @ulam[ * - 1 ] } ).min;
    }

    @ulam.join( ',' ).say;

}


The loop adds a new element to the @ulmar array only taking into account the min value of the susms that are greater than the last element of the array itself.

PWC 144 - Task 1 in PostgreSQL

The first task is a line-by-line implementation of the Raku solution, but since there are no such base functions in PostgreSQL, I did create some of them to make the code look nicer.

CREATE OR REPLACE FUNCTION f_is_prime( n int )
RETURNS bool
AS $CODE$
DECLARE
        divisor int;
BEGIN
        FOR divisor IN 2 .. ( n - 1 ) LOOP
            IF mod( n, divisor ) = 0 THEN
               RETURN false;
            END IF;
        END LOOP;

        RETURN TRUE;
END
$CODE$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION f_semiprime_factors( n int )
RETURNS SETOF int
AS $CODE$
DECLARE
        current_number int;
BEGIN
        FOR current_number IN 1 .. n LOOP
            IF f_is_prime( current_number ) AND mod( n, current_number ) = 0 AND f_is_prime(  ( n / current_number ) ) THEN
               RETURN NEXT current_number;
            END IF;
        END LOOP;

        RETURN;
END
$CODE$
LANGUAGE plpgsql;



CREATE OR REPLACE FUNCTION f_is_semiprime( n int )
RETURNS bool
AS $CODE$
   SELECT CASE count( * )
          WHEN 0 THEN false
          ELSE true
          END
          FROM f_semiprime_factors( n ) s;
$CODE$
LANGUAGE sql;



CREATE OR REPLACE FUNCTION f_find_semiprimes( lim int default 100 )
RETURNS SETOF int
AS $CODE$
DECLARE
      n int;
BEGIN
   n := 1;

   WHILE n < lim LOOP
       IF f_is_semiprime( n ) THEN
          RETURN NEXT n;
       END IF;
       n := n + 1;
   END LOOP;

   RETURN;
END
$CODE$
LANGUAGE plpgsql;




The f_is_prime function provides true or false depending if its input number is a prime. The f_semiprime_factors gives a result set of integers that represents all the semiprimes factors of a given number. Then, the f_semiprime function iterates over all integers up to the given limit lim and returns a result set of integers that represent only the semiprime condition.

Having f_is_semiprime in place, it is also possible to issue another query, without the need for f_semiprime:

testdb=> select v FROM generate_series( 1, 100 ) v WHERE f_is_semiprime( v );


The trick here is to use generate_series to generate all numbers and filter by f_is_semiprime.

PWC 144 - Task 2 in PostgreSQL

Producing the Ulmar sequence in PostgreSQL was simpler than in Raku this time, at least because I have already clear how to implement it. I decided to provide two functions, to split the task in a similar way to its Raku implementation:

CREATE OR REPLACE FUNCTION f_ulam_do_sum( ulam int[] )
RETURNS SETOF int
AS $CODE$
DECLARE
        left_index int;
        right_index int;
BEGIN

        FOR left_index IN 1 .. array_length( ulam, 1 ) LOOP
            FOR right_index IN left_index + 1 .. array_length( ulam, 1 ) LOOP
                RETURN NEXT ulam[ left_index ] + ulam[ right_index ];
            END LOOP;
        END LOOP;

        RETURN;
END
$CODE$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION f_ulam( u int, v int, lim int default 10 )
RETURNS int[]
AS $CODE$
DECLARE
        ulam int[];
        next_value int;
BEGIN

        ulam := ulam || u || v || u + v;

        WHILE array_length( ulam, 1 ) < lim LOOP

              SELECT vv
              INTO next_value
              FROM f_ulam_do_sum( ulam ) AS sums( vv )
              WHERE vv > ulam[ array_length( ulam, 1 ) ]
              GROUP BY 1
              HAVING COUNT( * ) = 1
              ORDER BY vv
              LIMIT 1;

              ulam := ulam || next_value;
        END LOOP;

        RETURN ulam;
END
$CODE$
LANGUAGE plpgsql;



The f_ulam_do_sums function accepts an array of integers, the Ulam sequence built so far, and produces an array of sums. The f_ulam starts with the two initial values, builds an array with the first three elements, and then performs a query that produces the very next element to append to the array. I use the array concation || to extend the array. The SQL query can be split into parts:
  • it asks the f_ulam_do_sums to provide the sums of the sequence so far;
  • it orders the list of sums so that the smallest one is the first;
  • it limits the query results to only one entry, thus the min value;
  • groups the results by value of sums, taking only those that have a count of 1 (i.e., appear only once);
  • selects only values that are greater than the last element of the current Ulam array.

The article Perl Weekly Challenge 144: headache! has been posted by Luca Ferrari on December 20, 2021