aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Bell_West <roger@firedrake.org>2025-11-12 15:43:21 +0000
committerRoger Bell_West <roger@firedrake.org>2025-11-12 15:43:21 +0000
commitc86f2fc4fdd294679676134971d229da135243d2 (patch)
tree3b3f80c88c4fd31c2040fde39e6d69a32bf0bc5b
parented251006b89063380df4e9ed19bee441bfb9c36c (diff)
downloadperlweeklychallenge-club-c86f2fc4fdd294679676134971d229da135243d2.tar.gz
perlweeklychallenge-club-c86f2fc4fdd294679676134971d229da135243d2.tar.bz2
perlweeklychallenge-club-c86f2fc4fdd294679676134971d229da135243d2.zip
RogerBW solutions for challenge no. 347
-rwxr-xr-xchallenge-347/roger-bell-west/crystal/ch-1.cr41
-rwxr-xr-xchallenge-347/roger-bell-west/crystal/ch-2.cr50
-rwxr-xr-xchallenge-347/roger-bell-west/javascript/ch-1.js74
-rwxr-xr-xchallenge-347/roger-bell-west/javascript/ch-2.js69
-rw-r--r--challenge-347/roger-bell-west/kotlin/ch-1.kt57
-rw-r--r--challenge-347/roger-bell-west/kotlin/ch-2.kt64
-rwxr-xr-xchallenge-347/roger-bell-west/lua/ch-1.lua69
-rwxr-xr-xchallenge-347/roger-bell-west/lua/ch-2.lua91
-rwxr-xr-xchallenge-347/roger-bell-west/perl/ch-1.pl33
-rwxr-xr-xchallenge-347/roger-bell-west/perl/ch-2.pl44
-rw-r--r--challenge-347/roger-bell-west/postscript/ch-1.ps195
-rw-r--r--challenge-347/roger-bell-west/postscript/ch-2.ps109
-rwxr-xr-xchallenge-347/roger-bell-west/python/ch-1.py46
-rwxr-xr-xchallenge-347/roger-bell-west/python/ch-2.py45
-rwxr-xr-xchallenge-347/roger-bell-west/raku/ch-1.p631
-rwxr-xr-xchallenge-347/roger-bell-west/raku/ch-2.p642
-rwxr-xr-xchallenge-347/roger-bell-west/ruby/ch-1.rb48
-rwxr-xr-xchallenge-347/roger-bell-west/ruby/ch-2.rb57
-rwxr-xr-xchallenge-347/roger-bell-west/rust/ch-1.rs57
-rwxr-xr-xchallenge-347/roger-bell-west/rust/ch-2.rs62
-rw-r--r--challenge-347/roger-bell-west/scala/ch-1.scala64
-rw-r--r--challenge-347/roger-bell-west/scala/ch-2.scala69
-rw-r--r--challenge-347/roger-bell-west/tests.json48
-rw-r--r--challenge-347/roger-bell-west/typst/ch-1.typ54
-rw-r--r--challenge-347/roger-bell-west/typst/ch-2.typ53
25 files changed, 1572 insertions, 0 deletions
diff --git a/challenge-347/roger-bell-west/crystal/ch-1.cr b/challenge-347/roger-bell-west/crystal/ch-1.cr
new file mode 100755
index 0000000000..76428efc0d
--- /dev/null
+++ b/challenge-347/roger-bell-west/crystal/ch-1.cr
@@ -0,0 +1,41 @@
+#! /usr/bin/crystal
+
+def formatdate(a)
+ a =~ /^([0-9]+)[a-z]+ ([A-Z][a-z]{2}) ([0-9]+)/;
+ y = $3
+ d = $1
+ m = {
+ "Jan" => 1,
+ "Feb" => 2,
+ "Mar" => 3,
+ "Apr" => 4,
+ "May" => 5,
+ "Jun" => 6,
+ "Jul" => 7,
+ "Aug" => 8,
+ "Sep" => 9,
+ "Oct" => 10,
+ "Nov" => 11,
+ "Dec" => 12,
+ }[$2]
+ sprintf("%04d-%02d-%02d", y, m, d)
+end
+
+require "spec"
+describe "formatdate" do
+ it "test_ex1" do
+ formatdate("1st Jan 2025").should eq "2025-01-01"
+ end
+ it "test_ex2" do
+ formatdate("22nd Feb 2025").should eq "2025-02-22"
+ end
+ it "test_ex3" do
+ formatdate("15th Apr 2025").should eq "2025-04-15"
+ end
+ it "test_ex4" do
+ formatdate("23rd Oct 2025").should eq "2025-10-23"
+ end
+ it "test_ex5" do
+ formatdate("31st Dec 2025").should eq "2025-12-31"
+ end
+end
diff --git a/challenge-347/roger-bell-west/crystal/ch-2.cr b/challenge-347/roger-bell-west/crystal/ch-2.cr
new file mode 100755
index 0000000000..81c1f16136
--- /dev/null
+++ b/challenge-347/roger-bell-west/crystal/ch-2.cr
@@ -0,0 +1,50 @@
+#! /usr/bin/crystal
+
+def formatphonenumber(a)
+ pure = a.chars.select{|x| x >= '0' && x <= '9'}
+ left = pure.size
+ out = Array(Char).new
+ i = 0
+ if left > 4
+ pure.each do |c|
+ out.push(c)
+ i += 1
+ left -= 1
+ if i % 3 == 0
+ out.push('-')
+ if left <= 4
+ break
+ end
+ end
+ end
+ end
+ if left == 4
+ out.push(pure[i])
+ out.push(pure[i + 1])
+ out.push('-')
+ i += 2
+ end
+ i.upto(pure.size - 1) do |p|
+ out.push(pure[p])
+ end
+ out.join("")
+end
+
+require "spec"
+describe "formatphonenumber" do
+ it "test_ex1" do
+ formatphonenumber("1-23-45-6").should eq "123-456"
+ end
+ it "test_ex2" do
+ formatphonenumber("1234").should eq "12-34"
+ end
+ it "test_ex3" do
+ formatphonenumber("12 345-6789").should eq "123-456-789"
+ end
+ it "test_ex4" do
+ formatphonenumber("123 4567").should eq "123-45-67"
+ end
+ it "test_ex5" do
+ formatphonenumber("123 456-78").should eq "123-456-78"
+ end
+end
diff --git a/challenge-347/roger-bell-west/javascript/ch-1.js b/challenge-347/roger-bell-west/javascript/ch-1.js
new file mode 100755
index 0000000000..4037b94564
--- /dev/null
+++ b/challenge-347/roger-bell-west/javascript/ch-1.js
@@ -0,0 +1,74 @@
+#! /usr/bin/node
+
+"use strict"
+
+function format(n, w) {
+ let s = n.toString();
+ while (s.length < w) {
+ s = "0" + s;
+ }
+ return s;
+}
+function formatdate(a) {
+ const dmy = a.split(" ");
+ let out = [];
+ out.push(parseInt(dmy[2]));
+ out.push(new Map([
+ [ "Jan", 1 ],
+ [ "Feb", 2 ],
+ [ "Mar", 3 ],
+ [ "Apr", 4 ],
+ [ "May", 5 ],
+ [ "Jun", 6 ],
+ [ "Jul", 7 ],
+ [ "Aug", 8 ],
+ [ "Sep", 9 ],
+ [ "Oct", 10 ],
+ [ "Nov", 11 ],
+ [ "Dec", 12 ]
+ ]).get(dmy[1]));
+ {
+ let c = dmy[0].split('');
+ let d = parseInt(c[0]);
+ if (c[1].search("[0-9]") != -1) {
+ d = d * 10 + parseInt(c[1]);
+ }
+ out.push(d);
+ }
+ return [
+ format(out[0], 4),
+ format(out[1], 2),
+ format(out[2], 2)
+ ].join('-');
+}
+
+if (formatdate('1st Jan 2025') == '2025-01-01') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatdate('22nd Feb 2025') == '2025-02-22') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatdate('15th Apr 2025') == '2025-04-15') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatdate('23rd Oct 2025') == '2025-10-23') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatdate('31st Dec 2025') == '2025-12-31') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-347/roger-bell-west/javascript/ch-2.js b/challenge-347/roger-bell-west/javascript/ch-2.js
new file mode 100755
index 0000000000..4ce9d64521
--- /dev/null
+++ b/challenge-347/roger-bell-west/javascript/ch-2.js
@@ -0,0 +1,69 @@
+#! /usr/bin/node
+
+"use strict"
+
+function formatphonenumber(a) {
+ let pure = [];
+ for (let c of a.split('')) {
+ if (c >= '0' && c <= '9') {
+ pure.push(c);
+ }
+ }
+ let left = pure.length;
+ let out = [];
+ let i = 0;
+ if (left > 4) {
+ for (let c of pure) {
+ out.push(c);
+ i += 1;
+ left -= 1;
+ if (i % 3 == 0) {
+ out.push('-');
+ if (left <= 4) {
+ break;
+ }
+ }
+ }
+ }
+ if (left == 4) {
+ out.push(pure[i]);
+ out.push(pure[i + 1]);
+ out.push('-');
+ i += 2;
+ }
+ for (let p = i; p < pure.length; p++) {
+ out.push(pure[p]);
+ }
+ return out.join("");
+}
+
+if (formatphonenumber('1-23-45-6') == '123-456') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatphonenumber('1234') == '12-34') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatphonenumber('12 345-6789') == '123-456-789') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatphonenumber('123 4567') == '123-45-67') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (formatphonenumber('123 456-78') == '123-456-78') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-347/roger-bell-west/kotlin/ch-1.kt b/challenge-347/roger-bell-west/kotlin/ch-1.kt
new file mode 100644
index 0000000000..e0bc8f47a6
--- /dev/null
+++ b/challenge-347/roger-bell-west/kotlin/ch-1.kt
@@ -0,0 +1,57 @@
+fun formatdate(a: String): String {
+ val re = "^([0-9]+)[a-z]+ ([A-Z][a-z]{2}) ([0-9]+)".toRegex()
+ val mr = re.find(a)
+ val d = mr!!.groupValues[1].toInt()
+ val mt = mr.groupValues[2]
+ val m = hashMapOf(
+ "Jan" to 1,
+ "Feb" to 2,
+ "Mar" to 3,
+ "Apr" to 4,
+ "May" to 5,
+ "Jun" to 6,
+ "Jul" to 7,
+ "Aug" to 8,
+ "Sep" to 9,
+ "Oct" to 10,
+ "Nov" to 11,
+ "Dec" to 12,
+ ).getValue(mt)
+ val y = mr.groupValues[3].toInt()
+ return "%04d-%02d-%02d".format(y, m, d)
+}
+
+fun main() {
+
+ if (formatdate("1st Jan 2025") == "2025-01-01") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatdate("22nd Feb 2025") == "2025-02-22") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatdate("15th Apr 2025") == "2025-04-15") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatdate("23rd Oct 2025") == "2025-10-23") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatdate("31st Dec 2025") == "2025-12-31") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-347/roger-bell-west/kotlin/ch-2.kt b/challenge-347/roger-bell-west/kotlin/ch-2.kt
new file mode 100644
index 0000000000..0d9dd03ab5
--- /dev/null
+++ b/challenge-347/roger-bell-west/kotlin/ch-2.kt
@@ -0,0 +1,64 @@
+fun formatphonenumber(a: String): String {
+ val pure = a.toList().filter{it >= '0' && it <= '9'}
+ var left = pure.size
+ var out = ArrayList<Char>()
+ var i = 0
+ if (left > 4) {
+ for (c in pure) {
+ out.add(c)
+ i += 1
+ left -= 1
+ if (i % 3 == 0) {
+ out.add('-')
+ if (left <= 4) {
+ break
+ }
+ }
+ }
+ }
+ if (left == 4) {
+ out.add(pure[i])
+ out.add(pure[i + 1])
+ out.add('-')
+ i += 2
+ }
+ for (p in i .. pure.size - 1) {
+ out.add(pure[p])
+ }
+ return out.joinToString("")
+}
+
+fun main() {
+
+ if (formatphonenumber("1-23-45-6") == "123-456") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatphonenumber("1234") == "12-34") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatphonenumber("12 345-6789") == "123-456-789") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatphonenumber("123 4567") == "123-45-67") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (formatphonenumber("123 456-78") == "123-456-78") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-347/roger-bell-west/lua/ch-1.lua b/challenge-347/roger-bell-west/lua/ch-1.lua
new file mode 100755
index 0000000000..1bddee2198
--- /dev/null
+++ b/challenge-347/roger-bell-west/lua/ch-1.lua
@@ -0,0 +1,69 @@
+#! /usr/bin/lua
+function dump(o)
+ if type(o) == 'table' then
+ local s = '{ '
+ for k,v in pairs(o) do
+ if type(k) ~= 'number' then k = '"'..k..'"' end
+ s = s .. '['..k..'] = ' .. dump(v) .. ','
+ end
+ return s .. '} '
+ else
+ return tostring(o)
+ end
+end
+
+function formatdate(a)
+ local d, m, y = string.match(a, "^(%d+)%l+ (%u%l+) (%d+)$")
+ local tab = {
+ Jan = 1;
+ Feb = 2;
+ Mar = 3;
+ Apr = 4;
+ May = 5;
+ Jun = 6;
+ Jul = 7;
+ Aug = 8;
+ Sep = 9;
+ Oct = 10;
+ Nov = 11;
+ Dec = 12
+ }
+ m = tab[m]
+ return string.format("%04d-%02d-%02d", y, m, d)
+ end
+
+if formatdate("1st Jan 2025") == "2025-01-01" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatdate("22nd Feb 2025") == "2025-02-22" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatdate("15th Apr 2025") == "2025-04-15" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatdate("23rd Oct 2025") == "2025-10-23" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatdate("31st Dec 2025") == "2025-12-31" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-347/roger-bell-west/lua/ch-2.lua b/challenge-347/roger-bell-west/lua/ch-2.lua
new file mode 100755
index 0000000000..76be8696de
--- /dev/null
+++ b/challenge-347/roger-bell-west/lua/ch-2.lua
@@ -0,0 +1,91 @@
+#! /usr/bin/lua
+
+function split(t)
+ local cl = {}
+ string.gsub(t,
+ "(.)",
+ function(c)
+ table.insert(cl, c)
+ end
+ )
+ return cl
+end
+
+function join(t)
+ local out=""
+ for i, v in ipairs(t) do
+ out = out .. v
+ end
+ return out
+end
+
+function formatphonenumber(a)
+ local pure = {}
+ for _, c in ipairs(split(a)) do
+ if c >= "0" and c <= "9" then
+ table.insert(pure, c)
+ end
+ end
+ local left = #pure
+ local out = {}
+ local i = 1
+ if left > 4 then
+ for _, c in ipairs(pure) do
+ table.insert(out, c)
+ i = i + 1
+ left = left - 1
+ if i % 3 == 1 then
+ table.insert(out, "-")
+ if left <= 4 then
+ break
+ end
+ end
+ end
+ end
+ if left == 4 then
+ table.insert(out, pure[i])
+ table.insert(out, pure[i + 1])
+ table.insert(out, "-")
+ i = i + 2
+ end
+ for p = i, #pure do
+ table.insert(out, pure[p])
+ end
+ return join(out)
+end
+
+if formatphonenumber("1-23-45-6") == "123-456" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatphonenumber("1234") == "12-34" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatphonenumber("12 345-6789") == "123-456-789" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatphonenumber("123 4567") == "123-45-67" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if formatphonenumber("123 456-78") == "123-456-78" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-347/roger-bell-west/perl/ch-1.pl b/challenge-347/roger-bell-west/perl/ch-1.pl
new file mode 100755
index 0000000000..88929cc34b
--- /dev/null
+++ b/challenge-347/roger-bell-west/perl/ch-1.pl
@@ -0,0 +1,33 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 5;
+
+is(formatdate('1st Jan 2025'), '2025-01-01', 'example 1');
+is(formatdate('22nd Feb 2025'), '2025-02-22', 'example 2');
+is(formatdate('15th Apr 2025'), '2025-04-15', 'example 3');
+is(formatdate('23rd Oct 2025'), '2025-10-23', 'example 4');
+is(formatdate('31st Dec 2025'), '2025-12-31', 'example 5');
+
+sub formatdate($a) {
+ $a =~ /^([0-9]+)[a-z]+ ([A-Z][a-z]{2}) ([0-9]+)/;
+ my @dmy = ($3, $2, $1);
+ $dmy[1] = {
+ "Jan" => 1,
+ "Feb" => 2,
+ "Mar" => 3,
+ "Apr" => 4,
+ "May" => 5,
+ "Jun" => 6,
+ "Jul" => 7,
+ "Aug" => 8,
+ "Sep" => 9,
+ "Oct" => 10,
+ "Nov" => 11,
+ "Dec" => 12,
+ }->{$dmy[1]};
+ sprintf('%04d-%02d-%02d', @dmy);
+}
diff --git a/challenge-347/roger-bell-west/perl/ch-2.pl b/challenge-347/roger-bell-west/perl/ch-2.pl
new file mode 100755
index 0000000000..bf1726485f
--- /dev/null
+++ b/challenge-347/roger-bell-west/perl/ch-2.pl
@@ -0,0 +1,44 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 5;
+
+is(formatphonenumber('1-23-45-6'), '123-456', 'example 1');
+is(formatphonenumber('1234'), '12-34', 'example 2');
+is(formatphonenumber('12 345-6789'), '123-456-789', 'example 3');
+is(formatphonenumber('123 4567'), '123-45-67', 'example 4');
+is(formatphonenumber('123 456-78'), '123-456-78', 'example 5');
+
+sub formatphonenumber($a) {
+ (my $p = $a) =~ s/\D+//g;
+ my @pure = split '', $p;
+ my $left = scalar @pure;
+ my @out = ();
+ my $i = 0;
+ if ($left > 4) {
+ foreach my $c (@pure) {
+ push @out, $c;
+ $i++;
+ $left--;
+ if ($i % 3 == 0) {
+ push @out,'-';
+ if ($left <= 4) {
+ last;
+ }
+ }
+ }
+ }
+ if ($left == 4) {
+ push @out, $pure[$i];
+ push @out, $pure[$i + 1];
+ push @out, '-';
+ $i += 2;
+ }
+ foreach my $p ($i .. $#pure) {
+ push @out, $pure[$p];
+ }
+ join('', @out);
+}
diff --git a/challenge-347/roger-bell-west/postscript/ch-1.ps b/challenge-347/roger-bell-west/postscript/ch-1.ps
new file mode 100644
index 0000000000..f75e53bff9
--- /dev/null
+++ b/challenge-347/roger-bell-west/postscript/ch-1.ps
@@ -0,0 +1,195 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/test {
+ /test.count test.count 1 add def
+ {
+ /test.pass test.pass 1 add def
+ } {
+ ( ) print
+ test.count (....) cvs print
+ (-fail) print
+ } ifelse
+} bind def
+
+/s2a {
+ [ exch { } forall ]
+} bind def
+
+/strconcat % (a) (b) -> (ab)
+{
+ [
+ 3 -1 roll
+ s2a aload length
+ 2 add -1 roll
+ s2a aload pop
+ ] a2s
+} bind def
+
+/strjoin % [(a) (b) (c)] (j) -> (ajbjc)
+{
+ 3 dict begin
+ /j exch def
+ dup 0 get /out exch def
+ /first true def
+ {
+ first {
+ pop
+ /first false def
+ } {
+ out j strconcat
+ exch strconcat
+ /out exch def
+ } ifelse
+ } forall
+ out
+ end
+} bind def
+
+/test.end {
+ ( ) print
+ test.count 0 gt {
+ (Passed ) print
+ test.pass (...) cvs print
+ (/) print
+ test.count (...) cvs print
+ ( \() print
+ test.pass 100 mul test.count idiv (...) cvs print
+ (%\)) print
+ (\r\n) print
+ } if
+} bind def
+
+/test.start {
+ print (:) print
+ /test.pass 0 def
+ /test.count 0 def
+} bind def
+
+/a2s {
+ 2 dict begin
+ /i exch def
+ i length dup string /o exch def
+ 1 sub 0 exch 1 exch {
+ dup i 3 -1 roll get o 3 1 roll put
+ } for
+ o
+ end
+} bind def
+
+/c.isdigit {
+ dup 48 ge exch 57 le and
+} bind def
+
+/strsplit % (ajbjc) (j) -> [ (a) (b) (c) ]
+{
+ 1 dict begin
+ /sep exch def
+ [ exch
+ {
+ dup length 0 eq {
+ pop
+ exit
+ } {
+ sep search {
+ exch pop
+ dup length 0 eq {
+ pop
+ } {
+ exch
+ } ifelse
+ } {
+ ()
+ } ifelse
+ } ifelse
+ } loop
+ ]
+ end
+} bind def
+
+/alloccvs {
+ 2 dict begin
+ /n exch def
+ /a 1 def
+ n
+ dup 0 lt {
+ /a a 1 add def
+ neg
+ } if
+ {
+ dup 10 lt {
+ exit
+ } if
+ /a a 1 add def
+ 10 idiv
+ } loop
+ pop
+ n a string cvs
+ end
+} bind def
+
+
+% end included library code
+
+/fmn {
+ 0 dict begin
+ /n exch def
+ /a exch def
+ /ap a alloccvs def
+ /apl ap length def
+ /out n string def
+ 0 1 n 1 sub {
+ out exch 48 put
+ } for
+ 0 1 apl 1 sub {
+ /i exch def
+ /o n apl sub i add def
+ out ap i get o exch put
+ } for
+ out
+ end
+} bind def
+
+
+/formatdate {
+ 0 dict begin
+ ( ) strsplit /dmy exch def
+ <<
+ (Jan) 1
+ (Feb) 2
+ (Mar) 3
+ (Apr) 4
+ (May) 5
+ (Jun) 6
+ (Jul) 7
+ (Aug) 8
+ (Sep) 9
+ (Oct) 10
+ (Nov) 11
+ (Dec) 12
+ >> dmy 1 get get dmy exch 1 exch put
+ dmy 0 get
+ dup
+ 0 exch
+ 1 get c.isdigit {
+ 2
+ } {
+ 1
+ } ifelse
+ getinterval cvi dmy exch 0 exch put
+ [
+ dmy 2 get cvi 4 fmn
+ dmy 1 get cvi 2 fmn
+ dmy 0 get cvi 2 fmn
+ ] (-) strjoin
+ end
+} bind def
+
+(formatdate) test.start
+(1st Jan 2025) formatdate (2025-01-01) eq test
+(22nd Feb 2025) formatdate (2025-02-22) eq test
+(15th Apr 2025) formatdate (2025-04-15) eq test
+(23rd Oct 2025) formatdate (2025-10-23) eq test
+(31st Dec 2025) formatdate (2025-12-31) eq test
+test.end
diff --git a/challenge-347/roger-bell-west/postscript/ch-2.ps b/challenge-347/roger-bell-west/postscript/ch-2.ps
new file mode 100644
index 0000000000..eeffc12cd1
--- /dev/null
+++ b/challenge-347/roger-bell-west/postscript/ch-2.ps
@@ -0,0 +1,109 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/test.start {
+ print (:) print
+ /test.pass 0 def
+ /test.count 0 def
+} bind def
+
+/test.end {
+ ( ) print
+ test.count 0 gt {
+ (Passed ) print
+ test.pass (...) cvs print
+ (/) print
+ test.count (...) cvs print
+ ( \() print
+ test.pass 100 mul test.count idiv (...) cvs print
+ (%\)) print
+ (\r\n) print
+ } if
+} bind def
+
+/filter { % array proc(bool) -> array
+ 1 dict begin
+ /p exch def
+ [ exch
+ {
+ dup p not
+ {
+ pop
+ } if
+ } forall
+ ]
+ end
+} bind def
+
+/a2s {
+ 2 dict begin
+ /i exch def
+ i length dup string /o exch def
+ 1 sub 0 exch 1 exch {
+ dup i 3 -1 roll get o 3 1 roll put
+ } for
+ o
+ end
+} bind def
+
+/c.isdigit {
+ dup 48 ge exch 57 le and
+} bind def
+
+/test {
+ /test.count test.count 1 add def
+ {
+ /test.pass test.pass 1 add def
+ } {
+ ( ) print
+ test.count (....) cvs print
+ (-fail) print
+ } ifelse
+} bind def
+
+/s2a {
+ [ exch { } forall ]
+} bind def
+
+
+% end included library code
+
+/formatphonenumber {
+ 0 dict begin
+ s2a { c.isdigit } filter /pure exch def
+ /left pure length def
+ /i 0 def
+ [
+ left 4 gt {
+ pure {
+ /i i 1 add def
+ /left left 1 sub def
+ i 3 mod 0 eq {
+ 45
+ left 4 le {
+ exit
+ } if
+ } if
+ } forall
+ } if
+ left 4 eq {
+ pure i get
+ pure i 1 add get
+ 45
+ /i i 2 add def
+ } if
+ i 1 pure length 1 sub {
+ pure exch get
+ } for
+ ] a2s
+ end
+} bind def
+
+(formatphonenumber) test.start
+(1-23-45-6) formatphonenumber (123-456) eq test
+(1234) formatphonenumber (12-34) eq test
+(12 345-6789) formatphonenumber (123-456-789) eq test
+(123 4567) formatphonenumber (123-45-67) eq test
+(123 456-78) formatphonenumber (123-456-78) eq test
+test.end
diff --git a/challenge-347/roger-bell-west/python/ch-1.py b/challenge-347/roger-bell-west/python/ch-1.py
new file mode 100755
index 0000000000..ede9f2b9a3
--- /dev/null
+++ b/challenge-347/roger-bell-west/python/ch-1.py
@@ -0,0 +1,46 @@
+#! /usr/bin/python3
+
+def formatdate(a):
+ dmy = a.split(" ")
+ out = []
+ out.append(int(dmy[2]))
+ out.append({
+ "Jan": 1,
+ "Feb": 2,
+ "Mar": 3,
+ "Apr": 4,
+ "May": 5,
+ "Jun": 6,
+ "Jul": 7,
+ "Aug": 8,
+ "Sep": 9,
+ "Oct": 10,
+ "Nov": 11,
+ "Dec": 12,
+ }[dmy[1]])
+ if dmy[0][1] >= "0" and dmy[0][1] <= "9":
+ out.append(int(dmy[0][0:2]))
+ else:
+ out.append(int(dmy[0][0:1]))
+ return "{:04d}-{:02d}-{:02d}".format(*out)
+
+import unittest
+
+class TestFormatdate(unittest.TestCase):
+
+ def test_ex1(self):
+ self.assertEqual(formatdate("1st Jan 2025"), "2025-01-01", 'example 1')
+
+ def test_ex2(self):
+ self.assertEqual(formatdate("22nd Feb 2025"), "2025-02-22", 'example 2')
+
+ def test_ex3(self):
+ self.assertEqual(formatdate("15th Apr 2025"), "2025-04-15", 'example 3')
+
+ def test_ex4(self):
+ self.assertEqual(formatdate("23rd Oct 2025"), "2025-10-23", 'example 4')
+
+ def test_ex5(self):
+ self.assertEqual(formatdate("31st Dec 2025"), "2025-12-31", 'example 5')
+
+unittest.main()
diff --git a/challenge-347/roger-bell-west/python/ch-2.py b/challenge-347/roger-bell-west/python/ch-2.py
new file mode 100755
index 0000000000..b2d5d6183b
--- /dev/null
+++ b/challenge-347/roger-bell-west/python/ch-2.py
@@ -0,0 +1,45 @@
+#! /usr/bin/python3
+
+def formatphonenumber(a):
+ pure = [x for x in a if x >= "0" and x <= "9"]
+ left = len(pure)