# Perl Weekly Challenge 206: hard times!

This post presents my solutions to the Perl Weekly Challenge 206.
I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:
The PL/Perl implementations are very similar to a pure Perl implementation, even if the PostgreSQL environment could involve some more constraints. Similarly, the PL/PgSQL implementations help me keeping my PostgreSQL programming skills in good shape.

# Raku Implementations

## PWC 206 - Task 1 - Raku Implementation

The first task was about computing the lowest difference between an array of times, expressed as `HH::MI` strings. At first, I thought at `DateTime` but unluckily I was on a machine were the Raku documentation was absent and the online manual `https://docs.raku.org` was suffering too. So, I had to implement it on my own.

``````sub diff ( \$start, \$end ) {
my ( \$start-hours, \$start-mins ) = \$start.chomp.split( ':' );
my ( \$end-hours, \$end-mins ) = \$end.chomp.split( ':' );

if ( \$start-hours == 0 ) {
\$start-hours = 23;
\$start-mins += 60;
}

if ( \$end-hours == 0 ) {
\$end-hours = 23;
\$end-mins += 60;
}

my \$diff-hours = abs( \$end-hours - \$start-hours );
my \$diff-mins  = abs( \$end-mins - \$start-mins ) % 60;

return \$diff-hours * 60 + \$diff-mins;

}

sub MAIN( :\$verbose = True, *@times where { @times.grep( * ~~ / ^ \d ** 2 ':' \d ** 2 \$ /  ).elems == @times.elems } ) {

my %diffs;
%diffs{ diff( \$_[ 1 ], \$_[ 0 ] ) } = [ \$_, \$_ ] for @times.sort.combinations( 2 );

%diffs.keys.map( *.Int ).min.say;
%diffs{ %diffs.keys.map( *.Int ).min }.join( ' - ' ).say if ( \$verbose );
}

``````

The function `diff` accepts two time strings, computes the numeric values for hours and minutes and then returns the difference expressed as the number of minutes. There is a little trick in there: if the hour is expressed as `00:MI` I downgrade it to `23` and add sixty minutes. This allows me to compare every time without having to worry about the round clock.
In the `MAIN` I create a `%diffs` hash keyed by the difference in minutes between every pair (`combinations(2)`) after the list has been `sorted`, so that I can consider one way difference. Last, I simply print the minimium key (as an integer, because it is managed as a string).

## PWC 206 - Task 2 - Raku Implementation

Given an array of integers, consider all the pairs and sum the minimum of every pair, then get the overall minimum sum.

``````sub MAIN( *@list where { @list.elems %% 2 && @list.grep( * ~~ Int ).elems == @list.elems } ) {
my @sums;

for @list.permutations {
for \$_.rotor( 2 ) -> \$a, \$b {
@sums.push: sum( min( \$a ) + min( \$b ) );
}
}

@sums.min.say;
}

``````

The above is a very unefficient solution, since it exploits all the `permutations` of the input array and consumes its as slices of two elements. When I was developing the PL/Perl solution a trick came into my mind.

# PL/Perl Implementations

A similar implementation to the Raku one: I use an anonymous subroutine to compute the difference between time strings.

## PWC 206 - Task 1 - PL/Perl Implementation

``````CREATE OR REPLACE FUNCTION
RETURNS int
AS \$CODE\$
my ( \$times ) = @_;
my \$computations = {};
my \$min;

my \$diff = sub {
my ( \$start, \$end ) = @_;
\$start =~ /^(\d{2}):(\d{2})\$/;
my ( \$start_hours, \$start_mins ) = ( \$1, \$2 );

\$end =~ /^(\d{2}):(\d{2})\$/;
my ( \$end_hours, \$end_mins ) = ( \$1, \$2 );

if ( \$start_hours == 0) {
\$start_hours = 23;
\$start_mins += 60;
}

if ( \$end_hours == 0) {
\$end_hours = 23;
\$end_mins += 60;
}

return abs( \$end_hours - \$start_hours ) * 60 + abs( \$end_mins - \$start_mins ) % 60;
};

for my \$begin ( sort \$times->@* ) {
for my \$end ( sort \$times->@* ) {
next if ( \$begin eq \$end );

my \$difference = \$diff->( \$end, \$begin );
\$computations->{ \$difference } = [ \$begin, \$end ];
\$min = \$difference if ( ! \$min || \$difference < \$min );
}
}

elog(INFO, "Min is \$min minutes from " . join( ',', \$computations->{ \$min }->@* ) );
return \$min;
\$CODE\$
LANGUAGE plperl;

``````

## PWC 206 - Task 2 - PL/Perl Implementation

Here I had an idea: if I sort the list of integers and then take one element out of two, I can sum all the mimum values of every pair and get, in a straight manner, the overall minimu sum.

``````CREATE OR REPLACE FUNCTION
RETURNS int
AS \$CODE\$
my ( \$list ) = sort \$_[ 0 ];
my \$sum = 0;

while ( \$list->@* ) {
\$sum += shift \$list->@*;
shift \$list->@*;
}

return \$sum;
\$CODE\$
LANGUAGE plperl;

``````

# PostgreSQL Implementations

## PWC 206 - Task 1 - PL/PgSQL Implementation

Luckily, a difference between two `time` values is another `time` value, therefore it handles the difference right.

``````CREATE OR REPLACE FUNCTION
RETURNS time
AS \$CODE\$

DECLARE
m time;
t1 text;
t2 text;
BEGIN

FOREACH t1 IN ARRAY t LOOP
FOREACH t2 IN ARRAY t LOOP
IF t1 = t2 THEN
CONTINUE;
END IF;

IF m IS NULL OR ( t2::time - t1::time ) < m THEN
m := ( t2::time - t1::time );
END IF;

END LOOP;
END LOOP;

RETURN m;
END
\$CODE\$
LANGUAGE plpgsql;

``````

## PWC 206 - Task 2 - PL/PgSQL Implementation

A single query to do the same as the PL/Perl implementation. I build a temporary table, using a CTE, where every row has a number associated to it by means of `row_number()`. Then I select all the odd rows, that represent the minimum value of the sorted input array, and the sum is the overall minimum.

``````
CREATE OR REPLACE FUNCTION
RETURNS int
AS \$CODE\$
DECLARE
res int;
BEGIN
WITH data AS (
SELECT v, row_number() OVER ( ORDER BY v ) r
FROM unnest( l ) v
)
SELECT sum( v )
INTO res
FROM data
WHERE r % 2 <> 0
;

RETURN res;
END
\$CODE\$
LANGUAGE plpgsql;
``````

The article Perl Weekly Challenge 206: hard times! has been posted by Luca Ferrari on February 27, 2023