Perl Weekly Challenge 148: eban vs cardano

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


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

PWC 148 - Task 1

This task was about finding out Eban numbers, those numbers that do not contain the letter e within their english name. I was not really inspired by this task, so I brutally created a set of arrays that contains the numbers that do not copntain an e in their english name, and I grep for those numbers:

sub MAIN() {

    my @eban-units = 2, 4, 6;
    my @eban-teens = 12;
    my @eban-tens  = 3, 4, 5, 6;

    $_.say if @eban-units.grep( $_ ) for 1 .. 10;
    $_.say if @eban-teens.grep( $_ ) for 11 .. 19;
    $_.say if @eban-tens.grep( ( $_ / 10 ).Int ) && @eban-units.grep( $_ % 10 ) for 20 .. 100;

}




PWC 148 - Task 2

This has been a more difficult task, that required me to learn about the =~= almost equality operator. The task required to print out the first five Cardano numbers, those that solve a particular equation given three variables. First of all, I implemented a couple of functions that, given the three variables, provide me the result of the Cardano domain belonging:

multi sub is-cardano-triplet( $a, $b, $c ) {

    my $left  = .sign * .abs**( 1 / 3 ) given ( $a + $b * $c.sqrt );
    my $right = .sign * .abs**( 1 / 3 ) given ( $a - $b * $c.sqrt );
    return 1 =~= ( $left + $right );
}


multi sub is-cardano-triplet( @triplet ) {
    return is-cardano-triplet( @triplet[ 0 ], @triplet[ 1 ], @triplet[ 2 ] );
}



The second implementation of the is-cardano-triplet function is only a placeholder to simplify when I use an array, and this will become clearer later. However, I could have simply used the |@triplet flatting operator.
The $left and $right are computed as the parts of the equation. The .sign method provides +1 or -1 depending on the sign of the result (the part in the given) and then I computer the third root of the absolute value, thus providing the resulting signed value. I needed a little help here about the math.
However, I was expecting this resulted in an integer result, but instead the result was a Rat, thus requiring me to use the new (to me) =~= operator, that is almost equal operator (see the documentation).
With all in place, I used a nested loop to generate a triplet, and then I compared every single permutation of the triple to see if the result was good enough:

sub MAIN( Int $limit = 5 ) {
    my @triplets = lazy gather {
        for 1 .. Inf -> $a {
            for 1 ..^ $a -> $b {
                for 1 ..^ $b -> $c {
                    $_.take if is-cardano-triplet( $_ ) for ( $a, $b, $c ).permutations;

                }
            }
        }
    };

    @triplets[ 0 .. $limit ].join( "\n" ).say;
}



I used a lazy gather to stop the computation as soon as the end is reached, and in fact I print out only the first $limit entries. This took near 7 seconds on my machine.

PWC 148 - Task 1 in PostgreSQL

The implementation, bit to bit, of the Raku solution for this task:

SELECT v
FROM generate_series( 1, 10 ) v
WHERE
v IN ( 2, 4, 6 )

UNION

SELECT v
FROM generate_series( 11, 19 ) v
WHERE v IN ( 12 )

UNION

SELECT v
FROM generate_series( 20, 100 ) v
WHERE
v % 10 IN ( 2, 4, 6 )
AND ( v / 10 )::int  IN ( 3, 4, 5, 6 );



A single query can do the trick.

PWC 148 - Task 2 in PostgreSQL

A recursive CTE implementation of the Raku solution for this task, limiting the numbers to 30 to avoid too much joins. Note that I search for the triplets that provide a sum that is near 1 by a small value.

WITH RECURSIVE
triplets AS
(
        SELECT a::numeric, b::numeric, c::numeric
        FROM generate_series( 1, 30 ) a
             , generate_series( 1, 30 ) b
             , generate_series( 1, 30 ) c
        ORDER BY a, b, c
)
, cardano_sum AS
(
        SELECT a, b, c,
               ( a + b * sqrt( c ) )   AS l
               ,( a - b * sqrt( c ) )  AS r
               FROM triplets
)
, cardano AS
(
        SELECT a, b, c, l, r
               , CASE WHEN l < 0 THEN -1 ELSE 1 END * pow( abs( l )::numeric, 1/3::numeric )
               + CASE WHEN r < 0 THEN -1 ELSE 1 END * pow( abs( r )::numeric, 1/3::numeric )
               AS triplet_sum
               FROM cardano_sum
)

SELECT *
FROM cardano
WHERE
abs( 1 - triplet_sum::numeric ) <= 0.0000000001
LIMIT 5
;



It takes 54 seconds to complete, and is a lot more of the Raku implementation. Why? Because the above query performs the computation for every join available.

The article Perl Weekly Challenge 148: eban vs cardano has been posted by Luca Ferrari on January 19, 2022