Perl Weekly Challenge 139: repeating and sorting

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 139.


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

PWC 139 - Task 1

The first task was really trivial, at least in its definition: do a Jort Sort, that is simply see if a given array of numbers is already sorted.
Therefore, it just needs to compare the input with the sorted version of itself:

sub MAIN(  *@n where { @n.elems > 0  && @n.grep( * ~~ Int ).elems == @n.elems } ) {
    '1'.say and exit if @n ~~ @n.sort;
    '0'.say;
}



As you can see, it does suffice to smart match the @n array with its sorted version.
Please note that there is literally much more code to do the input validation than to solve the task itself!

PWC 139 - Task 2

This task was about printing out the first five long primes, that are prime numbers where the decimal part is repeating with a size of the number less one.
But Raku already has a repeating-base method in the Rat type, so the implementation is as short as:

sub MAIN( Int $limit where { $limit >= 1 }  = 5 ) {
    my @solutions;
    for 1 .. Inf {
        next if ! $_.is-prime;
        my $repeating-size = $_ - 1;
        my $repeating-part = ( 1 / $_ ).Rat.base-repeating( 10 )[ 1 ];
        next if ! $repeating-part;

        @solutions.push: $_ if $repeating-part.Str.chars == $repeating-size;
        last if @solutions.elems == $limit;
    }

    @solutions.join( ', ' ).say;
}




The idea is that I iterate on every integer number, and skip all those that are not already prime. Then I get the repeating-base part, and skip all those numbers without a repeating part in the decimal part. Then, if the repeating part has a textual length (i.e., a number of digits) that is $repeating-size (the current value minus one), the number is ok and is therefore added to the array of @solutions.
If the array of @solutions is already filled with the required number of elements (in default five), the loop is terminated and the array is printed.

PWC 139 - Task 1 in PostgreSQL plpgsql

This implementation tried to emulate the Raku proposed one, but clearly there is no smart matching operator. Therefore, I decided to convert an array into a string of text, and see if its textual representation is the same of the sorted array one.

CREATE OR REPLACE FUNCTION
  f_jort_sort( n int[] )
  RETURNS int
AS $CODE$
  DECLARE
  n_ordered int[];
BEGIN
  SELECT ( ARRAY( SELECT unnest( n ) ORDER BY 1 ) )
    INTO n_ordered;

  IF array_to_string( n, '|' ) = array_to_string( n_ordered, '|' ) THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END IF;
END
  $CODE$
  LANGUAGE plpgsql;
                   


In order to sort the array, I used a subquery to get it sorted by an ORDER BY clause over the unnest of the array.

PWC 139 - Task 2 in PostgreSQL plpgsql

This task has been really complex to implement in plpgsql, because of the lack of a clear way to find out the repeating base part.

CREATE OR REPLACE FUNCTION
  f_long_primes( l int default 5 )
  RETURNS SETOF bigint
AS $CODE$
  DECLARE
  current_value numeric;
  i int;
  is_prime bool;
  rational_part text;
  done int := 0;
  repeating_part_size int := 0;
  repeating_part      text;
  result_as_text      text;
  text_array          text[];

BEGIN

  for current_value IN 1 .. 99999 LOOP
    is_prime := true;

    -- check if this is prime
    for i in 2 .. ( current_value - 1 ) LOOP
      if current_value % i = 0 THEN
        is_prime := false;
        exit; -- terminate this loop
        END IF;
    END LOOP;

    -- avoid inspecting this number if it is not prime
    if is_prime = false THEN
      continue;
    END IF;

    result_as_text := ( 1 / current_value::numeric )::text;
    i := strpos( result_as_text, '.' );
    rational_part := substr( result_as_text, i + 1 );
    repeating_part_size :=  current_value - 1;
    repeating_part      := substr( rational_part, 1, repeating_part_size );

    RAISE DEBUG 'Inspecting % -> % % => % ( %, % )',
      current_value,
      rational_part,
      repeating_part_size,
      repeating_part,
      i,
      ( 1 / current_value )::text;


    text_array := regexp_split_to_array( rational_part, repeating_part  );
    is_prime   := true;
    FOREACH result_as_text IN ARRAY text_array LOOP
      IF result_as_text <> '' AND substr( repeating_part, 1, length( result_as_text ) ) <> result_as_text THEN
        is_prime := false;
        exit;
      END IF;
    END LOOP;

    IF is_prime THEN
      RETURN NEXT current_value;
      done := done + 1;
    END IF;

    IF done > l THEN
      EXIT;
    END IF;
  END LOOP;

  RETURN;
END
  $CODE$
  LANGUAGE plpgsql;
                                                                         



The idea is as follows:
  • I do loop to geenrate all integers and check if the number is prime, otherwise I cannot continue with this value;
  • once I tested the number is prime, I do get its numeric division, so to get the max precision available;
  • I split the string representation of the division to keep the rational_part and then I extract the first digits for the repeating_part;
  • now the complex part is to check if the part is repeating, so I use regexp_split_to_array of the decimal part and the repeating part. If the repeating_part matches, the array element will be an empty string, so I have to check every element of the regexp matching array to see if it is an empty string. There is, however, a critical case, that is if the number of digits terminates before having composed all the repeating part, so I simply checks if the last element of the array is within the repeating part string.
    In the end, I do return a number and terminate everything if the returned numbers collection is of the right size.

The article Perl Weekly Challenge 139: repeating and sorting has been posted by Luca Ferrari on November 15, 2021