diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,11 @@
 # Changelog for hasklepias
 
+## 0.18.0
+
+* Refactors the `Features.Compose` module a bit. Adds constructors for composing `Definition`. For example, `D1C :: (a2 -> a1 -> a)  -> Definition (F n1 b -> F n02 a2) -> Definition (F n1 b -> F n01 a1) -> Definition (F n1 b -> F n0 a )`. Such constructors allow one to build definitions from other definitions, provided the input types are the same. Note: there is *not* a single interface to these constructors like is provided by the `define` and `defineA` functions. The poorly designed `Eval` typeclass is now replaced with a single `eval` function which simply pattern matches on the various shapes of `Definition`s. I fully expect this module to get another refactor in the future, but it is shaping up OK.
+* Examples are updated according to the changes `define`/`eval`.
+* Adds the `buildNofXOrNofYWithGapBool` template, which can be used for the common feature or 2-outpatient events with at least some gap between them or 1 inpatient event.
+
 ## 0.17.1
 
 * Fixes silly mistake with git.
diff --git a/examples/ExampleCohort1.hs b/examples/ExampleCohort1.hs
--- a/examples/ExampleCohort1.hs
+++ b/examples/ExampleCohort1.hs
@@ -202,7 +202,8 @@
        -> Feature "isEnrolled" Status
        -> Feature "isContinuousEnrolled" Status
        )
-critEnrolled455 = buildContinuousEnrollment baselineInterval isEnrollmentEvent 30
+critEnrolled455 =
+  buildContinuousEnrollment baselineInterval isEnrollmentEvent 30
 
 -- | Exclude if the subject is dead before the time of index.
 critDead
@@ -278,10 +279,10 @@
  where
   crit1   = eval critFemale featEvs
   crit2   = eval critOver50 agefeat
-  crit3   = eval critEnrolled (featInd, featEvs)
-  crit4   = eval critEnrolled455 (featInd, featEvs, crit3)
-  crit5   = eval critDead (featInd, dead)
-  agefeat = eval age (featInd, featEvs)
+  crit3   = eval critEnrolled featInd featEvs
+  crit4   = eval critEnrolled455 featInd featEvs crit3
+  crit5   = eval critDead featInd dead
+  agefeat = eval age featInd featEvs
   dead    = eval deathDay featEvs
   featInd = featureIndex index
   featEvs = featureEvents events
@@ -290,10 +291,10 @@
 makeFeatureRunner :: Index Interval Day -> Events Day -> Featureset
 makeFeatureRunner index events = featureset
   (  packFeature idx
-  :| [ packFeature $ eval diabetes (idx, ef)
-     , packFeature $ eval ckd (idx, ef)
-     , packFeature $ eval ppi (idx, ef)
-     , packFeature $ eval glucocorticoids (idx, ef)
+  :| [ packFeature $ eval diabetes idx ef
+     , packFeature $ eval ckd idx ef
+     , packFeature $ eval ppi idx ef
+     , packFeature $ eval glucocorticoids idx ef
      ]
   )
  where
diff --git a/examples/ExampleFeatures1.hs b/examples/ExampleFeatures1.hs
--- a/examples/ExampleFeatures1.hs
+++ b/examples/ExampleFeatures1.hs
@@ -10,24 +10,32 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 module ExampleFeatures1
   ( exampleFeatures1Spec
   ) where
 
+import           Cohort                         ( AssessmentInterval )
 import           Control.Monad
 import           ExampleEvents
+import           Features                       ( HasAttributes )
 import           Hasklepias
+import           Hasklepias                     ( IntervalSizeable )
+import           Hasklepias.ReexportsUnsafe     ( ToJSON )
 import           Test.Hspec
 
 {-
 Index is defined as the first occurrence of an Orca bite.
 -}
 indexDef
-  :: (Ord a) => Definition (FeatureData (Events a) -> FeatureData (Interval a))
+  :: (Ord a)
+  => Definition
+       (Feature "events" (Events a) -> Feature "index" (Index Interval a))
 indexDef = defineA
   (\events -> case firstConceptOccurrence ["wasBitByOrca"] events of
-    Nothing -> featureDataL (Other "No occurrence of Orca bite")
-    Just x  -> pure (getInterval x)
+    Nothing -> makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+    Just x  -> pure (makeIndex $ getInterval x)
   )
 
 {-  
@@ -36,70 +44,76 @@
 as an argument, so that the baseline FeatureData can be used to filter events
 based on different predicate functions.
 -}
-bline
-  :: (IntervalSizeable a b)
-  => FeatureData (Events a)
-  -> FeatureData (Interval a)
-bline x = fmap (enderval 60 . begin) (eval indexDef x)
+bline :: (IntervalSizeable a b) => Index Interval a -> AssessmentInterval a
+bline = makeBaselineFromIndex 60
 
-flwup
-  :: (IntervalSizeable a b)
-  => FeatureData (Events a)
-  -> FeatureData (Interval a)
-flwup x = fmap (beginerval 30 . begin) (eval indexDef x)
+flwup :: (IntervalSizeable a b) => Index Interval a -> AssessmentInterval a
+flwup = makeFollowupFromIndex 30
 
 {-
-Define enrolled as the indicator of whether all of the gaps between the union of 
-all enrollment intervals (+ allowableGap) 
--}
-enrolled :: (IntervalSizeable a b) => b -> Interval a -> Events a -> Bool
-enrolled allowableGap i events =
-  events
-    |> makeConceptsFilter ["enrollment"]
-    |> combineIntervals
-    |> gapsWithin i
-    |> maybe False (all (< allowableGap) . durations)
-
-enrolledDef
-  :: IntervalSizeable a b
-  => b
-  -> Definition
-       (  FeatureData (Interval a)
-       -> FeatureData (Events a)
-       -> FeatureData Bool
-       )
-enrolledDef allowableGap = define (enrolled allowableGap)
-{-
 Define features that identify whether a subject as bit/struck by a duck and
 bit/struck by a macaw.
 -}
-makeHxDef
-  :: (Ord a) => [Text] -> Interval a -> Events a -> (Bool, Maybe (Interval a))
-makeHxDef cnpts i events =
+makeHx
+  :: (Ord a)
+  => [Text]
+  -> AssessmentInterval a
+  -> Events a
+  -> (Bool, Maybe (Interval a))
+makeHx cnpts i events =
   (isNotEmpty (f i events), lastMay $ intervals (f i events))
   where f i x = makePairedFilter enclose i (`hasConcepts` cnpts) x
 
-duckHxDef :: (Ord a) => Interval a -> Events a -> (Bool, Maybe (Interval a))
-duckHxDef = makeHxDef ["wasBitByDuck", "wasStruckByDuck"]
+duckHx
+  :: (Ord a) => AssessmentInterval a -> Events a -> (Bool, Maybe (Interval a))
+duckHx = makeHx ["wasBitByDuck", "wasStruckByDuck"]
 
-macawHxDef :: (Ord a) => Interval a -> Events a -> (Bool, Maybe (Interval a))
-macawHxDef = makeHxDef ["wasBitByMacaw", "wasStruckByMacaw"]
+duckHxDef
+  :: (Ord a)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "duck history" (Bool, Maybe (Interval a))
+       )
+duckHxDef = define duckHx
 
+macawHx
+  :: (Ord a) => AssessmentInterval a -> Events a -> (Bool, Maybe (Interval a))
+macawHx = makeHx ["wasBitByMacaw", "wasStruckByMacaw"]
+
+macawHxDef
+  :: (Ord a)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "macaw history" (Bool, Maybe (Interval a))
+       )
+macawHxDef = define macawHx
+
 -- | a helper function for 'twoMinorOrOneMajorDef' 
 twoXOrOneY :: [Text] -> [Text] -> Events a -> Bool
 twoXOrOneY x y es = atleastNofX 2 x es || atleastNofX 1 y es
 
 -- | Define an event that identifies whether the subject has two minor or one major
 --   surgery.
-twoMinorOrOneMajorDef :: (Ord a) => Interval a -> Events a -> Bool
-twoMinorOrOneMajorDef i events =
+twoMinorOrOneMajor :: (Ord a) => AssessmentInterval a -> Events a -> Bool
+twoMinorOrOneMajor i events =
   twoXOrOneY ["hadMinorSurgery"] ["hadMajorSurgery"] (filterEnclose i events)
 
+twoMinorOrOneMajorDef
+  :: (Ord a)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "two major or one minor" Bool
+       )
+twoMinorOrOneMajorDef = define twoMinorOrOneMajor
+
 -- | Time from end of baseline to end of most recent Antibiotics
 --   with 5 day grace period
-timeSinceLastAntibioticsDef
-  :: (IntervalSizeable a b) => Interval a -> Events a -> Maybe b
-timeSinceLastAntibioticsDef i =
+timeSinceLastAntibiotics
+  :: (IntervalSizeable a b) => AssessmentInterval a -> Events a -> Maybe b
+timeSinceLastAntibiotics i =
   lastMay                                 -- want the last one
     . map (max 0 . diff (end i) . end)        -- distances between end of baseline and antibiotic intervals
     . filterNotDisjoint i                     -- filter to intervals not disjoint from baseline interval
@@ -107,25 +121,47 @@
     . map (expandr 5)                         -- allow grace period
     . makeConceptsFilter ["tookAntibiotics"]  -- filter to only antibiotics events 
 
+timeSinceLastAntibioticsDef
+  :: (IntervalSizeable a b)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "time since antibiotics" (Maybe b)
+       )
+timeSinceLastAntibioticsDef = define timeSinceLastAntibiotics
+
+
 -- | Count of hospital events in a interval and duration of the last one
-countOfHospitalEventsDef
-  :: (IntervalSizeable a b) => Interval a -> Events a -> (Int, Maybe b)
-countOfHospitalEventsDef i =
+countOfHospitalEvents
+  :: (IntervalSizeable a b)
+  => AssessmentInterval a
+  -> Events a
+  -> (Int, Maybe b)
+countOfHospitalEvents i =
   (\x -> (length x, duration <$> lastMay x))
     . filterNotDisjoint i                    -- filter to intervals not disjoint from interval
     . combineIntervals                       -- combine overlapping intervals
     . makeConceptsFilter ["wasHospitalized"]  -- filter to only antibiotics events
 
+countOfHospitalEventsDef
+  :: (IntervalSizeable a b)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "count of hospitalizations" (Int, Maybe b)
+       )
+countOfHospitalEventsDef = define countOfHospitalEvents
+
 -- | time of distcontinuation of antibiotics
 --   and time from start of follow up
 --   This needs to be generalized as Nothing could either indicate they didn't 
 --   discontinue or that they simply got no antibiotics records.
-so :: Ord a => ComparativePredicateOf1 (Interval a)
+so :: Intervallic i a => ComparativePredicateOf1 (i a)
 so = unionPredicates [startedBy, overlappedBy]
 
-discontinuationDef
-  :: (IntervalSizeable a b) => Interval a -> Events a -> Maybe (a, b)
-discontinuationDef i events =
+discontinuation
+  :: (IntervalSizeable a b) => AssessmentInterval a -> Events a -> Maybe (a, b)
+discontinuation i events =
   (\x -> Just
       ( begin x       -- we want the begin of this interval 
       , diff (begin x) (begin i)
@@ -133,7 +169,7 @@
     )
     =<< headMay                    -- if there are any gaps the first one is the first discontinuation
     =<< gapsWithin i               -- find gaps to intervals clipped to i
-    =<< ( nothingIfNone (so i)      -- if none of the intervals start or overlap 
+    =<< ( nothingIfNone (so (getInterval i))      -- if none of the intervals start or overlap 
                         -- the followup, then never started antibiotics
         . combineIntervals          -- combine overlapping intervals
         . map (expandr 5)           -- allow grace period
@@ -142,31 +178,50 @@
         )
           events
 
+discontinuationDef
+  :: (IntervalSizeable a b)
+  => Definition
+       (  Feature "index" (AssessmentInterval a)
+       -> Feature "events" (Events a)
+       -> Feature "discontinuation" (Maybe (a, b))
+       )
+discontinuationDef = define discontinuation
+
+{-
+  Tests
+-}
+
 type MyData
-  = ( FeatureData (Interval Int)
-    , FeatureData Bool
-    , FeatureData (Bool, Maybe (Interval Int))
-    , FeatureData (Bool, Maybe (Interval Int))
-    , FeatureData Bool
-    , FeatureData (Maybe Int)
-    , FeatureData (Int, Maybe Int)
-    , FeatureData (Maybe (Int, Int))
+  = ( Feature "index" (Index Interval Int)
+    , Feature "enrolled" Status
+    , Feature "duck history" (Bool, Maybe (Interval Int))
+    , Feature "macaw history" (Bool, Maybe (Interval Int))
+    , Feature "two major or one minor" Bool
+    , Feature "time since antibiotics" (Maybe Int)
+    , Feature "count of hospitalizations" (Int, Maybe Int)
+    , Feature "discontinuation" (Maybe (Int, Int))
     )
 
 getUnitFeatures :: Events Int -> MyData
 getUnitFeatures x =
-  ( eval indexDef        evs
-  , eval (enrolledDef 8) (bline evs, evs)
-  -- TODO: feature functions below need to be put into a FeatureDefintion
-  --      in order to run eval
-  , liftA2 duckHxDef                   (bline evs) evs
-  , liftA2 macawHxDef                  (bline evs) evs
-  , liftA2 twoMinorOrOneMajorDef       (bline evs) evs
-  , liftA2 timeSinceLastAntibioticsDef (bline evs) evs
-  , liftA2 countOfHospitalEventsDef    (bline evs) evs
-  , liftA2 discontinuationDef          (flwup evs) evs
+  ( idx
+  , eval
+    (buildContinuousEnrollment bline (containsConcepts ["enrollment"]) 60)
+    idx
+    evs
+    (pure Include)
+  , eval duckHxDef                   bl evs
+  , eval macawHxDef                  bl evs
+  , eval twoMinorOrOneMajorDef       bl evs
+  , eval timeSinceLastAntibioticsDef bl evs
+  , eval countOfHospitalEventsDef    bl evs
+  , eval discontinuationDef          fl evs
   )
-  where evs = pure x
+ where
+  evs = pure x
+  idx = eval indexDef evs
+  bl  = fmap bline idx
+  fl  = fmap flwup idx
 
 
 includeAll :: Events Int -> Criteria
@@ -179,8 +234,8 @@
 
 example1results :: MyData
 example1results =
-  ( pure (beginerval 1 (60 :: Int))
-  , pure True
+  ( pure $ makeIndex (beginerval 1 (60 :: Int))
+  , pure Include
   , pure (True, Just $ beginerval 1 (51 :: Int))
   , pure (False, Nothing)
   , pure True
@@ -191,14 +246,14 @@
 
 example2results :: MyData
 example2results =
-  ( featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
-  , featureDataL (Other "No occurrence of Orca bite")
+  ( makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
+  , makeFeature $ featureDataL (Other "No occurrence of Orca bite")
   )
 
 exampleFeatures1Spec :: Spec
diff --git a/examples/ExampleFeatures2.hs b/examples/ExampleFeatures2.hs
--- a/examples/ExampleFeatures2.hs
+++ b/examples/ExampleFeatures2.hs
@@ -17,8 +17,8 @@
 import           Hasklepias
 import           Test.Hspec
 
-durationOfHospitalizedAntibiotics ::
-  (Show a, IntervalSizeable a b) => Events a -> FeatureData [b]
+durationOfHospitalizedAntibiotics
+  :: (Show a, IntervalSizeable a b) => Events a -> FeatureData [b]
 durationOfHospitalizedAntibiotics es | null y = featureDataL $ Other "no cases"
                                      | otherwise = pure $ durations y
  where
diff --git a/examples/ExampleFeatures4.hs b/examples/ExampleFeatures4.hs
--- a/examples/ExampleFeatures4.hs
+++ b/examples/ExampleFeatures4.hs
@@ -460,7 +460,7 @@
   :: (Integral b, IntervalSizeable a b)
   => [Event a]
   -> Feature "pcskProtocols" (Protocols b)
-testProtocols input = eval pcskProtocols (idx, pcev)
+testProtocols input = eval pcskProtocols idx pcev
  where
   evs  = pure input
   idx  = eval index evs
@@ -512,16 +512,16 @@
      , Feature "accident" (NegOutcomes b)
      )
 testOutcomes input =
-  (eval o1 (idx, flevs, prot, ctime), eval o2 (idx, flevs, prot, ctime))
+  (eval o1 idx flevs prot ctime, eval o2 idx flevs prot ctime)
  where
   evs   = pure input
   idx   = eval index evs
-  flevs = eval flupEvents (idx, evs)
+  flevs = eval flupEvents idx evs
   pcev  = eval pcskEvents evs
   dth   = eval death flevs
-  disen = eval disenrollment (idx, evs)
-  prot  = eval pcskProtocols (idx, pcev)
-  ctime = eval censorTime (dth, disen)
+  disen = eval disenrollment idx evs
+  prot  = eval pcskProtocols idx pcev
+  ctime = eval censorTime dth disen
 
 p1Outcomes
   :: (Integral b)
diff --git a/hasklepias.cabal b/hasklepias.cabal
--- a/hasklepias.cabal
+++ b/hasklepias.cabal
@@ -1,6 +1,6 @@
 cabal-version:  2.2
 name:           hasklepias
-version:        0.17.1
+version:        0.18.0
 description:    Please see the README on GitHub at <https://github.com/novisci/asclepias#readme>
 homepage:       https://github.com/novisci/asclepias/#readme
 bug-reports:    https://github.com/novisci/asclepias/issues
diff --git a/src/Cohort/Criteria.hs b/src/Cohort/Criteria.hs
--- a/src/Cohort/Criteria.hs
+++ b/src/Cohort/Criteria.hs
@@ -50,7 +50,7 @@
 
 -- | Defines the return type for @'Criterion'@ indicating whether to include or 
 -- exclude a subject.
-data Status = Include | Exclude deriving (Eq, Show)
+data Status = Include | Exclude deriving (Eq, Show, Generic)
 
 -- | Defines subject's diposition in a cohort either included or which criterion
 -- they were excluded by. See @'checkCohortStatus'@ for evaluating a @'Criteria'@
diff --git a/src/EventData.hs b/src/EventData.hs
--- a/src/EventData.hs
+++ b/src/EventData.hs
@@ -10,7 +10,7 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 
 module EventData
-  ( -- * Events
+  ( -- ** Event Core
     module EventData.Core
     -- ** Event Contexts
   , module EventData.Context
@@ -24,6 +24,8 @@
   , module EventData.Aeson
     -- ** Generating arbitrary events
   , module EventData.Arbitrary
+
+
   ) where
 
 import           EventData.Aeson
@@ -33,4 +35,5 @@
 import           EventData.Core
 import           EventData.Predicates
 import           EventData.Accessors
+
 
diff --git a/src/Features/Compose.hs b/src/Features/Compose.hs
--- a/src/Features/Compose.hs
+++ b/src/Features/Compose.hs
@@ -12,18 +12,11 @@
 {-# LANGUAGE Safe #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE FunctionalDependencies #-}
-{-# LANGUAGE AllowAmbiguousTypes #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE InstanceSigs #-}
 
 module Features.Compose
   (
@@ -49,7 +42,7 @@
   , DefineA(..)
 
   --- *** Evalution of Definitions
-  , Eval(..)
+  , eval
 
   ) where
 
@@ -277,16 +270,37 @@
 See @'eval'@ for evaluating @Defintions@. 
 
 -}
-data Definition d where
-  D1  ::(b -> a) -> Definition (f1 b -> f0 a)
-  D1A ::(b -> f0 a) -> Definition (f1 b -> f0 a)
-  D2  ::(c -> b -> a) -> Definition (f2 c -> f1 b -> f0 a)
-  D2A ::(c -> b -> f0 a) -> Definition (f2 c -> f1 b -> f0 a)
-  D3  ::(d -> c -> b -> a) -> Definition (f3 d -> f2 c -> f1 b -> f0 a)
-  D3A ::(d -> c -> b -> f0 a) -> Definition (f3 d -> f2 c -> f1 b -> f0 a)
-  D4  ::(e -> d -> c -> b -> a) -> Definition (f4 e -> f3 d -> f2 c -> f1 b -> f0 a)
-  D4A ::(e -> d -> c -> b -> f0 a) -> Definition (f4 e -> f3 d -> f2 c -> f1 b -> f0 a)
 
+type F n d = Feature n d
+data Definition d  where
+  Pure :: a -> Definition (F n0 a )
+  D1  :: (b -> a) -> Definition (F n1 b -> F n0 a)
+  D1A :: (b -> F n0 a) -> Definition (F n1 b -> F n0 a)
+  D1C  :: (a2 -> a1 -> a) 
+          -> Definition (F n1 b -> F n02 a2)
+          -> Definition (F n1 b -> F n01 a1)
+          -> Definition (F n1 b -> F n0 a )
+  D2  :: (c -> b -> a) -> Definition (F n2 c -> F n1 b -> F n0 a)
+  D2A :: (c -> b -> F n0 a) -> Definition (F n2 c -> F n1 b -> F n0 a)
+  D2C  :: (a2 -> a1 -> a) 
+          -> Definition (F n2 c -> F n1 b -> F n02 a2)
+          -> Definition (F n2 c -> F n1 b -> F n01 a1)
+          -> Definition (F n2 c -> F n1 b -> F n0 a )       
+  D3  :: (d -> c -> b -> a) -> Definition (F n3 d -> F n2 c -> F n1 b -> F n0 a)
+  D3A :: (d -> c -> b -> F n0 a) -> Definition (F n3 d -> F n2 c -> F n1 b -> F n0 a) 
+  D3C  :: (a2 -> a1 -> a) 
+          -> Definition (F n3 d -> F n2 c -> F n1 b -> F n02 a2)
+          -> Definition (F n3 d -> F n2 c -> F n1 b -> F n01 a1)
+          -> Definition (F n3 d -> F n2 c -> F n1 b -> F n0 a )    
+  D4  :: (e -> d -> c -> b -> a) 
+         -> Definition (F n4 e -> F n3 d -> F n2 c -> F n1 b -> F n0 a)
+  D4A :: (e -> d -> c -> b -> F n0 a) 
+           -> Definition (F n4 e -> F n3 d -> F n2 c -> F n1 b -> F n0 a)
+  D4C  :: (a2 -> a1 -> a) 
+          -> Definition (F n4 e -> F n3 d -> F n2 c -> F n1 b -> F n02 a2)
+          -> Definition (F n4 e -> F n3 d -> F n2 c -> F n1 b -> F n01 a1)
+          -> Definition (F n4 e -> F n3 d -> F n2 c -> F n1 b -> F n0 a )    
+
 {- | Define (and @'DefineA@) provide a means to create new @'Definition'@s via 
 @'define'@ (@'defineA'@). The @'define'@ function takes a single function input 
 and returns a lifted function. For example,
@@ -320,28 +334,8 @@
 class Define inputs def | def -> inputs where
   define :: inputs -> Definition def
 
--- | See @'Define'@.
-class DefineA inputs def | def -> inputs where
-  defineA :: inputs -> Definition def
-
-instance Define (b -> a) (FeatureData b -> FeatureData a) where
-  define = D1
-instance Define (c -> b -> a) (FeatureData c -> FeatureData b -> FeatureData a) where
-  define = D2
-instance Define (d -> c -> b -> a) (FeatureData d -> FeatureData c -> FeatureData b -> FeatureData a) where
-  define = D3
-instance Define (e -> d -> c -> b -> a) (FeatureData e -> FeatureData d -> FeatureData c -> FeatureData b -> FeatureData a) where
-  define = D4
-
-instance DefineA (b -> FeatureData a) (FeatureData b -> FeatureData a) where
-  defineA = D1A
-instance DefineA (c -> b -> FeatureData a) (FeatureData c -> FeatureData b -> FeatureData a) where
-  defineA = D2A
-instance DefineA (d -> c -> b -> FeatureData a) (FeatureData d -> FeatureData c -> FeatureData b -> FeatureData a) where
-  defineA = D3A
-instance DefineA (e -> d -> c -> b -> FeatureData a) (FeatureData e -> FeatureData d -> FeatureData c -> FeatureData b -> FeatureData a) where
-  defineA = D4A
-
+instance Define a (Feature n0 a) where
+  define = Pure
 instance Define (b -> a) (Feature n1 b -> Feature n0 a) where
   define = D1
 instance Define (c -> b -> a) (Feature n2 c -> Feature n1 b -> Feature n0 a) where
@@ -351,6 +345,10 @@
 instance Define (e -> d -> c -> b -> a) (Feature n4 e -> Feature n3 d -> Feature n2 c -> Feature n1 b -> Feature n0 a) where
   define = D4
 
+-- | See @'Define'@.
+class DefineA inputs def | def -> inputs where
+  defineA :: inputs -> Definition def
+
 instance DefineA (b -> Feature n0 a) (Feature n1 b -> Feature n0 a) where
   defineA = D1A
 instance DefineA (c -> b -> Feature n0 a) (Feature n2 c -> Feature n1 b -> Feature n0 a) where
@@ -360,7 +358,6 @@
 instance DefineA (e -> d -> c -> b -> Feature n0 a) (Feature n4 e -> Feature n3 d -> Feature n2 c -> Feature n1 b -> Feature n0 a) where
   defineA = D4A
 
-
 {- | Evaluate a @Definition@. Note that (currently), the second argument of 'eval'
 is a *tuple* of inputs. For example,
 
@@ -379,73 +376,37 @@
 b :: Feature "B" String
 b = pure "yes"
 
-c = eval myFeature (a, b)
+c = eval myFeature a b
 @
 
 -}
-class Eval def args return | def -> args return where
-  eval :: Definition def -- ^ a @'Definition'@
-                     -> args -- ^ a tuple of arguments to the @'Definition'@
-                     -> return
 
-instance Eval (FeatureData b -> FeatureData a)
-              (FeatureData b)  (FeatureData a) where
-  eval (D1  f) x = fmap f x
-  eval (D1A f) x = x >>= f
-
-instance Eval (Feature n1 b -> Feature n0 a)
-              (Feature n1 b)  (Feature n0 a) where
-  eval (D1  f) (MkFeature x) = MkFeature $ fmap f x
-  eval (D1A f) (MkFeature x) = case fmap f x of
-    MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
-    MkFeatureData (Right r) -> r
-
-
-instance Eval (FeatureData c -> FeatureData b -> FeatureData a)
-              (FeatureData c,   FeatureData b) (FeatureData a) where
-  eval (D2  f) (x, y) = liftA2 f x y
-  eval (D2A f) (x, y) = join (liftA2 f x y)
-
-instance Eval (Feature n2 c -> Feature n1 b -> Feature n0 a)
-              (Feature n2 c,   Feature n1 b)  (Feature n0 a)
-  where
-  eval (D2  f) (MkFeature x, MkFeature y) = MkFeature $ liftA2 f x y
-  eval (D2A f) (MkFeature x, MkFeature y) = case liftA2 f x y of
-    MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
-    MkFeatureData (Right r) -> r
-
-instance Eval (FeatureData d -> FeatureData c -> FeatureData b -> FeatureData a)
-              (FeatureData d,   FeatureData c,   FeatureData b)  (FeatureData a)
-  where
-  eval (D3  f) (x, y, z) = liftA3 f x y z
-  eval (D3A f) (x, y, z) = join (liftA3 f x y z)
-
-instance Eval (Feature n3 d -> Feature n2 c -> Feature n1 b -> Feature n0 a)
-              (Feature n3 d,   Feature n2 c,   Feature n1 b)  (Feature n0 a)
-   where
-  eval (D3 f) (MkFeature x, MkFeature y, MkFeature z) =
+eval :: Definition d -> d
+eval d = case d of
+  Pure x -> pure x
+  D1 f  -> \(MkFeature x) -> MkFeature $ fmap f x
+  D1A f -> \(MkFeature x) -> 
+            case fmap f x of
+                MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
+                MkFeatureData (Right r) -> r
+  D1C f d1 d2 -> \x -> MkFeature $ liftA2 f (getFData (eval d1 x)) (getFData (eval d2 x))
+  D2  f -> \(MkFeature x) (MkFeature y) -> MkFeature $ liftA2 f x y
+  D2A f -> \(MkFeature x) (MkFeature y) -> 
+            case liftA2 f x y of
+                MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
+                MkFeatureData (Right r) -> r
+  D2C f d1 d2 -> \x y -> MkFeature $ liftA2 f (getFData (eval d1 x y)) (getFData (eval d2 x y))
+  D3 f -> \(MkFeature x) (MkFeature y) (MkFeature z) -> 
     MkFeature $ liftA3 f x y z
-  eval (D3A f) (MkFeature x, MkFeature y, MkFeature z) = case liftA3 f x y z of
-    MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
-    MkFeatureData (Right r) -> r
-
-instance Eval (Feature n4 e -> Feature n3 d -> Feature n2 c -> Feature n1 b -> Feature n0 a)
-              (Feature n4 e,   Feature n3 d,   Feature n2 c,   Feature n1 b)  (Feature n0 a)
-   where
-  eval (D4 f) (MkFeature v, MkFeature x, MkFeature y, MkFeature z) =
+  D3A f -> \(MkFeature x) (MkFeature y) (MkFeature z) -> 
+            case liftA3 f x y z of
+                MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
+                MkFeatureData (Right r) -> r
+  D3C f d1 d2 -> \x y z -> MkFeature $ liftA2 f (getFData $ eval d1 x y z) (getFData $ eval d2 x y z)
+  D4 f -> \(MkFeature v) (MkFeature x) (MkFeature y) (MkFeature z) ->
     MkFeature $ liftM4 f v x y z
-  eval (D4A f) (MkFeature v, MkFeature x, MkFeature y, MkFeature z) =
-    case liftM4 f v x y z of
-      MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
-      MkFeatureData (Right r) -> r
-
-{- | Initializes @Feature@ @Attributes@ to empty strings -}
-
--- class HasAttributes n a where
---   getAttributes :: Feature n a -> Attributes
---   getAttributes _ = MkAttributes "" "" ""
-
--- instance (KnownSymbol n) => HasAttributes n a where
-  -- getAttributes :: Feature n a -> Attributes
-  -- getAttributes _ = MkAttributes "" "" ""
-
+  D4A f -> \(MkFeature v) (MkFeature x) (MkFeature y) (MkFeature z) ->
+            case liftM4 f v x y z of
+              MkFeatureData (Left  l) -> MkFeature $ MkFeatureData (Left l)
+              MkFeatureData (Right r) -> r
+  D4C f d1 d2 -> \v x y z -> MkFeature $ liftA2 f (getFData $ eval d1 v x y z) (getFData $ eval d2 v x y z)
diff --git a/src/Hasklepias.hs b/src/Hasklepias.hs
--- a/src/Hasklepias.hs
+++ b/src/Hasklepias.hs
@@ -6,13 +6,21 @@
 License     : BSD3
 Maintainer  : bsaul@novisci.com
 
-See the examples folder and manual for further documentation.
 -}
 
 module Hasklepias
-  ( module EventData
+  ( -- $about
 
+    -- * Event Data
+    {-|
+    Events depend heavily on the [interval-algebra library](https://hackage.haskell.org/package/interval-algebra). 
+    See that pacakge's documentation for information about the types and functions 
+    for working with intervals.
+    -}
+    module EventData
+
     -- * Working with Features
+    -- $features
   , module Features
 
     -- * Feature definition builders 
@@ -47,11 +55,10 @@
   , module Hasklepias.ReexportsUnsafe
   ) where
 
+
+
 import           EventData
 
-import           IntervalAlgebra
-import           IntervalAlgebra.IntervalUtilities
-import           IntervalAlgebra.PairedInterval
 
 import           Features
 
@@ -65,3 +72,222 @@
 import           Hasklepias.Templates
 
 import           Stype
+
+{- $about
+
+@Hasklepias@ is an embedded domain specific language (eDSL) written in [Haskell](https://www.haskell.org/).
+To get started, then, you'll need to install the Haskell toolchain, especially 
+the [Glasgow Haskell Compiler](https://www.haskell.org/ghc/) (GHC) and the building
+and packaging system [cabal](https://www.haskell.org/cabal), for which you can
+use the [@ghcup@ utility](https://www.haskell.org/ghcup).
+
+You can use any development environment you choose, but for maximum coding pleasure,
+you should install the [Haskell language server](https://github.com/haskell/haskell-language-server)
+(@hsl@). This can be installed using @ghcup@. Some integrated development
+environments, such as [Visual Studio Code](https://code.visualstudio.com/, have 
+[excellent @hsl@ integration](https://marketplace.visualstudio.com/items?itemName=haskell.haskell).
+
+In summary,
+
+* Install [@ghcup@](https://www.haskell.org/ghcup).
+
+@ 
+  curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
+@
+* Inspect your toolchain installation using @ghcup list@. You will need @ghc@ 
+(>= 8.10.4) , @hls@ (>= 1.2), and @cabal@ (>= 3.4) installed.
+* Upgrade toolchain components as necesarry. For example:
+
+@
+  ghcup install ghc {ghcVersion}
+  ghcup set ghc {ghcVersion}
+  ghcup install cabal {cabalVersion}
+  ghcup set cabal {cabalVersion} 
+@
+
+* Setup your IDE. (e.g. in Visual Studio, you'll want to install the [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) extension.
+
+=== Getting started in Haskell
+
+Since @Hasklepias@ is written in [Haskell](https://www.haskell.org/), you'll need 
+to understand the syntax of Haskell function and a few concepts. The Haskell 
+language is over 30 years old and has many, many features. Here are a few resources:
+
+* [Learn You a Haskell for Great Good!](http://learnyouahaskell.com/chapters): good intro text
+* [Programming in Haskell](https://www.cs.nott.ac.uk/~pszgmh/pih.html): excellent intro text
+* [What I wish I knew when learning Haskell](http://dev.stephendiehl.com/hask/): excellent resource
+* [Haskeller competency matrix](https://gist.github.com/graninas/833a9ff306338aefec7e543100c16ea1)
+* [Hoogle](https://hoogle.haskell.org/): search engine for Haskell functions
+* [5 years of Haskell in production](https://www.youtube.com/watch?v=hZgW4mT1PkE[): video on using Haskell in production environment
+* [Things software engineers trip up on when learning Haskell](https://williamyaoh.com/posts/2020-04-12-software-engineer-hangups.html): a software engineer's list of tips on using Haskell
+
+=== Interacting with the examples (using GHCi)
+
+To run the examples interactively, open a @ghci@ session with:
+
+@
+cabal repl hasklepias:examples 
+@
+
+In @ghci@ you have access to all exposed functions in @hasklepias@, @interval-algebra@, 
+and those in the [examples](https://github.com/novisci/asclepias/tree/master/examples) folder.
+
+-}
+
+{- $features
+
+A 'Feature' is a type parametrized by two types: @name@ and @d@. The type @d@ here
+stands for "data", which then parametrizes the 'FeatureData' type which is the 
+singular value which a 'Feature' contains. The @d@ here can be almost anything
+and need not be a scalar, for example, all the following are valid types for @d@:
+
+* 'Int'
+* 'Text'
+* @(Int, Maybe Text)@ 
+* @[Double]@
+
+The @name@ type a bit special: it does not appear on the right-hand side of the `=`. 
+In type-theory parlance, @name@ is a [phantom type](https://wiki.haskell.org/Phantom_type). 
+We'll see in a bit how this can be useful. For now, think of the @name@ as the
+name of a variable as you would in most programming languages. To summarize, 
+a `Feature` 's type constructor takes two arguments (@name@ and @d@), but its
+*value* constructor (@MkFeature@) takes a single value of type @FeatureData d@.
+
+Values of the 'FeatureData' type contain the data we're ultimately interested 
+in analyzing or passing along to downstream applications. However, a 'FeatureData'
+value does not simply contain data of type @d@. The type allows for the possibility
+of missingness, failures, or errors by using the 'Data.Either.Either' type. A value
+of a 'FeatureData', then, is either a @'Data.Either.Left' 'MissingReason'@ or a
+@'Data.Either.Right' d@.
+
+The use of @Either@ has important implications when defining Features, as we will see. 
+Now that we know the internals of a 'Feature', how do we create 'Feature' s? There
+are two ways to create features: (1) 'pure'ly lifting data into a 'Feature' or
+(2) writing a 'Definition': a function that 'define's a 'Feature' based on other 
+'Feature's. 
+
+The first method is a way to get data directly into a 'Feature'. Fhe following
+function takes a list of 'Events' and makes a 'Feature' of them:
+
+@
+allEvents :: [Event Day] -> Feature "allEvents" [Event Day]
+allEvents = pure
+@
+
+The 'pure' lifting is generally used to lift a subject's input data into a 'Feature',
+so that other features can be defined from a subject's data. 'Feature' s are
+derived from other 'Feature's by the 'Definition' type. Specifically, 
+'Definition' is a type which contains a function which maps 'Feature' inputs
+to a 'Feature' output, for example:
+
+@
+myDef :: Definition (Feature "a" Int -> Feature "b" Bool)
+myDef = define (\x -> if x > 0 then True else False)
+@
+
+A 'Definition' is created by the 'define' (or 'defineA') function. One may ask
+why 'define' is necessary, and we don't directly define the function 
+(@Feature "a" Int -> Feature "b" Bool@) directly. What may not be obvious in 
+the above, is that @x@ is type @Int@ not @Feature "a" Int@ and the return type 
+is @Bool@ not @Feature "b" Bool@. The 'define' function and 'Definition' type
+do the magic of lifting these types to the 'Feature' level. To see this, 
+in the following, @myDef2@ is equivalent to @myDef@: 
+
+@
+intToBool :: Int -> Bool
+intToBool x = if x > 0 then True else False)
+
+myDef2 :: Definition (Feature "a" Int -> Feature "b" Bool)
+myDef2 = define intToBoo
+@
+
+The 'define' function, then, let's us focus on the *logic* of our 'Feature's 
+without needing to worry handling the error cases. If we were to write a function
+with signature @Feature "a" Int -> Feature "b" Bool@ directly, it would look
+something like:
+
+@
+myFeat :: Feature "a" Int -> Feature "b" Bool
+myFeat (MkFeature (MkFeatureData (Left r))) = MkFeature (MkFeatureData (Left r))
+myFeat (MkFeature (MkFeatureData (Right x))) = MkFeature (MkFeatureData (Right $ intToBool x))
+@
+
+One would need to pattern match all the possible types of inputs, which gets
+more complicated as the number of inputs increases. As an aside, since @Feature@s are
+[Functors]( https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-Functor.html),
+one could instead write:
+
+@
+myFeat :: Feature "a" Int -> Feature "b" Bool
+myFeat = fmap intToBool
+@
+
+This would require understanding how Functors and similar structures are used.
+The 'define' and 'defineA' functions provide a common interface to these structures
+without needing to understand the details.
+
+== Evaluating Definitions
+
+To evaluate a 'Definition', we use the 'eval' function. Consider the following example.
+The input data is a list of 'Int's if the list is empty ('null'), this is considered
+an error in @feat1@. If the list has more than 3 elements, then in @feat2@, 
+the sum is computed; otherwise @0@ is returned.
+
+@
+featInts :: [Int] -> Feature "someInts" [Int]
+featInts = pure
+
+feat1 :: Definition (Feature "someInts" [Int] -> Feature "hasMoreThan3" Bool)
+feat1 = defineA
+  (\ints -> if null ints then makeFeature (missingBecause $ Other "no data")
+           else makeFeature $ featureDataR (length ints > 3))
+
+feat2 :: Definition (
+      Feature "hasMoreThan3" Bool
+  -> Feature "someInts" [Int]
+  -> Feature "sum" Int)
+feat2 = define (\b ints -> if b then sum ints else 0)
+
+ex0 = featInts []
+ex0a = eval feat1 ex0 -- MkFeature (MkFeatureData (Left (Other "no data")))
+ex0b = eval feat2 (ex0a, ex0) -- MkFeature (MkFeatureData (Left (Other "no data")))
+
+ex1 = featInts [3, 8]
+ex1a = eval feat1 ex1 -- MkFeature (MkFeatureData (Right False))
+ex1b = eval feat2 (ex1a, ex1) -- MkFeature (MkFeatureData (Right 0))
+
+ex2 = featInts [1..4]
+ex2a = eval feat1 ex2 -- MkFeature (MkFeatureData (Right True))
+ex2b = eval feat2 (ex2a, ex2) -- MkFeature (MkFeatureData (Right 10))
+@
+
+Note the value of @ex0b@. It is a 'Left' because the value of @ex0a@ is a 'Left';
+in other words, errors propogate along 'Feature's. If a given @Feature@'s dependency 
+is a 'Left' then that 'Feature' will also be 'Left'. A @Feature@'s internal
+'Either' structure has important implications for designing 'Feature's and
+performance. Capturing an error in a 'Left' is a way to prevent downstream
+dependencies from needing to be computed.
+
+== Type Safety of Features
+
+In describing the 'Feature' type, the utility of having the name as a type may
+not have been clear. To clarify, consider the following example:
+
+@
+x :: Feature "someInt" Natural
+x = pure 39
+
+y :: Feature "age" Natural
+y = pure 43
+
+f :: Definition (Feature "age" Natural -> Feature "isOld" Bool)
+f = define (>= 39)
+
+fail = eval f x 
+pass = eval f y
+@   
+
+In the example, @fail@ does not compile because @"someInt"@ is not @"age"@, 
+even though both the data type are @Natural@.
+
+-}
diff --git a/src/Hasklepias/Reexports.hs b/src/Hasklepias/Reexports.hs
--- a/src/Hasklepias/Reexports.hs
+++ b/src/Hasklepias/Reexports.hs
@@ -137,16 +137,15 @@
 import safe Data.Text                       ( pack, Text )
 import safe Data.Tuple                      ( fst, snd, uncurry, curry )
 import safe Data.Tuple.Curry                ( curryN, uncurryN )
-import safe IntervalAlgebra
-import safe IntervalAlgebra.IntervalUtilities
-import safe IntervalAlgebra.PairedInterval
 
 import safe Witherable                      ( Filterable(filter), Witherable(..) )
 import safe Flow                            ( (!>), (.>), (<!), (<.), (<|), (|>) )
 import Safe                                 ( headMay, lastMay )
--- import Data.Vector.Fusion.Bundle (scanl1)
--- import Data.Map (fromList)
--- import qualified Data.Map as Set
+
+
+import safe          IntervalAlgebra
+import safe          IntervalAlgebra.IntervalUtilities
+import safe         IntervalAlgebra.PairedInterval
 
 setFromList :: (Ord a) =>  [a] -> Set.Set a
 setFromList = Set.fromList
diff --git a/src/Hasklepias/Templates/Features.hs b/src/Hasklepias/Templates/Features.hs
--- a/src/Hasklepias/Templates/Features.hs
+++ b/src/Hasklepias/Templates/Features.hs
@@ -26,4 +26,7 @@
     , buildNofXWithGap
     , buildNofXWithGapBool
     , buildNofXWithGapBinary
-    , buildNofUniqueBegins)
+    , buildNofUniqueBegins
+    , buildNofXOrNofYWithGap
+    , buildNofXOrNofYWithGapBool
+    , buildNofXOrNofYWithGapBinary )
diff --git a/src/Hasklepias/Templates/Features/Enrollment.hs b/src/Hasklepias/Templates/Features/Enrollment.hs
--- a/src/Hasklepias/Templates/Features/Enrollment.hs
+++ b/src/Hasklepias/Templates/Features/Enrollment.hs
@@ -104,7 +104,7 @@
   "Tests of isEnrolled template"
   (fmap
     (\x -> testCase (getTestName x)
-                    (makeAssertion x (buildIsEnrolled (getBuilderArgs x)))
+                    (makeAssertion x (uncurryN $ eval (buildIsEnrolled (getBuilderArgs x))))
     )
     buildIsEnrolledTestCases
   )
@@ -258,7 +258,7 @@
       (getTestName x)
       (makeAssertion
         x
-        (buildContinuousEnrollment (makeBaselineFromIndex 10) isEnrollmentEvent 3)
+        (uncurryN $ eval (buildContinuousEnrollment (makeBaselineFromIndex 10) isEnrollmentEvent 3))
       )
     )
     buildContinuousEnrollmentTestCases
diff --git a/src/Hasklepias/Templates/Features/NsatisfyP.hs b/src/Hasklepias/Templates/Features/NsatisfyP.hs
--- a/src/Hasklepias/Templates/Features/NsatisfyP.hs
+++ b/src/Hasklepias/Templates/Features/NsatisfyP.hs
@@ -23,6 +23,9 @@
   , buildNofXWithGapBool
   , buildNofXWithGapBinary
   , buildNofUniqueBegins
+  , buildNofXOrNofYWithGap
+  , buildNofXOrNofYWithGapBool
+  , buildNofXOrNofYWithGapBinary
   ) where
 
 import           Cohort
@@ -39,7 +42,11 @@
 -- | All the buildNSatisfyP tests.
 buildNsatisfyPTests :: TestTree
 buildNsatisfyPTests =
-  testGroup "NsatisfyP" [buildNofXTests, buildNofXWithGapTests, buildNofUniqueBeginsTests]
+  testGroup "NsatisfyP" 
+    [ buildNofXTests
+    , buildNofXWithGapTests
+    , buildNofXOrNofYWithGapTests
+    , buildNofUniqueBeginsTests]
 
 {-|
 -}
@@ -60,7 +67,7 @@
        -> Feature eventsName (container0 (Event a))
        -> Feature varName outputType
        )
-buildNofXBase runProcess runPostProcess runCast makeAssessmentInterval relation predicate
+buildNofXBase runPreProcess runProcess runPostProcess makeAssessmentInterval relation predicate
   = define
     (\index ->
       -- filter events to those satisfying both
@@ -68,12 +75,12 @@
       -- AND the given predicate
       filter
           (relation (makeAssessmentInterval index) &&& getPredicate predicate)
+      -- run the preprocessing function
+        .> runPreProcess
       -- run the processing function
         .> runProcess
-      -- run the post processing function
-        .> runPostProcess
-      -- run the casting function
-        .> runCast (makeAssessmentInterval index)
+      -- run the postprocessing function
+        .> runPostProcess (makeAssessmentInterval index)
     )
 
 {-| Do N events relating to the 'AssessmentInterval' in some way the satisfy 
@@ -248,7 +255,7 @@
   (fmap
     (\x -> testCase
       (getTestName x)
-      (makeAssertion x (uncurryN buildNofXBool (getBuilderArgs x)))
+      (makeAssertion x (uncurryN $ eval (uncurryN buildNofXBool (getBuilderArgs x))))
     )
     buildNofXTestCases
   )
@@ -472,15 +479,234 @@
   (fmap
     (\x -> testCase
       (getTestName x)
-      (makeAssertion x (uncurryN buildNofXWithGapBool (getBuilderArgs x)))
+      (makeAssertion x (uncurryN $ eval $ uncurryN buildNofXWithGapBool (getBuilderArgs x)))
     )
     buildNofXWithGapTestCases
   )
 
+{-|
+Is either 'buildNofX' or 'buildNofXWithGap' satisfied
 
-{-
 -}
+buildNofXOrNofYWithGap
+  :: ( Intervallic i a
+     , IntervalSizeable a b
+     , IntervalCombinable i a
+     , Witherable container
+     )
+  => (outputType -> outputType -> outputType) 
+  -> (Bool -> outputType)
+  -> Natural -- ^ count passed to 'buildNofX'
+  -> Predicate (Event a)
+  -> Natural -- ^ the minimum number of gaps passed to 'buildNofXWithGap'
+  -> b -- ^ the minimum duration of a gap passed to 'buildNofXWithGap'
+  -> (Index i a -> AssessmentInterval a)
+  -> ComparativePredicateOf2 (AssessmentInterval a) (Event a)
+  -> Predicate (Event a)
+  -> Definition
+       (  Feature indexName (Index i a)
+       -> Feature eventsName (container (Event a))
+       -> Feature varName outputType
+       )
+buildNofXOrNofYWithGap f cast xCount xPred gapCount gapDuration assess intervalPred yPred = 
+  D2C f
+      (buildNofX cast xCount assess intervalPred xPred)
+      (buildNofXWithGap cast gapCount gapDuration assess intervalPred yPred)
 
+-- | 'buildNofXOrNofYWithGap' specialized to return @Bool@. 
+buildNofXOrNofYWithGapBool
+  :: ( Intervallic i a
+     , IntervalSizeable a b
+     , IntervalCombinable i a
+     , Witherable container
+     )
+  => Natural -- ^ count passed to 'buildNofX'
+  -> Predicate (Event a)
+  -> Natural -- ^ the minimum number of gaps passed to 'buildNofXWithGap'
+  -> b -- ^ the minimum duration of a gap passed to 'buildNofXWithGap'
+  -> (Index i a -> AssessmentInterval a)
+  -> ComparativePredicateOf2 (AssessmentInterval a) (Event a)
+  -> Predicate (Event a)
+  -> Definition
+       (  Feature indexName (Index i a)
+       -> Feature eventsName (container (Event a))
+       -> Feature varName Bool
+       )
+buildNofXOrNofYWithGapBool = buildNofXOrNofYWithGap (||) id
+
+-- | 'buildNofXOrNofYWithGap' specialized to return @Binary@. 
+buildNofXOrNofYWithGapBinary
+  :: ( Intervallic i a
+     , IntervalSizeable a b
+     , IntervalCombinable i a
+     , Witherable container
+     )
+  => Natural -- ^ count passed to 'buildNofX'
+  -> Predicate (Event a)
+  -> Natural -- ^ the minimum number of gaps passed to 'buildNofXWithGap'
+  -> b -- ^ the minimum duration of a gap passed to 'buildNofXWithGap'
+  -> (Index i a -> AssessmentInterval a)
+  -> ComparativePredicateOf2 (AssessmentInterval a) (Event a)
+  -> Predicate (Event a)
+  -> Definition
+       (  Feature indexName (Index i a)
+       -> Feature eventsName (container (Event a))
+       -> Feature varName Binary
+       )
+buildNofXOrNofYWithGapBinary = 
+  buildNofXOrNofYWithGap (\x y -> fromBool $ (||) (toBool x)  (toBool y) ) fromBool
+
+type NofXOrNofYWithGapArgs
+  = ( Natural
+    , Predicate (Event Int)
+    , Natural
+    , Int
+    , Index Interval Int -> AssessmentInterval Int
+    , ComparativePredicateOf2 (AssessmentInterval Int) (Event Int)
+    , Predicate (Event Int)
+    )
+
+type NofXOrNofYWithGapTestCase
+  = TestCase
+      (F "index" (Index Interval Int), F "events" [Event Int])
+      Bool
+      NofXOrNofYWithGapArgs
+
+buildNofXOrNofYWithGapTestCases :: [NofXOrNofYWithGapTestCase]
+buildNofXOrNofYWithGapTestCases =
+  [ f "True if looking for no events and there are no events"
+      (0, containsConcepts ["A"], 0, 3, makeBaselineFromIndex 10, concur, isEnrollmentEvent)
+      (10, 11)
+      []
+      True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+                              
+        |--------------|
+      -}
+  , f
+    "True if looking for (at least) no events and there are events satisfying gap condition"
+    (0, containsConcepts ["A"], 0, 3, makeBaselineFromIndex 10, concur, isEnrollmentEvent)
+    (10, 11)
+    [g (1, 2), g (8, 9)]
+    True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+         -       -            <- Enrollment
+        |--------------|
+      -}
+  , f "False if no X or Y events and looking for 1 X or 1 Y gap"
+      (1, containsConcepts ["A"], 1, 3, makeBaselineFromIndex 10, concur, isEnrollmentEvent)
+      (10, 11)
+      []
+      False
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+                              <- Enrollment
+        |--------------|
+      -}
+  , f "False if a no X and Y single event and looking for gap"
+      (1, containsConcepts ["A"], 1, 3, makeBaselineFromIndex 10, concur, isEnrollmentEvent)
+      (10, 11)
+      [g (8, 9)]
+      False
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+                 -            <- Enrollment
+        |--------------|
+      -}
+  , f "False if no X 1 gap but not satisfying gap condition"
+      (1, containsConcepts ["A"], 1, 3, makeBaselineFromIndex 10, concur, isEnrollmentEvent)
+      (10, 11)
+      [g (6, 7), g (8, 9)]
+      False
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+               - -            <- Enrollment
+        |--------------|
+      -}
+  , f "True if 1 gap satisfy gap condition"
+      (1, containsConcepts ["D"], 1, 3, makeBaselineFromIndex 10, concur, containsConcepts ["A"])
+      (10, 11)
+      [h ["C", "A"] (2, 3), h ["A", "B"] (8, 9)]
+      True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+          -                   <- ["C", "A"]
+                 -            <- ["A", "B"] 
+        |--------------|
+      -}
+    , f "True if 1 X event"
+      (1, containsConcepts ["C"], 1, 3, makeBaselineFromIndex 10, concur, containsConcepts ["A"])
+      (10, 11)
+      [h ["C", "A"] (2, 3)]
+      True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+          -                   <- ["C", "A"]
+        |--------------|
+      -}
+  , f "True if 1 gap satisfy gap condition "
+      (2, containsConcepts ["D"], 1, 3, makeBaselineFromIndex 10, concur, containsConcepts ["A"])
+      (10, 11)
+      [h ["C", "A"] (2, 3), h ["D", "E"] (5, 6), h ["A", "B"] (8, 9)]
+      True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+          -                   <- ["C", "A"]
+              -               <- ["D", "E"]
+                 -            <- ["A", "B"] 
+        |--------------|
+      -}
+  , f "False if only one X and if no gap satisfy gap condition "
+      (2, containsConcepts ["D"], 1, 3, makeBaselineFromIndex 10, concur, containsConcepts ["A"])
+      (10, 11)
+      [h ["C", "A"] (2, 3), h ["D", "A"] (4, 5)]
+      False
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+          -                   <- ["C", "A"]
+            -                 <- ["D", "A"]
+        |--------------|
+      -}
+  , f "True if two X and if no gap satisfy gap condition "
+      (2, containsConcepts ["D"], 1, 3, makeBaselineFromIndex 10, concur, containsConcepts ["A"])
+      (10, 11)
+      [h ["D", "A"] (2, 3), h ["D", "A"] (4, 5)]
+      True
+      {-
+                   -          <- Index
+         ----------           <- Baseline
+          -                   <- ["D", "A"]
+            -                 <- ["D", "A"]
+        |--------------|
+      -}
+
+  ] where
+  f = makeTestInputs
+  g = makeEnrollmentEvent
+  h = makeEventWithConcepts
+
+buildNofXOrNofYWithGapTests :: TestTree
+buildNofXOrNofYWithGapTests = testGroup
+  "Tests of NofXOrNofYWithGap template"
+  (fmap
+    (\x -> testCase
+      (getTestName x)
+      (makeAssertion x (uncurryN $ eval $ uncurryN buildNofXOrNofYWithGapBool (getBuilderArgs x)))
+    )
+    buildNofXOrNofYWithGapTestCases
+  )
+
 {-| Do N events relating to the 'AssessmentInterval' in some way the satisfy 
     the given predicate? 
 -}
@@ -579,7 +805,7 @@
   (fmap
     (\x -> testCase
       (getTestName x)
-      (makeAssertion x (uncurryN buildNofUniqueBegins (getBuilderArgs x)))
+      (makeAssertion x (uncurryN $ eval $ uncurryN buildNofUniqueBegins (getBuilderArgs x)))
     )
     buildNofUniqueBeginsTestCases
   )
diff --git a/src/Hasklepias/Templates/TestUtilities.hs b/src/Hasklepias/Templates/TestUtilities.hs
--- a/src/Hasklepias/Templates/TestUtilities.hs
+++ b/src/Hasklepias/Templates/TestUtilities.hs
@@ -38,7 +38,7 @@
 import Features.Compose                 ( Feature
                                         , Definition(..)
                                         , Define(..)
-                                        , Eval(..) )
+                                        , eval )
 import Hasklepias.Misc
 
 import IntervalAlgebra
@@ -54,14 +54,29 @@
   } deriving (Eq, Show)
 
 
-evalTestCase :: (Eval def defArgs return) =>
+-- evalTestCase :: 
+--   TestCase defArgs b builderArgs
+--   -> (def -> return)
+--   -- -> Definition def
+--   -> ( return, Feature "result" b )
+-- evalTestCase (MkTestCase buildArgs _ inputs truth) def = ( eval def inputs, truth )
+
+evalTestCase :: 
   TestCase defArgs b builderArgs
-  -> Definition def
+  -> (defArgs -> return)
+  -- -> Definition def
   -> ( return, Feature "result" b )
-evalTestCase (MkTestCase buildArgs _ inputs truth) def = ( eval def inputs, truth )
+evalTestCase (MkTestCase buildArgs _ inputs truth) def = ( def inputs, truth )
 
-makeAssertion :: (Eq b, Show b, Eval def defArgs (Feature "result" b)) =>
-  TestCase defArgs b  builderArgs -> Definition def -> Assertion
+-- makeAssertion :: (Eq b, Show b) =>
+--   TestCase defArgs b  builderArgs -> Definition def -> Assertion
+-- makeAssertion x def = uncurry (@?=) (evalTestCase x def)
+
+-- z :: Definition d -> (b -> a)
+-- z def = (eval def)
+
+makeAssertion :: (Eq b, Show b) =>
+  TestCase defArgs b builderArgs ->  (defArgs -> Feature "result" b) -> Assertion
 makeAssertion x def = uncurry (@?=) (evalTestCase x def)
 
 readIntervalSafe :: (Integral b, IntervalSizeable a b) => (a, a) -> Interval a
diff --git a/test/FeaturesSpec.hs b/test/FeaturesSpec.hs
--- a/test/FeaturesSpec.hs
+++ b/test/FeaturesSpec.hs
@@ -14,30 +14,10 @@
 
 
 -----------------------------------
--- example :: Feature "test" ()
--- example = MkFeature $ pure ()
 
--- d0 :: Definition (FeatureData Int)
--- d0 = define 5
-
-d1 :: Definition (FeatureData Int -> FeatureData Int)
-d1 = defineA
-  (\x -> if x < 0 then missingBecause $ Other "at least 1 < 0" else pure (x + 1)
-  )
-
-d2 :: Definition (FeatureData Int -> FeatureData Int)
-d2 = define (* 2)
-
-
-d3 :: Definition (FeatureData Int -> FeatureData Int -> FeatureData Int)
-d3 = define (+)
-
 f1 :: Int -> Int
 f1 = (+ 2)
 
-f1D :: Definition (FeatureData Int -> FeatureData Int)
-f1D = define f1
-
 f1F :: Definition (Feature "someInt" Int -> Feature "anotherInt" Int)
 f1F = define f1
 
@@ -45,9 +25,6 @@
 f2 True  = pure 1
 f2 False = missingBecause $ Other "test"
 
-f2D :: Definition (FeatureData Bool -> FeatureData Int)
-f2D = defineA f2
-
 f2' :: Bool -> Feature "someInt" Int
 f2' True  = pure 1
 f2' False = makeFeature $ missingBecause $ Other "test"
@@ -60,9 +37,6 @@
 f3 False 9 = "that"
 f3 _     _ = "otherwise"
 
-f3D :: Definition (FeatureData Bool -> FeatureData Int -> FeatureData String)
-f3D = define f3
-
 f3F
   :: Definition
        (  Feature "myBool" Bool
@@ -73,7 +47,7 @@
 
 
 featInts :: [Int] -> Feature "someInts" [Int]
-featInts = pure
+featInts = pure 
 
 feat1 :: Definition (Feature "someInts" [Int] -> Feature "hasMoreThan3" Bool)
 feat1 = defineA
@@ -92,81 +66,35 @@
 
 ex0 = featInts []
 ex0a = eval feat1 ex0 -- MkFeature (MkFeatureData (Left (Other "no data")))
-ex0b = eval feat2 (ex0a, ex0) -- MkFeature (MkFeatureData (Left (Other "no data")))
+ex0b = eval feat2 ex0a ex0 -- MkFeature (MkFeatureData (Left (Other "no data")))
 
 ex1 = featInts [3, 8]
 ex1a = eval feat1 ex1 -- MkFeature (MkFeatureData (Right False))
-ex1b = eval feat2 (ex1a, ex1) -- MkFeature (MkFeatureData (Right 0))
+ex1b = eval feat2 ex1a ex1 -- MkFeature (MkFeatureData (Right 0))
 
 ex2 = featInts [1 .. 4]
 ex2a = eval feat1 ex2 -- MkFeature (MkFeatureData (Right True))
-ex2b = eval feat2 (ex2a, ex2) -- MkFeature (MkFeatureData (Right 10))
+ex2b = eval feat2 ex2a ex2 -- MkFeature (MkFeatureData (Right 10))
 
 spec :: Spec
 spec = do
 
-  describe "checking d1" $ do
-    it "eval of d1 on d0" $ eval d1 (pure 5) `shouldBe` featureDataR 6
-    it "eval of d1 returns correct value"
-      $          eval d1 (featureDataR 5)
-      `shouldBe` featureDataR 6
-    it "d1 returns correct error"
-      $          eval d1 (featureDataR (-1))
-      `shouldBe` missingBecause (Other "at least 1 < 0")
-
-  describe "checking d2" $ do
-    it "eval of d2 returns correct value"
-      $          eval d2 (featureDataR 5)
-      `shouldBe` featureDataR 10
-    it "eval of d2 returns correct value"
-      $          eval d2 (featureDataR 5)
-      `shouldBe` featureDataR 10
-    it "d2 returns correct error"
-      $          eval d2 (featureDataL (Other "at least 1 < 0"))
-      `shouldBe` featureDataL (Other "at least 1 < 0")
-
-  describe "checking d3" $ do
-    it "eval of d2 returns correct values"
-      $          eval d3 (featureDataR 5, featureDataR 6)
-      `shouldBe` featureDataR 11
-    it "eval of d3 returns correct values"
-      $          eval d3 (featureDataR 5, featureDataR 5)
-      `shouldBe` featureDataR 10
-    it "d3 returns correct error"
-      $          eval d3 (featureDataL (Other "at least 1 < 0"), featureDataR 6)
-      `shouldBe` featureDataL (Other "at least 1 < 0")
-    it "d3 returns correct error"
-      $          eval d3 (featureDataR 6, featureDataL (Other "at least 1 < 0"))
-      `shouldBe` featureDataL (Other "at least 1 < 0")
-
   describe "checking f1" $ do
     it "eval of f1F on d0 returns correct value"
       $          eval f1F (pure 5)
       `shouldBe` pure 7
 
   describe "checking f2" $ do
-    it "eval of f2D on d0 returns correct value"
-      $          eval f2D (pure True)
-      `shouldBe` pure 1
     it "eval of f1F on d0 returns correct value"
-      $          eval f2D (pure False)
-      `shouldBe` missingBecause (Other "test")
-    it "eval of f1F on d0 returns correct value"
       $          eval f2F (pure False)
       `shouldBe` makeFeature (missingBecause (Other "test"))
 
   describe "checking f3" $ do
-    it "eval of f3D returns correct value"
-      $          eval f3D (pure True, pure 1)
-      `shouldBe` pure "this"
-    it "eval of f3D returns correct value"
-      $          eval f3D (pure True, pure 9)
-      `shouldBe` pure "otherwise"
     it "eval of f3F returns correct value"
-      $          eval f3F (pure True, eval f2F (pure False))
+      $          eval f3F (pure True) (eval f2F (pure False))
       `shouldBe` makeFeature (missingBecause (Other "test"))
     it "eval of f3F  returns correct value"
-      $          eval f3F (pure True, eval f2F (pure True))
+      $          eval f3F (pure True) (eval f2F (pure True))
       `shouldBe` pure "this"
 
   describe "checking ex0-2" $ do
