Archive

Archive for May, 2022

Reducing sets

May 25, 2022 1 comment

This week´s PWC asks us for hexadecimal words and distinctly different directories.

sub term:<pwc-166-1> {
    sub less-substitutions-then ($_, $n) {
        .subst(/<-[olist]>/, :g, '').chars < $n
    }

    '/usr/share/dict/words'.IO.words.race\
        .grep({.chars ≤ 8 && .&less-substitutions-then(4)})\
        .map(*.trans(<o l i s t> => <0 1 1 5 7>))\
        .grep(/^ <[0..9 a..f A..F]>+ $/)\
        .sort(-*.chars)\
        .say;
};

Once again, we write the algorithm down. Get the words, drop anything longer then 8 chars or what would need more then 4 substitutions. Then do the substitutions and grep anything that looks like a hexadecimal numeral. Sort for good measure and output the first 100 elements.

The second task provides us with a little challenge. We need to mock listing directories and working with them. Since dir returns a sequence of IO::Path I can create those by hand and mixin a role that mimics some filesystem operations. I can overload dir to provide a drop-in replacement.

sub term:<pwc-166-2> {
    sub basename(IO::Path $_) { .basename ~ (.d ?? '/' !! '') }
    sub pad(Str $_, $width, $padding = ' ') { .Str ~ $padding x ($width - .chars) }

    sub dir(Str $name) {
        sub mock-file(*@names) { @names.map({ IO::Path.new($_) but role :: { method f ( --> True ) {}; method e ( --> True ) {} } } ) }
        sub mock-dir(*@names) { @names.map({ IO::Path.new($_) but role :: { method d ( --> True ) {}; method e ( --> True) {} } }) }

        constant %dirs = dir_a => flat(mock-file(<Arial.ttf Comic_Sans.ttf Georgia.ttf Helvetica.ttf Impact.otf Verdana.ttf>), mock-dir(<Old_Fonts>)),
                         dir_b => mock-file(<Arial.ttf Comic_Sans.ttf Courier_New.ttf Helvetica.ttf Impact.otf Tahoma.ttf Verdana.ttf>),
                         dir_c => mock-file(<Arial.ttf Courier_New.ttf Helvetica.ttf Impact.otf Monaco.ttf Verdana.ttf>);

        %dirs{$name}
    }

    sub dir-diff(+@dirs) {
        my @content = @dirs».&dir».&basename;
        my @relevant = (([∪] @content) ∖ [∩] @content).keys.sort;

        my @columns = @content.map(-> @col { @relevant.map({ $_ ∈ @col ?? $_ !! '' }) });
        my $col-width = [max] @columns[*;*]».chars;

        put @dirs».&pad($col-width).join(' | ');
        put (''.&pad($col-width, '-') xx 3).join('-+-');
        .put for ([Z] @columns)».&pad($col-width)».join(' | ');
    }

    dir-diff(<dir_a dir_b dir_c>);
};

I’m asked to add a / to directories and do so with a custom basename. The rest is liberal application of set theory. Only names that don’t show up in all directories are relevant. Columns are created by matching the content of each directory against the relevant names. The width of columns is the longest string. The header is put on screen. To output the columns line by line, x and y are flipped with [Z].

After careful study of the solutions written in other languages, I believe it is fair to call Raku an eco-friendly language. Our keyboards are going to last at least twice a long.

Categories: Raku, Uncategorized

Writing it down

May 21, 2022 2 comments

PWC 165 refers us to mathsisfun for the algorithm to be used. Let’s write it down.

    my $input = '333,129  39,189 140,156 292,134 393,52  160,166 362,122  13,193
                341,104 320,113 109,177 203,152 343,100 225,110  23,186 282,102
                284,98  205,133 297,114 292,126 339,112 327,79  253,136  61,169
                128,176 346,72  316,103 124,162  65,181 159,137 212,116 337,86
                215,136 153,137 390,104 100,180  76,188  77,181  69,195  92,186
                275,96  250,147  34,174 213,134 186,129 189,154 361,82  363,89';

    my @points = $input.words».split(',')».Int;

    my \term:<x²> := @points[*;0]».&(*²);
    my \xy = @points[*;0] »*« @points[*;1];
    my \Σx = [+] @points[*;0];
    my \Σy = [+] @points[*;1];
    my \term:<Σx²> = [+] x²;
    my \Σxy = [+] xy;
    my \N = +@points;

    my $m = (N * Σxy - Σx * Σy) / (N * Σx² - (Σx)²);
    my $b = (Σy - $m * Σx) / N;

    say [$m, $b];

That was exceedingly simple. If the point cloud is large, this might also be exceedingly slow. There are no side effects and everything is nice and constant. Should be easy to get the CPU busy.

    sub delayed(&c) is rw {
        my $p = start { c };
        Proxy.new: STORE => method (|) {}, FETCH => method { await $p; $p.result }
    }

    my \term:<x²> = delayed { @points[*;0]».&(*²) };
    my \xy = delayed { @points[*;0] »*« @points[*;1] };
    my \Σx = delayed { [+] @points[*;0] };
    my \Σy = delayed { [+] @points[*;1] }
    my \term:<Σx²> = delayed { [+] x² };
    my \Σxy = delayed { [+] xy };
    my \N = +@points;

    my $m = (N * Σxy - Σx * Σy) / (N * Σx² - (Σx)²);
    my $b = (Σy - $m * Σx) / N;

    say [$m, $b];

Since I use sigil-less symbols, I get binding for free. This in turn makes using Proxy easy. The await $p part is called more then once per Proxy but not in a loop so there is no real need to optimise here. On my box the Proxyed version is almost twice as fast. A nice win for such a simple change.

I somehow feel this solution might please a mathematician — as Raku should.

Categories: Raku

Antipairing

May 1, 2022 1 comment

As suspicious questions on IRC and Discord revealed, there are quite a few solutions to the PWC that are not made public. One question in particular indicates that there is a build-in missing in Raku.

Nemokosch: let’s say I have a 5×5 table for some reason

PWC162-2 is asking to implement an encryption algorithm that uses a simple substitution table. Having a data-struction that allowes to turn a character into a 2-dimensional @index and then use it to @replacement[||@index] would be very helpful indeed. We can use .antipairs to turn a simple list into something we can assign to a Hash and be done with it. With 2-dimension, we have to create our own.

proto sub deepantipairs(@positional, $dimensions --> Positional) {*}
multi sub deepantipairs(@a, 2) {
    @a.pairs.map({ my $outer-key = .key; .value.antipairs.map({ .key => ($outer-key, .value) }) }).flat
}
my $phrase = 'Spring has sprung!';
my @table = flat($phrase.lc.comb.grep(/\w/), 'a'..'z').unique[^25].batch(5);
my %antitable = @table.&deepantipairs(2);

# OUTPUT:

# Array @table = [("S", "p", "r", "i", "n"), ("g", "h", "a", "s", "u"), ("!", "b", "c", "d", "e"), ("f", "j", "k", "l", "m"), ("o", "q", "t", "v", "w")]
# Hash %antitable = {"!" => $(2, 0), :S($(0, 0)), :a($(1, 2)), :b($(2, 1)), :c($(2, 2)), :d($(2, 3)), :e($(2, 4)), :f($(3, 0)), :g($(1, 0)), :h($(1, 1)), :i($(0, 3)), :j($(3, 1)), :k($(3, 2)), :l($(3, 3)), :m($(3, 4)), :n($(0, 4)), :o($(4, 0)), :p($(0, 1)), :q($(4, 1)), :r($(0, 2)), :s($(1, 3)), :t($(4, 2)), :u($(1, 4)), :v($(4, 3)), :w($(4, 4))}

Let’s rotate the table (the basic idea behind the Enigma) to create a much more interesting cypher-text.

sub encrypt($phrase, $text --> Str) {
    my @table = flat($phrase.lc.comb.grep(/\w/), 'a'..'z').unique[^25].batch(5);
    my %antitable = @table.&deepantipairs(2);

    my $retval;

    for $text.lc.comb.grep(/\w/) -> $char {
        my @deepindex := %antitable{$char};
        $retval ~= @table[||@deepindex];
        @table = @table».List.flat[1..*,0].flat.batch(5);
    }

    $retval
}

say encrypt($phrase, 'As suspicious questions on IRC and Discord revealed, there are quite a few solutions to the PWC that are not made public. One question in particular indicates that there is a build-in in Raku missing.');

# OUTPUT:
# aprdnhbmdrodhvpkdtdxtjpppcuhjkuhmpdvfybpwannjpuychrfvdasojvvadgnwcqcwqkjmndpxexcepjcvjnagvpiopidyalietcknsbseejeqkbopsbpwypbrcuwknsejinlxjsmkxppwdasrrniboewbauejl

Please note the tripple “p” in the cypher. By rotating the replacement table, we mess up statistical properties of the natural language we encrypt. This makes limiting the key-space much harder.

As you likely spotted, I defined a proto with the argument $dimensions (our item may be an Iterable so we can’t infer this argument). Raku has many very handy methods defined in List that work very well with a single dimension. There may be more work to do when we start to support fixed size shaped arrays well.

Categories: Raku