diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@
 eg/bits
 eg/peano
 eg/these
+eg/tuple
 bench/carry-on
 bench/strategies
 bench/self
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,7 @@
   eg/bits \
   eg/peano \
   eg/these \
+  eg/tuple \
   bench/candidates \
   bench/redundants \
   bench/erroneous \
diff --git a/TODO.md b/TODO.md
--- a/TODO.md
+++ b/TODO.md
@@ -3,119 +3,92 @@
 
 A non-exhaustive list of things TO DO for Conjure.
 
-* Better error reporting when `Listable` is out-of-scope when using `deriveConjurable`.
-  This needs to be implemented on LeanCheck itself.
+* Warn when there are no tests!
+  Test this on high order functions.
+  Encode `error "could not reify specification"`
 
-* forbid recursion into negatives
+* change cannot conjure to "search exhausted",
+  to indicate that Conjure has _shown_ that
+  there is no way to build the given function
+  with the given ingredients up to the given size.
 
-* consider the size of patterns to thin-out each size partition
+* Expand tests on `conjurableOK`?
 
-* consider non top-level cases
+* Add `test/conjure.hs` with some basic conjuring...
 
+* Fix bug with pairwise in eg/tuple
 
-## Forbid recursion into negatives
+* Allow timeout setting?
 
-Instead of reporting:
+* Rethink Conjurable typeclass?
 
-	tri 1  =  1
-	tri x  =  x + tri (x - 1)
+* Rename primitives to ingredients?
 
-Report:
+* Allow chains of guards (see below).
 
-	tri 1          =  1
-	tri x | x > 1  =  x + tri (x - 1)
+* Move `Args` into `[Prim]`?
 
-This is not trivial to implement.
-Tentative steps:
+* Better error reporting when `Listable` is out-of-scope when using `deriveConjurable`.
+  This needs to be implemented on LeanCheck itself.
 
-1. create a `descents` function, similar to `descends`,
-   that is able to list the groups of variable that have recursive descents.
-   This will somehow need a dummy `isDecOf` function.
-   This may require changing the format definitions themselves...
-2. use this on `showDefn` somehow
-3. use this on `toDynamicWithDefn` somehow
+* forbid recursion into negatives (see below)
 
+* Warn when no tests are present somehow?
 
-## Thin-out size partitions
 
-Consider the following two candidates:
+## Allow chains of guards
 
-	fib01 x y z  =  dec x
+With the use of `guard` right now,
+Conjure can generate functions such as the following:
 
-	fib01 x y 0  =  x
-	fib01 x y z  =  y
+	function x y z
+	  | x < 123    =  ...
+	  | otherwise  =  ...
 
-They both currently have size 2.
-Perhaps the second should be bigger than the first?
-"Simple" solution:
-consider the number of (extra) patterns as part of the size.
+We should probably allow chains of guards such as the following:
 
-See the following two candidates of size 3:
+	function x y z
+	  | x < 123    =  ...
+	  | x == 321   =  ...
+	  | y == 12    =  ...
+	  | otherwise  =  ...
 
-	fib01 x y z  =  x + x
+Internally, these are just chains of if-then-else applications:
 
-	fib01 x y 0  =  x
-	fib01 x y z  =  dec x
+	function x y z  =  if x < 123
+	                   then ...
+					   else if x == 321
+					   then ...
+					   else if y == 12
+					   then ...
+					   else ...
 
-The same thing can be said.
+This change shouldn't be so complicated to introduce
+requiring a change in `enumerateAppsFor` relaxing `ufor hx`
+depending on what we have on the left...
 
-Now a little bit more complex...
-The following two candidates appear at size 3 for fib01:
 
-	fib01 x 0 y  =  x
-	fib01 x y 0  =  y
-	fib01 x y z  =  0
-
-	fib01 x 0 0  =  x
-	fib01 x 0 y  =  y
-	fib01 x y z  =  0
-
-Shouldn't these two have different size?
-The second feels bigger than the first.
-Maybe the size of non-variable patterns should be taken into account.
-
-This relates to the eg/fib01 example.
-
-Also:
-see the allowed patterns on the Int functions of two arguments
-in bench/candidates.txt.  Search for "allowed patterns".
-
-There are two levels here:
-
-1. considering the number of patterns as part of the size
-2. considering the number of non-variable LHS arguments as part of the size
-
-Level 2. is not addressed at all.  But it turns out that level 1. is already
-handled at the conjurePatterns function.  The patterns reported with showPatterns
-and on bench/candidates already seem to be divided considering the number of
-lines...
-
-Something to keep in mind: tiers enumerations have size "0".
-If one wants to increase size, one needs to push things to size "1".
-So the size is actually being considered.  One would need a delayedProducts to
-complete "1.".  Maybe it is better to start investigating from "2."
-
-The enumeration currently works like so:
-the size of patterns is defined by the number of patterns that are allowed to
-have _another_ option other than a simple variable.
-Perhaps these should be thinned-out in post processing to achieve "2."?
+## Forbid recursion into negatives
 
+Instead of reporting:
 
-## Non top-level cases
+	tri 1  =  1
+	tri x  =  x + tri (x - 1)
 
-Consider allowing non top-level cases,
-so that functions like the following are reachable:
+Report:
 
-	last []  =  undefined
-	last [x]  =  x
-	last (x:y:xs)  =  last (y:xs)
+	tri 1          =  1
+	tri x | x > 1  =  x + tri (x - 1)
 
-Perhaps the negative runtime impact would not be so great
-given the delayed tiers enumeration,
-and we would avoid some `prif` hacks
-needed to conjure some functions.
+This is not trivial to implement.
+Tentative steps:
 
-The one downside is that this would take a few days of work to implement.
+1. create a `descents` function, similar to `descends`,
+   that is able to list the groups of variable that have recursive descents.
+   This will somehow need a dummy `isDecOf` function.
+   This may require changing the format definitions themselves...
+2. use this on `showDefn` somehow
+3. use this on `toDynamicWithDefn` somehow
 
 
 This file is part of Conjure,
diff --git a/bench/gps.txt b/bench/gps.txt
--- a/bench/gps.txt
+++ b/bench/gps.txt
@@ -339,7 +339,7 @@
 gps18 xs ys  =  zipWith (+) xs ys
 
 gps19 :: Int -> [Char] -> [Char]
--- testing 2 combinations of argument values
+-- testing 3 combinations of argument values
 -- pruning with 0/0 rules
 -- 1 candidates of size 1
 -- 0 candidates of size 2
diff --git a/bench/gps2.hs b/bench/gps2.hs
--- a/bench/gps2.hs
+++ b/bench/gps2.hs
@@ -3,10 +3,6 @@
 -- Copyright (C) 2021-2025 Rudy Matela
 -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
 {-# LANGUAGE CPP, TemplateHaskell #-}
-#if __GLASGOW_HASKELL__ <= 710
-{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}
-import Data.Typeable
-#endif
 import Conjure
 import System.Environment (getArgs)
 
@@ -665,15 +661,7 @@
               |  Empty
               deriving (Eq, Show)
 
-#if __GLASGOW_HASKELL__ <= 710
-deriving instance Typeable Twitter
-
-instance Express Twitter where
-  expr (Tweet n)  =  value "Tweet" Tweet :$ expr n
-  expr t  =  val t
-#else
 deriveExpress  ''Twitter
-#endif
 deriveListable ''Twitter
 deriveName     ''Twitter
 
diff --git a/bench/ill-hit.hs b/bench/ill-hit.hs
--- a/bench/ill-hit.hs
+++ b/bench/ill-hit.hs
@@ -2,7 +2,7 @@
 --
 -- Based on an example sent by Colin Runciman
 --
--- Even though sum' is defined for 6 prims,
+-- Even though sum' is defined with 6 equations,
 -- Conjure only ever hits 4 of them.
 import Conjure
 
diff --git a/bench/runtime/lapmatrud/bench/candidates.runtime b/bench/runtime/lapmatrud/bench/candidates.runtime
--- a/bench/runtime/lapmatrud/bench/candidates.runtime
+++ b/bench/runtime/lapmatrud/bench/candidates.runtime
@@ -1,1 +1,1 @@
-9.4
+9.2
diff --git a/bench/runtime/lapmatrud/bench/erroneous.runtime b/bench/runtime/lapmatrud/bench/erroneous.runtime
--- a/bench/runtime/lapmatrud/bench/erroneous.runtime
+++ b/bench/runtime/lapmatrud/bench/erroneous.runtime
@@ -1,1 +1,1 @@
-2.8
+2.9
diff --git a/bench/runtime/lapmatrud/bench/gps.runtime b/bench/runtime/lapmatrud/bench/gps.runtime
--- a/bench/runtime/lapmatrud/bench/gps.runtime
+++ b/bench/runtime/lapmatrud/bench/gps.runtime
@@ -1,1 +1,1 @@
-14.2
+14.7
diff --git a/bench/runtime/lapmatrud/bench/gps2.runtime b/bench/runtime/lapmatrud/bench/gps2.runtime
--- a/bench/runtime/lapmatrud/bench/gps2.runtime
+++ b/bench/runtime/lapmatrud/bench/gps2.runtime
@@ -1,1 +1,1 @@
-12.1
+11.8
diff --git a/bench/runtime/lapmatrud/bench/ill-hit.runtime b/bench/runtime/lapmatrud/bench/ill-hit.runtime
--- a/bench/runtime/lapmatrud/bench/ill-hit.runtime
+++ b/bench/runtime/lapmatrud/bench/ill-hit.runtime
@@ -1,1 +1,1 @@
-0.4
+0.5
diff --git a/bench/runtime/lapmatrud/bench/lowtests.runtime b/bench/runtime/lapmatrud/bench/lowtests.runtime
--- a/bench/runtime/lapmatrud/bench/lowtests.runtime
+++ b/bench/runtime/lapmatrud/bench/lowtests.runtime
@@ -1,1 +1,1 @@
-0.3
+0.4
diff --git a/bench/runtime/lapmatrud/bench/redundants.runtime b/bench/runtime/lapmatrud/bench/redundants.runtime
--- a/bench/runtime/lapmatrud/bench/redundants.runtime
+++ b/bench/runtime/lapmatrud/bench/redundants.runtime
@@ -1,1 +1,1 @@
-2.9
+3.0
diff --git a/bench/runtime/lapmatrud/bench/strategies.runtime b/bench/runtime/lapmatrud/bench/strategies.runtime
--- a/bench/runtime/lapmatrud/bench/strategies.runtime
+++ b/bench/runtime/lapmatrud/bench/strategies.runtime
@@ -1,1 +1,1 @@
-4.4
+4.6
diff --git a/bench/runtime/lapmatrud/bench/terpret.runtime b/bench/runtime/lapmatrud/bench/terpret.runtime
--- a/bench/runtime/lapmatrud/bench/terpret.runtime
+++ b/bench/runtime/lapmatrud/bench/terpret.runtime
@@ -1,1 +1,1 @@
-7.5
+7.3
diff --git a/bench/runtime/lapmatrud/bench/unique.runtime b/bench/runtime/lapmatrud/bench/unique.runtime
--- a/bench/runtime/lapmatrud/bench/unique.runtime
+++ b/bench/runtime/lapmatrud/bench/unique.runtime
@@ -1,1 +1,1 @@
-2.1
+2.4
diff --git a/bench/runtime/lapmatrud/bench/weird.runtime b/bench/runtime/lapmatrud/bench/weird.runtime
--- a/bench/runtime/lapmatrud/bench/weird.runtime
+++ b/bench/runtime/lapmatrud/bench/weird.runtime
@@ -1,1 +1,1 @@
-2.6
+2.5
diff --git a/bench/runtime/lapmatrud/eg/bits.runtime b/bench/runtime/lapmatrud/eg/bits.runtime
--- a/bench/runtime/lapmatrud/eg/bits.runtime
+++ b/bench/runtime/lapmatrud/eg/bits.runtime
@@ -1,1 +1,1 @@
-3.1
+3.0
diff --git a/bench/runtime/lapmatrud/eg/bools.runtime b/bench/runtime/lapmatrud/eg/bools.runtime
--- a/bench/runtime/lapmatrud/eg/bools.runtime
+++ b/bench/runtime/lapmatrud/eg/bools.runtime
@@ -1,1 +1,1 @@
-1.3
+1.6
diff --git a/bench/runtime/lapmatrud/eg/bst.runtime b/bench/runtime/lapmatrud/eg/bst.runtime
--- a/bench/runtime/lapmatrud/eg/bst.runtime
+++ b/bench/runtime/lapmatrud/eg/bst.runtime
@@ -1,1 +1,1 @@
-7.4
+4.3
diff --git a/bench/runtime/lapmatrud/eg/count.runtime b/bench/runtime/lapmatrud/eg/count.runtime
--- a/bench/runtime/lapmatrud/eg/count.runtime
+++ b/bench/runtime/lapmatrud/eg/count.runtime
@@ -1,1 +1,1 @@
-0.2
+0.5
diff --git a/bench/runtime/lapmatrud/eg/dupos.runtime b/bench/runtime/lapmatrud/eg/dupos.runtime
--- a/bench/runtime/lapmatrud/eg/dupos.runtime
+++ b/bench/runtime/lapmatrud/eg/dupos.runtime
@@ -1,1 +1,1 @@
-3.1
+1.9
diff --git a/bench/runtime/lapmatrud/eg/factorial.runtime b/bench/runtime/lapmatrud/eg/factorial.runtime
--- a/bench/runtime/lapmatrud/eg/factorial.runtime
+++ b/bench/runtime/lapmatrud/eg/factorial.runtime
@@ -1,1 +1,1 @@
-1.2
+1.4
diff --git a/bench/runtime/lapmatrud/eg/fib01.runtime b/bench/runtime/lapmatrud/eg/fib01.runtime
--- a/bench/runtime/lapmatrud/eg/fib01.runtime
+++ b/bench/runtime/lapmatrud/eg/fib01.runtime
@@ -1,1 +1,1 @@
-0.7
+0.5
diff --git a/bench/runtime/lapmatrud/eg/fibonacci.runtime b/bench/runtime/lapmatrud/eg/fibonacci.runtime
--- a/bench/runtime/lapmatrud/eg/fibonacci.runtime
+++ b/bench/runtime/lapmatrud/eg/fibonacci.runtime
@@ -1,1 +1,1 @@
-2.9
+3.2
diff --git a/bench/runtime/lapmatrud/eg/gcd.runtime b/bench/runtime/lapmatrud/eg/gcd.runtime
--- a/bench/runtime/lapmatrud/eg/gcd.runtime
+++ b/bench/runtime/lapmatrud/eg/gcd.runtime
@@ -1,1 +1,1 @@
-0.1
+0.2
diff --git a/bench/runtime/lapmatrud/eg/higher.runtime b/bench/runtime/lapmatrud/eg/higher.runtime
--- a/bench/runtime/lapmatrud/eg/higher.runtime
+++ b/bench/runtime/lapmatrud/eg/higher.runtime
@@ -1,1 +1,1 @@
-0.6
+0.8
diff --git a/bench/runtime/lapmatrud/eg/list.runtime b/bench/runtime/lapmatrud/eg/list.runtime
--- a/bench/runtime/lapmatrud/eg/list.runtime
+++ b/bench/runtime/lapmatrud/eg/list.runtime
@@ -1,1 +1,1 @@
-0.5
+0.6
diff --git a/bench/runtime/lapmatrud/eg/maybe.runtime b/bench/runtime/lapmatrud/eg/maybe.runtime
--- a/bench/runtime/lapmatrud/eg/maybe.runtime
+++ b/bench/runtime/lapmatrud/eg/maybe.runtime
@@ -1,1 +1,1 @@
-0.2
+0.3
diff --git a/bench/runtime/lapmatrud/eg/oddeven.runtime b/bench/runtime/lapmatrud/eg/oddeven.runtime
--- a/bench/runtime/lapmatrud/eg/oddeven.runtime
+++ b/bench/runtime/lapmatrud/eg/oddeven.runtime
@@ -1,1 +1,1 @@
-1.4
+1.5
diff --git a/bench/runtime/lapmatrud/eg/pow.runtime b/bench/runtime/lapmatrud/eg/pow.runtime
--- a/bench/runtime/lapmatrud/eg/pow.runtime
+++ b/bench/runtime/lapmatrud/eg/pow.runtime
@@ -1,1 +1,1 @@
-3.8
+2.8
diff --git a/bench/runtime/lapmatrud/eg/setelem.runtime b/bench/runtime/lapmatrud/eg/setelem.runtime
--- a/bench/runtime/lapmatrud/eg/setelem.runtime
+++ b/bench/runtime/lapmatrud/eg/setelem.runtime
@@ -1,1 +1,1 @@
-0.7
+0.8
diff --git a/bench/runtime/lapmatrud/eg/sort.runtime b/bench/runtime/lapmatrud/eg/sort.runtime
--- a/bench/runtime/lapmatrud/eg/sort.runtime
+++ b/bench/runtime/lapmatrud/eg/sort.runtime
@@ -1,1 +1,1 @@
-3.3
+3.1
diff --git a/bench/runtime/lapmatrud/eg/spec.runtime b/bench/runtime/lapmatrud/eg/spec.runtime
--- a/bench/runtime/lapmatrud/eg/spec.runtime
+++ b/bench/runtime/lapmatrud/eg/spec.runtime
@@ -1,1 +1,1 @@
-0.6
+0.8
diff --git a/bench/runtime/lapmatrud/eg/subset.runtime b/bench/runtime/lapmatrud/eg/subset.runtime
--- a/bench/runtime/lapmatrud/eg/subset.runtime
+++ b/bench/runtime/lapmatrud/eg/subset.runtime
@@ -1,1 +1,1 @@
-0.4
+0.6
diff --git a/bench/runtime/lapmatrud/eg/take-drop.runtime b/bench/runtime/lapmatrud/eg/take-drop.runtime
--- a/bench/runtime/lapmatrud/eg/take-drop.runtime
+++ b/bench/runtime/lapmatrud/eg/take-drop.runtime
@@ -1,1 +1,1 @@
-0.2
+0.3
diff --git a/bench/runtime/lapmatrud/eg/these.runtime b/bench/runtime/lapmatrud/eg/these.runtime
--- a/bench/runtime/lapmatrud/eg/these.runtime
+++ b/bench/runtime/lapmatrud/eg/these.runtime
@@ -1,1 +1,1 @@
-1.1
+1.7
diff --git a/bench/runtime/lapmatrud/eg/tree.runtime b/bench/runtime/lapmatrud/eg/tree.runtime
--- a/bench/runtime/lapmatrud/eg/tree.runtime
+++ b/bench/runtime/lapmatrud/eg/tree.runtime
@@ -1,1 +1,1 @@
-1.3
+1.4
diff --git a/bench/runtime/lapmatrud/eg/tri.runtime b/bench/runtime/lapmatrud/eg/tri.runtime
--- a/bench/runtime/lapmatrud/eg/tri.runtime
+++ b/bench/runtime/lapmatrud/eg/tri.runtime
@@ -1,1 +1,1 @@
-0.3
+0.2
diff --git a/bench/runtime/lapmatrud/eg/tuple.runtime b/bench/runtime/lapmatrud/eg/tuple.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/lapmatrud/eg/tuple.runtime
@@ -0,0 +1,1 @@
+0.5
diff --git a/bench/runtime/zero/bench/candidates.runtime b/bench/runtime/zero/bench/candidates.runtime
--- a/bench/runtime/zero/bench/candidates.runtime
+++ b/bench/runtime/zero/bench/candidates.runtime
@@ -1,1 +1,1 @@
-18.1
+17.8
diff --git a/bench/runtime/zero/bench/carry-on.runtime b/bench/runtime/zero/bench/carry-on.runtime
--- a/bench/runtime/zero/bench/carry-on.runtime
+++ b/bench/runtime/zero/bench/carry-on.runtime
@@ -1,1 +1,1 @@
-4.9
+4.6
diff --git a/bench/runtime/zero/bench/erroneous.runtime b/bench/runtime/zero/bench/erroneous.runtime
--- a/bench/runtime/zero/bench/erroneous.runtime
+++ b/bench/runtime/zero/bench/erroneous.runtime
@@ -1,1 +1,1 @@
-6.1
+5.7
diff --git a/bench/runtime/zero/bench/gps.runtime b/bench/runtime/zero/bench/gps.runtime
--- a/bench/runtime/zero/bench/gps.runtime
+++ b/bench/runtime/zero/bench/gps.runtime
@@ -1,1 +1,1 @@
-28.1
+28.7
diff --git a/bench/runtime/zero/bench/gps2.runtime b/bench/runtime/zero/bench/gps2.runtime
--- a/bench/runtime/zero/bench/gps2.runtime
+++ b/bench/runtime/zero/bench/gps2.runtime
@@ -1,1 +1,1 @@
-23.2
+23.6
diff --git a/bench/runtime/zero/bench/ill-hit.runtime b/bench/runtime/zero/bench/ill-hit.runtime
--- a/bench/runtime/zero/bench/ill-hit.runtime
+++ b/bench/runtime/zero/bench/ill-hit.runtime
@@ -1,1 +1,1 @@
-0.9
+1.0
diff --git a/bench/runtime/zero/bench/p30.runtime b/bench/runtime/zero/bench/p30.runtime
--- a/bench/runtime/zero/bench/p30.runtime
+++ b/bench/runtime/zero/bench/p30.runtime
@@ -1,1 +1,1 @@
-0.1
+0.0
diff --git a/bench/runtime/zero/bench/redundants.runtime b/bench/runtime/zero/bench/redundants.runtime
--- a/bench/runtime/zero/bench/redundants.runtime
+++ b/bench/runtime/zero/bench/redundants.runtime
@@ -1,1 +1,1 @@
-6.6
+6.3
diff --git a/bench/runtime/zero/bench/strategies.runtime b/bench/runtime/zero/bench/strategies.runtime
--- a/bench/runtime/zero/bench/strategies.runtime
+++ b/bench/runtime/zero/bench/strategies.runtime
@@ -1,1 +1,1 @@
-9.4
+8.9
diff --git a/bench/runtime/zero/bench/unique.runtime b/bench/runtime/zero/bench/unique.runtime
--- a/bench/runtime/zero/bench/unique.runtime
+++ b/bench/runtime/zero/bench/unique.runtime
@@ -1,1 +1,1 @@
-4.9
+4.8
diff --git a/bench/runtime/zero/bench/weird.runtime b/bench/runtime/zero/bench/weird.runtime
--- a/bench/runtime/zero/bench/weird.runtime
+++ b/bench/runtime/zero/bench/weird.runtime
@@ -1,1 +1,1 @@
-4.8
+4.9
diff --git a/bench/runtime/zero/eg/arith.runtime b/bench/runtime/zero/eg/arith.runtime
--- a/bench/runtime/zero/eg/arith.runtime
+++ b/bench/runtime/zero/eg/arith.runtime
@@ -1,1 +1,1 @@
-1.9
+2.0
diff --git a/bench/runtime/zero/eg/bools.runtime b/bench/runtime/zero/eg/bools.runtime
--- a/bench/runtime/zero/eg/bools.runtime
+++ b/bench/runtime/zero/eg/bools.runtime
@@ -1,1 +1,1 @@
-3.0
+3.3
diff --git a/bench/runtime/zero/eg/bst.runtime b/bench/runtime/zero/eg/bst.runtime
--- a/bench/runtime/zero/eg/bst.runtime
+++ b/bench/runtime/zero/eg/bst.runtime
@@ -1,1 +1,1 @@
-9.0
+8.6
diff --git a/bench/runtime/zero/eg/count.runtime b/bench/runtime/zero/eg/count.runtime
--- a/bench/runtime/zero/eg/count.runtime
+++ b/bench/runtime/zero/eg/count.runtime
@@ -1,1 +1,1 @@
-1.0
+1.1
diff --git a/bench/runtime/zero/eg/dupos.runtime b/bench/runtime/zero/eg/dupos.runtime
--- a/bench/runtime/zero/eg/dupos.runtime
+++ b/bench/runtime/zero/eg/dupos.runtime
@@ -1,1 +1,1 @@
-3.6
+3.8
diff --git a/bench/runtime/zero/eg/fib01.runtime b/bench/runtime/zero/eg/fib01.runtime
--- a/bench/runtime/zero/eg/fib01.runtime
+++ b/bench/runtime/zero/eg/fib01.runtime
@@ -1,1 +1,1 @@
-1.0
+1.1
diff --git a/bench/runtime/zero/eg/fibonacci.runtime b/bench/runtime/zero/eg/fibonacci.runtime
--- a/bench/runtime/zero/eg/fibonacci.runtime
+++ b/bench/runtime/zero/eg/fibonacci.runtime
@@ -1,1 +1,1 @@
-6.1
+6.3
diff --git a/bench/runtime/zero/eg/higher.runtime b/bench/runtime/zero/eg/higher.runtime
--- a/bench/runtime/zero/eg/higher.runtime
+++ b/bench/runtime/zero/eg/higher.runtime
@@ -1,1 +1,1 @@
-1.6
+1.8
diff --git a/bench/runtime/zero/eg/ints.runtime b/bench/runtime/zero/eg/ints.runtime
--- a/bench/runtime/zero/eg/ints.runtime
+++ b/bench/runtime/zero/eg/ints.runtime
@@ -1,1 +1,1 @@
-1.0
+1.1
diff --git a/bench/runtime/zero/eg/maybe.runtime b/bench/runtime/zero/eg/maybe.runtime
--- a/bench/runtime/zero/eg/maybe.runtime
+++ b/bench/runtime/zero/eg/maybe.runtime
@@ -1,1 +1,1 @@
-0.6
+0.8
diff --git a/bench/runtime/zero/eg/oddeven.runtime b/bench/runtime/zero/eg/oddeven.runtime
--- a/bench/runtime/zero/eg/oddeven.runtime
+++ b/bench/runtime/zero/eg/oddeven.runtime
@@ -1,1 +1,1 @@
-2.8
+2.9
diff --git a/bench/runtime/zero/eg/peano.runtime b/bench/runtime/zero/eg/peano.runtime
--- a/bench/runtime/zero/eg/peano.runtime
+++ b/bench/runtime/zero/eg/peano.runtime
@@ -1,1 +1,1 @@
-0.5
+0.6
diff --git a/bench/runtime/zero/eg/pow.runtime b/bench/runtime/zero/eg/pow.runtime
--- a/bench/runtime/zero/eg/pow.runtime
+++ b/bench/runtime/zero/eg/pow.runtime
@@ -1,1 +1,1 @@
-5.0
+5.2
diff --git a/bench/runtime/zero/eg/setelem.runtime b/bench/runtime/zero/eg/setelem.runtime
--- a/bench/runtime/zero/eg/setelem.runtime
+++ b/bench/runtime/zero/eg/setelem.runtime
@@ -1,1 +1,1 @@
-1.4
+1.5
diff --git a/bench/runtime/zero/eg/spec.runtime b/bench/runtime/zero/eg/spec.runtime
--- a/bench/runtime/zero/eg/spec.runtime
+++ b/bench/runtime/zero/eg/spec.runtime
@@ -1,1 +1,1 @@
-1.7
+1.6
diff --git a/bench/runtime/zero/eg/these.runtime b/bench/runtime/zero/eg/these.runtime
--- a/bench/runtime/zero/eg/these.runtime
+++ b/bench/runtime/zero/eg/these.runtime
@@ -1,1 +1,1 @@
-1.6
+3.5
diff --git a/bench/runtime/zero/eg/tree.runtime b/bench/runtime/zero/eg/tree.runtime
--- a/bench/runtime/zero/eg/tree.runtime
+++ b/bench/runtime/zero/eg/tree.runtime
@@ -1,1 +1,1 @@
-2.8
+2.7
diff --git a/bench/runtime/zero/eg/tuple.runtime b/bench/runtime/zero/eg/tuple.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/eg/tuple.runtime
@@ -0,0 +1,1 @@
+0.9
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,18 @@
 ============================
 
 
+v0.6.10 (February 2025)
+-----------------------
+
+* fix bug preventing Conjure to work in the presence of argument types
+  whose tiers enumeration would have no values of the first tier
+* allow configuring `maxPatternDepth`.  Default 1, interesting 2.
+* slightly increase default `maxSearchTests`
+* slight improvements in performance by improve memo tables
+* improve internal tests
+* add `eg/tuple`
+
+
 v0.6.8 (February 2025)
 ----------------------
 
diff --git a/code-conjure.cabal b/code-conjure.cabal
--- a/code-conjure.cabal
+++ b/code-conjure.cabal
@@ -3,7 +3,7 @@
 -- Copyright (C) 2021-2025 Rudy Matela
 -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
 name:                code-conjure
-version:             0.6.8
+version:             0.6.10
 synopsis:            synthesize Haskell functions out of partial definitions
 description:
   Conjure is a tool that synthesizes Haskell functions out of partial definitions.
@@ -69,7 +69,7 @@
 source-repository this
   type:            git
   location:        https://github.com/rudymatela/conjure
-  tag:             v0.6.8
+  tag:             v0.6.10
 
 library
   exposed-modules: Conjure
diff --git a/eg/bst.hs b/eg/bst.hs
--- a/eg/bst.hs
+++ b/eg/bst.hs
@@ -71,11 +71,12 @@
 mem y (Node l x r)  =  y == x || (if y < x then mem y l else mem y r)
 
 insert :: Int -> Tree -> Tree
-insert x Leaf  =  unit x
-insert x (Node l y r)  =  case compare x y of
-  LT -> Node (insert x l) y r
-  EQ -> Node l y r
-  GT -> Node l y (insert x r)
+insert x Leaf          =  unit x  -- 2
+insert x (Node l y r)  =
+  case compare x y of             -- 6
+  LT -> Node (insert x l) y r     -- 12
+  EQ -> Node l y r                -- 16
+  GT -> Node l y (insert x r)     -- 22
 
 before :: Int -> Tree -> Tree
 before _ Leaf  =  Leaf
@@ -145,11 +146,9 @@
     , primOrdCaseFor (undefined :: Tree)
     ]
 
-  -- reachable in 15s after 32878 at size 14.
-  -- out of reach performance-wise (reaching 16 but need 19)
-  -- cf. the primOrdCaseFor version below
+  -- reachable in 15s, candidate #32878 at size 14.
   -- increase target to 50400 to reach...
-  conjureFromSpecWith args{target=10080} "before" beforeSpec
+  conjureFromSpecWith args{target=5040} "before" beforeSpec
     [ pr Leaf
     , prim "Node" Node
     , prim "==" ((==) :: Int -> Int -> Bool)
@@ -157,6 +156,16 @@
     , guard
     ]
 
+  -- reachable in 14s, candidate #32747 at size 14.
+  -- increase target to 50400 to reach...
+  conjureFromSpecWith args{target=5040} "beyond" beyondSpec
+    [ pr Leaf
+    , prim "Node" Node
+    , prim "==" ((==) :: Int -> Int -> Bool)
+    , prim "<=" ((<) :: Int -> Int -> Bool)
+    , guard
+    ]
+
   -- with 15, this reaches the solution, using 12 for shorter runtime
   -- using maxEquationSize = 7 reduces runtime from 13s to 11s
   conjureFromSpecWith args{maxSize = 12, maxEquationSize = 7} "before" beforeSpec
@@ -175,8 +184,9 @@
     , primOrdCaseFor (undefined :: Tree)
     ]
 
-  -- out of reach (reaching 12 but need 13)
-  conjureWith args{maxSize = 9} "union" union
+  -- reachable in 55s, candidate #173109 at size 13.
+  -- increase to 554400 to reach
+  conjureWith args{target=5544} "union" union
     [ pr Leaf
     , prim "Node" Node
     , prim "before" before
diff --git a/eg/bst.txt b/eg/bst.txt
--- a/eg/bst.txt
+++ b/eg/bst.txt
@@ -70,11 +70,25 @@
 -- 104 candidates of size 9
 -- 1342 candidates of size 10
 -- 3543 candidates of size 11
--- 2552 candidates of size 12
--- 23874 candidates of size 13
--- tested 31769 candidates
+-- tested 5343 candidates
 cannot conjure
 
+beyond :: Int -> Tree -> Tree
+-- pruning with 5/6 rules
+-- 2 candidates of size 1
+-- 2 candidates of size 2
+-- 0 candidates of size 3
+-- 4 candidates of size 4
+-- 21 candidates of size 5
+-- 0 candidates of size 6
+-- 86 candidates of size 7
+-- 239 candidates of size 8
+-- 104 candidates of size 9
+-- 1342 candidates of size 10
+-- 3543 candidates of size 11
+-- tested 5343 candidates
+cannot conjure
+
 before :: Int -> Tree -> Tree
 -- pruning with 2/4 rules
 -- 2 candidates of size 1
@@ -121,6 +135,7 @@
 -- 240 candidates of size 7
 -- 82 candidates of size 8
 -- 3018 candidates of size 9
--- tested 3433 candidates
+-- 8098 candidates of size 10
+-- tested 11531 candidates
 cannot conjure
 
diff --git a/eg/count.hs b/eg/count.hs
--- a/eg/count.hs
+++ b/eg/count.hs
@@ -1,4 +1,6 @@
--- count.hs: elem and set functions
+-- count.hs: conjuring an element count function
+--
+-- 2021-2025 Rudy Matela
 import Conjure
 
 -- an idiomatic count without using filter
diff --git a/eg/list.hs b/eg/list.hs
--- a/eg/list.hs
+++ b/eg/list.hs
@@ -86,6 +86,12 @@
     , prim "undefined" (undefined :: Int)
     ]
 
+  conjureWith args{maxPatternDepth=2} "last" last'
+    [ pr ([] :: [Int])
+    , prim ":" ((:) :: Int -> [Int] -> [Int])
+    , prim "undefined" (undefined :: Int)
+    ]
+
   conjure "zip" (zip')
     [ pr ([] :: [(Int,Int)])
     , prim ":" ((:) :: (Int,Int) -> [(Int,Int)] -> [(Int,Int)])
diff --git a/eg/list.txt b/eg/list.txt
--- a/eg/list.txt
+++ b/eg/list.txt
@@ -63,6 +63,20 @@
   | null xs  =  x
   | otherwise  =  last xs
 
+last :: [Int] -> Int
+-- testing 360 combinations of argument values
+-- pruning with 0/0 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 1 candidates of size 4
+-- 2 candidates of size 5
+-- 2 candidates of size 6
+-- tested 5 candidates
+last []  =  undefined
+last [x]  =  x
+last (x:y:xs)  =  last (y:xs)
+
 zip :: [Int] -> [Int] -> [(Int,Int)]
 -- testing 360 combinations of argument values
 -- pruning with 0/0 rules
diff --git a/eg/maybe.hs b/eg/maybe.hs
--- a/eg/maybe.hs
+++ b/eg/maybe.hs
@@ -56,7 +56,7 @@
   conjure "maybeToList"   maybeToList' primitives
 
   -- only top-level break downs, so would need morePrimitives
-  conjure "catMaybes"     catMaybes'   primitives
+  conjureWith args{maxPatternDepth=2} "catMaybes"     catMaybes'   primitives
   -- conjure "mapMaybe" mapMaybe' primitives  -- same
 
 primitives :: [Prim]
diff --git a/eg/maybe.txt b/eg/maybe.txt
--- a/eg/maybe.txt
+++ b/eg/maybe.txt
@@ -60,6 +60,13 @@
 -- pruning with 0/0 rules
 -- 1 candidates of size 1
 -- 0 candidates of size 2
--- tested 1 candidates
-cannot conjure
+-- 0 candidates of size 3
+-- 0 candidates of size 4
+-- 0 candidates of size 5
+-- 1 candidates of size 6
+-- 2 candidates of size 7
+-- tested 3 candidates
+catMaybes []  =  []
+catMaybes (Nothing:mxs)  =  catMaybes mxs
+catMaybes (Just x:mxs)  =  x:catMaybes mxs
 
diff --git a/eg/these.hs b/eg/these.hs
--- a/eg/these.hs
+++ b/eg/these.hs
@@ -7,8 +7,8 @@
 
 -- This was inspired by the These datatype from the cathis package.
 -- https://hackage.haskell.org/package/cathis
--- This is an extension of the type there with the added None constructor.
-data These a b  =  None | This a | That b | These a b
+-- This is an extension of the type there with the added Neither constructor.
+data These a b  =  Neither | This a | That b | These a b
   deriving (Eq, Ord, Show, Read)
 
 isThis :: These a b -> Bool
@@ -34,7 +34,7 @@
 
 
 fromThese' :: A -> B -> These A B -> (A, B)
-fromThese' 0 1 None  =  (0, 1)
+fromThese' 0 1 Neither  =  (0, 1)
 fromThese' 0 1 (This 2)  =  (2,1)
 fromThese' 0 1 (That 2)  =  (0,2)
 fromThese' 0 1 (These 1 0)  =  (1,0)
@@ -45,22 +45,29 @@
 listhese' (That 2)  =  [2]
 
 cathis' :: [These A B] -> [A]
+cathis' []  =  []
 cathis' [This 0, This 1]  =  [0,1]
-cathis' [None, That 0]  =  []
-cathis' [This 0, None, That 1]  =  [0]
+cathis' [Neither, That 0]  =  []
+cathis' [That 0, This 1]  =  [1]
+cathis' [This 0, Neither, That 1]  =  [0]
 cathis' [This 0, These 0 1]  =  [0,0]
-cathis' [These 0 1, None, This 0]  =  [0,0]
+cathis' [These 0 1, Neither, This 0]  =  [0,0]
+cathis' [These 0 1, This 2]  =  [0,2]
 
 cathat' :: [These A B] -> [B]
+cathat' []  =  []
 cathat' [This 0, This 1]  =  []
-cathat' [None, That 0]  =  [0]
-cathat' [This 0, None, That 1]  =  [1]
+cathat' [Neither, That 0]  =  [0]
+cathat' [This 0, Neither, That 1]  =  [1]
 cathat' [That 0, These 0 1]  =  [0,1]
+cathat' [These 0 1, That 0]  =  [1,0]
 
 cathese' :: [These A A] -> [A]
+cathese' []  =  []
 cathese' [This 0, This 1]  =  [0,1]
-cathese' [None, That 0]  =  [0]
-cathese' [This 0, None, That 1]  =  [0,1]
+cathese' [Neither, That 0]  =  [0]
+cathese' [That 0, This 1]  =  [0,1]
+cathese' [This 0, Neither, That 1]  =  [0,1]
 cathese' [This 0, These 0 1]  =  [0,0,1]
 cathese' [These 0 1, That 2]  =  [0,1,2]
 
@@ -91,21 +98,22 @@
     , guard
     ]
 
-  -- couldn't make this reachable, I didn't try much...
-  conjureWith args{target = 1080} "cathese" cathese'
+  conjureWith args{maxPatternDepth = 2} "cathis" cathis'
     [ pr ([] :: [A])
     , prim ":" ((:) :: A -> [A] -> [A])
-    , prim "++" ((++) :: [A] -> [A] -> [A])
-    , prim "isThis" (isThis :: These A A -> Bool)
-    , prim "fromThis" (fromThis :: These A A -> A)
-    , prim "isThat" (isThat :: These A A -> Bool)
-    , prim "fromThat" (fromThat :: These A A -> A)
-    -- , prif (undefined :: A)
-    -- , prif (undefined :: [A])
-    , guard
     ]
-  -- expected functionality
-  -- these []  =  []
-  -- these (This x : ts)  =  x : these ts
-  -- these (That y : ts)  =  y : these ts
-  -- these (These x y : ts)  =  x : y : these ts
+
+  conjureWith args{maxPatternDepth = 2} "cathat" cathat'
+    [ pr ([] :: [B])
+    , prim ":" ((:) :: B -> [B] -> [B])
+    ]
+
+  conjureWith args{maxPatternDepth = 2} "cathese" cathese'
+    [ pr ([] :: [A])
+    , prim ":" ((:) :: A -> [A] -> [A])
+    ]
+  -- cathese []  =  []
+  -- cathese (Neither : ts)  =  cathese ts
+  -- cathese (This x : ts)  =  x : these ts
+  -- cathese (That y : ts)  =  y : these ts
+  -- cathese (These x y : ts)  =  x : y : these ts
diff --git a/eg/these.txt b/eg/these.txt
--- a/eg/these.txt
+++ b/eg/these.txt
@@ -12,7 +12,7 @@
 -- 0 candidates of size 9
 -- 1 candidates of size 10
 -- tested 2 candidates
-fromThese x y None  =  (x,y)
+fromThese x y Neither  =  (x,y)
 fromThese x y (This z)  =  (z,y)
 fromThese x y (That z)  =  (x,z)
 fromThese x y (These z x')  =  (z,x')
@@ -31,13 +31,13 @@
 -- 0 candidates of size 9
 -- 1 candidates of size 10
 -- tested 2 candidates
-listhese None  =  []
-listhese (This x)  =  [x]
+listhese Neither  =  []
 listhese (That x)  =  [x]
+listhese (This x)  =  [x]
 listhese (These x y)  =  [x,y]
 
 cathis :: [These A B] -> [A]
--- testing 5 combinations of argument values
+-- testing 8 combinations of argument values
 -- pruning with 3/3 rules
 -- 1 candidates of size 1
 -- 0 candidates of size 2
@@ -57,7 +57,7 @@
   | otherwise  =  cathis ts
 
 cathat :: [These A B] -> [B]
--- testing 4 combinations of argument values
+-- testing 6 combinations of argument values
 -- pruning with 3/3 rules
 -- 1 candidates of size 1
 -- 0 candidates of size 2
@@ -76,27 +76,70 @@
   | isThat t  =  fromThat t:cathat ts
   | otherwise  =  cathat ts
 
-cathese :: [These A A] -> [A]
--- testing 5 combinations of argument values
--- pruning with 7/7 rules
+cathis :: [These A B] -> [A]
+-- testing 8 combinations of argument values
+-- pruning with 0/0 rules
 -- 1 candidates of size 1
 -- 0 candidates of size 2
 -- 0 candidates of size 3
 -- 0 candidates of size 4
 -- 0 candidates of size 5
--- 3 candidates of size 6
--- 4 candidates of size 7
+-- 0 candidates of size 6
+-- 0 candidates of size 7
 -- 2 candidates of size 8
--- 9 candidates of size 9
--- 20 candidates of size 10
--- 22 candidates of size 11
--- 35 candidates of size 12
--- 84 candidates of size 13
--- 86 candidates of size 14
--- 137 candidates of size 15
--- 324 candidates of size 16
--- 346 candidates of size 17
--- 511 candidates of size 18
--- tested 1584 candidates
-cannot conjure
+-- 6 candidates of size 9
+-- 7 candidates of size 10
+-- 13 candidates of size 11
+-- tested 18 candidates
+cathis []  =  []
+cathis (Neither:ts)  =  cathis ts
+cathis (This x:ts)  =  x:cathis ts
+cathis (That x:ts)  =  cathis ts
+cathis (These x y:ts)  =  x:cathis ts
+
+cathat :: [These A B] -> [B]
+-- testing 6 combinations of argument values
+-- pruning with 0/0 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 0 candidates of size 4
+-- 0 candidates of size 5
+-- 0 candidates of size 6
+-- 0 candidates of size 7
+-- 0 candidates of size 8
+-- 2 candidates of size 9
+-- 4 candidates of size 10
+-- 3 candidates of size 11
+-- tested 9 candidates
+cathat []  =  []
+cathat (Neither:ts)  =  cathat ts
+cathat (This x:ts)  =  cathat ts
+cathat (That x:ts)  =  x:cathat ts
+cathat (These x y:ts)  =  y:cathat ts
+
+cathese :: [These A A] -> [A]
+-- testing 7 combinations of argument values
+-- pruning with 0/0 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 0 candidates of size 4
+-- 0 candidates of size 5
+-- 0 candidates of size 6
+-- 0 candidates of size 7
+-- 0 candidates of size 8
+-- 4 candidates of size 9
+-- 8 candidates of size 10
+-- 11 candidates of size 11
+-- 38 candidates of size 12
+-- 26 candidates of size 13
+-- 140 candidates of size 14
+-- 57 candidates of size 15
+-- tested 269 candidates
+cathese []  =  []
+cathese (Neither:ts)  =  cathese ts
+cathese (That x:ts)  =  x:cathese ts
+cathese (This x:ts)  =  x:cathese ts
+cathese (These x y:ts)  =  x:y:cathese ts
 
diff --git a/eg/tuple.hs b/eg/tuple.hs
new file mode 100644
--- /dev/null
+++ b/eg/tuple.hs
@@ -0,0 +1,116 @@
+-- tuple.hs: conjuring functions involving tuples
+--
+-- 2025 Rudy Matela
+-- Distributed under a 3-Clause BSD licence (see the file LICENSE).
+import Conjure
+import Test.LeanCheck
+
+
+-- First, all functions from Data.Tuple
+-- Though their types have been simplified to (A, A),
+-- they might as well have had (A, B), (B, A), etc in types.
+-- This makes it to use a single background primitives list for everything.
+
+fst' :: (A,A) -> A
+fst' (0,1)  =  0
+fst' (1,0)  =  1
+fst' (0,2)  =  0
+fst' (2,1)  =  2
+
+snd' :: (A,A) -> A
+snd' (0,1)  =  1
+snd' (1,0)  =  0
+snd' (0,2)  =  2
+snd' (2,1)  =  1
+
+swap' :: (A,A) -> (A,A)
+swap' (0,1)  =  (1,0)
+swap' (1,0)  =  (0,1)
+swap' (2,1)  =  (1,2)
+swap' (1,2)  =  (2,1)
+
+type Curry a b c  =  ((a,b) -> c) -> (a -> b -> c)
+
+currySpec :: Curry A A A -> Bool
+currySpec curry  =  and
+  [ holds n $ \x y -> curry fst x y == x
+  , holds n $ \x y -> curry snd x y == y
+  , holds n $ \x y -> curry (\(i,j) -> i + j) x y == x + y
+  ]
+  where
+  n = 360
+
+type Uncurry a b c  =  (a -> b -> c) -> ((a,b) -> c)
+
+uncurrySpec :: Uncurry A A A -> Bool
+uncurrySpec uncurry  =  and
+  [ holds n $ \x y -> uncurry (+) (x,y) == x + y
+  , holds n $ \x y -> uncurry (*) (x,y) == x * y
+  ]
+  where
+  n = 360
+
+
+-- now two functions that are a bit more interesting:
+
+pairwise :: [A] -> [(A,A)]
+pairwise []  =  []
+-- pairwise [x]  =  []
+pairwise [x,y]  =  [(x,y)]
+-- pairwise [x,y,z]  =  [] -- TODO: why this does not work
+pairwise [x,y,z,w]  =  [(x,y), (z,w)]
+-- pairwise [0,1,2,3]  =  [(0,1), (2,3)]
+-- pairwise [0,1,0,1]  =  [(0,1), (0,1)]
+-- pairwise [0,0,0,0,0]  =  [(0,0), (0,0)]
+-- pairwise [0,0,0,0,0,0]  =  [(0,0), (0,0), (0,0)]
+
+catpairs :: [(A,A)] -> [A]
+catpairs [(x,y)]  =  [x,y]
+catpairs [(x,y), (z,w)]  =  [x,y,z,w]
+
+main :: IO ()
+main = do
+  -- the following 5 are pretty easy to Conjure:
+  conjure "fst"  fst'   []
+  conjure "snd"  snd'   []
+  conjure "swap" swap'  primitives
+  conjureFromSpec "curry"     currySpec primitives
+  conjureFromSpec "uncurry" uncurrySpec primitives
+
+  -- these are more interesting:
+  conjure "pairwise" pairwise primitives
+  conjure "catpairs" catpairs primitives
+
+  -- by increasing the pattern depth, we find shorter versions:
+  conjureWith args{maxPatternDepth=2} "pairwise" pairwise primitives
+  conjureWith args{maxPatternDepth=2} "catpairs" catpairs primitives
+
+primitives :: [Prim]
+primitives  =
+  -- pairs
+  [ prim "," ((,) :: A -> A -> (A,A))
+  , prim "fst" (fst :: (A,A) -> A)
+  , prim "snd" (snd :: (A,A) -> A)
+
+  -- lists
+  , prim "[]" ([] :: [A])
+  , prim ":" ((:) :: A -> [A] -> [A])
+  , prim "null" (null :: [A] -> Bool)
+  , prim "head" (head :: [A] -> A)
+  , prim "tail" (tail :: [A] -> [A])
+
+  -- lists of pairs
+  , prim "[]" ([] :: [(A,A)])
+  , prim ":" ((:) :: (A,A) -> [(A,A)] -> [(A,A)])
+
+  -- allow guards
+  , guard
+  ]
+
+-- expected pairwise
+-- TODO: this appears in showCandidates but is not selected for some reason...
+pw :: [A] -> [(A,A)]
+pw []  =  []
+pw (x:xs)
+  | null xs  =  []
+  | otherwise  =  (x,head xs) : pw (tail xs)
diff --git a/eg/tuple.txt b/eg/tuple.txt
new file mode 100644
--- /dev/null
+++ b/eg/tuple.txt
@@ -0,0 +1,107 @@
+fst :: (A,A) -> A
+-- testing 4 combinations of argument values
+-- pruning with 0/0 rules
+-- 0 candidates of size 1
+-- 1 candidates of size 2
+-- tested 1 candidates
+fst (x,y)  =  x
+
+snd :: (A,A) -> A
+-- testing 4 combinations of argument values
+-- pruning with 0/0 rules
+-- 0 candidates of size 1
+-- 1 candidates of size 2
+-- tested 1 candidates
+snd (x,y)  =  y
+
+swap :: (A,A) -> (A,A)
+-- testing 4 combinations of argument values
+-- pruning with 10/10 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 1 candidates of size 4
+-- tested 2 candidates
+swap (x,y)  =  (y,x)
+
+curry :: ((A,A) -> A) -> A -> A -> A
+-- pruning with 10/10 rules
+-- 2 candidates of size 1
+-- 2 candidates of size 2
+-- 2 candidates of size 3
+-- 6 candidates of size 4
+-- tested 8 candidates
+curry f x y  =  f (x,y)
+
+uncurry :: (A -> A -> A) -> (A,A) -> A
+-- pruning with 10/10 rules
+-- 0 candidates of size 1
+-- 4 candidates of size 2
+-- 0 candidates of size 3
+-- 4 candidates of size 4
+-- tested 6 candidates
+uncurry f (x,y)  =  f x y
+
+pairwise :: [A] -> [(A,A)]
+-- testing 360 combinations of argument values
+-- pruning with 10/10 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 0 candidates of size 4
+-- 0 candidates of size 5
+-- 0 candidates of size 6
+-- 7 candidates of size 7
+-- 19 candidates of size 8
+-- 34 candidates of size 9
+-- tested 36 candidates
+pairwise []  =  []
+pairwise (x:xs)  =  (x,head xs):pairwise (tail xs)
+
+catpairs :: [(A,A)] -> [A]
+-- testing 360 combinations of argument values
+-- pruning with 12/13 rules
+-- 1 candidates of size 1
+-- 1 candidates of size 2
+-- 1 candidates of size 3
+-- 3 candidates of size 4
+-- 5 candidates of size 5
+-- 11 candidates of size 6
+-- 22 candidates of size 7
+-- 46 candidates of size 8
+-- 106 candidates of size 9
+-- tested 124 candidates
+catpairs []  =  []
+catpairs (xy:xys)  =  fst xy:snd xy:catpairs xys
+
+pairwise :: [A] -> [(A,A)]
+-- testing 360 combinations of argument values
+-- pruning with 10/10 rules
+-- 1 candidates of size 1
+-- 0 candidates of size 2
+-- 0 candidates of size 3
+-- 0 candidates of size 4
+-- 0 candidates of size 5
+-- 0 candidates of size 6
+-- 7 candidates of size 7
+-- 26 candidates of size 8
+-- tested 23 candidates
+pairwise []  =  []
+pairwise [x]  =  []
+pairwise (x:y:xs)  =  (x,y):pairwise xs
+
+catpairs :: [(A,A)] -> [A]
+-- testing 360 combinations of argument values
+-- pruning with 12/13 rules
+-- 1 candidates of size 1
+-- 1 candidates of size 2
+-- 1 candidates of size 3
+-- 3 candidates of size 4
+-- 6 candidates of size 5
+-- 15 candidates of size 6
+-- 31 candidates of size 7
+-- 68 candidates of size 8
+-- tested 97 candidates
+catpairs []  =  []
+catpairs ((x,y):xys)  =  x:y:catpairs xys
+
diff --git a/mk/depend.mk b/mk/depend.mk
--- a/mk/depend.mk
+++ b/mk/depend.mk
@@ -802,6 +802,23 @@
   src/Conjure/Conjurable.hs \
   src/Conjure/Conjurable/Derive.hs \
   eg/tri.hs
+eg/tuple: \
+  eg/tuple.hs \
+  mk/toplibs
+eg/tuple.o: \
+  src/Conjure/Utils.hs \
+  src/Conjure/Red.hs \
+  src/Conjure/Reason.hs \
+  src/Conjure/Prim.hs \
+  src/Conjure.hs \
+  src/Conjure/Expr.hs \
+  src/Conjure/Engine.hs \
+  src/Conjure/Defn/Test.hs \
+  src/Conjure/Defn/Redundancy.hs \
+  src/Conjure/Defn.hs \
+  src/Conjure/Conjurable.hs \
+  src/Conjure/Conjurable/Derive.hs \
+  eg/tuple.hs
 mk/All.o: \
   src/Conjure/Utils.hs \
   src/Conjure/Red.hs \
@@ -863,6 +880,8 @@
   src/Conjure/Defn/Test.hs \
   src/Conjure/Defn.hs \
   src/Conjure/Conjurable.hs
+src/Conjure/Engine: \
+  mk/toplibs
 src/Conjure/Engine.o: \
   src/Conjure/Utils.hs \
   src/Conjure/Red.hs \
diff --git a/mk/haskell.mk b/mk/haskell.mk
--- a/mk/haskell.mk
+++ b/mk/haskell.mk
@@ -69,6 +69,10 @@
 %.hugs: %.hs
 	$(HUGSCMD) $<
 
+.PHONY: %.runhaskell
+%.runhaskell: %.hs
+	$(GHCCMD) -O0 --run $<
+
 .PHONY: %.runhugs
 %.runhugs: %.hs
 	$(RUNHUGSCMD) $<
diff --git a/src/Conjure/Conjurable.hs b/src/Conjure/Conjurable.hs
--- a/src/Conjure/Conjurable.hs
+++ b/src/Conjure/Conjurable.hs
@@ -44,6 +44,9 @@
   , conjureArgumentPats
   , conjureMostGeneralCanonicalVariation
   , conjureCasesFor
+  , conjurePatternsFor
+  , conjureArgumentPatterns
+  , conjurePatternsDebug
   )
 where
 
@@ -390,14 +393,10 @@
 -- | Compute 'tiers' of values encoded as 'Expr's
 --   of the type of the given 'Expr'.
 conjureTiersFor :: Conjurable f => f -> Expr -> [[Expr]]
-conjureTiersFor f e  =  tf allTiers
-  where
-  allTiers :: [ [[Expr]] ]
-  allTiers  =  [etiers | (_,_,Just etiers,_,_,_) <- conjureReification f]
-  tf []  =  [[e]] -- no tiers found, keep variable
-  tf (etiers:etc)  =  case etiers of
-                      ((e':_):_) | typ e' == typ e -> etiers
-                      _                            -> tf etc
+conjureTiersFor f e  =
+  case [metiers | (eh,_,metiers,_,_,_) <- conjureReification f, typ e == typ eh] of
+  (Nothing:_) -> [[e]] -- no tiers found, keep variable
+  (Just etiers:_) -> etiers
 
 conjureGrounds :: Conjurable f => f -> Expr -> [Expr]
 conjureGrounds  =  grounds . conjureTiersFor
@@ -695,12 +694,12 @@
 
 -- | Computes tiers of sets of patterns for the given function.
 --
--- > > conjurePats [zero] "f" (undefined :: Int -> Int)
+-- > > conjurePats 1 [zero] "f" (undefined :: Int -> Int)
 -- > [[[f x :: Int]],[[f 0 :: Int,f x :: Int]]]
-conjurePats :: Conjurable f => [Expr] -> String -> f -> [[ [Expr] ]]
-conjurePats  es nm f  =  mapT (map mkApp)
-                      $  combinePatternOptions
-                      $  conjureArgumentPats es f
+conjurePats :: Conjurable f => Int -> [Expr] -> String -> f -> [[ [Expr] ]]
+conjurePats d es nm f  =  mapT (map mkApp)
+                       $  combinePatternOptions
+                       $  conjureArgumentPatterns d es f
   where
   mkApp  =  foldApp . (ef:)
          .  unfold
@@ -781,6 +780,82 @@
 --       but may be useful in the future if we decide to handle
 --       non-top-level case breakdowns.
 --       It can be used in building a replacement for conjureArgumentPats
+
+
+-- | Towards a replacement for conjurePats that allows non-top-level breakdowns...
+--
+-- This function is currently experimental and unused in Conjure.
+--
+-- > > conjurePatternsFor (undefined :: [Int] -> ()) (is_)
+-- > [ [ [ _ ] ]
+-- > , [ [ [], (_:_) ] ]
+-- > , [ [ [], [_], (_:_:_) ] ]
+-- > , [ [ [], [_], [_,_], (_:_:_:_) ] ]
+-- > , ...
+-- > ]
+--
+-- > > let mis_ = hole (undefined :: [Maybe Int])
+-- > > conjurePatternsFor (undefined :: [Maybe Int] -> ()) mis_
+-- > [ [ [ _ ] ]
+-- > , [ [ [], (_:_) ]
+-- > , [ [ [], (Nothing:_), (Just _:_) ]
+-- >   , [ [], [_], (_:_:_) ] ]
+-- > , [ [ [], [Nothing], [Just _], (_:_:_) ] ]
+-- > , ...
+-- > ]
+--
+-- Ints are /crudely/ supported
+--
+-- > ghci> putLL 4 $ conjurePatternsDebug [zero] (undefined :: [Int])
+-- > [_ :: [Int]]
+-- >
+-- > [[] :: [Int],_:_ :: [Int]]
+-- >
+-- > [[] :: [Int],[_] :: [Int],_:_:_ :: [Int]]
+-- > [[] :: [Int],_:_ :: [Int],0:_ :: [Int]]
+conjurePatternsFor :: Conjurable f => Int -> [Expr] -> f -> Expr -> [[ [Expr] ]]
+conjurePatternsFor d es f  =  cpf d . holeAsTypeOf
+  where
+  casesFor  =  conjureCasesFor f
+  cpf :: Int -> Expr -> [[ [Expr] ]]
+  cpf d h
+    | d == 0     =  [[ [h] ]]
+    | null cs    =  mapT (++ [h]) $ setsOf [[e] | e <- es, typ e == typ h]
+    | otherwise  =  [ [h] ] : rest
+    where
+    cs  =  casesFor h
+    rest :: [[ [Expr] ]]
+    rest  =  mapT nubSort
+          $  mapT (concatMap $ unfold . fill (fold cs))
+          $  productsWith prods
+          $  map (cpf (d-1))
+          $  concatMap holes cs
+    -- TODO: avoid nubSorting here, by somehow not generating repeats
+    -- the repeats appear because of the way we fold before concatMapping
+    -- this may cause a significant performance issue on wider types ([Bool])
+
+  productsWith :: ([a] -> b) -> [ [[a]] ] -> [[b]]
+  productsWith f  =  mapT f . products
+  -- TODO: move productsWith to LeanCheck?
+
+
+-- | A drop-in replacement for conjureArgumentPats.
+--
+-- Still not in use by Conjure.
+conjureArgumentPatterns :: Conjurable f => Int -> [Expr] -> f -> [ [[ [Expr] ]] ]
+conjureArgumentPatterns depth es f  =  map (conjurePatternsFor depth es f) $ conjureArgumentHoles f
+
+
+-- | Use this instead of conjurePatternsFor for simpler calling.
+--
+-- This is an interface for running internal experiments.
+-- It will go away in a future version of Conjure.
+conjurePatternsDebug :: Conjurable a => [Expr] -> a -> [[ [Expr] ]]
+conjurePatternsDebug es a  =  conjurePatternsFor (-1) es foo (hole a)
+  where
+  foo x  =  ()  where _  =  x `asTypeOf` a
+
+
 
 -- -- -- other Conjurable instances -- -- --
 
diff --git a/src/Conjure/Engine.hs b/src/Conjure/Engine.hs
--- a/src/Conjure/Engine.hs
+++ b/src/Conjure/Engine.hs
@@ -171,6 +171,7 @@
   , maxDeconstructionSize :: Int  -- ^ maximum size of deconstructions (e.g.: @_ - 1@)
   , maxConstantSize       :: Int  -- ^ maximum size of constants (0 for no limit)
   , maxPatternSize        :: Int  -- ^ maximum size of patterns (0 for no limit)
+  , maxPatternDepth       :: Int  -- ^ maximum depth of patterns
 
   -- advanced & debug options --
   , carryOn               :: Bool -- ^ whether to carry on after finding a suitable candidate
@@ -214,10 +215,11 @@
   , target                 =  10080
   , maxEvalRecursions      =  60
   , maxEquationSize        =   5
-  , maxSearchTests         =  100000
+  , maxSearchTests         =  110880
   , maxDeconstructionSize  =   4
   , maxConstantSize        =   0 -- unlimited
   , maxPatternSize         =   0 -- unlimited
+  , maxPatternDepth        =   1
 
   -- advanced & debug options --
   , carryOn                =  False
@@ -499,8 +501,8 @@
   , deconstructions
   )
   where
-  pats | maxPatternSize > 0  =  take maxPatternSize $ conjurePats es nm f
-       | otherwise           =                        conjurePats es nm f
+  pats | maxPatternSize > 0  =  take maxPatternSize $ conjurePats maxPatternDepth es nm f
+       | otherwise           =                        conjurePats maxPatternDepth es nm f
   fss  =  concatMapT ps2fss pats
   -- replaces the any guard symbol with a guard of the correct type
   es  =  [if isGuardSymbol e then conjureGuard f else e | (e,_) <- ps]
@@ -613,28 +615,38 @@
     where
     hs  =  nub $ conjureArgumentHoles f
 
+  recs :: Expr -> [[Expr]]
   recs ep  =  filterT (keepR ep)
            .  discardT (\e -> e == ep)
            $  recsV' (tail (vars ep))
+
+  recsV :: [Expr] -> [[Expr]]
   recsV vs  =  filterT (\e -> any (`elem` vs) (vars e))
             $  foldAppProducts ef [unguardT $ appsWith h vs | h <- conjureArgumentHoles f]
+
   -- like recs, but memoized
+  recs' :: Expr -> [[Expr]]
   recs' ep  =  fromMaybe errRP $ lookup ep eprs
     where
-    eprs = [(ep, recs ep) | ep <- possiblePats]
-  possiblePats  =  nubSort . concat . concat $ pats
+    eprs = [(ep, recs ep) | ep <- concat possiblePats]
+
+  possiblePats :: [[Expr]]
+  possiblePats  =  map (nubSort . concat) $ pats
+
   -- like recsV, but memoized
-  recsV' vs  =  fromMaybe errRV $ lookup vs evrs
+  recsV' :: [Expr] -> [[Expr]]
+  recsV' vs  =  fromMaybe errRV $ lookup (nubSort vs) evrs
     where
-    evrs = [(vs, recsV vs) | vs <- nubSort $ map (tail . vars) possiblePats]
+    evrs = [(vs, recsV vs) | vs <- concatMap nubSort $ mapT (nubSort . tail . vars) possiblePats]
 
+  errRP  =  error "candidateDefnsC: unexpected pattern.  You have found a bug, please report it."
+  errRV  =  error "candidateDefnsC: unexpected variables.  You have found a bug, please report it."
+
   thy  =  doubleCheck (===)
        .  theoryFromAtoms (===) maxEquationSize . (:[]) . nub
        $  cjHoles (prim nm f:ps) ++ [val False, val True] ++ es
   (===)  =  cjAreEqual (prim nm f:ps) maxTests
   isUnbreakable  =  conjureIsUnbreakable f
-  errRP  =  error "candidateDefnsC: unexpected pattern.  You have found a bug, please report it."
-  errRV  =  error "candidateDefnsC: unexpected variables.  You have found a bug, please report it."
 
 
 -- | Checks if the given pattern is a ground pattern.
diff --git a/src/Conjure/Utils.hs b/src/Conjure/Utils.hs
--- a/src/Conjure/Utils.hs
+++ b/src/Conjure/Utils.hs
@@ -46,6 +46,7 @@
   , both
   , (+++)
   , isSubsetOf
+  , prods
   )
 where
 
@@ -221,3 +222,7 @@
 (+++) :: Ord a => [a] -> [a] -> [a]
 (+++) = nubMerge
 infixr 5 +++
+
+prods :: [[a]] -> [[a]]
+prods []  =  [[]]
+prods (xs:xss)  =  [y:ys | y <- xs, ys <- prods xss]
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -57,7 +57,7 @@
 conjurableOK x  =  and
   [ holds 60 $ (-==-) ==== (==)
   , holds 60 $ expr' === expr
-  , tiers =| 6 |= (tiers -: [[x]])
+  , tiers' =| 6 |= (mapT val $ tiers -: [[x]])
   , all isWellTyped cases'
   , all (\c -> typ c == typeOf x) cases'
   ]
@@ -65,3 +65,4 @@
   (-==-)  =  evl (fromJust $ conjureEquality x) -:> x
   expr'  =  (conjureExpress x . val) -:> x
   cases'  =  conjureCases x
+  tiers'  =  conjureTiersFor x (hole x)
diff --git a/test/conjurable.hs b/test/conjurable.hs
--- a/test/conjurable.hs
+++ b/test/conjurable.hs
@@ -1,16 +1,12 @@
 -- Copyright (C) 2021-2025 Rudy Matela
 -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
 
-{-# Language DeriveDataTypeable, StandaloneDeriving #-}  -- for GHC < 7.10
-
 import Test
 import Data.Dynamic
 
 -- An Unit type that is not an Eq instance
 data Unit  =  Unit  deriving Show
 
-deriving instance Typeable Unit  -- for GHC < 7.10
-
 instance Listable Unit where list = [Unit]
 instance Name Unit where name _ = "u"
 instance Conjurable Unit where
@@ -132,7 +128,7 @@
   , map length (conjureArgumentCases (undefined :: () -> Bool -> Int -> [Int] -> [Bool] -> ()))
     == [1, 2, 0, 2, 2]
 
-  , conjurePats [zero, one] "f" (undefined :: Int -> Int)
+  , conjurePats 1 [zero, one] "f" (undefined :: Int -> Int)
     == [ [ [ ff xx
            ]
          ]
@@ -151,7 +147,7 @@
          ]
        ]
 
-  , conjurePats [] "f" (undefined :: [Int] -> Int)
+  , conjurePats 1 [] "f" (undefined :: [Int] -> Int)
     == [ [ [ ffs xxs
            ]
          ]
@@ -161,7 +157,7 @@
          ]
        ]
 
-  , take 3 (conjurePats [zero, one] "?" (undefined :: Int -> Int -> Int))
+  , take 3 (conjurePats 1 [zero, one] "?" (undefined :: Int -> Int -> Int))
     == [ [ [ xx -?- yy
            ]
          ]
@@ -189,14 +185,14 @@
          ]
        ]
 
-  , mapT length (conjurePats [zero, one] "foo" (undefined :: [Int] -> Int -> Int))
+  , mapT length (conjurePats 1 [zero, one] "foo" (undefined :: [Int] -> Int -> Int))
     == [[1],[2,2],[2,3,3],[3,3,4,3],[4,4,4,4],[5,4,5],[5,5],[6]]
 
 
-  , mapT length (conjurePats [zero, one] "foo" (undefined :: Int -> [Int] -> Int))
+  , mapT length (conjurePats 1 [zero, one] "foo" (undefined :: Int -> [Int] -> Int))
     == [[1],[2,2],[3,3,2],[4,3,3,3],[4,4,4,4],[5,5,5],[6]]
 
-  , conjurePats [] "foo" (undefined :: [Int] -> [Char] -> Int)
+  , conjurePats 1 [] "foo" (undefined :: [Int] -> [Char] -> Int)
     == [ [ [ ffoo xxs ccs
            ]
          ]
@@ -313,6 +309,27 @@
        , (zero -:- val [0::Int], zero -:- zero -:- nil)
        , (one -:- nil,           one -:- nil)
        ]
+
+  , conjureArgumentPatterns 1 [] (undefined :: Int -> ())
+      ==  conjureArgumentPats [] (undefined :: Int -> ())
+
+  , conjureArgumentPatterns 1 [] (undefined :: Bool -> ())
+      ==  conjureArgumentPats [] (undefined :: Bool -> ())
+
+  , conjureArgumentPatterns 1 [] (undefined :: [Bool] -> ())
+      ==  conjureArgumentPats [] (undefined :: [Bool] -> ())
+
+  , conjureArgumentPatterns 1 [zero, one] (undefined :: [Int] -> ())
+      ==  conjureArgumentPats [zero, one] (undefined :: [Int] -> ())
+
+  , conjureArgumentPatterns 1 [] (undefined :: [Maybe Bool] -> ())
+      ==  conjureArgumentPats [] (undefined :: [Maybe Bool] -> ())
+
+  , conjureArgumentPatterns 1 [] (undefined :: Maybe Bool -> ())
+      ==  conjureArgumentPats [] (undefined :: Maybe Bool -> ())
+
+  , conjureArgumentPatterns 1 [zero, one] (undefined :: Int -> ())
+      ==  conjureArgumentPats [zero, one] (undefined :: Int -> ())
   ]
 
 isNumeric :: Expr -> Bool
diff --git a/test/derive.hs b/test/derive.hs
--- a/test/derive.hs
+++ b/test/derive.hs
@@ -1,7 +1,6 @@
 -- Copyright (c) 2019-2025 Rudy Matela.
 -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
 {-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE DeriveDataTypeable #-}
 
 -- uncomment to debug derivation:
 -- {-# OPTIONS_GHC -ddump-splices #-}
@@ -9,33 +8,51 @@
 import Test hiding ((-:), (->:))
 -- -: and ->: should be generated by deriveConjurable
 
-data Choice  =  Ae | Bee | Cee deriving (Show, Eq, Typeable)
-data Peano  =  Zero | Succ Peano deriving (Show, Eq, Typeable)
-data Lst a  =  a :- Lst a | Nil deriving (Show, Eq, Typeable)
-data Bush a  =  Bush a :-: Bush a | Leaf a deriving (Show, Eq, Typeable)
-data Tree a  =  Node (Tree a) a (Tree a) | Null deriving (Show, Eq, Typeable)
+-- replication of Haskell's built-in data types
+-- in the order of the Haskell98 standard
+-- https://www.haskell.org/onlinereport/basic.html
+data  Choice  =  Yes | No  deriving (Show, Eq)
+data  Peano  =  Zero | Succ Peano  deriving (Show, Eq)
+data  Lst a  =  Nil | a :- Lst a  deriving (Show, Eq)
+data  Unit  =  Unit  deriving (Show, Eq)
+data  Perhaps a  =  Naught | Precisely a  deriving (Show, Eq)
+data  Alternatively a b  =  Sinister a | Dexter b  deriving (Show, Eq)
+data  Relation  =  Smaller | Same | Bigger  deriving (Show, Eq)
 
 deriveConjurable ''Choice
 deriveConjurable ''Peano
 deriveConjurable ''Lst
+deriveConjurable ''Unit
+deriveConjurable ''Perhaps
+deriveConjurable ''Alternatively
+deriveConjurable ''Relation
+
+-- tree types
+data  Bush a  =  Bush a :-: Bush a | Leaf a  deriving (Show, Eq)
+data  Tree a  =  Node (Tree a) a (Tree a) | Null  deriving (Show, Eq)
+
 deriveConjurable ''Bush
 deriveConjurable ''Tree
 
+-- inner/outer
+data  Inner  =  I  deriving (Eq, Ord, Show)
+data  Outer  =  O Inner  deriving (Eq, Ord, Show)
+
+deriveConjurableCascading ''Outer
+
 -- Nested datatype cascade
-data Nested  =  Nested N0 (N1 Int) (N2 Int Int) deriving (Eq, Show, Typeable)
-data N0      =  R0 Int deriving (Eq, Show, Typeable)
-data N1 a    =  R1 a   deriving (Eq, Show, Typeable)
-data N2 a b  =  R2 a b deriving (Eq, Show, Typeable)
+data  Nested  =  Nested N0 (N1 Int) (N2 Int Int)  deriving (Eq, Show)
+data  N0      =  R0 Int  deriving (Eq, Show)
+data  N1 a    =  R1 a    deriving (Eq, Show)
+data  N2 a b  =  R2 a b  deriving (Eq, Show)
 
 deriveConjurableCascading ''Nested
 
 -- Recursive nested datatype cascade
-data RN       =  RN RN0 (RN1 Int) (RN2 Int RN) deriving (Eq, Show, Typeable)
-data RN0      =  Nest0 Int | Recurse0 RN deriving (Eq, Show, Typeable)
-data RN1 a    =  Nest1 a   | Recurse1 RN deriving (Eq, Show, Typeable)
-data RN2 a b  =  Nest2 a b | Recurse2 RN deriving (Eq, Show, Typeable)
--- beware: values of the above type are always infinite!
---         derivation works but full evaluation does not terminate
+data  RN       =  RN RN0 (RN1 Int) (RN2 Int RN0)  deriving (Eq, Show)
+data  RN0      =  Nest0 Int | Recurse0 RN  deriving (Eq, Show)
+data  RN1 a    =  Nest1 a   | Recurse1 RN  deriving (Eq, Show)
+data  RN2 a b  =  Nest2 a b | Recurse2 RN  deriving (Eq, Show)
 
 deriveConjurableCascading ''RN
 
@@ -51,8 +68,8 @@
 deriveConjurableIfNeeded ''Maybe
 deriveConjurableIfNeeded ''Either
 
-data Mutual    =  Mutual0   | Mutual CoMutual deriving (Eq, Show, Typeable)
-data CoMutual  =  CoMutual0 | CoMutual Mutual deriving (Eq, Show, Typeable)
+data Mutual    =  Mutual0   | Mutual CoMutual deriving (Eq, Show)
+data CoMutual  =  CoMutual0 | CoMutual Mutual deriving (Eq, Show)
 
 deriveConjurableCascading ''Mutual
 
@@ -64,33 +81,55 @@
 tests n  =
   [ True
 
+  -- re-test standard types
   , conjurableOK (undefined :: Bool)
   , conjurableOK (undefined :: Int)
-  , conjurableOK (undefined :: Char)
-  , conjurableOK (undefined :: [Bool])
+  , conjurableOK (undefined :: [A])
   , conjurableOK (undefined :: [Int])
-  , conjurableOK (undefined :: String)
+  , conjurableOK (undefined :: [Bool])
+  , conjurableOK (undefined :: ())
+  , conjurableOK (undefined :: Maybe A)
+  , conjurableOK (undefined :: Either A B)
+  , conjurableOK (undefined :: Ordering)
 
+  -- replication of Haskell's built-in types
   , conjurableOK (undefined :: Choice)
   , conjurableOK (undefined :: Peano)
-  , conjurableOK (undefined :: Lst Int)
+  , conjurableOK (undefined :: Lst A)
+  , conjurableOK (undefined :: Lst Peano)
+  , conjurableOK (undefined :: Lst Choice)
+  , conjurableOK (undefined :: Unit)
+  , conjurableOK (undefined :: Perhaps A)
+  , conjurableOK (undefined :: Alternatively A B)
+  , conjurableOK (undefined :: Relation)
+
+  -- tree types
   , conjurableOK (undefined :: Bush Int)
   , conjurableOK (undefined :: Tree Int)
---, conjurableOK (undefined :: RN) -- TODO: FIX: infinite loop somewhere...
 
-  , conjureSize Ae == 1
-  , conjureSize Bee == 1
-  , conjureSize Cee == 1
+  , conjurableOK (undefined :: Inner)
+  , conjurableOK (undefined :: Outer)
+
+  , conjurableOK (undefined :: RN)
+  , conjurableOK (undefined :: Mutual)
+  , conjurableOK (undefined :: CoMutual)
+
+  , conjureSize Yes == 1
+  , conjureSize No == 1
+
   , conjureSize Zero == 1
   , conjureSize (Succ Zero) == 2
   , conjureSize (Succ (Succ Zero)) == 3
+  , conjureSize (Succ (Succ (Succ Zero))) == 4
+
   , conjureSize (Nil :: Lst Int) == 1
   , conjureSize (10 :- (20 :- Nil) :: Lst Int) == 33
 
+  , conjureSize Unit == 1
+
   , conjureCases choice
-    == [ val Ae
-       , val Bee
-       , val Cee
+    == [ val Yes
+       , val No
        ]
 
   , conjureCases peano
@@ -99,10 +138,16 @@
        ]
 
   , conjureCases (lst int)
-    == [ value ":-" ((:-) ->>: lst int) :$ hole int :$ hole (lst int)
-       , val (Nil :: Lst Int)
+    == [ val (Nil :: Lst Int)
+       , value ":-" ((:-) ->>: lst int) :$ hole int :$ hole (lst int)
        ]
 
+  , conjureCases relation
+    == [ val Smaller
+       , val Same
+       , val Bigger
+       ]
+
   , conjureCases (bush int)
     == [ value ":-:" ((:-:) ->>: bush int) :$ hole (bush int) :$ hole (bush int)
        , value "Leaf" (Leaf ->: bush int) :$ hole int
@@ -124,9 +169,13 @@
                                          , hole (undefined :: Bool)
                                          ]
   , conjureHoles (undefined :: Lst Int) == [ hole (undefined :: Int)
-                                            , hole (undefined :: Lst Int)
-                                            , hole (undefined :: Bool)
-                                            ]
+                                           , hole (undefined :: Lst Int)
+                                           , hole (undefined :: Bool)
+                                           ]
+  , conjureHoles (undefined :: Lst Peano) == [ hole (undefined :: Peano)
+                                             , hole (undefined :: Lst Peano)
+                                             , hole (undefined :: Bool)
+                                             ]
   , conjureHoles (undefined :: Nested) == [ hole (undefined :: N0)
                                           , hole (undefined :: N1 Int)
                                           , hole (undefined :: Int)
@@ -134,10 +183,10 @@
                                           , hole (undefined :: Nested)
                                           , hole (undefined :: Bool)
                                           ]
-  , conjureHoles (undefined :: RN) == [ hole (undefined :: RN0)
-                                      , hole (undefined :: RN1 Int)
+  , conjureHoles (undefined :: RN) == [ hole (undefined :: RN1 Int)
                                       , hole (undefined :: Int)
-                                      , hole (undefined :: RN2 Int RN)
+                                      , hole (undefined :: RN0)
+                                      , hole (undefined :: RN2 Int RN0)
                                       , hole (undefined :: RN)
                                       , hole (undefined :: Bool)
                                       ]
@@ -161,6 +210,9 @@
 
 lst :: a -> Lst a
 lst _  =  undefined
+
+relation :: Relation
+relation  =  undefined
 
 bush :: a -> Bush a
 bush _  =  undefined
diff --git a/test/utils.hs b/test/utils.hs
--- a/test/utils.hs
+++ b/test/utils.hs
@@ -20,4 +20,8 @@
   , sets [1]  ==  [[1], [] :: [Int]]
   , sets [1,2]  ==  [[1,2], [1], [2], [] :: [Int]]
   , sets [1,2,3]  ==  [[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[] :: [Int]]
+
+  , prods [ [1], [2], [3] ] == [[1,2,3]]
+  , prods [ [1,2], [3,4], [5] ] == [[1,3,5],[1,4,5],[2,3,5],[2,4,5]]
+  , prods [ [1,2], [3], [4,5] ] == [[1,3,4],[1,3,5],[2,3,4],[2,3,5]]
   ]
