aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörg Sommrey <28217714+jo-37@users.noreply.github.com>2025-01-24 11:15:47 +0100
committerJörg Sommrey <28217714+jo-37@users.noreply.github.com>2025-01-24 11:15:47 +0100
commit1b9d73bcdda08e3875253c7688bc3b5b655aeee5 (patch)
treeb392edb8750b3eac52c8193cb21e89110da82c0a
parent092e53fa7335deeac05c85c055733255dd5c4c89 (diff)
parent359fcfb8a64f069f77dca0194a3b10dde4f10ea2 (diff)
downloadperlweeklychallenge-club-1b9d73bcdda08e3875253c7688bc3b5b655aeee5.tar.gz
perlweeklychallenge-club-1b9d73bcdda08e3875253c7688bc3b5b655aeee5.tar.bz2
perlweeklychallenge-club-1b9d73bcdda08e3875253c7688bc3b5b655aeee5.zip
Solutions to challenge 305
-rw-r--r--challenge-305/jo-37/blog.txt1
-rwxr-xr-xchallenge-305/jo-37/perl/ch-1.pl109
-rwxr-xr-xchallenge-305/jo-37/perl/ch-2.pl130
3 files changed, 240 insertions, 0 deletions
diff --git a/challenge-305/jo-37/blog.txt b/challenge-305/jo-37/blog.txt
new file mode 100644
index 0000000000..7eedfbe5eb
--- /dev/null
+++ b/challenge-305/jo-37/blog.txt
@@ -0,0 +1 @@
+https://github.sommrey.de/the-bears-den/2025/01/24/ch-305.html
diff --git a/challenge-305/jo-37/perl/ch-1.pl b/challenge-305/jo-37/perl/ch-1.pl
new file mode 100755
index 0000000000..fb09a2cddd
--- /dev/null
+++ b/challenge-305/jo-37/perl/ch-1.pl
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+
+use v5.26;
+use Test2::V0 '!array';
+use Test2::Tools::Subtest 'subtest_streamed';
+use Getopt::Long;
+use bigint;
+use Math::Prime::Util 'is_prime';
+use List::Util 'reductions';
+use warnings FATAL => 'all';
+use Data::Dump qw(dd pp);
+use experimental 'signatures';
+
+### Options and Arguments
+
+my ($tests, $examples, $verbose);
+GetOptions(
+ 'examples!' => \$examples,
+ 'tests!' => \$tests,
+ 'verbose!' => \$verbose,
+) or usage();
+
+run_tests($examples, $tests); # tests do not return
+
+usage() unless @ARGV;
+
+sub usage {
+ die <<~EOS;
+ $0 - binary prefix
+
+ usage: $0 [-examples] [-tests] [B...]
+
+ -examples
+ run the examples from the challenge
+
+ -tests
+ run some tests
+
+ B...
+ list of binary digits
+
+ EOS
+}
+
+
+### Input and Output
+
+say join ', ', map +(qw(false true))[$_], binary_prefix(@ARGV);
+
+
+### Implementation
+#
+# For details see:
+# https://github.sommrey.de/the-bears-den/2025/01/24/ch-305.html#task-1
+
+sub binary_prefix {
+ map {is_prime($_) <=> 0} reductions {2 * $a + $b} @_;
+}
+
+
+### Examples and Tests
+
+sub run_tests ($examples, $tests) {
+ return unless $examples || $tests;
+
+ # convert a list of numbers into a ref to an array of
+ # Test2::Compare::Number objects
+ state sub numarr {
+ [map number($_), @_];
+ }
+
+ state sub run_example ($args, $expected, $name) {
+ my $result = [binary_prefix(@$args)];
+ is $result, numarr(@$expected), "$name: (@$args) -> (@$expected)";
+ }
+
+ plan 2;
+
+ $examples ? subtest_streamed(examples => sub {
+ my @examples = (
+ [[1, 0, 1], [0, 1, 1], 'example 1'],
+ [[1, 1, 0], [0, 1, 0], 'example 2'],
+ [[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1],
+ [0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ 'example 3'],
+ );
+ plan scalar @examples;
+ for (@examples) {
+ run_example @$_;
+ }
+ }) : pass 'skip examples';
+
+ $tests ? subtest_streamed(tests => sub {
+ plan 5;
+ is [binary_prefix(0, 0)], numarr(0, 0), '0, 0';
+ is [binary_prefix(0, 1)], numarr(0, 0), '0, 1';
+ is [binary_prefix(1, 0)], numarr(0, 1), '1, 2';
+ is [binary_prefix(1, 1)], numarr(0, 1), '1, 3';
+
+ my @me = (2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521);
+ my @expected = (0) x $me[-1];
+ @expected[map $_ - 1, @me] = (1) x @me;
+ is [binary_prefix((1) x $me[-1])], numarr(@expected),
+ 'Mersenne primes';
+
+ }) : pass 'skip tests';
+
+ exit;
+}
diff --git a/challenge-305/jo-37/perl/ch-2.pl b/challenge-305/jo-37/perl/ch-2.pl
new file mode 100755
index 0000000000..0eccd16975
--- /dev/null
+++ b/challenge-305/jo-37/perl/ch-2.pl
@@ -0,0 +1,130 @@
+#!/usr/bin/perl -T
+
+use v5.26;
+use Test2::V0;
+use Test2::Tools::Subtest 'subtest_streamed';
+use Getopt::Long;
+use Unicode::Normalize;
+use warnings FATAL => 'all';
+use Data::Dump qw(dd pp);
+use experimental 'signatures';
+use utf8;
+
+### Options and Arguments
+
+my ($tests, $examples, $verbose, $order);
+GetOptions(
+ 'examples!' => \$examples,
+ 'tests!' => \$tests,
+ 'verbose!' => \$verbose,
+ 'order=s' => \$order,
+) or usage();
+
+run_tests($examples, $tests); # tests do not return
+
+usage() unless @ARGV;
+
+sub usage {
+ die <<~EOS;
+ $0 - alien sort
+
+ usage: $0 [-examples] [-tests] [-order C,... W...]
+
+ -examples
+ run the examples from the challenge
+
+ -tests
+ run some tests
+
+ -order C,...
+ comma and/or whitspace separated list of characters 'a' .. 'z'
+
+ W...
+ list of words
+
+ EOS
+}
+
+
+### Input and Output
+
+say "@{[alien_sort([split /[,\s]\s*/, $order], @ARGV)]}";
+
+
+### Implementation
+#
+# For details see:
+# https://github.sommrey.de/the-bears-den/2025/01/24/ch-305.html#task-2
+
+BEGIN {
+ my @all = 'a' .. 'z';
+
+ sub alien_sort ($order, @words) {
+ my %a2n;
+ @a2n{@$order} = @all;
+ die 'order not valid' if @all > grep defined, @a2n{@all};
+ my %n2a = map {$a2n{$_} => $_} @all;
+ my $alien = join '', @n2a{@all};
+
+ map $_->[0],
+ sort {$a->[1] cmp $b->[1]}
+ map {
+ [$_, eval "NFD(fc) =~ tr/a-z//cdr =~ tr/$alien/a-z/r"]
+ } @words;
+ }
+}
+
+
+### Examples and Tests
+
+sub run_tests ($examples, $tests) {
+ return unless $examples || $tests;
+
+ state sub run_example ($args, $expected, $name) {
+ my $result = [alien_sort(@$args)];
+ my $alien = shift @$args;
+ like $result, $expected,
+ "$name: alien='@$alien' (@$args) -> (@$expected)", $@;
+ }
+
+ plan 2;
+
+ $examples ? subtest_streamed(examples => sub {
+ my @examples = (
+ [[[qw/h l a b y d e f g i r k m n o p q j s t u v w x c z/],
+ "perl", "python", "raku"],
+ ["raku", "python", "perl"], 'example 1'],
+ [[[qw/c o r l d a b t e f g h i j k m n p q s w u v x y z/],
+ "the", "weekly", "challenge"],
+ ["challenge", "the", "weekly"], 'example 2'],
+ );
+ plan scalar @examples;
+ for (@examples) {
+ run_example @$_;
+ }
+ }) : pass 'skip examples';
+
+ $tests ? subtest_streamed(tests => sub {
+ plan 5;
+ my $tainted = substr $ENV{PATH}, 0, 0;
+
+ ok lives {alien_sort([map $_ . $tainted, 'a' .. 'z'], 'abc')},
+ 'laundered', $@;
+
+ like dies {alien_sort(['//;die-gotcha;tr/a', 'a' .. 'z'], 'abc')},
+ qr/order not valid/, 'missing letter in order';
+
+ ok lives {alien_sort(['a' .. 'z', '//;exit;tr/a-z'],
+ 'abc', 'bcd')}, 'ignore injection', $@;
+
+ is [alien_sort([qw(z y a c b), 'd' .. 'x'], qw(ab ac yb zb))],
+ [qw(zb yb ac ab)], 'alien order';
+
+ is [alien_sort([qw(a e i o u b c d f g h j k
+ l m n p q r s t v w x y z),],
+ 'Oc', 'Öc', 'oc', 'öc', 'oe', 'öe', 'Oe', 'Öe')],
+ ['oe', 'öe', 'Oe', 'Öe', 'Oc', 'Öc', 'oc', 'öc'], 'upper and umlauts';
+ }) : pass 'skip tests';
+
+ exit;
+}