aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Bell_West <roger@firedrake.org>2022-05-23 09:12:11 +0100
committerRoger Bell_West <roger@firedrake.org>2022-05-23 09:12:11 +0100
commitbff4843d39dd97ab2b8616da4c32e67438148744 (patch)
tree6ad8602f4a6d3c7978663f7ea17b3d4673ace8ef
parent1931aa4dfae6e683c1e5fd85c453f1440d9de568 (diff)
downloadperlweeklychallenge-club-bff4843d39dd97ab2b8616da4c32e67438148744.tar.gz
perlweeklychallenge-club-bff4843d39dd97ab2b8616da4c32e67438148744.tar.bz2
perlweeklychallenge-club-bff4843d39dd97ab2b8616da4c32e67438148744.zip
Solutions for challenge #166
-rwxr-xr-xchallenge-166/roger-bell-west/javascript/ch-1.js134
-rwxr-xr-xchallenge-166/roger-bell-west/javascript/ch-2.js59
-rw-r--r--challenge-166/roger-bell-west/kotlin/ch-1.kt133
-rw-r--r--challenge-166/roger-bell-west/kotlin/ch-2.kt55
-rwxr-xr-xchallenge-166/roger-bell-west/lua/ch-1.lua144
-rwxr-xr-xchallenge-166/roger-bell-west/perl/ch-1.pl98
-rwxr-xr-xchallenge-166/roger-bell-west/perl/ch-2.pl59
-rw-r--r--challenge-166/roger-bell-west/postscript/ch-1.ps263
-rw-r--r--challenge-166/roger-bell-west/postscript/ch-2.ps206
-rwxr-xr-xchallenge-166/roger-bell-west/python/ch-1.py87
-rwxr-xr-xchallenge-166/roger-bell-west/python/ch-2.py39
-rwxr-xr-xchallenge-166/roger-bell-west/raku/ch-1.p679
-rwxr-xr-xchallenge-166/roger-bell-west/raku/ch-2.p651
-rwxr-xr-xchallenge-166/roger-bell-west/ruby/ch-1.rb103
-rwxr-xr-xchallenge-166/roger-bell-west/ruby/ch-2.rb54
-rw-r--r--challenge-166/roger-bell-west/rust/ch-1.rs97
-rwxr-xr-xchallenge-166/roger-bell-west/rust/ch-2.rs75
17 files changed, 1736 insertions, 0 deletions
diff --git a/challenge-166/roger-bell-west/javascript/ch-1.js b/challenge-166/roger-bell-west/javascript/ch-1.js
new file mode 100755
index 0000000000..6e8f662d3a
--- /dev/null
+++ b/challenge-166/roger-bell-west/javascript/ch-1.js
@@ -0,0 +1,134 @@
+#! /usr/bin/node
+
+function hexwords(lo,hi,sb) {
+ const fs = require("fs")
+ let out = []
+ let buffer = fs.readFileSync("dictionary.txt","utf8")
+ buffer.split(/\r?\n/).forEach(line => {
+ if (line.length >= lo && line.length <= hi) {
+ let valid = true
+ let sbc = 0
+ for (let c of line.split("") ) {
+ if (c == 'o' || c == 'i' || c == 'l' || c == 's' || c == 't') {
+ sbc++
+ if (sbc > sb) {
+ valid = false
+ }
+ } else if (c < 'a' || c > 'f') {
+ valid = false
+ }
+ if (!valid) {
+ break
+ }
+ }
+ if (valid) {
+ out.push(line)
+ }
+ }
+ })
+ return out
+}
+
+function combiwords(wl,l) {
+ let wh = new Map()
+ for (let w of wl) {
+ let lw = w.length
+ if (!wh.has(lw)) {
+ wh.set(lw,[])
+ }
+ let t = wh.get(lw)
+ t.push(w)
+ wh.set(lw,t)
+ }
+ let tmap = [[]]
+ let omap = []
+ while (tmap.length > 0) {
+ let c = tmap.pop()
+ let s = c.reduce((x,y) => x+y, 0)
+ for (let j=1; j < l-s; j++) {
+ if (wh.has(j)) {
+ let cc = [...c]
+ cc.push(j)
+ tmap.push(cc)
+ }
+ }
+ if (wh.has(l-s)) {
+ c.push(l-s)
+ omap.push(c)
+ }
+ }
+ let out = []
+ for (let pat of omap) {
+ let c = []
+ let cm = []
+ for (let i of pat) {
+ cm.push(wh.get(i).length-1)
+ c.push(0)
+ }
+ let ex = false
+ while (!ex) {
+ let o = []
+ for (let i = 0; i < c.length; i++) {
+ o.push(wh.get(pat[i])[c[i]])
+ }
+ out.push(o.join(""))
+ let ss = c.length-1
+ while (true) {
+ c[ss]++
+ if (c[ss] > cm[ss]) {
+ if (ss == 0) {
+ ex = true
+ break
+ }
+ c[ss] = 0
+ ss--
+ } else {
+ break
+ }
+ }
+ }
+ }
+ return out
+}
+
+if (hexwords(2,8,8).length == 1463) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write(" ");
+
+if (hexwords(8,8,8).length == 164) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write(" ");
+
+if (hexwords(2,8,0).length == 45) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write(" ");
+
+if (hexwords(2,8,1).length == 244) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write(" ");
+
+if (combiwords(hexwords(4,4,0),8).length == 144) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write(" ");
+
+if (combiwords(hexwords(3,5,0),8).length == 274) {
+ process.stdout.write("Pass")
+} else {
+ process.stdout.write("FAIL")
+}
+process.stdout.write("\n");
diff --git a/challenge-166/roger-bell-west/javascript/ch-2.js b/challenge-166/roger-bell-west/javascript/ch-2.js
new file mode 100755
index 0000000000..7380afa1e0
--- /dev/null
+++ b/challenge-166/roger-bell-west/javascript/ch-2.js
@@ -0,0 +1,59 @@
+#! /usr/bin/node
+
+function kdd(dirlist0) {
+ const fs = require("fs")
+ let dirlist = Array.from(dirlist0)
+ dirlist.sort()
+ let fx = new Map()
+ for (let d of dirlist) {
+ for (let entry of fs.readdirSync(d, { withFileTypes: true })) {
+ let nn = entry.name
+ if (entry.isDirectory()) {
+ nn += "/"
+ }
+ if (!fx.has(nn)) {
+ fx.set(nn,new Set())
+ }
+ let t = fx.get(nn)
+ t.add(d)
+ fx.set(nn,t)
+ }
+ }
+ let mm = dirlist.length
+ let out = [dirlist]
+ for (let f of [...fx.keys()].sort()) {
+ if (fx.get(f).size != mm) {
+ let l = []
+ for (let d of dirlist) {
+ if (fx.get(f).has(d)) {
+ l.push(f)
+ } else {
+ l.push("")
+ }
+ }
+ out.push(l)
+ }
+ }
+ tabular(out)
+}
+
+function tabular(d) {
+ let columnlength = []
+ d.forEach(row => {
+ row.forEach((col, i) => {
+ while (columnlength.length <= i) {
+ columnlength.push(0)
+ }
+ columnlength[i] = Math.max(columnlength[i], col.length)
+ })
+ })
+ d.forEach(row => {
+ let s = []
+ row.forEach((col, i) => {
+ s.push(col.padEnd(columnlength[i]))
+ })
+ console.log(s.join(" | "))
+ })
+}
+
+kdd(["dir_a","dir_b","dir_c"])
diff --git a/challenge-166/roger-bell-west/kotlin/ch-1.kt b/challenge-166/roger-bell-west/kotlin/ch-1.kt
new file mode 100644
index 0000000000..c391716661
--- /dev/null
+++ b/challenge-166/roger-bell-west/kotlin/ch-1.kt
@@ -0,0 +1,133 @@
+import kotlin.io.*
+
+fun hexwords(lo: Int, hi: Int, sb: Int): List<String> {
+ var out = ArrayList<String>()
+ java.io.File("dictionary.txt").forEachLine { line ->
+ if (line.length >= lo && line.length <= hi) {
+ var valid = true
+ var sbc = 0
+ for (c in line.toCharArray().toList()) {
+ if (c == 'o' || c == 'i' || c == 'l' || c == 's' || c == 't') {
+ sbc += 1
+ if (sbc > sb) {
+ valid = false
+ }
+ } else if (c < 'a' || c > 'f') {
+ valid = false
+ }
+ if (!valid) {
+ break
+ }
+ }
+ if (valid) {
+ out.add(line)
+ }
+ }
+ }
+ return out
+}
+
+fun combiwords(wl: List<String>, l: Int): List<String> {
+ var wh = mutableMapOf<Int,ArrayList<String>>()
+ for (w in wl) {
+ if (!wh.containsKey(w.length)) {
+ wh[w.length] = ArrayList<String>()
+ }
+ wh[w.length]!!.add(w)
+ }
+ var tmap = ArrayList<ArrayList<Int>>()
+ tmap.add(ArrayList<Int>())
+ var omap = ArrayList<ArrayList<Int>>()
+ while (tmap.size > 0) {
+ var c = tmap.removeLast()
+ val s = c.sum()
+ val ls = l - s
+ for (j in 1..ls-1) {
+ if (wh.containsKey(j)) {
+ var cc = ArrayList(c)
+ cc.add(j)
+ tmap.add(cc)
+ }
+ }
+ if (wh.containsKey(ls)) {
+ c.add(ls)
+ omap.add(c)
+ }
+ }
+ var out = ArrayList<String>()
+ for (pat in omap) {
+ var c = ArrayList<Int>()
+ var cm = ArrayList<Int>()
+ for (i in pat) {
+ cm.add(wh[i]!!.size-1)
+ c.add(0)
+ }
+ var ex = false
+ while (!ex) {
+ var o = ArrayList<String>()
+ for (i in 0..c.size-1) {
+ o.add(wh[pat[i]]!![c[i]])
+ }
+ out.add(o.joinToString(""))
+ var ss = c.size-1
+ while (true) {
+ c[ss] += 1
+ if (c[ss] > cm[ss]) {
+ if (ss == 0) {
+ ex = true
+ break
+ }
+ c[ss] = 0
+ ss--
+ } else {
+ break
+ }
+ }
+ }
+ }
+ return out
+}
+
+fun main() {
+ if (hexwords(2,8,8).size == 1463) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ print(" ")
+
+ if (hexwords(8,8,8).size == 164) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ print(" ")
+
+ if (hexwords(2,8,0).size == 45) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ print(" ")
+
+ if (hexwords(2,8,1).size == 244) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ print(" ")
+
+ if (combiwords(hexwords(4,4,0),8).size == 144) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ print(" ")
+
+ if (combiwords(hexwords(3,5,0),8).size == 274) {
+ print("Pass")
+ } else {
+ print("FAIL")
+ }
+ println("")
+}
diff --git a/challenge-166/roger-bell-west/kotlin/ch-2.kt b/challenge-166/roger-bell-west/kotlin/ch-2.kt
new file mode 100644
index 0000000000..b69f6eb44f
--- /dev/null
+++ b/challenge-166/roger-bell-west/kotlin/ch-2.kt
@@ -0,0 +1,55 @@
+import kotlin.io.*
+import kotlin.math.max
+
+fun kdd(dirlist0: ArrayList<String>): ArrayList<ArrayList<String>> {
+ val dirlist = ArrayList(dirlist0.sorted())
+ var fx = mutableMapOf<String,MutableSet<String>>()
+ for (d in dirlist) {
+ java.io.File(d).listFiles().forEach() { entry ->
+ var nn = entry.getName()
+ if (entry.isDirectory()) {
+ nn += "/"
+ }
+ if (!fx.containsKey(nn)) {
+ fx[nn] = mutableSetOf<String>()
+ }
+ fx[nn]!!.add(d)
+ }
+ }
+ val mm = dirlist.size
+ var out = arrayListOf(dirlist)
+ for (f in fx.keys.sorted()) {
+ if (fx[f]!!.size != mm) {
+ var l = arrayListOf<String>()
+ for (d in dirlist) {
+ if (fx[f]!!.contains(d)) {
+ l.add(f)
+ } else {
+ l.add("")
+ }
+ }
+ out.add(l)
+ }
+ }
+ return(out)
+}
+
+fun tabular(d: ArrayList<ArrayList<String>>) {
+ var columnlength = ArrayList<Int>()
+ for (row in d) {
+ row.forEachIndexed {i,col ->
+ while (columnlength.size <= i) {
+ columnlength.add(0)
+ }
+ columnlength[i] = max(columnlength[i],col.length)
+ }
+ }
+ val fstr=columnlength.map {l -> "%-" + l.toString() + "s"}.joinToString(" | ")
+ for (row in d) {
+ println(fstr.format(*row.toTypedArray()))
+ }
+}
+
+fun main() {
+ tabular(kdd(arrayListOf<String>("dir_a","dir_b","dir_c")))
+}
diff --git a/challenge-166/roger-bell-west/lua/ch-1.lua b/challenge-166/roger-bell-west/lua/ch-1.lua
new file mode 100755
index 0000000000..4b7858db3c
--- /dev/null
+++ b/challenge-166/roger-bell-west/lua/ch-1.lua
@@ -0,0 +1,144 @@
+#! /usr/bin/lua
+
+function join(t)
+ local out=""
+ for k,v in pairs(t) do
+ out = out .. v
+ end
+ return out
+end
+
+function hexwords(lo,hi,sb)
+ local out = {}
+ for line in io.lines("dictionary.txt") do
+ if #line >= lo and #line <= hi then
+ local valid = true
+ local sbc = 0
+ for i = 1,#line do
+ local c = string.sub(line,i,i)
+ if c == "o" or c == "i" or c == "l" or c == "s" or c == "t" then
+ sbc = sbc + 1
+ if sbc > sb then
+ valid = false
+ end
+ elseif c < "a" or c > "f" then
+ valid = false
+ end
+ if not valid then
+ break
+ end
+ end
+ if valid then
+ table.insert(out,line)
+ end
+ end
+ end
+ return out
+end
+
+function combiwords(wl,l)
+ local wh = {}
+ for k,w in pairs(wl) do
+ if wh[#w] == nil then
+ wh[#w] = {}
+ end
+ table.insert(wh[#w],w)
+ end
+ local tmap = { {} }
+ local omap = {}
+ while #tmap > 0 do
+ local c = table.remove(tmap)
+ local s = 0
+ for k,x in pairs(c) do
+ s = s + x
+ end
+ for j = 1, l-s-1 do
+ if wh[j] ~= nil then
+ local cc = {}
+ for k,x in pairs(c) do
+ table.insert(cc,x)
+ end
+ table.insert(cc,j)
+ table.insert(tmap,cc)
+ end
+ end
+ if wh[l-s] ~= nil then
+ table.insert(c,l-s)
+ table.insert(omap,c)
+ end
+ end
+ local out = {}
+ for k,pat in pairs(omap) do
+ local c = {}
+ local cm = {}
+ for kk,i in pairs(pat) do
+ table.insert(cm,#wh[i])
+ table.insert(c,1)
+ end
+ local ex = false
+ while not ex do
+ local o = {}
+ for i = 1,#c do
+ table.insert(o,wh[pat[i]][c[i]])
+ end
+ table.insert(out,join(o,""))
+ local ss = #c
+ while true do
+ c[ss] = c[ss] + 1
+ if c[ss] > cm[ss] then
+ if ss == 1 then
+ ex = true
+ break
+ end
+ c[ss] = 1
+ ss = ss - 1
+ else
+ break
+ end
+ end
+ end
+ end
+ return out
+end
+
+if #hexwords(2,8,8) == 1463 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if #hexwords(8,8,8) == 164 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if #hexwords(2,8,0) == 45 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if #hexwords(2,8,1) == 244 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if #combiwords(hexwords(4,4,0),8) == 144 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if #combiwords(hexwords(3,5,0),8) == 274 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
diff --git a/challenge-166/roger-bell-west/perl/ch-1.pl b/challenge-166/roger-bell-west/perl/ch-1.pl
new file mode 100755
index 0000000000..a87a976310
--- /dev/null
+++ b/challenge-166/roger-bell-west/perl/ch-1.pl
@@ -0,0 +1,98 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+use experimental 'signatures';
+
+use List::Util qw(sum0);
+
+use Test::More tests => 6;
+
+is(scalar @{hexwords(2,8,8)},1463,'example 1');
+
+is(scalar @{hexwords(8,8,8)},164,'example 2');
+
+is(scalar @{hexwords(2,8,0)},45,'example 3');
+
+is(scalar @{hexwords(2,8,1)},244,'example 4');
+
+is(scalar @{combiwords(hexwords(4,4,0),8)},144,'example 5');
+
+is(scalar @{combiwords(hexwords(3,5,0),8)},274,'example 6');
+
+sub hexwords($lo,$hi,$sb) {
+ my @out;
+ open my $fh,'<','dictionary.txt';
+ while (my $line = <$fh>) {
+ chomp $line;
+ if (length($line) >= $lo && length($line) <= $hi) {
+ my $valid = 1;
+ my $sbc = 0;
+ foreach my $c (split '',$line) {
+ if ($c =~ /[oilst]/) {
+ $sbc++;
+ if ($sbc > $sb) {
+ $valid = 0;
+ }
+ } elsif ($c lt 'a' || $c gt 'f') {
+ $valid = 0;
+ }
+ unless ($valid) {
+ last;
+ }
+ }
+ if ($valid) {
+ push @out,$line;
+ }
+ }
+ }
+ close $fh;
+ return \@out;
+}
+
+sub combiwords($wl,$l) {
+ my %wh;
+ foreach my $w (@{$wl}) {
+ push @{$wh{length($w)}},$w;
+ }
+ my @tmap=([]);
+ my @omap;
+ while (scalar @tmap > 0) {
+ my $c=pop @tmap;
+ my $s=sum0(@{$c});
+ my $ls=$l-$s;
+ foreach my $j (1..$ls-1) {
+ if (exists $wh{$j}) {
+ push @tmap,[@{$c},$j];
+ }
+ }
+ if (exists $wh{$ls}) {
+ push @omap,[@{$c},$ls];
+ }
+ }
+ my @out;
+ foreach my $pat (@omap) {
+ my @c = (0) x (scalar @{$pat});
+ my @cm = map {scalar @{$wh{$pat->[$_]}}-1} (0..$#c);
+ my $ex = 0;
+ while (!$ex) {
+ push @out,join('',map {$wh{$pat->[$_]}->[$c[$_]]} (0..$#c));
+ my $ss = $#c;
+ while (1) {
+ $c[$ss]++;
+ if ($c[$ss] > $cm[$ss]) {
+ if ($ss == 0) {
+ $ex = 1;
+ last;
+ }
+ $c[$ss] = 0;
+ $ss--;
+ } else {
+ last;
+ }
+ }
+ }
+ }
+ return \@out;
+}
diff --git a/challenge-166/roger-bell-west/perl/ch-2.pl b/challenge-166/roger-bell-west/perl/ch-2.pl
new file mode 100755
index 0000000000..d5e7ea52e6
--- /dev/null
+++ b/challenge-166/roger-bell-west/perl/ch-2.pl
@@ -0,0 +1,59 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+use experimental 'signatures';
+
+sub kdd(@dirlist0) {
+ my @dirlist = sort @dirlist0;
+ my %fx;
+ foreach my $d (@dirlist) {
+ opendir (my $dh,$d);
+ foreach my $entry (grep !/^\./,readdir $dh) {
+ my $nn = $entry;
+ if (-d "$d/$entry") {
+ $nn .= '/';
+ }
+ $fx{$nn}{$d} = 1;
+ }
+ closedir $dh;
+ }
+ my $mm=scalar @dirlist;
+ my @out=(\@dirlist);
+ foreach my $f (sort keys %fx) {
+ unless (scalar keys %{$fx{$f}} == $mm) {
+ my @l;
+ foreach my $d (@dirlist) {
+ if (exists $fx{$f}{$d}) {
+ push @l,$f;
+ } else {
+ push @l,'';
+ }
+ }
+ push @out,\@l;
+ }
+ }
+ return \@out;
+}
+
+
+sub tabular($d) {
+ my @columnlength;
+ foreach my $row (@{$d}) {
+ foreach my $colno (0..$#{$row}) {
+ if (!defined($columnlength[$colno]) ||
+ $columnlength[$colno] < length($row->[$colno])) {
+ $columnlength[$colno]=length($row->[$colno]);
+ }
+ }
+ }
+ my $format=join(' | ',map {"%-${_}s"} @columnlength);
+ my $result='';
+ foreach my $row (@{$d}) {
+ $result .= sprintf($format,@{$row})."\n";
+ }
+ return $result;
+}
+
+print tabular(kdd("dir_a","dir_b","dir_c"));
diff --git a/challenge-166/roger-bell-west/postscript/ch-1.ps b/challenge-166/roger-bell-west/postscript/ch-1.ps
new file mode 100644
index 0000000000..3fed3b9fe5
--- /dev/null
+++ b/challenge-166/roger-bell-west/postscript/ch-1.ps
@@ -0,0 +1,263 @@
+%!PS
+
+/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
+
+/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
+
+/apush.right { % [a b] c -> [a b c]
+ exch
+ [ exch aload length 2 add -1 roll ]
+} bind def
+
+/apop.right { % [a b c] -> [a b] c
+ [ exch aload length 1 add 1 roll ] exch
+} bind def
+
+/keys { % dict -> array of dict keys
+ [ exch
+ {
+ pop
+ } forall
+ ]
+} bind def
+
+/filter { % array proc(bool) -> array
+ 1 dict begin
+ /p exch def
+ [ exch
+ {
+ dup p not
+ {
+ pop
+ } if
+ } forall
+ ]
+ end
+} bind def
+
+/reduce_init { % value array proc -> value
+ 2 dict begin
+ /p exch def
+ /a exch def
+ 0 1 a length 1 sub {
+ a exch get
+ p
+ } for
+ 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
+
+/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
+
+/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
+
+/cartesianproduct { % [ [ a b ] [ c d ] ] -> [ [ a c ] [ a d ] [ b c ] [ b d ] ]
+ 5 dict begin
+ /pat exch def
+ /c [ pat length { 0 } repeat ] def
+ /cm [ pat { length 1 sub } forall ] def
+ /ex false def
+ [
+ {
+ ex {
+ exit
+ } if
+ [
+ 0 1 c length 1 sub {
+ /i exch def
+ pat i get c i get get
+ } for
+ ]
+ /ss c length 1 sub def
+ {
+ c ss c ss get 1 add put
+ c ss get cm ss get gt {
+ ss 0 eq {
+ /ex true def
+ exit
+ } if
+ c ss 0 put
+ /ss ss 1 sub def
+ } {
+ exit
+ } ifelse
+ } loop
+ } loop
+ end
+ ]
+} bind def
+
+/hexwords {
+ 7 dict begin
+ /sb exch def
+ /hi exch def
+ /lo exch def
+ /abcdef (abcdef) def
+ /oilst (oilst) def
+ [
+ (dictionary.txt) (r) file dup bytesavailable string readstring pop
+ (\n) strsplit {
+ dup length dup lo ge exch hi le and {
+ /valid true def
+ /sbc 0 def
+ dup {
+ (.) dup 0 3 index put exch pop
+ dup oilst exch search {
+ pop pop pop pop
+ /sbc sbc 1 add def
+ sbc sb gt {
+ /valid false def
+ } if
+ } {
+ pop
+ abcdef exch search {
+ pop pop pop
+ } {
+ pop
+ /valid false def
+ } ifelse
+ } ifelse
+ valid not {
+ exit
+ } if
+ } forall
+ valid not {
+ pop
+ } if
+ } {
+ pop
+ } ifelse
+ } forall
+ ]
+ end
+} bind def
+
+/combiwords {
+ 7 dict begin
+ /l exch def
+ /wh 0 dict def
+ {
+ dup length /lx exch def
+ wh lx known not {
+ wh lx 0 array put
+ } if
+ wh lx get exch apush.right wh exch lx exch put
+ } forall
+ /tmap [ 0 array ] def
+ /omap 0 array def
+ {
+ tmap length 0 eq {
+ exit
+ } if
+ tmap apop.right /c exch def /tmap exch def
+ /s 0 c { add } reduce_init def
+ 1 1 l s sub {
+ /j exch def
+ wh j known {
+ /tmap tmap c j apush.right apush.right def
+ } if
+ } for
+ wh l s sub known {
+ /omap omap c l s sub apush.right apush.right def
+ } if
+ } loop
+ [
+ omap {
+ [ exch
+ { wh exch get } forall
+ ]
+ cartesianproduct
+ {
+ () strjoin
+ } forall
+ } forall
+ ]
+ end
+} bind def
+
+(hexwords) test.start
+2 8 8 hexwords length 1463 eq test
+8 8 8 hexwords length 164 eq test
+2 8 0 hexwords length 45 eq test
+2 8 1 hexwords length 244 eq test
+test.end
+
+(combiwords) test.start
+4 4 0 hexwords 8 combiwords length 144 eq test
+3 5 0 hexwords 8 combiwords length 274 eq test
+test.end
diff --git a/challenge-166/roger-bell-west/postscript/ch-2.ps b/challenge-166/roger-bell-west/postscript/ch-2.ps
new file mode 100644
index 0000000000..46dac29cb2
--- /dev/null
+++ b/challenge-166/roger-bell-west/postscript/ch-2.ps
@@ -0,0 +1,206 @@
+%! PS
+
+/quicksort { % [ a c b ] -> [ a b c ]
+ 1 dict begin
+ /arr exch def
+ 0 arr length 1 sub quicksort.main
+ arr
+ end
+} bind def
+
+/quicksort.main { % lo hi -> (null)
+ 3 dict begin
+ /hi exch def
+ /lo exch def
+ /xit false def
+ lo 0 lt {
+ /xit true def
+ } if
+ hi 0 lt {
+ /xit true def
+ } if
+ lo hi ge {
+ /xit true def
+ } if
+ xit not {
+ /p quicksort.partition def
+ lo p quicksort.main
+ p 1 add hi quicksort.main
+ } if
+ end
+} bind def
+
+/quicksort.partition {
+ 3 dict begin
+ /pivot arr hi lo add 2 idiv get def
+ /i lo 1 sub def
+ /j hi 1 add def
+ {
+ {
+ /i i 1 add def
+ arr i get pivot ge {
+ exit
+ } if
+ } loop
+ {
+ /j j 1 sub def
+ arr j get pivot le {
+ exit
+ } if
+ } loop
+ i j ge {
+ j
+ exit
+ } if
+ i j quicksort.swap
+ } loop
+ end
+} bind def
+
+/quicksort.swap {
+ 2 dict begin
+ /bi exch def
+ /ai exch def
+ arr ai get
+ arr bi get
+ arr exch ai exch put