aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Bell_West <roger@firedrake.org>2023-07-25 11:43:11 +0100
committerRoger Bell_West <roger@firedrake.org>2023-07-25 11:43:11 +0100
commit292d4ad9841c63b4eff85294bfd426ee8c479a6b (patch)
treef3555b3b01df7b51fc7a285889f05cd4f07e4147
parente4bdf5dcb6e741f1fb8e1b145fd2111f05ed6445 (diff)
downloadperlweeklychallenge-club-292d4ad9841c63b4eff85294bfd426ee8c479a6b.tar.gz
perlweeklychallenge-club-292d4ad9841c63b4eff85294bfd426ee8c479a6b.tar.bz2
perlweeklychallenge-club-292d4ad9841c63b4eff85294bfd426ee8c479a6b.zip
RogerBW solutions for challenge no. 227
-rwxr-xr-xchallenge-227/roger-bell-west/javascript/ch-1.js20
-rwxr-xr-xchallenge-227/roger-bell-west/javascript/ch-2.js209
-rw-r--r--challenge-227/roger-bell-west/kotlin/ch-1.kt22
-rw-r--r--challenge-227/roger-bell-west/kotlin/ch-2.kt223
-rwxr-xr-xchallenge-227/roger-bell-west/lua/ch-1.lua37
-rwxr-xr-xchallenge-227/roger-bell-west/lua/ch-2.lua240
-rwxr-xr-xchallenge-227/roger-bell-west/perl/ch-1.pl21
-rwxr-xr-xchallenge-227/roger-bell-west/perl/ch-2.pl108
-rw-r--r--challenge-227/roger-bell-west/postscript/ch-1.ps72
-rw-r--r--challenge-227/roger-bell-west/postscript/ch-2.ps293
-rwxr-xr-xchallenge-227/roger-bell-west/python/ch-1.py19
-rwxr-xr-xchallenge-227/roger-bell-west/python/ch-2.py138
-rwxr-xr-xchallenge-227/roger-bell-west/raku/ch-1.p617
-rwxr-xr-xchallenge-227/roger-bell-west/raku/ch-2.p6108
-rwxr-xr-xchallenge-227/roger-bell-west/ruby/ch-1.rb23
-rwxr-xr-xchallenge-227/roger-bell-west/ruby/ch-2.rb171
-rwxr-xr-xchallenge-227/roger-bell-west/rust/ch-1.rs24
-rwxr-xr-xchallenge-227/roger-bell-west/rust/ch-2.rs184
-rw-r--r--challenge-227/roger-bell-west/tests.yaml51
19 files changed, 1980 insertions, 0 deletions
diff --git a/challenge-227/roger-bell-west/javascript/ch-1.js b/challenge-227/roger-bell-west/javascript/ch-1.js
new file mode 100755
index 0000000000..f1d67fa55f
--- /dev/null
+++ b/challenge-227/roger-bell-west/javascript/ch-1.js
@@ -0,0 +1,20 @@
+#! /usr/bin/node
+
+"use strict"
+
+function friday13th(y) {
+ let f = 0;
+ for (let m = 1; m <= 12; m++) {
+ if (new Date(y, m-1, 13, 12, 0, 0).getDay() == 5) {
+ f++;
+ }
+ }
+ return f;
+}
+
+if (friday13th(2023) == 2) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-227/roger-bell-west/javascript/ch-2.js b/challenge-227/roger-bell-west/javascript/ch-2.js
new file mode 100755
index 0000000000..ac7ff3fbfc
--- /dev/null
+++ b/challenge-227/roger-bell-west/javascript/ch-2.js
@@ -0,0 +1,209 @@
+#! /usr/bin/node
+
+"use strict"
+
+function roman2int(roman) {
+ if (roman == "") {
+ return 0;
+ }
+ for (let s of [
+ [ 'M', 1000 ],
+ [ 'CM', 900 ],
+ [ 'D', 500 ],
+ [ 'CD', 400 ],
+ [ 'C', 100 ],
+ [ 'XC', 90 ],
+ [ 'L', 50 ],
+ [ 'XL', 40 ],
+ [ 'X', 10 ],
+ [ 'IX', 9 ],
+ [ 'V', 5 ],
+ [ 'IV', 4 ],
+ [ 'I', 1 ],
+ ]) {
+ if (roman.startsWith(s[0])) {
+ return s[1] + roman2int(roman.slice(s[0].length));
+ }
+ }
+ return 0;
+}
+
+function int2roman(n0) {
+ let number = n0;
+ let mn = "";
+ for (let s of [
+ [ 'M', 1000 ],
+ [ 'CM', 900 ],
+ [ 'D', 500 ],
+ [ 'CD', 400 ],
+ [ 'C', 100 ],
+ [ 'XC', 90 ],
+ [ 'L', 50 ],
+ [ 'XL', 40 ],
+ [ 'X', 10 ],
+ [ 'IX', 9 ],
+ [ 'V', 5 ],
+ [ 'IV', 4 ],
+ [ 'I', 1 ],
+ ]) {
+ while (s[1] <= number) {
+ mn += s[0]
+ number -= s[1];
+ }
+ }
+ return mn;
+}
+
+function romanmaths(ax) {
+ const elems = ax.split(" ");
+ const a = roman2int(elems[0]);
+ const b = roman2int(elems[2]);
+ let c = -1;
+ if (elems[1] == "+") {
+ c = a + b;
+ } else if (elems[1] == "-") {
+ c = a - b;
+ } else if (elems[1] == "*") {
+ c = a * b;
+ } else if (elems[1] == "/") {
+ if (a % b == 0) {
+ c = a / b;
+ }
+ } else if (elems[1] == "**") {
+ c = a ** b;
+ }
+ if (c > 3999 || c < 0) {
+ return 'non potest';
+ } else if (c == 0) {
+ return 'nulla';
+ } else {
+ return int2roman(c);
+ }
+}
+
+if (roman2int('I') == 1) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (roman2int('II') == 2) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (roman2int('IV') == 4) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (roman2int('IX') == 9) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (roman2int('XXX') == 30) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (roman2int('MDCLXVI') == 1666) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(1) == 'I') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(2) == 'II') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(4) == 'IV') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(9) == 'IX') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(30) == 'XXX') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (int2roman(1666) == 'MDCLXVI') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('IV + V') == 'IX') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('M - I') == 'CMXCIX') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('X / II') == 'V') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('XI * VI') == 'LXVI') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('VII ** III') == 'CCCXLIII') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('V - V') == 'nulla') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('V / II') == 'non potest') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('MMM + M') == 'non potest') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (romanmaths('V - X') == 'non potest') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-227/roger-bell-west/kotlin/ch-1.kt b/challenge-227/roger-bell-west/kotlin/ch-1.kt
new file mode 100644
index 0000000000..33a112b884
--- /dev/null
+++ b/challenge-227/roger-bell-west/kotlin/ch-1.kt
@@ -0,0 +1,22 @@
+import java.time.LocalDate
+
+fun friday13th(y: Int): Int {
+ var f = 0
+ for (m in 1..12) {
+ if (LocalDate.of(y, m, 13).dayOfWeek.getValue() == 5) {
+ f += 1
+ }
+ }
+ return f
+}
+
+fun main() {
+
+ if (friday13th(2023) == 2) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-227/roger-bell-west/kotlin/ch-2.kt b/challenge-227/roger-bell-west/kotlin/ch-2.kt
new file mode 100644
index 0000000000..d3be43c216
--- /dev/null
+++ b/challenge-227/roger-bell-west/kotlin/ch-2.kt
@@ -0,0 +1,223 @@
+fun roman2int(roman: String): Int {
+ if (roman == "") {
+ return 0
+ }
+ for (s in listOf(
+ Pair( "M", 1000 ),
+ Pair( "CM", 900 ),
+ Pair( "D", 500 ),
+ Pair( "CD", 400 ),
+ Pair( "C", 100 ),
+ Pair( "XC", 90 ),
+ Pair( "L", 50 ),
+ Pair( "XL", 40 ),
+ Pair( "X", 10 ),
+ Pair( "IX", 9 ),
+ Pair( "V", 5 ),
+ Pair( "IV", 4 ),
+ Pair( "I", 1 ),
+ )) {
+ if (roman.startsWith(s.first)) {
+ return s.second + roman2int(roman.slice(s.first.length .. roman.length-1))
+ }
+ }
+ return 0
+}
+
+fun int2roman(n0: Int): String {
+ var number = n0
+ var mn = ""
+ for (s in listOf(
+ Pair( "M", 1000 ),
+ Pair( "CM", 900 ),
+ Pair( "D", 500 ),
+ Pair( "CD", 400 ),
+ Pair( "C", 100 ),
+ Pair( "XC", 90 ),
+ Pair( "L", 50 ),
+ Pair( "XL", 40 ),
+ Pair( "X", 10 ),
+ Pair( "IX", 9 ),
+ Pair( "V", 5 ),
+ Pair( "IV", 4 ),
+ Pair( "I", 1 ),
+ )) {
+ while (s.second <= number) {
+ mn += s.first
+ number -= s.second
+ }
+ }
+ return mn
+}
+
+fun intPow(x0: Int,pow0: Int): Int {
+ var x=x0
+ var pow=pow0
+ var ret=1
+ while (pow > 0) {
+ if ( (pow and 1) == 1 ) {
+ ret *= x
+ }
+ x *= x
+ pow = pow shr 1
+ }
+ return ret
+}
+
+fun romanmaths(ax: String): String {
+ val elems = ax.split(" ")
+ val a = roman2int(elems[0])
+ val b = roman2int(elems[2])
+ var c = -1
+ if (elems[1] == "+") {
+ c = a + b
+ } else if (elems[1] == "-") {
+ c = a - b
+ } else if (elems[1] == "*") {
+ c = a * b
+ } else if (elems[1] == "/") {
+ if (a % b == 0) {
+ c = a / b
+ }
+ } else if (elems[1] == "**") {
+ c = intPow(a, b)
+ }
+ if (c > 3999 || c < 0) {
+ return "non potest"
+ } else if (c == 0) {
+ return "nulla"
+ } else {
+ return int2roman(c)
+ }
+}
+
+fun main() {
+
+ if (roman2int("I") == 1) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (roman2int("II") == 2) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (roman2int("IV") == 4) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (roman2int("IX") == 9) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (roman2int("XXX") == 30) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (roman2int("MDCLXVI") == 1666) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(1) == "I") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(2) == "II") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(4) == "IV") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(9) == "IX") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(30) == "XXX") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (int2roman(1666) == "MDCLXVI") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("IV + V") == "IX") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("M - I") == "CMXCIX") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("X / II") == "V") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("XI * VI") == "LXVI") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("VII ** III") == "CCCXLIII") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("V - V") == "nulla") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("V / II") == "non potest") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("MMM + M") == "non potest") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (romanmaths("V - X") == "non potest") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-227/roger-bell-west/lua/ch-1.lua b/challenge-227/roger-bell-west/lua/ch-1.lua
new file mode 100755
index 0000000000..09c6bdcd3d
--- /dev/null
+++ b/challenge-227/roger-bell-west/lua/ch-1.lua
@@ -0,0 +1,37 @@
+#! /usr/bin/lua
+
+function idiv(a0, b)
+ local a = math.abs(a0)
+ local q = a // b
+ if a0 < 0 then
+ q = -q
+ end
+ return q
+end
+
+function ymd2jd(y, m, d)
+ local mn = idiv(m - 14, 12)
+ return idiv((y + 4800 + mn) * 1461, 4) + idiv((m - 2 - 12 * mn) * 367, 12) - idiv(idiv(y + 4900 + mn, 100) * 3, 4) + d - 32075
+end
+
+function jd2dow(jd)
+ return (jd + 1) % 7
+end
+
+function friday13th(y)
+ local f = 0
+ for m = 1, 12 do
+ if jd2dow(ymd2jd(y, m, 13)) == 5 then
+ f = f + 1
+ end
+ end
+ return f
+end
+
+if friday13th(2023) == 2 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-227/roger-bell-west/lua/ch-2.lua b/challenge-227/roger-bell-west/lua/ch-2.lua
new file mode 100755
index 0000000000..75d6ed9884
--- /dev/null
+++ b/challenge-227/roger-bell-west/lua/ch-2.lua
@@ -0,0 +1,240 @@
+#! /usr/bin/lua
+
+function roman2int(roman)
+ if roman == "" then
+ return 0
+ end
+ for _, s in ipairs({
+ { 'M', 1000 },
+ { 'CM', 900 },
+ { 'D', 500 },
+ { 'CD', 400 },
+ { 'C', 100 },
+ { 'XC', 90 },
+ { 'L', 50 },
+ { 'XL', 40 },
+ { 'X', 10 },
+ { 'IX', 9 },
+ { 'V', 5 },
+ { 'IV', 4 },
+ { 'I', 1 }
+ }) do
+ if string.find(roman, s[1], 1, true) == 1 then
+ return s[2] + roman2int(string.sub(roman, #s[1] + 1))
+ end
+ end
+ return 0
+end
+
+function int2roman(n0)
+ local number = n0
+ local mn = ""
+ for _, s in ipairs({
+ { 'M', 1000 },
+ { 'CM', 900 },
+ { 'D', 500 },
+ { 'CD', 400 },
+ { 'C', 100 },
+ { 'XC', 90 },
+ { 'L', 50 },
+ { 'XL', 40 },
+ { 'X', 10 },
+ { 'IX', 9 },
+ { 'V', 5 },
+ { 'IV', 4 },
+ { 'I', 1 }
+ }) do
+ while s[2] <= number do
+ mn = mn .. s[1]
+ number = number - s[2]
+ end
+ end
+ return mn
+end
+
+-- bart at https://stackoverflow.com/questions/1426954/split-string-in-lua
+function split(inputstr, sep)
+ sep=sep or '%s'
+ local t={}
+ for field,s in string.gmatch(inputstr, "([^"..sep.."]*)("..sep.."?)") do
+ table.insert(t,field)
+ if s=="" then
+ return t
+ end
+ end
+end
+
+function romanmaths(ax)
+ local elems = split(ax, " ")
+ local a = roman2int(elems[1])
+ local b = roman2int(elems[3])
+ local c = -1
+ if elems[2] == "+" then
+ c = a + b
+ elseif elems[2] == "-" then
+ c = a - b
+ elseif elems[2] == "*" then
+ c = a * b
+ elseif elems[2] == "/" then
+ if a % b == 0 then
+ c = a // b
+ end
+ elseif elems[2] == "**" then
+ c = a ^ b
+ end
+ if c > 3999 or c < 0 then
+ return "non potest"
+ elseif c == 0 then
+ return "nulla"
+ else
+ return int2roman(c)
+ end
+end
+
+if roman2int("I") == 1 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if roman2int("II") == 2 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if roman2int("IV") == 4 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if roman2int("IX") == 9 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if roman2int("XXX") == 30 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if roman2int("MDCLXVI") == 1666 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(1) == "I" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(2) == "II" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(4) == "IV" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(9) == "IX" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(30) == "XXX" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if int2roman(1666) == "MDCLXVI" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("IV + V") == "IX" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("M - I") == "CMXCIX" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("X / II") == "V" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("XI * VI") == "LXVI" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("VII ** III") == "CCCXLIII" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("V - V") == "nulla" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("V / II") == "non potest" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("MMM + M") == "non potest" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if romanmaths("V - X") == "non potest" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-227/roger-bell-west/perl/ch-1.pl b/challenge-227/roger-bell-west/perl/ch-1.pl
new file mode 100755
index 0000000000..1e51393880
--- /dev/null
+++ b/challenge-227/roger-bell-west/perl/ch-1.pl
@@ -0,0 +1,21 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 1;
+
+is(friday13th(2023), 2, 'example 1');
+
+use Time::Local qw(timegm_modern);
+
+sub friday13th($y) {
+ my $f = 0;
+ foreach my $m (1 .. 12) {
+ if ((gmtime(timegm_modern(0, 0, 12, 13, $m - 1, $y)))[6] == 5 ) {
+ $f++;
+ }
+ }
+ return $f;
+}
diff --git a/challenge-227/roger-bell-west/perl/ch-2.pl b/challenge-227/roger-bell-west/perl/ch-2.pl
new file mode 100755
index 0000000000..9988ccb895
--- /dev/null
+++ b/challenge-227/roger-bell-west/perl/ch-2.pl
@@ -0,0 +1,108 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 21;
+
+is(roman2int('I'), 1, 'example 1');
+is(roman2int('II'), 2, 'example 2');
+is(roman2int('IV'), 4, 'example 3');
+is(roman2int('IX'), 9, 'example 4');
+is(roman2int('XXX'), 30, 'example 5');
+is(roman2int('MDCLXVI'), 1666, 'example 6');
+is(int2roman(1), 'I', 'example 7');
+is(int2roman(2), 'II', 'example 8');
+is(int2roman(4), 'IV', 'example 9');
+is(int2roman(9), 'IX', 'example 10');
+is(int2roman(30), 'XXX', 'example 11');
+is(int2roman(1666), 'MDCLXVI', 'example 12');
+is(romanmaths('IV + V'), 'IX', 'example 13');
+is(romanmaths('M - I'), 'CMXCIX', 'example 14');
+is(romanmaths('X / II'), 'V', 'example 15');
+is(romanmaths('XI * VI'), 'LXVI', 'example 16');
+is(romanmaths('VII ** III'), 'CCCXLIII', 'example 17');
+is(romanmaths('V - V'), 'nulla', 'example 18');
+is(romanmaths('V / II'), 'non potest', 'example 19');
+is(romanmaths('MMM + M'), 'non potest', 'example 20');
+is(romanmaths('V - X'), 'non potest', 'example 21');
+
+sub roman2int($roman) {
+ if ($roman eq '') {
+ return 0;
+ }
+ foreach my $s (
+ [ 'M', 1000 ],
+ [ 'CM', 900 ],
+ [ 'D', 500 ],
+ [ 'CD', 400 ],
+ [ 'C', 100 ],
+ [ 'XC', 90 ],
+ [ 'L', 50 ],
+ [ 'XL', 40 ],
+ [ 'X', 10 ],
+ [ 'IX', 9 ],
+ [ 'V', 5 ],
+ [ 'IV', 4 ],
+ [ 'I', 1 ],
+ ) {
+ if (index($roman, $s->[0]) == 0) {
+ return $s->[1] + roman2int(substr($roman, length($s->[0])));
+ }
+ }
+ return 0;
+}
+
+sub int2roman($n0) {
+ my $number = $n0;
+ my $mn = "";
+ foreach my $s (
+ [ 'M', 1000 ],
+ [ 'CM', 900 ],
+ [ 'D', 500 ],
+ [ 'CD', 400 ],
+ [ 'C', 100 ],
+ [ 'XC', 90 ],
+ [ 'L', 50 ],
+ [ 'XL', 40 ],
+ [ 'X', 10 ],
+ [ 'IX', 9 ],
+ [ 'V', 5 ],
+ [ 'IV', 4 ],
+ [ 'I', 1 ],
+ ) {
+ while ($s->[1] <= $number) {
+ $mn .= $s->[0];
+ $number -= $s->[1];
+ }
+ }
+ return $mn;
+}
+
+sub romanmaths($ax) {
+ my @elems = split ' ',$ax;
+ my $a = roman2int($elems[0]);
+ my $b = roman2int($elems[2]);
+ my $c = -1;
+ if ($elems[1] eq '+') {
+ $c = $a + $b;
+ } elsif ($elems[1] eq '-') {
+ $c = $a - $b;
+ } elsif ($elems[1] eq '*') {
+ $c = $a * $b;
+ } elsif ($elems[1] eq '/') {
+ if ($a % $b == 0) {
+ $c = $a / $b;
+ }
+ } elsif ($elems[1] eq '**') {
+ $c = $a ** $b;
+ }
+ if ($c > 3999 || $c < 0) {
+ return 'non potest';
+ } elsif ($c == 0) {
+ return 'nulla';
+ } else {
+ return int2roman($c);
+ }
+}
diff --git a/challenge-227/roger-bell-west/postscript/ch-1.ps b/challenge-227/roger-bell-west/postscript/ch-1.ps
new file mode 100644
index 0000000000..747c8c6085
--- /dev/null
+++ b/challenge-227/roger-bell-west/postscript/ch-1.ps
@@ -0,0 +1,72 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/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
+
+/ymd2jd {
+ 4 dict begin
+ aload pop
+ /d exch def
+ /m exch def
+ /y exch def
+ /mn m 14 sub 12 idiv def
+ y 4800 add mn add 1461 mul 4 idiv
+ mn 12 mul neg 2 sub m add 367 mul 12 idiv add
+ y 4900 add mn add 100 idiv 3 mul 4 idiv sub
+ d add
+ 32075 sub
+ end
+} 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
+
+/jd2dow {
+ 1 add 7 mod
+} bind def
+
+
+% end included library code
+
+/friday13th {
+ 1 dict begin
+ /y exch def
+ 0
+ 1 1 12 {
+ [ exch y exch 13 ] ymd2jd jd2dow 5 eq {
+ 1 add
+ } if
+ } for
+ end
+} bind def
+
+(friday13th) test.start
+2023 friday13th 2 eq test
+test.end
diff --git a/challenge-227/roger-bell-west/postscript/ch-2.ps b/challenge-227/roger-bell-west/postscript/ch-2.ps
new file mode 100644
index 0000000000..c9a46fb250
--- /dev/null
+++ b/challenge-227/roger-bell-west/postscript/ch-2.ps
@@ -0,0 +1,293 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/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
+
+/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
+
+/deepeq {
+ 2 dict begin
+ /a exch def
+ /b exch def
+ a type b type eq {
+ a type /dicttype eq {
+ a length b length eq {
+ <<
+ a {
+ pop
+ true
+ } forall
+ b {
+ pop
+ true
+ } forall
+ >>
+ true exch
+ {
+ pop
+ dup a exch known {
+ dup b exch known {
+ dup a exch get exch b exch get deepeq not {
+ pop false
+ } if
+ } {
+ false
+ } ifelse
+ } {
+ false
+ } ifelse
+ } forall
+ } {
+ false
+ } ifelse
+ } {
+ a type dup /arraytype eq exch /stringtype eq or {
+ a length b length eq {
+ true
+ 0 1 a length 1 sub {
+ dup a exch get exch b exch get deepeq not {
+ pop false
+ exit
+ } if
+ } for
+ } {
+ false
+ } ifelse
+ } {
+ a b eq
+ } ifelse
+ } ifelse
+ } {
+ false
+ } ifelse
+ end
+} bind def
+
+/strconcat % (a) (b) -> (ab)
+{ exch dup length
+ 2 index length add string
+ dup dup 4 2 roll copy length
+ 4 -1 roll putinterval
+} bind def
+
+/