The Bear's Den

Enter at your own risk

Euclidean Paths

Task 1: Smallest non-prime Euclid number


The numbers formed by adding one to the products of the smallest primes are called the Euclid Numbers (see wiki). Write a script that finds the smallest Euclid Number that is not prime. This challenge was proposed by Laurent Rosenfeld.


Solution

My comment and solution dated 13 Mar 2023:

Build a generator for non-prime Euclid numbers and print the first N thereof (Default: N = 1)

use strict;
use warnings;
use bigint;
use List::Gen;
use Math::Prime::Util qw(is_prime pn_primorial);

<1..>->map(sub{pn_primorial($_)+1})->filter(sub{!is_prime($_)})->say(shift||1);

See the full solution to task 1.

Task 2: Common directory


Write a script that finds the common directory path, given a collection of paths and directory separator. For example, if the following paths are supplied.

/a/b/c/d
/a/b/cd
/a/b/cc
/a/b/c/d/e

and the path separator is /. Your script should return /a/b as common directory path.


Solution

There is room for interpreting this task.

Here are some assumptions / decisions:

The solution consists in canonicalizing the given paths and finding the longest common directory prefix thereof. The root path shall be provided as the fallback when all paths are absolute but do not share any longer common directory path.

The NUL character is the only character that cannot be part of a path. Therefore all the given paths may be joined with the NUL character resulting in an unambiguous representation that can be matched against a regular expression to find the largest common directory prefix.

Using the (quoted) first character from the provided first argument as the separator. Most characters should work, though using a dot . will disable the usage of “stepping up” as .a....b will be canonicalized to .a.b instead of .b

use strict;
use warnings;
use experimental 'signatures';

sub common_path ($sep, @paths) {
    my $qs = quotemeta substr $sep, 0, 1;
    my $cp = qr{
        ^
        (?:
            ((?&P)+) (?&T)
            (?: \1 (?&T))*+
        |
            (?=((?&S))) (?&T)++
        )
        \z

        (?(DEFINE)
            (?<S> $qs)                   # Separator
            (?<P> $qs?+ [^$qs\0]++)      # path Part
            (?<T> $qs [^\0]*+ (?:\0|\z)) # path Tail
        )
    }x;
    for (@paths) {
        s/($qs)+/$1/g;
        s/(?:^|$qs)\K\.$qs//g;
        1 while s/(?:$qs|^)[^$qs]*+$qs\.\.(?=\z|$qs)//;
    }

    join("\0", @paths) =~ m{$cp};

    $1 // $2;
}

See the full solution to task 2.